Skip to content

Commit

Permalink
Merge pull request #295 from ladislav-zezula/LZ_ReadError_Volcanis
Browse files Browse the repository at this point in the history
Lz read error volcanis
  • Loading branch information
ladislav-zezula authored Jun 7, 2023
2 parents 51ba11c + fb11167 commit 28c9b4b
Show file tree
Hide file tree
Showing 11 changed files with 602 additions and 2,797 deletions.
100 changes: 29 additions & 71 deletions src/SBaseFileTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,59 +312,38 @@ static ULONGLONG DetermineArchiveSize_V1(
return (EndOfMpq - MpqOffset);
}

static ULONGLONG DetermineArchiveSize_V2(
TMPQHeader * pHeader,
ULONGLONG MpqOffset,
ULONGLONG FileSize)
static ULONGLONG DetermineBlockTableSize_V2(TMPQHeader * pHeader, ULONGLONG MpqHeaderPos, ULONGLONG FileSize)
{
ULONGLONG TableOffset;
ULONGLONG TableSize;
ULONGLONG MaxOffset = 0;

// This could only be called for MPQs version 2.0
assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_2);
ULONGLONG BlockTablePos = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
ULONGLONG ArchiveSize = FileSize - MpqHeaderPos;

// Is the hash table end greater than max offset?
TableOffset = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
TableSize = (ULONGLONG)pHeader->dwHashTableSize * sizeof(TMPQHash);
if((TableOffset + TableSize) > MaxOffset)
MaxOffset = TableOffset + TableSize;

// Is the block table end greater than max offset?
TableOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
TableSize = (ULONGLONG)pHeader->dwBlockTableSize * sizeof(TMPQBlock);
if((TableOffset + TableSize) > MaxOffset)
MaxOffset = TableOffset + TableSize;

// Is the hi-block table end greater than max offset?
if((TableOffset = pHeader->HiBlockTablePos64) != 0)
// If there is a hi-block table and it is beyond the block table,
// we can determine the block table size from it
if(pHeader->HiBlockTablePos64 != 0)
{
TableSize = (ULONGLONG)pHeader->dwBlockTableSize * sizeof(USHORT);
if((TableOffset + TableSize) > MaxOffset)
MaxOffset = TableOffset + TableSize;
if(pHeader->HiBlockTablePos64 > BlockTablePos)
{
return (pHeader->HiBlockTablePos64 - BlockTablePos);
}
}

// Cut to the file size
if(MaxOffset > (FileSize - MpqOffset))
MaxOffset = FileSize - MpqOffset;
return MaxOffset;
}

static ULONGLONG DetermineBlockTableSize_V2(TMPQHeader * pHeader)
{
ULONGLONG BlockTableOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
ULONGLONG TableOffset;
ULONGLONG TableSize = (pHeader->ArchiveSize64 - BlockTableOffset);

// Subtract the offset of the hi-block table
if((TableOffset = pHeader->HiBlockTablePos64) != 0)
// If we have valid archive size, we can determine the block table size from the archive size
else
{
if(TableOffset > BlockTableOffset)
if((BlockTablePos >> 0x20) == 0 && (ArchiveSize >> 0x20) == 0)
{
TableSize = TableOffset - BlockTableOffset;
DWORD dwBlockTablePos32 = (DWORD)(BlockTablePos);
DWORD dwArchiveSize32 = (DWORD)(ArchiveSize);

if(pHeader->dwArchiveSize == dwArchiveSize32)
{
return (dwArchiveSize32 - dwBlockTablePos32);
}
}
}
return TableSize;

// Default is the block table size from MPQ header
return (ULONGLONG)(pHeader->dwBlockTableSize) * sizeof(TMPQBlock);
}

static ULONGLONG DetermineArchiveSize_V4(
Expand Down Expand Up @@ -566,35 +545,14 @@ DWORD ConvertMpqHeaderToFormat4(
// We require the block table to follow hash table
if(BlockTablePos64 >= HashTablePos64)
{
// HashTableSize64 may be less than TblSize * sizeof(TMPQHash).
// That means that the hash table is compressed.
// Determine whether the hash table is compressed. This can be detected
// by subtracting hash table position from the block table position.
pHeader->HashTableSize64 = BlockTablePos64 - HashTablePos64;

// Calculate the compressed block table size
if(pHeader->HiBlockTablePos64 != 0)
{
// BlockTableSize64 may be less than TblSize * sizeof(TMPQBlock).
// That means that the block table is compressed.
pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - BlockTablePos64;
assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock)));

// Determine real archive size
pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize);

// Calculate the size of the hi-block table
pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64;
assert(pHeader->HiBlockTableSize64 == (pHeader->dwBlockTableSize * sizeof(USHORT)));
}
else
{
// Determine real archive size
pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize);
pHeader->dwArchiveSize = (DWORD)(pHeader->ArchiveSize64);

// Calculate size of the block table
pHeader->BlockTableSize64 = DetermineBlockTableSize_V2(pHeader);
assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock)));
}
// Also, block table may be compressed. We check whether the HiBlockTable is there.
// If not, we try to use the archive size. Note that ArchiveSize may have
// an arbitrary value, because it is not tested by Blizzard games anymore
pHeader->BlockTableSize64 = DetermineBlockTableSize_V2(pHeader, ByteOffset, FileSize);
}
else
{
Expand Down
69 changes: 45 additions & 24 deletions src/SCompression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,16 @@ int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuff
/* */
/*****************************************************************************/

// Decompression table specific for Starcraft I BETA
// WAVE files are compressed by different ADPCM compression
static TDecompressTable dcmp_table_sc_beta[] =
{
{MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
{MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
{0x10, Decompress_ADPCM1_sc1b}, // IMA ADPCM mono decompression
{0x20, Decompress_ADPCM2_sc1b}, // IMA ADPCM stereo decompression
};

// This table contains decompress functions which can be applied to
// uncompressed data. The compression mask is stored in the first byte
// of compressed data
Expand All @@ -909,25 +919,22 @@ static TDecompressTable dcmp_table[] =
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
};

// Decompression table specific for Starcraft I BETA
// WAVE files are compressed by different ADPCM compression
static TDecompressTable dcmp_table_sc1b[] =
{
{MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
{MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
{0x10, Decompress_ADPCM1_sc1b}, // IMA ADPCM mono decompression
{0x20, Decompress_ADPCM2_sc1b}, // IMA ADPCM stereo decompression
};

static int SCompDecompressInternal(TDecompressTable * table, size_t table_length, void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
static int SCompDecompressInternal(
TDecompressTable * table,
size_t table_length,
void * pvOutBuffer,
int * pcbOutBuffer,
void * pvInBuffer,
int cbInBuffer,
unsigned uValidMask = 0xFF)
{
unsigned char * pbWorkBuffer = NULL;
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
unsigned char * pbOutput = (unsigned char *)pvOutBuffer;
unsigned char * pbInput;
unsigned uCompressionMask; // Decompressions applied to the data
unsigned uCompressionCopy; // Decompressions applied to the data
unsigned uCompressionMask1; // Decompressions applied to the data
unsigned uCompressionMask2; // Decompressions applied to the data
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
int cbInLength; // Current size of the input buffer
int nCompressCount = 0; // Number of compressions to be applied
Expand All @@ -948,29 +955,30 @@ static int SCompDecompressInternal(TDecompressTable * table, size_t table_length
}

// Get applied compression types and decrement data length
uCompressionMask = uCompressionCopy = (unsigned char)(*pbInBuffer++);
uCompressionMask1 = ((unsigned char)(*pbInBuffer++) & (uValidMask));
uCompressionMask2 = uCompressionMask1;
cbInBuffer--;

// Get current compressed data and length of it
pbInput = pbInBuffer;
cbInLength = cbInBuffer;

// This compression function doesn't support LZMA
assert(uCompressionMask != MPQ_COMPRESSION_LZMA);
assert(uCompressionMask1 != MPQ_COMPRESSION_LZMA);

// Parse the compression mask
for(size_t i = 0; i < table_length; i++)
{
// If the mask agrees, insert the compression function to the array
if(uCompressionMask & table[i].uMask)
if(uCompressionMask1 & table[i].uMask)
{
uCompressionCopy &= ~table[i].uMask;
uCompressionMask2 &= ~table[i].uMask;
nCompressCount++;
}
}

// If at least one of the compressions remaing unknown, return an error
if(nCompressCount == 0 || uCompressionCopy != 0)
if(nCompressCount == 0 || uCompressionMask2 != 0)
{
SetLastError(ERROR_NOT_SUPPORTED);
return 0;
Expand All @@ -994,7 +1002,7 @@ static int SCompDecompressInternal(TDecompressTable * table, size_t table_length
for(size_t i = 0; i < table_length; i++)
{
// Perform the (next) decompression
if(uCompressionMask & table[i].uMask)
if(uCompressionMask1 & table[i].uMask)
{
// Get the correct output buffer
pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
Expand Down Expand Up @@ -1030,11 +1038,6 @@ int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBu
return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
}

int WINAPI SCompDecompress_SC1B(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
{
return SCompDecompressInternal(dcmp_table_sc1b, _countof(dcmp_table_sc1b), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
}

int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
{
DECOMPRESS pfnDecompress1 = NULL;
Expand Down Expand Up @@ -1149,6 +1152,24 @@ int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInB
return nResult;
}

int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
{
// MPQs version 2 use their own fixed list of compression flags.
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
{
return SCompDecompress2(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
}

// Starcraft BETA has specific decompression table.
if(ha->dwFlags & MPQ_FLAG_STARCRAFT_BETA)
{
return SCompDecompressInternal(dcmp_table_sc_beta, _countof(dcmp_table_sc_beta), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
}

// Default: Use the common MPQ v1 decompression routine
return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
}

/*****************************************************************************/
/* */
/* File decompression for MPK archives */
Expand Down
19 changes: 11 additions & 8 deletions src/SFileOpenArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,16 +463,19 @@ bool WINAPI SFileOpenArchive(
if(IsStarcraftBetaArchive(ha->pHeader))
ha->dwFlags |= MPQ_FLAG_STARCRAFT_BETA;

// Remember whether whis is a map for Warcraft III
if(MapType == MapTypeWarcraft3)
// Maps from StarCraft and Warcraft III need special treatment
switch(MapType)
{
ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_W3X;
ha->dwFlags |= MPQ_FLAG_WAR3_MAP;
}
case MapTypeStarcraft:
ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_SCX;
ha->dwFlags |= MPQ_FLAG_STARCRAFT;
break;

// If this is starcraft map, set the flag mask
if(MapType == MapTypeStarcraft)
ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_SCX;
case MapTypeWarcraft3:
ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_W3X;
ha->dwFlags |= MPQ_FLAG_WAR3_MAP;
break;
}

// Set the size of file sector
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
Expand Down
12 changes: 6 additions & 6 deletions src/SFileOpenFileEx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,32 +298,32 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
if(dwErrCode == ERROR_SUCCESS)
{
// If we didn't find the file, try to open it using pseudo file name ("File
if (pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
{
// Check the pseudo-file name ("File00000001.ext")
if ((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true)
if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true)
{
// Get the file entry for the file
if (dwFileIndex < ha->dwFileTableSize)
if(dwFileIndex < ha->dwFileTableSize)
{
pFileEntry = ha->pFileTable + dwFileIndex;
}
}

// Still not found?
if (pFileEntry == NULL)
if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
{
dwErrCode = ERROR_FILE_NOT_FOUND;
}
}

// Perform some checks of invalid files
if (pFileEntry != NULL)
if(pFileEntry != NULL)
{
// MPQ protectors use insanely amount of fake files, often with very high size.
// We won't open any files whose compressed size is bigger than archive size
// If the file is not compressed, its size cannot be bigger than archive size
if ((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
if((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
{
dwErrCode = ERROR_FILE_CORRUPT;
pFileEntry = NULL;
Expand Down
21 changes: 9 additions & 12 deletions src/SFileReadFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
#include "StormLib.h"
#include "StormCommon.h"

//-----------------------------------------------------------------------------
// External references (not public functions)

int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pbInBuffer, int cbInBuffer);

//-----------------------------------------------------------------------------
// Local functions

Expand Down Expand Up @@ -171,18 +176,10 @@ static DWORD ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset,
// Remember the last used compression
hf->dwCompression0 = pbInSector[0];

// Decompress the data
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
{
nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector);
}
else
{
if(ha->dwFlags & MPQ_FLAG_STARCRAFT_BETA)
nResult = SCompDecompress_SC1B(pbOutSector, &cbOutSector, pbInSector, cbInSector);
else
nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector);
}
// Decompress the data. We need to perform MPQ-specific decompression,
// as multiple Blizzard games may have their own decompression tables
// and even decompression methods.
nResult = SCompDecompressX(ha, pbOutSector, &cbOutSector, pbInSector, cbInSector);
}

// Is the file compressed by PKWARE Data Compression Library ?
Expand Down
Loading

0 comments on commit 28c9b4b

Please sign in to comment.