/*****************************************************************************/ /* SCommon.h Copyright (c) Ladislav Zezula 2003 */ /*---------------------------------------------------------------------------*/ /* Common functions for encryption/decryption from Storm.dll. Included by */ /* SFile*** functions, do not include and do not use this file directly */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 24.03.03 1.00 Lad The first version of SFileCommon.h */ /* 12.06.04 1.00 Lad Renamed to SCommon.h */ /* 06.09.10 1.00 Lad Renamed to StormCommon.h */ /*****************************************************************************/ #ifndef __STORMCOMMON_H__ #define __STORMCOMMON_H__ //----------------------------------------------------------------------------- // Compression support // Include functions from Pkware Data Compression Library #include "pklib/pklib.h" // Include functions from Huffmann compression #include "huffman/huff.h" // Include functions from IMA ADPCM compression #include "adpcm/adpcm.h" // Include functions from SPARSE compression #include "sparse/sparse.h" // Include functions from LZMA compression #include "lzma/C/LzmaEnc.h" #include "lzma/C/LzmaDec.h" // Include functions from zlib #ifndef __SYS_ZLIB #include "zlib/zlib.h" #else #include #endif // Include functions from bzlib #ifndef __SYS_BZLIB #include "bzip2/bzlib.h" #else #include #endif //----------------------------------------------------------------------------- // Cryptography support // Headers from LibTomCrypt #include "libtomcrypt/src/headers/tomcrypt.h" // For HashStringJenkins #include "jenkins/lookup.h" //----------------------------------------------------------------------------- // StormLib private defines #define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE') // Prevent problems with CRT "min" and "max" functions, // as they are not defined on all platforms #define STORMLIB_MIN(a, b) ((a < b) ? a : b) #define STORMLIB_MAX(a, b) ((a > b) ? a : b) #define STORMLIB_UNUSED(p) ((void)(p)) // Checks for data pointers aligned to 4-byte boundary #define STORMLIB_DWORD_ALIGNED(ptr) (((size_t)(ptr) & 0x03) == 0) // Macro for building 64-bit file offset from two 32-bit #define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) //----------------------------------------------------------------------------- // MTYPE definition - specifies what kind of MPQ is the map type typedef enum _MTYPE { MapTypeNotChecked, // The map type was not checked yet MapTypeNotRecognized, // The file does not seems to be a map MapTypeAviFile, // The file is actually an AVI file (Warcraft III cinematics) MapTypeWarcraft3, // The file is a Warcraft III map MapTypeStarcraft2 // The file is a Starcraft II map } MTYPE, *PMTYPE; //----------------------------------------------------------------------------- // MPQ signature information // Size of each signature type #define MPQ_WEAK_SIGNATURE_SIZE 64 #define MPQ_STRONG_SIGNATURE_SIZE 256 #define MPQ_STRONG_SIGNATURE_ID 0x5349474E // ID of the strong signature ("NGIS") #define MPQ_SIGNATURE_FILE_SIZE (MPQ_WEAK_SIGNATURE_SIZE + 8) // MPQ signature info typedef struct _MPQ_SIGNATURE_INFO { ULONGLONG BeginMpqData; // File offset where the hashing starts ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file) ULONGLONG EndExclude; // End of the excluded area (used for (signature) file) ULONGLONG EndMpqData; // File offset where the hashing ends ULONGLONG EndOfFile; // Size of the entire file BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10]; DWORD cbSignatureSize; // Length of the signature DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX } MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO; //----------------------------------------------------------------------------- // Memory management // // We use our own macros for allocating/freeing memory. If you want // to redefine them, please keep the following rules: // // - The memory allocation must return NULL if not enough memory // (i.e not to throw exception) // - The allocating function does not need to fill the allocated buffer with zeros // - Memory freeing function doesn't have to test the pointer to NULL // //#if defined(_MSC_VER) && defined(_DEBUG) // //#define STORM_ALLOC(type, nitems) (type *)HeapAlloc(GetProcessHeap(), 0, ((nitems) * sizeof(type))) //#define STORM_REALLOC(type, ptr, nitems) (type *)HeapReAlloc(GetProcessHeap(), 0, ptr, ((nitems) * sizeof(type))) //#define STORM_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr) // //#else #define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type)) #define STORM_REALLOC(type, ptr, nitems) (type *)realloc(ptr, ((nitems) * sizeof(type))) #define STORM_FREE(ptr) free(ptr) //#endif //----------------------------------------------------------------------------- // StormLib internal global variables extern DWORD g_dwMpqSignature; // Marker for MPQ header extern DWORD g_dwHashTableKey; // Key for hash table extern DWORD g_dwBlockTableKey; // Key for block table extern LCID g_lcFileLocale; // Preferred file locale //----------------------------------------------------------------------------- // Conversion to uppercase/lowercase (and "/" to "\") extern unsigned char AsciiToLowerTable[256]; extern unsigned char AsciiToUpperTable[256]; //----------------------------------------------------------------------------- // Safe string functions template XCHAR * IntToString(XCHAR * szBuffer, size_t cchMaxChars, XINT nValue, size_t nDigitCount = 0) { XCHAR * szBufferEnd = szBuffer + cchMaxChars - 1; XCHAR szNumberRev[0x20]; size_t nLength = 0; // Always put the first digit szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; nValue /= 10; // Continue as long as we have non-zero while(nValue != 0) { szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; nValue /= 10; } // Fill zeros, if needed while(szBuffer < szBufferEnd && nLength < nDigitCount) { *szBuffer++ = '0'; nDigitCount--; } // Fill the buffer while(szBuffer < szBufferEnd && nLength > 0) { nLength--; *szBuffer++ = szNumberRev[nLength]; } // Terminate the number with zeros szBuffer[0] = 0; return szBuffer; } char * StringCopy(char * szTarget, size_t cchTarget, const char * szSource); void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource); void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned int nIndex, const char * szExtension); #ifdef _UNICODE void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource); void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource); void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource); void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource); #endif //----------------------------------------------------------------------------- // Encryption and decryption functions #define MPQ_HASH_TABLE_INDEX 0x000 #define MPQ_HASH_NAME_A 0x100 #define MPQ_HASH_NAME_B 0x200 #define MPQ_HASH_FILE_KEY 0x300 #define MPQ_HASH_KEY2_MIX 0x400 DWORD HashString(const char * szFileName, DWORD dwHashType); DWORD HashStringSlash(const char* szFileName, DWORD dwHashType); DWORD HashStringSlash2(const char * szFileName, DWORD dwHashType); DWORD HashStringLower(const char * szFileName, DWORD dwHashType); void InitializeMpqCryptography(); DWORD GetNearestPowerOfTwo(DWORD dwFileCount); bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex); ULONGLONG HashStringJenkins(const char * szFileName); DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion); void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey); void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey); DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwSectorOffsLen); DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize); DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags); bool IsValidMD5(LPBYTE pbMd5); bool IsValidSignature(LPBYTE pbSignature); bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); //----------------------------------------------------------------------------- // Handle validation functions TMPQArchive * IsValidMpqHandle(HANDLE hMpq); TMPQFile * IsValidFileHandle(HANDLE hFile); //----------------------------------------------------------------------------- // Support for MPQ file tables ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset); ULONGLONG CalculateRawSectorOffset(TMPQFile * hf, DWORD dwSectorOffset); DWORD ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags, MTYPE MapType); bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash); TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwName1, DWORD dwName2, LCID lcLocale); TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName); TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash); TMPQHash * AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry, LCID lcLocale); TMPQExtHeader * LoadExtTable(TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size, DWORD dwSignature, DWORD dwKey); TMPQHetTable * LoadHetTable(TMPQArchive * ha); TMPQBetTable * LoadBetTable(TMPQArchive * ha); TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries = false); TMPQBlock * TranslateBlockTable(TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable); ULONGLONG FindFreeMpqSpace(TMPQArchive * ha); // Functions that load the HET and BET tables DWORD CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize); DWORD LoadAnyHashTable(TMPQArchive * ha); DWORD BuildFileTable(TMPQArchive * ha); DWORD DefragmentFileTable(TMPQArchive * ha); DWORD CreateFileTable(TMPQArchive * ha, DWORD dwFileTableSize); DWORD RebuildHetTable(TMPQArchive * ha); DWORD RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize); DWORD SaveMPQTables(TMPQArchive * ha); TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData); void FreeHetTable(TMPQHetTable * pHetTable); TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount); void FreeBetTable(TMPQBetTable * pBetTable); // Functions for finding files in the file table TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex); TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale); TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex); // Allocates file name in the file entry void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szFileName); // Allocates new file entry in the MPQ tables. Reuses existing, if possible TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex); DWORD RenameFileEntry(TMPQArchive * ha, TMPQFile * hf, const char * szNewFileName); DWORD DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf); // Invalidates entries for (listfile) and (attributes) void InvalidateInternalFiles(TMPQArchive * ha); // Retrieves information about the strong signature bool QueryMpqSignatureInfo(TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSignatureInfo); //----------------------------------------------------------------------------- // Support for alternate file formats (SBaseSubTypes.cpp) DWORD ConvertSqpHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); TMPQHash * LoadSqpHashTable(TMPQArchive * ha); TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha); DWORD ConvertMpkHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); void DecryptMpkTable(void * pvMpkTable, size_t cbSize); TMPQHash * LoadMpkHashTable(TMPQArchive * ha); TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha); int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); //----------------------------------------------------------------------------- // Common functions - MPQ File TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry); TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize); void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut); DWORD AllocateSectorBuffer(TMPQFile * hf); DWORD AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); DWORD AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); DWORD AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile); DWORD WritePatchInfo(TMPQFile * hf); DWORD WriteSectorOffsets(TMPQFile * hf); DWORD WriteSectorChecksums(TMPQFile * hf); DWORD WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize); DWORD WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize); void FreeFileHandle(TMPQFile *& hf); void FreeArchiveHandle(TMPQArchive *& ha); //----------------------------------------------------------------------------- // Patch functions // Structure used for the patching process typedef struct _TMPQPatcher { BYTE this_md5[MD5_DIGEST_SIZE]; // MD5 of the current file state LPBYTE pbFileData1; // Primary working buffer LPBYTE pbFileData2; // Secondary working buffer DWORD cbMaxFileData; // Maximum allowed size of the patch data DWORD cbFileData; // Current size of the result data DWORD nCounter; // Counter of the patch process } TMPQPatcher; bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize); DWORD Patch_InitPatcher(TMPQPatcher * pPatcher, TMPQFile * hf); DWORD Patch_Process(TMPQPatcher * pPatcher, TMPQFile * hf); void Patch_Finalize(TMPQPatcher * pPatcher); //----------------------------------------------------------------------------- // Utility functions bool IsInternalMpqFileName(const char * szFileName); template const XCHAR * GetPlainFileName(const XCHAR * szFileName) { const XCHAR * szPlainName = szFileName; while(*szFileName != 0) { if(*szFileName == '\\' || *szFileName == '/') szPlainName = szFileName + 1; szFileName++; } return szPlainName; } //----------------------------------------------------------------------------- // Internal support for MPQ modifications DWORD SFileAddFile_Init( TMPQArchive * ha, const char * szArchivedName, ULONGLONG ft, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf ); DWORD SFileAddFile_Init( TMPQArchive * ha, TMPQFile * hfSrc, TMPQFile ** phf ); DWORD SFileAddFile_Write( TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression ); DWORD SFileAddFile_Finish( TMPQFile * hf ); //----------------------------------------------------------------------------- // Attributes support DWORD SAttrLoadAttributes(TMPQArchive * ha); DWORD SAttrFileSaveToMpq(TMPQArchive * ha); //----------------------------------------------------------------------------- // Listfile functions DWORD SListFileSaveToMpq(TMPQArchive * ha); //----------------------------------------------------------------------------- // Weak signature support DWORD SSignFileCreate(TMPQArchive * ha); DWORD SSignFileFinish(TMPQArchive * ha); //----------------------------------------------------------------------------- // Dump data support #ifdef __STORMLIB_DUMP_DATA__ void DumpMpqHeader(TMPQHeader * pHeader); void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize); void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable); void DumpFileTable(TFileEntry * pFileTable, DWORD dwFileTableSize); #else #define DumpMpqHeader(h) /* */ #define DumpHashTable(t, s) /* */ #define DumpHetAndBetTable(t, s) /* */ #define DumpFileTable(t, s) /* */ #endif #endif // __STORMCOMMON_H__