Check for errors when reading and decompressing datafile data

Check if datafile data cannot be read entirely (according to the data size specified in the header) and check for decompression errors. In case of errors, let `GetData` return `nullptr` and `GetDataSize` return `0` for the respective index.

Internally the decompressed size is set to `-1` for data which failed to load, so loading of those data will not be attempted again because it would only fail again and can cause additional log messages.
This commit is contained in:
Robert Müller 2023-07-26 16:31:21 +02:00
parent fd13ae5fd5
commit 7069d74beb
2 changed files with 55 additions and 27 deletions

View file

@ -295,7 +295,10 @@ int CDataFileReader::GetDataSize(int Index) const
return GetFileDataSize(Index); return GetFileDataSize(Index);
} }
} }
return m_pDataFile->m_pDataSizes[Index]; const int Size = m_pDataFile->m_pDataSizes[Index];
if(Size < 0)
return 0; // summarize all errors as zero size
return Size;
} }
void *CDataFileReader::GetDataImpl(int Index, int Swap) void *CDataFileReader::GetDataImpl(int Index, int Swap)
@ -311,45 +314,73 @@ void *CDataFileReader::GetDataImpl(int Index, int Swap)
// load it if needed // load it if needed
if(!m_pDataFile->m_ppDataPtrs[Index]) if(!m_pDataFile->m_ppDataPtrs[Index])
{ {
// don't try to load again if it previously failed
if(m_pDataFile->m_pDataSizes[Index] < 0)
return nullptr;
// fetch the data size // fetch the data size
int DataSize = GetFileDataSize(Index); unsigned DataSize = GetFileDataSize(Index);
#if defined(CONF_ARCH_ENDIAN_BIG) #if defined(CONF_ARCH_ENDIAN_BIG)
int SwapSize = DataSize; unsigned SwapSize = DataSize;
#endif #endif
if(m_pDataFile->m_Header.m_Version == 4) if(m_pDataFile->m_Header.m_Version == 4)
{ {
// v4 has compressed data // v4 has compressed data
void *pTemp = malloc(DataSize); const unsigned OriginalUncompressedSize = m_pDataFile->m_Info.m_pDataSizes[Index];
unsigned long UncompressedSize = m_pDataFile->m_Info.m_pDataSizes[Index]; unsigned long UncompressedSize = OriginalUncompressedSize;
unsigned long s;
log_trace("datafile", "loading data index=%d size=%d uncompressed=%lu", Index, DataSize, UncompressedSize); log_trace("datafile", "loading data. index=%d size=%u uncompressed=%u", Index, DataSize, OriginalUncompressedSize);
m_pDataFile->m_ppDataPtrs[Index] = (char *)malloc(UncompressedSize);
m_pDataFile->m_pDataSizes[Index] = UncompressedSize;
// read the compressed data // read the compressed data
io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START); void *pCompressedData = malloc(DataSize);
io_read(m_pDataFile->m_File, pTemp, DataSize); unsigned ActualDataSize = 0;
if(io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START) == 0)
ActualDataSize = io_read(m_pDataFile->m_File, pCompressedData, DataSize);
if(DataSize != ActualDataSize)
{
log_error("datafile", "truncation error, could not read all data. index=%d wanted=%u got=%u", Index, DataSize, ActualDataSize);
free(pCompressedData);
m_pDataFile->m_ppDataPtrs[Index] = nullptr;
m_pDataFile->m_pDataSizes[Index] = -1;
return nullptr;
}
// decompress the data
m_pDataFile->m_ppDataPtrs[Index] = (char *)malloc(UncompressedSize);
m_pDataFile->m_pDataSizes[Index] = UncompressedSize;
const int Result = uncompress((Bytef *)m_pDataFile->m_ppDataPtrs[Index], &UncompressedSize, (Bytef *)pCompressedData, DataSize);
free(pCompressedData);
if(Result != Z_OK || UncompressedSize != OriginalUncompressedSize)
{
log_error("datafile", "uncompress error. result=%d wanted=%u got=%lu", Result, OriginalUncompressedSize, UncompressedSize);
free(m_pDataFile->m_ppDataPtrs[Index]);
m_pDataFile->m_ppDataPtrs[Index] = nullptr;
m_pDataFile->m_pDataSizes[Index] = -1;
return nullptr;
}
// decompress the data, TODO: check for errors
s = UncompressedSize;
uncompress((Bytef *)m_pDataFile->m_ppDataPtrs[Index], &s, (Bytef *)pTemp, DataSize);
#if defined(CONF_ARCH_ENDIAN_BIG) #if defined(CONF_ARCH_ENDIAN_BIG)
SwapSize = s; SwapSize = UncompressedSize;
#endif #endif
// clean up the temporary buffers
free(pTemp);
} }
else else
{ {
// load the data // load the data
log_trace("datafile", "loading data index=%d size=%d", Index, DataSize); log_trace("datafile", "loading data. index=%d size=%d", Index, DataSize);
m_pDataFile->m_ppDataPtrs[Index] = (char *)malloc(DataSize); m_pDataFile->m_ppDataPtrs[Index] = static_cast<char *>(malloc(DataSize));
m_pDataFile->m_pDataSizes[Index] = DataSize; m_pDataFile->m_pDataSizes[Index] = DataSize;
io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START); unsigned ActualDataSize = 0;
io_read(m_pDataFile->m_File, m_pDataFile->m_ppDataPtrs[Index], DataSize); if(io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START) == 0)
ActualDataSize = io_read(m_pDataFile->m_File, m_pDataFile->m_ppDataPtrs[Index], DataSize);
if(DataSize != ActualDataSize)
{
log_error("datafile", "truncation error, could not read all data. index=%d wanted=%u got=%u", Index, DataSize, ActualDataSize);
free(m_pDataFile->m_ppDataPtrs[Index]);
m_pDataFile->m_ppDataPtrs[Index] = nullptr;
m_pDataFile->m_pDataSizes[Index] = -1;
return nullptr;
}
} }
#if defined(CONF_ARCH_ENDIAN_BIG) #if defined(CONF_ARCH_ENDIAN_BIG)
@ -373,10 +404,7 @@ void *CDataFileReader::GetDataSwapped(int Index)
void CDataFileReader::ReplaceData(int Index, char *pData, size_t Size) void CDataFileReader::ReplaceData(int Index, char *pData, size_t Size)
{ {
// make sure the data has been loaded free(m_pDataFile->m_ppDataPtrs[Index]);
GetDataImpl(Index, 0);
UnloadData(Index);
m_pDataFile->m_ppDataPtrs[Index] = pData; m_pDataFile->m_ppDataPtrs[Index] = pData;
m_pDataFile->m_pDataSizes[Index] = Size; m_pDataFile->m_pDataSizes[Index] = Size;
} }

View file

@ -38,7 +38,7 @@ public:
void *GetData(int Index); void *GetData(int Index);
void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved
int GetDataSize(int Index) const; int GetDataSize(int Index) const;
void ReplaceData(int Index, char *pData, size_t Size); void ReplaceData(int Index, char *pData, size_t Size); // memory for data must have been allocated with malloc
void UnloadData(int Index); void UnloadData(int Index);
int NumData() const; int NumData() const;