aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SBaseFileTable.cpp100
-rw-r--r--src/SCompression.cpp69
-rw-r--r--src/SFileOpenArchive.cpp19
-rw-r--r--src/SFileOpenFileEx.cpp12
-rw-r--r--src/SFileReadFile.cpp21
-rw-r--r--src/StormLib.h24
6 files changed, 112 insertions, 133 deletions
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp
index 20d7cc4..befe40f 100644
--- a/src/SBaseFileTable.cpp
+++ b/src/SBaseFileTable.cpp
@@ -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(
@@ -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
{
diff --git a/src/SCompression.cpp b/src/SCompression.cpp
index 659c9a8..d6d2e27 100644
--- a/src/SCompression.cpp
+++ b/src/SCompression.cpp
@@ -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
@@ -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
@@ -948,7 +955,8 @@ 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
@@ -956,21 +964,21 @@ static int SCompDecompressInternal(TDecompressTable * table, size_t table_length
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;
@@ -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;
@@ -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;
@@ -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 */
diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp
index 16e2491..f19a6e7 100644
--- a/src/SFileOpenArchive.cpp
+++ b/src/SFileOpenArchive.cpp
@@ -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);
diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp
index a14ffee..b5829b4 100644
--- a/src/SFileOpenFileEx.cpp
+++ b/src/SFileOpenFileEx.cpp
@@ -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;
diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp
index 39f794e..b0698c1 100644
--- a/src/SFileReadFile.cpp
+++ b/src/SFileReadFile.cpp
@@ -14,6 +14,11 @@
#include "StormCommon.h"
//-----------------------------------------------------------------------------
+// External references (not public functions)
+
+int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pbInBuffer, int cbInBuffer);
+
+//-----------------------------------------------------------------------------
// Local functions
// hf - MPQ File handle.
@@ -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 ?
diff --git a/src/StormLib.h b/src/StormLib.h
index 07408b2..4aa51c1 100644
--- a/src/StormLib.h
+++ b/src/StormLib.h
@@ -143,8 +143,8 @@ extern "C" {
//-----------------------------------------------------------------------------
// Defines
-#define STORMLIB_VERSION 0x0918 // Current version of StormLib (9.24)
-#define STORMLIB_VERSION_STRING "9.24" // String version of StormLib version
+#define STORMLIB_VERSION 0x0919 // Current version of StormLib
+#define STORMLIB_VERSION_STRING "9.25" // Current version of StormLib as string
#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
@@ -194,15 +194,16 @@ extern "C" {
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000020 // Checking sector CRC when reading files
#define MPQ_FLAG_SAVING_TABLES 0x00000040 // If set, we are saving MPQ internal files and MPQ tables
#define MPQ_FLAG_PATCH 0x00000080 // If set, this MPQ is a patch archive
-#define MPQ_FLAG_WAR3_MAP 0x00000100 // If set, this MPQ is a Warcraft III map
-#define MPQ_FLAG_STARCRAFT_BETA 0x00000200 // If set, this MPQ is StarDat.mpq from Starcraft I BETA
-#define MPQ_FLAG_LISTFILE_NONE 0x00000400 // Set when no (listfile) was found in InvalidateInternalFiles
-#define MPQ_FLAG_LISTFILE_NEW 0x00000800 // Set when (listfile) invalidated by InvalidateInternalFiles
-#define MPQ_FLAG_LISTFILE_FORCE 0x00001000 // Save updated listfile on exit
-#define MPQ_FLAG_ATTRIBUTES_NONE 0x00002000 // Set when no (attributes) was found in InvalidateInternalFiles
-#define MPQ_FLAG_ATTRIBUTES_NEW 0x00004000 // Set when (attributes) invalidated by InvalidateInternalFiles
-#define MPQ_FLAG_SIGNATURE_NONE 0x00008000 // Set when no (signature) was found in InvalidateInternalFiles
-#define MPQ_FLAG_SIGNATURE_NEW 0x00010000 // Set when (signature) invalidated by InvalidateInternalFiles
+#define MPQ_FLAG_STARCRAFT_BETA 0x00000100 // If set, this MPQ is from Starcraft I BETA
+#define MPQ_FLAG_STARCRAFT 0x00000200 // If set, this MPQ is from Starcraft I
+#define MPQ_FLAG_WAR3_MAP 0x00000400 // If set, this MPQ is a Warcraft III map
+#define MPQ_FLAG_LISTFILE_NONE 0x00000800 // Set when no (listfile) was found in InvalidateInternalFiles
+#define MPQ_FLAG_LISTFILE_NEW 0x00001000 // Set when (listfile) invalidated by InvalidateInternalFiles
+#define MPQ_FLAG_LISTFILE_FORCE 0x00002000 // Save updated listfile on exit
+#define MPQ_FLAG_ATTRIBUTES_NONE 0x00004000 // Set when no (attributes) was found in InvalidateInternalFiles
+#define MPQ_FLAG_ATTRIBUTES_NEW 0x00008000 // Set when (attributes) invalidated by InvalidateInternalFiles
+#define MPQ_FLAG_SIGNATURE_NONE 0x00010000 // Set when no (signature) was found in InvalidateInternalFiles
+#define MPQ_FLAG_SIGNATURE_NEW 0x00020000 // Set when (signature) invalidated by InvalidateInternalFiles
// Values for TMPQArchive::dwSubType
#define MPQ_SUBTYPE_MPQ 0x00000000 // The file is a MPQ file (Blizzard games)
@@ -1121,7 +1122,6 @@ int WINAPI SCompExplode (void * pvOutBuffer, int * pcbOutBuffer, void * pv
int WINAPI SCompCompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel);
int WINAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
-int WINAPI SCompDecompress_SC1B(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
//-----------------------------------------------------------------------------
// Non-Windows support for SetLastError/GetLastError