diff options
| author | briaguya <70942617+briaguya-ai@users.noreply.github.com> | 2022-11-14 05:22:34 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-14 11:22:34 +0100 |
| commit | 3914781ebfd5cb22717cc207cb3d37d8754d0eb7 (patch) | |
| tree | 2e412b87e66b1deef47bef242e7189a200cea0ff /StormLib/src/SFileAttributes.cpp | |
| parent | 9eb29ed81cb497a6a216811255f8af7daf3f8bd8 (diff) | |
Import libultraship as a submodule (#1943)
Co-authored-by: briaguya <briaguya>
Co-authored-by: Christopher Leggett <chris@leggett.dev>
Co-authored-by: David Chavez <davi@dcvz.io>
Diffstat (limited to 'StormLib/src/SFileAttributes.cpp')
| -rw-r--r-- | StormLib/src/SFileAttributes.cpp | 570 |
1 files changed, 0 insertions, 570 deletions
diff --git a/StormLib/src/SFileAttributes.cpp b/StormLib/src/SFileAttributes.cpp deleted file mode 100644 index eb9c9ce07..000000000 --- a/StormLib/src/SFileAttributes.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/*****************************************************************************/ -/* SAttrFile.cpp Copyright (c) Ladislav Zezula 2007 */ -/*---------------------------------------------------------------------------*/ -/* Description: */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 12.06.04 1.00 Lad The first version of SAttrFile.cpp */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local structures - -typedef struct _MPQ_ATTRIBUTES_HEADER -{ - DWORD dwVersion; // Version of the (attributes) file. Must be 100 (0x64) - DWORD dwFlags; // See MPQ_ATTRIBUTE_XXXX - - // Followed by an array of CRC32 - // Followed by an array of file times - // Followed by an array of MD5 - // Followed by an array of patch bits - - // Note: The MD5 in (attributes), if present, is a hash of the entire file. - // In case the file is an incremental patch, it contains MD5 of the file - // after being patched. - -} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER; - -//----------------------------------------------------------------------------- -// Local functions - -static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwBlockTableSize) -{ - DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER); - - // Calculate size of the (attributes) file - if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - cbAttrFile += dwBlockTableSize * sizeof(DWORD); - if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - cbAttrFile += dwBlockTableSize * sizeof(ULONGLONG); - if(dwAttrFlags & MPQ_ATTRIBUTE_MD5) - cbAttrFile += dwBlockTableSize * MD5_DIGEST_SIZE; - - // The bit array has been created without the last bit belonging to (attributes) - // When the number of files is a multiplier of 8 plus one, then the size of (attributes) - // if 1 byte less than expected. - // Example: wow-update-13164.MPQ: BlockTableSize = 0x62E1, but there's only 0xC5C bytes - if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) - cbAttrFile += (dwBlockTableSize + 6) / 8; - - return cbAttrFile; -} - -static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwBlockTableSize) -{ - DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER); - DWORD cbChecksumSize1 = 0; - DWORD cbChecksumSize2 = 0; - DWORD cbFileTimeSize1 = 0; - DWORD cbFileTimeSize2 = 0; - DWORD cbFileHashSize1 = 0; - DWORD cbFileHashSize2 = 0; - DWORD cbPatchBitSize1 = 0; - DWORD cbPatchBitSize2 = 0; - DWORD cbPatchBitSize3 = 0; - - // - // Various variants with the patch bit - // - // interface.MPQ.part from WoW build 10958 has - // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. - // The array is filled with zeros, so we don't know what it should contain - // - // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing - // - // Elimination Tournament 2.w3x's (attributes) have one entry less - // - // There may be two variants: Either the (attributes) file has full - // number of entries, or has one entry less - // - - // Get the expected size of CRC32 array - if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - { - cbChecksumSize1 += dwBlockTableSize * sizeof(DWORD); - cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD); - } - - // Get the expected size of FILETIME array - if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - { - cbFileTimeSize1 += dwBlockTableSize * sizeof(ULONGLONG); - cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG); - } - - // Get the expected size of MD5 array - if(dwAttrFlags & MPQ_ATTRIBUTE_MD5) - { - cbFileHashSize1 += dwBlockTableSize * MD5_DIGEST_SIZE; - cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE; - } - - // Get the expected size of patch bit array - if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) - { - cbPatchBitSize1 = - cbPatchBitSize2 = ((dwBlockTableSize + 6) / 8); - cbPatchBitSize3 = dwBlockTableSize * sizeof(DWORD); - } - - // Check if the (attributes) file entry count is equal to our file table size - if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1)) - return dwBlockTableSize; - - // Check if the (attributes) file entry count is equal to our file table size minus one - if(cbAttrFile == (cbHeaderSize + cbChecksumSize2 + cbFileTimeSize2 + cbFileHashSize2 + cbPatchBitSize2)) - return dwBlockTableSize - 1; - - // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing - if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1)) - return dwBlockTableSize; - - // interface.MPQ.part (WoW build 10958) has the MPQ_ATTRIBUTE_PATCH_BIT set - // but there's an array of DWORDs (filled with zeros) instead of array of bits - if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize3)) - return dwBlockTableSize; - -#ifdef __STORMLIB_TEST__ - // Invalid size of the (attributes) file - // Note that many MPQs, especially Warcraft III maps have the size of (attributes) invalid. - // We only perform this check if this is the STORMLIB testprogram itself -// assert(false); -#endif - - return 0; -} - -static DWORD LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile) -{ - LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile; - LPBYTE pbAttrPtr = pbAttrFile; - DWORD dwAttributesEntries = 0; - DWORD i; - - // Load and verify the header - if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd) - { - PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr; - - // Verify the header version - BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER)); - if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1) - return ERROR_BAD_FORMAT; - - // Verify the flags - if(pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) - return ERROR_BAD_FORMAT; - - // Verify whether file size of (attributes) is expected - dwAttributesEntries = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, ha->pHeader->dwBlockTableSize); - if(dwAttributesEntries == 0) - return ERROR_BAD_FORMAT; - - ha->dwAttrFlags = pAttrHeader->dwFlags; - pbAttrPtr = (LPBYTE)(pAttrHeader + 1); - } - - // Load the CRC32 (if present) - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - { - LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr; - DWORD cbArraySize = dwAttributesEntries * sizeof(DWORD); - - // Verify if there's enough data - if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) - return ERROR_FILE_CORRUPT; - - BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbArraySize); - for(i = 0; i < dwAttributesEntries; i++) - ha->pFileTable[i].dwCrc32 = ArrayCRC32[i]; - pbAttrPtr += cbArraySize; - } - - // Load the FILETIME (if present) - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - { - ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr; - DWORD cbArraySize = dwAttributesEntries * sizeof(ULONGLONG); - - // Verify if there's enough data - if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) - return ERROR_FILE_CORRUPT; - - BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbArraySize); - for(i = 0; i < dwAttributesEntries; i++) - ha->pFileTable[i].FileTime = ArrayFileTime[i]; - pbAttrPtr += cbArraySize; - } - - // Load the MD5 (if present) - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) - { - LPBYTE ArrayMd5 = pbAttrPtr; - DWORD cbArraySize = dwAttributesEntries * MD5_DIGEST_SIZE; - - // Verify if there's enough data - if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) - return ERROR_FILE_CORRUPT; - - for(i = 0; i < dwAttributesEntries; i++) - { - memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE); - ArrayMd5 += MD5_DIGEST_SIZE; - } - pbAttrPtr += cbArraySize; - } - - // Read the patch bit for each file (if present) - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) - { - LPBYTE pbBitArray = pbAttrPtr; - DWORD cbArraySize = (dwAttributesEntries + 7) / 8; - DWORD dwByteIndex = 0; - DWORD dwBitMask = 0x80; - - // Verify if there's enough data - if((pbAttrPtr + cbArraySize) == pbAttrFileEnd) - { - for(i = 0; i < dwAttributesEntries; i++) - { - ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0; - dwByteIndex += (dwBitMask & 0x01); - dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); - } - } - } - - return ERROR_SUCCESS; -} - -static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile) -{ - PMPQ_ATTRIBUTES_HEADER pAttrHeader; - TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize; - TFileEntry * pFileEntry; - LPBYTE pbAttrFile; - LPBYTE pbAttrPtr; - size_t cbAttrFile; - - // Check if we need patch bits in the (attributes) file - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) - { - ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT; - break; - } - } - - // Allocate the buffer for holding the entire (attributes) - // Allocate 1 byte more (See GetSizeOfAttributesFile for more info) - cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, ha->pHeader->dwBlockTableSize); - pbAttrFile = pbAttrPtr = STORM_ALLOC(BYTE, cbAttrFile + 1); - if(pbAttrFile != NULL) - { - // Make sure it's all zeroed - memset(pbAttrFile, 0, cbAttrFile + 1); - - // Write the header of the (attributes) file - pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr; - pAttrHeader->dwVersion = BSWAP_INT32_UNSIGNED(100); - pAttrHeader->dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL)); - pbAttrPtr = (LPBYTE)(pAttrHeader + 1); - - // Write the array of CRC32, if present - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - { - LPDWORD pArrayCRC32 = (LPDWORD)pbAttrPtr; - - // Copy from file table - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - *pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32); - - // Update pointer - pbAttrPtr = (LPBYTE)pArrayCRC32; - } - - // Write the array of file time - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - { - ULONGLONG * pArrayFileTime = (ULONGLONG *)pbAttrPtr; - - // Copy from file table - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - *pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime); - - // Update pointer - pbAttrPtr = (LPBYTE)pArrayFileTime; - } - - // Write the array of MD5s - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) - { - LPBYTE pbArrayMD5 = pbAttrPtr; - - // Copy from file table - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - memcpy(pbArrayMD5, pFileEntry->md5, MD5_DIGEST_SIZE); - pbArrayMD5 += MD5_DIGEST_SIZE; - } - - // Update pointer - pbAttrPtr = pbArrayMD5; - } - - // Write the array of patch bits - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) - { - LPBYTE pbBitArray = pbAttrPtr; - DWORD dwByteIndex = 0; - BYTE dwBitMask = 0x80; - - // Copy from file table - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - // Set the bit, if needed - if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) - pbBitArray[dwByteIndex] |= dwBitMask; - - // Update bit index and bit mask - dwByteIndex += (dwBitMask & 0x01); - dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); - } - - // Move past the bit array - pbAttrPtr += (ha->pHeader->dwBlockTableSize + 6) / 8; - } - - // Now we expect that current position matches the estimated size - // Note that if there is 1 extra bit above the byte size, - // the table is actually 1 byte shorter in Blizzard MPQs. See GetSizeOfAttributesFile - assert((size_t)(pbAttrPtr - pbAttrFile) == cbAttrFile); - } - - // Give away the attributes file - if(pcbAttrFile != NULL) - *pcbAttrFile = (DWORD)cbAttrFile; - return pbAttrFile; -} - -//----------------------------------------------------------------------------- -// Public functions (internal use by StormLib) - -DWORD SAttrLoadAttributes(TMPQArchive * ha) -{ - HANDLE hFile = NULL; - LPBYTE pbAttrFile; - DWORD dwBytesRead; - DWORD cbAttrFile = 0; - DWORD dwErrCode = ERROR_FILE_CORRUPT; - - // File table must be initialized - assert(ha->pFileTable != NULL); - assert((ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0); - - // Don't load the attributes file from malformed Warcraft III maps - if(ha->dwFlags & MPQ_FLAG_MALFORMED) - return ERROR_FILE_CORRUPT; - - // Attempt to open the "(attributes)" file. - if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile)) - { - // Retrieve and check size of the (attributes) file - cbAttrFile = SFileGetFileSize(hFile, NULL); - - // Integer overflow check - if((cbAttrFile + 1) > cbAttrFile) - { - // Size of the (attributes) might be 1 byte less than expected - // See GetSizeOfAttributesFile for more info - pbAttrFile = STORM_ALLOC(BYTE, cbAttrFile + 1); - if(pbAttrFile != NULL) - { - // Set the last byte to 0 in case the size should be 1 byte greater - pbAttrFile[cbAttrFile] = 0; - - // Load the entire file to memory - SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL); - if(dwBytesRead == cbAttrFile) - dwErrCode = LoadAttributesFile(ha, pbAttrFile, cbAttrFile); - - // Free the buffer - STORM_FREE(pbAttrFile); - } - } - - // Close the attributes file - SFileCloseFile(hFile); - } - - return dwErrCode; -} - -// Saves the (attributes) to the MPQ -DWORD SAttrFileSaveToMpq(TMPQArchive * ha) -{ - TMPQFile * hf = NULL; - LPBYTE pbAttrFile; - DWORD cbAttrFile = 0; - DWORD dwErrCode = ERROR_SUCCESS; - - // Only save the attributes if we should do so - if(ha->dwFileFlags2 != 0) - { - // At this point, we expect to have at least one reserved entry in the file table - assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW); - assert(ha->dwReservedFiles > 0); - - // Create the raw data that is to be written to (attributes) - // Note: Blizzard MPQs have entries for (listfile) and (attributes), - // but they are filled empty - pbAttrFile = CreateAttributesFile(ha, &cbAttrFile); - if(pbAttrFile != NULL) - { - // Determine the real flags for (attributes) - if(ha->dwFileFlags2 == MPQ_FILE_DEFAULT_INTERNAL) - ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion); - - // Create the attributes file in the MPQ - dwErrCode = SFileAddFile_Init(ha, ATTRIBUTES_NAME, - 0, - cbAttrFile, - LANG_NEUTRAL, - ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, - &hf); - - // Write the attributes file raw data to it - if(dwErrCode == ERROR_SUCCESS) - { - // Write the content of the attributes file to the MPQ - dwErrCode = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB); - SFileAddFile_Finish(hf); - } - - // Clear the number of reserved files - ha->dwFlags &= ~(MPQ_FLAG_ATTRIBUTES_NEW | MPQ_FLAG_ATTRIBUTES_NONE); - ha->dwReservedFiles--; - - // Free the attributes buffer - STORM_FREE(pbAttrFile); - } - else - { - // If the (attributes) file would be empty, its OK - dwErrCode = (cbAttrFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; - } - } - - return dwErrCode; -} - -//----------------------------------------------------------------------------- -// Public functions - -DWORD WINAPI SFileGetAttributes(HANDLE hMpq) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - - // Verify the parameters - if(!IsValidMpqHandle(hMpq)) - { - SetLastError(ERROR_INVALID_PARAMETER); - return SFILE_INVALID_ATTRIBUTES; - } - - return ha->dwAttrFlags; -} - -bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - - // Verify the parameters - if(!IsValidMpqHandle(hMpq)) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - // Not allowed when the archive is read-only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - { - SetLastError(ERROR_ACCESS_DENIED); - return false; - } - - // Set the attributes - InvalidateInternalFiles(ha); - ha->dwAttrFlags = (dwFlags & MPQ_ATTRIBUTE_ALL); - return true; -} - -bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName) -{ - hash_state md5_state; - TMPQArchive * ha = (TMPQArchive *)hMpq; - TMPQFile * hf; - BYTE Buffer[0x1000]; - HANDLE hFile = NULL; - DWORD dwTotalBytes = 0; - DWORD dwBytesRead; - DWORD dwCrc32; - - // Verify the parameters - if(!IsValidMpqHandle(ha)) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - // Not allowed when the archive is read-only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - { - SetLastError(ERROR_ACCESS_DENIED); - return false; - } - - // Attempt to open the file - if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) - return false; - - // Get the file size - hf = (TMPQFile *)hFile; - dwTotalBytes = hf->pFileEntry->dwFileSize; - - // Initialize the CRC32 and MD5 contexts - md5_init(&md5_state); - dwCrc32 = crc32(0, Z_NULL, 0); - - // Go through entire file and calculate both CRC32 and MD5 - while(dwTotalBytes != 0) - { - // Read data from file - SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); - if(dwBytesRead == 0) - break; - - // Update CRC32 and MD5 - dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); - md5_process(&md5_state, Buffer, dwBytesRead); - - // Decrement the total size - dwTotalBytes -= dwBytesRead; - } - - // Update both CRC32 and MD5 - hf->pFileEntry->dwCrc32 = dwCrc32; - md5_done(&md5_state, hf->pFileEntry->md5); - - // Remember that we need to save the MPQ tables - InvalidateInternalFiles(ha); - SFileCloseFile(hFile); - return true; -} |
