aboutsummaryrefslogtreecommitdiff
path: root/src/SFileAttributes.cpp
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2013-12-05 15:59:00 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2013-12-05 15:59:00 +0100
commitc34c37b3418f1e5ab3678ce65d46f81803dec91d (patch)
tree4a9cf4c61634691981f9dc367b53dac4070f8d0d /src/SFileAttributes.cpp
parentff0c25952a28a927c48738ab5207b9bda69e588a (diff)
+ StormLib 9.0 BETA
Diffstat (limited to 'src/SFileAttributes.cpp')
-rw-r--r--src/SFileAttributes.cpp559
1 files changed, 284 insertions, 275 deletions
diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp
index 995e5b1..bca1f66 100644
--- a/src/SFileAttributes.cpp
+++ b/src/SFileAttributes.cpp
@@ -27,343 +27,352 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
//-----------------------------------------------------------------------------
-// Public functions (internal use by StormLib)
+// Local functions
-int SAttrLoadAttributes(TMPQArchive * ha)
+static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)
{
- MPQ_ATTRIBUTES_HEADER AttrHeader;
- HANDLE hFile = NULL;
+ DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER);
+
+ // Calculate size of the (attributes) file
+ if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
+ cbAttrFile += dwFileTableSize * sizeof(DWORD);
+ if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
+ cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
+ if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
+ cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
+
+ // Weird: When there's 1 extra bit in the patch bit array, it's ignored
+ // wow-update-13164.MPQ: BlockTableSize = 0x62E1, but there's only 0xC5C bytes
+ if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
+ cbAttrFile += (dwFileTableSize + 6) / 8;
+
+ return cbAttrFile;
+}
+
+static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile)
+{
+ LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile;
+ LPBYTE pbAttrPtr = pbAttrFile;
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
- DWORD dwArraySize;
- DWORD dwBytesRead;
DWORD i;
- int nError = ERROR_SUCCESS;
- // File table must be initialized
- assert(ha->pFileTable != NULL);
+ // Load and verify the header
+ if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd)
+ {
+ PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr;
+
+ BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER));
+ if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1)
+ return ERROR_BAD_FORMAT;
+
+ // Verify the flags and size of the file
+ assert((pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) == 0);
+ assert(GetSizeOfAttributesFile(pAttrHeader->dwFlags, dwBlockTableSize) == cbAttrFile);
+
+ ha->dwAttrFlags = pAttrHeader->dwFlags;
+ pbAttrPtr = (LPBYTE)(pAttrHeader + 1);
+ }
- // Attempt to open the "(attributes)" file.
- // If it's not there, then the archive doesn't support attributes
- if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
+ // Load the CRC32 (if present)
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
{
- // Load the content of the attributes file
- SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL);
- if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
- nError = ERROR_FILE_CORRUPT;
+ LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr;
+ DWORD cbArraySize = dwBlockTableSize * sizeof(DWORD);
- // Verify the header of the (attributes) file
- if(nError == ERROR_SUCCESS)
- {
- AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion);
- AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags);
- ha->dwAttrFlags = AttrHeader.dwFlags;
- if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
- nError = ERROR_FILE_CORRUPT;
- }
+ // Verify if there's enough data
+ if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
+ return ERROR_FILE_CORRUPT;
- // Verify format of the attributes
- if(nError == ERROR_SUCCESS)
- {
- if(AttrHeader.dwVersion > MPQ_ATTRIBUTES_V1)
- nError = ERROR_BAD_FORMAT;
- }
+ BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbCRC32Size);
+ for(i = 0; i < dwBlockTableSize; i++)
+ ha->pFileTable[i].dwCrc32 = ArrayCRC32[i];
+ pbAttrPtr += cbArraySize;
+ }
- // Load the CRC32 (if any)
- if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32))
- {
- LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize);
+ // Load the FILETIME (if present)
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
+ {
+ ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr;
+ DWORD cbArraySize = dwBlockTableSize * sizeof(ULONGLONG);
- if(pArrayCRC32 != NULL)
- {
- dwArraySize = dwBlockTableSize * sizeof(DWORD);
- SFileReadFile(hFile, pArrayCRC32, dwArraySize, &dwBytesRead, NULL);
- if(dwBytesRead == dwArraySize)
- {
- for(i = 0; i < dwBlockTableSize; i++)
- ha->pFileTable[i].dwCrc32 = BSWAP_INT32_UNSIGNED(pArrayCRC32[i]);
- }
- else
- nError = ERROR_FILE_CORRUPT;
-
- STORM_FREE(pArrayCRC32);
- }
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
+ // Verify if there's enough data
+ if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
+ return ERROR_FILE_CORRUPT;
- // Read the array of file times
- if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME))
- {
- ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize);
+ BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbFileTimeSize);
+ for(i = 0; i < dwBlockTableSize; i++)
+ ha->pFileTable[i].FileTime = ArrayFileTime[i];
+ pbAttrPtr += cbArraySize;
+ }
- if(pArrayFileTime != NULL)
- {
- dwArraySize = dwBlockTableSize * sizeof(ULONGLONG);
- SFileReadFile(hFile, pArrayFileTime, dwArraySize, &dwBytesRead, NULL);
- if(dwBytesRead == dwArraySize)
- {
- for(i = 0; i < dwBlockTableSize; i++)
- ha->pFileTable[i].FileTime = BSWAP_INT64_UNSIGNED(pArrayFileTime[i]);
- }
- else
- nError = ERROR_FILE_CORRUPT;
-
- STORM_FREE(pArrayFileTime);
- }
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
+ // Load the MD5 (if present)
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
+ {
+ LPBYTE ArrayMd5 = pbAttrPtr;
+ DWORD cbArraySize = dwBlockTableSize * MD5_DIGEST_SIZE;
- // Read the MD5 (if any)
- // Note: MD5 array can be incomplete, if it's the last array in the (attributes)
- if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_MD5))
- {
- unsigned char * pArrayMD5 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE));
- unsigned char * md5;
+ // Verify if there's enough data
+ if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
+ return ERROR_FILE_CORRUPT;
- if(pArrayMD5 != NULL)
- {
- dwArraySize = dwBlockTableSize * MD5_DIGEST_SIZE;
- SFileReadFile(hFile, pArrayMD5, dwArraySize, &dwBytesRead, NULL);
- if(dwBytesRead == dwArraySize)
- {
- md5 = pArrayMD5;
- for(i = 0; i < dwBlockTableSize; i++)
- {
- memcpy(ha->pFileTable[i].md5, md5, MD5_DIGEST_SIZE);
- md5 += MD5_DIGEST_SIZE;
- }
- }
- else
- nError = ERROR_FILE_CORRUPT;
-
- STORM_FREE(pArrayMD5);
- }
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ for(i = 0; i < dwBlockTableSize; i++)
+ {
+ memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE);
+ ArrayMd5 += MD5_DIGEST_SIZE;
}
+ pbAttrPtr += cbArraySize;
+ }
- // Read the patch bit for each file
- if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_PATCH_BIT))
- {
- LPBYTE pbBitArray;
- DWORD dwByteSize = ((dwBlockTableSize - 1) / 8) + 1;
+ // Read the patch bit for each file (if present)
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
+ {
+ LPBYTE pbBitArray = pbAttrPtr;
+ DWORD cbArraySize = (dwBlockTableSize + 7) / 8;
+ DWORD dwByteIndex = 0;
+ DWORD dwBitMask = 0x80;
- pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
- if(pbBitArray != NULL)
- {
- SFileReadFile(hFile, pbBitArray, dwByteSize, &dwBytesRead, NULL);
- if(dwBytesRead == dwByteSize)
- {
- for(i = 0; i < dwBlockTableSize; i++)
- {
- DWORD dwByteIndex = i / 8;
- DWORD dwBitMask = 0x80 >> (i & 7);
-
- // Is the appropriate bit set?
- if(pbBitArray[dwByteIndex] & dwBitMask)
- {
- // At the moment, we assume that the patch bit is present
- // in both file table and (attributes)
- assert((ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) != 0);
- ha->pFileTable[i].dwFlags |= MPQ_FILE_PATCH_FILE;
- }
- }
- }
- else
- nError = ERROR_FILE_CORRUPT;
-
- STORM_FREE(pbBitArray);
- }
+ // Verify if there's enough data
+ if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
+ return ERROR_FILE_CORRUPT;
+
+ for(i = 0; i < dwBlockTableSize; i++)
+ {
+ ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0;
+ dwByteIndex += (dwBitMask & 0x01);
+ dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
}
- //
- // Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
- // Sometimes, number of entries in the (attributes) was 1 item less
- // than block table size.
- // If we encounter such table, we will zero all three arrays
- //
+ pbAttrPtr += cbArraySize;
+ }
- if(nError != ERROR_SUCCESS)
- ha->dwAttrFlags = 0;
+ //
+ // Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
+ // Sometimes, number of entries in the (attributes) was 1 item less
+ // than block table size.
+ // If we encounter such table, we will zero all three arrays
+ //
- // Cleanup & exit
- SFileCloseFile(hFile);
- }
- return nError;
+ if(pbAttrPtr != pbAttrFileEnd)
+ ha->dwAttrFlags = 0;
+ return ERROR_SUCCESS;
}
-int SAttrFileSaveToMpq(TMPQArchive * ha)
+static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
{
- MPQ_ATTRIBUTES_HEADER AttrHeader;
+ PMPQ_ATTRIBUTES_HEADER pAttrHeader;
+ TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
- TMPQFile * hf = NULL;
- DWORD dwFinalBlockTableSize = ha->dwFileTableSize;
- DWORD dwFileSize = 0;
- DWORD dwToWrite;
- DWORD i;
- int nError = ERROR_SUCCESS;
+ LPBYTE pbAttrFile;
+ LPBYTE pbAttrPtr;
+ size_t cbAttrFile;
+ DWORD dwFinalEntries = ha->dwFileTableSize + ha->dwReservedFiles;
- // Now we have to check if we need patch bits in the (attributes)
- if(nError == ERROR_SUCCESS)
+ // Check if we need patch bits in the (attributes) file
+ for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
- for(i = 0; i < ha->dwFileTableSize; i++)
+ if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
{
- if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
- {
- ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
- break;
- }
+ ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
+ break;
}
}
- // If the (attributes) is not in the file table yet,
- // we have to increase the final block table size
- pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
- if(pFileEntry != NULL)
- {
- // If "(attributes)" file exists, and it's set to 0, then remove it
- if(ha->dwAttrFlags == 0)
- {
- FreeFileEntry(ha, pFileEntry);
- return ERROR_SUCCESS;
- }
- }
- else
+ // Allocate the buffer for holding the entire (attributes)
+ // Allodate 1 byte more (See GetSizeOfAttributesFile for more info)
+ cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, dwFinalEntries);
+ pbAttrFile = pbAttrPtr = STORM_ALLOC(BYTE, cbAttrFile + 1);
+ if(pbAttrFile != NULL)
{
- // If we don't want to create file atributes, do nothing
- if(ha->dwAttrFlags == 0)
- return ERROR_SUCCESS;
-
- // Check where the file entry is going to be allocated.
- // If at the end of the file table, we have to increment
- // the expected size of the (attributes) file.
- pFileEntry = FindFreeFileEntry(ha);
- if(pFileEntry == ha->pFileTable + ha->dwFileTableSize)
- dwFinalBlockTableSize++;
- }
+ // Make sure it's all zeroed
+ memset(pbAttrFile, 0, cbAttrFile + 1);
- // Calculate the size of the attributes file
- if(nError == ERROR_SUCCESS)
- {
- dwFileSize = sizeof(MPQ_ATTRIBUTES_HEADER); // Header
+ // 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)
- dwFileSize += dwFinalBlockTableSize * sizeof(DWORD);
- if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
- dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG);
- if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
- dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE;
- if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
- dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1;
- }
+ {
+ LPDWORD pArrayCRC32 = (LPDWORD)pbAttrPtr;
- // Determine the flags for (attributes)
- if(ha->dwFileFlags2 == 0)
- ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize);
+ // Copy from file table
+ for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
+ *pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32);
- // Create the attributes file in the MPQ
- nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
- 0,
- dwFileSize,
- LANG_NEUTRAL,
- ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
- &hf);
+ // Skip the reserved entries
+ pbAttrPtr = (LPBYTE)(pArrayCRC32 + ha->dwReservedFiles);
+ }
- // Write all parts of the (attributes) file
- if(nError == ERROR_SUCCESS)
- {
- assert(ha->dwFileTableSize == dwFinalBlockTableSize);
+ // Write the array of file time
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
+ {
+ ULONGLONG * pArrayFileTime = (ULONGLONG *)pbAttrPtr;
- // Note that we don't know what the new bit (0x08) means.
- AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100);
- AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
- dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER);
- nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB);
- }
+ // Copy from file table
+ for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
+ *pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime);
- // Write the array of CRC32
- if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32))
- {
- LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize);
+ // Skip the reserved entries
+ pbAttrPtr = (LPBYTE)(pArrayFileTime + ha->dwReservedFiles);
+ }
- if(pArrayCRC32 != NULL)
+ // Write the array of MD5s
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
{
+ LPBYTE pbArrayMD5 = pbAttrPtr;
+
// Copy from file table
- for(i = 0; i < ha->dwFileTableSize; i++)
- pArrayCRC32[i] = BSWAP_INT32_UNSIGNED(ha->pFileTable[i].dwCrc32);
+ for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
+ {
+ memcpy(pbArrayMD5, pFileEntry->md5, MD5_DIGEST_SIZE);
+ pbArrayMD5 += MD5_DIGEST_SIZE;
+ }
- dwToWrite = ha->dwFileTableSize * sizeof(DWORD);
- nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB);
- STORM_FREE(pArrayCRC32);
+ // Skip the reserved items
+ pbAttrPtr = pbArrayMD5 + (ha->dwReservedFiles * MD5_DIGEST_SIZE);
}
- }
-
- // Write the array of file time
- if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME))
- {
- ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize);
- if(pArrayFileTime != NULL)
+ // Write the array of patch bits
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
{
+ LPBYTE pbBitArray = pbAttrPtr;
+ DWORD dwByteSize = (dwFinalEntries + 7) / 8;
+ DWORD dwByteIndex = 0;
+ DWORD dwBitMask = 0x80;
+
// Copy from file table
- for(i = 0; i < ha->dwFileTableSize; i++)
- pArrayFileTime[i] = BSWAP_INT64_UNSIGNED(ha->pFileTable[i].FileTime);
+ 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);
+ }
- dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG);
- nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB);
- STORM_FREE(pArrayFileTime);
+ // Move past the bit array
+ pbAttrPtr = (pbBitArray + dwByteSize);
}
+
+ // 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 shorted in Blizzard MPQs. See GetSizeOfAttributesFile
+ assert((size_t)(pbAttrPtr - pbAttrFile) == cbAttrFile + ((dwFinalEntries & 0x07) == 1) ? 1 : 0);
}
- // Write the array of MD5s
- if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5))
- {
- char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE);
+ // Give away the attributes file
+ if(pcbAttrFile != NULL)
+ *pcbAttrFile = (DWORD)cbAttrFile;
+ return pbAttrFile;
+}
- if(pArrayMD5 != NULL)
- {
- // Copy from file table
- for(i = 0; i < ha->dwFileTableSize; i++)
- memcpy(&pArrayMD5[i * MD5_DIGEST_SIZE], ha->pFileTable[i].md5, MD5_DIGEST_SIZE);
+//-----------------------------------------------------------------------------
+// Public functions (internal use by StormLib)
- dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE;
- nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB);
- STORM_FREE(pArrayMD5);
- }
- }
+int SAttrLoadAttributes(TMPQArchive * ha)
+{
+ HANDLE hFile = NULL;
+ LPBYTE pbAttrFile;
+ DWORD dwBytesRead;
+ DWORD cbAttrFile = 0;
+ int nError = ERROR_FILE_CORRUPT;
+
+ // File table must be initialized
+ assert(ha->pFileTable != NULL);
- // Write the array of patch bits
- if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT))
+ // Attempt to open the "(attributes)" file.
+ // If it's not there, then the archive doesn't support attributes
+ if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
{
- LPBYTE pbBitArray;
- DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1;
+ // Retrieve and check size of the (attributes) file
+ cbAttrFile = SFileGetFileSize(hFile, NULL);
- pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
- if(pbBitArray != NULL)
+ // 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)
{
- memset(pbBitArray, 0, dwByteSize);
- for(i = 0; i < ha->dwFileTableSize; i++)
- {
- DWORD dwByteIndex = i / 8;
- DWORD dwBitMask = 0x80 >> (i & 7);
+ // Set the last byte to 0 in case the size should be 1 byte greater
+ pbAttrFile[cbAttrFile] = 0;
- if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
- pbBitArray[dwByteIndex] |= dwBitMask;
- }
+ // Load the entire file to memory
+ SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL);
+ if(dwBytesRead == cbAttrFile)
+ nError = LoadAttributesFile(ha, pbAttrFile, cbAttrFile);
- nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
- STORM_FREE(pbBitArray);
+ // Free the buffer
+ STORM_FREE(pbAttrFile);
}
+
+ // Close the attributes file
+ SFileCloseFile(hFile);
}
- // Finalize the file in the archive
- if(hf != NULL)
+ return nError;
+}
+
+// Saves the (attributes) to the MPQ
+int SAttrFileSaveToMpq(TMPQArchive * ha)
+{
+ TMPQFile * hf = NULL;
+ LPBYTE pbAttrFile;
+ DWORD cbAttrFile = 0;
+ int nError = ERROR_SUCCESS;
+
+ // If there are no file flags for (attributes) file, do nothing
+ if(ha->dwFileFlags2 == 0 || ha->dwMaxFileCount == 0)
+ return ERROR_SUCCESS;
+
+ // We expect at least one reserved entry to be there
+ assert(ha->dwReservedFiles >= 1);
+
+ // 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);
+
+ // Now we decrement the number of reserved files.
+ // This frees one slot in the file table, so the subsequent file create operation should succeed
+ // This must happen even if CreateAttributesFile failed
+ ha->dwReservedFiles--;
+
+ // If we created something, write the attributes to the MPQ
+ if(pbAttrFile != NULL)
{
- SFileAddFile_Finish(hf);
- }
+ // We expect it to be nonzero size
+ assert(cbAttrFile != 0);
+
+ // Determine the real flags for (attributes)
+ if(ha->dwFileFlags2 == MPQ_FILE_EXISTS)
+ ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion);
+
+ // Create the attributes file in the MPQ
+ nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
+ 0,
+ cbAttrFile,
+ LANG_NEUTRAL,
+ ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
+ &hf);
+
+ // Write the attributes file raw data to it
+ if(nError == ERROR_SUCCESS)
+ {
+ // Write the content of the attributes file to the MPQ
+ nError = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB);
+ SFileAddFile_Finish(hf);
+
+ // Clear the invalidate flag
+ ha->dwFlags &= ~MPQ_FLAG_ATTRIBUTES_INVALID;
+ }
- if(nError == ERROR_SUCCESS)
- ha->dwFlags &= ~MPQ_FLAG_ATTRIBUTES_INVALID;
+ // Free the attributes buffer
+ STORM_FREE(pbAttrFile);
+ }
+
return nError;
}
@@ -375,7 +384,7 @@ DWORD WINAPI SFileGetAttributes(HANDLE hMpq)
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify the parameters
- if(!IsValidMpqHandle(ha))
+ if(!IsValidMpqHandle(hMpq))
{
SetLastError(ERROR_INVALID_PARAMETER);
return SFILE_INVALID_ATTRIBUTES;
@@ -389,7 +398,7 @@ bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags)
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify the parameters
- if(!IsValidMpqHandle(ha))
+ if(!IsValidMpqHandle(hMpq))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
@@ -439,7 +448,7 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName)
// Get the file size
hf = (TMPQFile *)hFile;
- SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD), NULL);
+ dwTotalBytes = hf->pFileEntry->dwFileSize;
// Initialize the CRC32 and MD5 contexts
md5_init(&md5_state);