diff options
author | Shauren <shauren.trinity@gmail.com> | 2014-10-10 20:17:30 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2014-10-10 20:17:30 +0200 |
commit | 88ae3da6373dee1f04d03b823ee63d6f1db1502e (patch) | |
tree | f9ac27f0a743a57b70e90b37f5971e024992eb00 /dep/StormLib/src/SFileAddFile.cpp | |
parent | bc97908822c4afa23740ce70151c2486c340e2c2 (diff) |
Tools/Extractors: Updated map extractor
Diffstat (limited to 'dep/StormLib/src/SFileAddFile.cpp')
-rw-r--r-- | dep/StormLib/src/SFileAddFile.cpp | 1286 |
1 files changed, 0 insertions, 1286 deletions
diff --git a/dep/StormLib/src/SFileAddFile.cpp b/dep/StormLib/src/SFileAddFile.cpp deleted file mode 100644 index dda47370bd1..00000000000 --- a/dep/StormLib/src/SFileAddFile.cpp +++ /dev/null @@ -1,1286 +0,0 @@ -/*****************************************************************************/ -/* SFileAddFile.cpp Copyright (c) Ladislav Zezula 2010 */ -/*---------------------------------------------------------------------------*/ -/* MPQ Editing functions */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 27.03.10 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local structures - -#define FILE_SIGNATURE_RIFF 0x46464952 -#define FILE_SIGNATURE_WAVE 0x45564157 -#define FILE_SIGNATURE_FMT 0x20746D66 -#define AUDIO_FORMAT_PCM 1 - -typedef struct _WAVE_FILE_HEADER -{ - DWORD dwChunkId; // 0x52494646 ("RIFF") - DWORD dwChunkSize; // Size of that chunk, in bytes - DWORD dwFormat; // Must be 0x57415645 ("WAVE") - - // Format sub-chunk - DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ") - DWORD dwSubChunk1Size; // 0x16 for PCM - USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression - USHORT wChannels; // Number of channels - DWORD dwSampleRate; // 8000, 44100, etc. - DWORD dwBytesRate; // SampleRate * NumChannels * BitsPerSample/8 - USHORT wBlockAlign; // NumChannels * BitsPerSample/8 - USHORT wBitsPerSample; // 8 bits = 8, 16 bits = 16, etc. - - // Followed by "data" sub-chunk (we don't care) -} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; - -//----------------------------------------------------------------------------- -// Local variables - -// Data compression for SFileAddFile -// Kept here for compatibility with code that was created with StormLib version < 6.50 -static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; - -static SFILE_ADDFILE_CALLBACK AddFileCB = NULL; -static void * pvUserData = NULL; - -//----------------------------------------------------------------------------- -// MPQ write data functions - -#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) - -static int IsWaveFile( - LPBYTE pbFileData, - DWORD cbFileData, - LPDWORD pdwChannels) -{ - PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData; - - if(cbFileData > sizeof(WAVE_FILE_HEADER)) - { - if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE) - { - if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM) - { - *pdwChannels = pWaveHdr->wChannels; - return true; - } - } - } - - return false; -} - - -static int WriteDataToMpqFile( - TMPQArchive * ha, - TMPQFile * hf, - LPBYTE pbFileData, - DWORD dwDataSize, - DWORD dwCompression) -{ - TFileEntry * pFileEntry = hf->pFileEntry; - ULONGLONG ByteOffset; - LPBYTE pbCompressed = NULL; // Compressed (target) data - LPBYTE pbToWrite = NULL; // Data to write to the file - int nCompressionLevel = -1; // ADPCM compression level (only used for wave files) - int nError = ERROR_SUCCESS; - - // If the caller wants ADPCM compression, we will set wave compression level to 4, - // which corresponds to medium quality - if(dwCompression & LOSSY_COMPRESSION_MASK) - nCompressionLevel = 4; - - // Make sure that the caller won't overrun the previously initiated file size - assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize); - assert(hf->dwSectorCount != 0); - assert(hf->pbFileSector != NULL); - if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize) - return ERROR_DISK_FULL; - pbToWrite = hf->pbFileSector; - - // Now write all data to the file sector buffer - if(nError == ERROR_SUCCESS) - { - DWORD dwBytesInSector = hf->dwFilePos % hf->dwSectorSize; - DWORD dwSectorIndex = hf->dwFilePos / hf->dwSectorSize; - DWORD dwBytesToCopy; - - // Process all data. - while(dwDataSize != 0) - { - dwBytesToCopy = dwDataSize; - - // Check for sector overflow - if(dwBytesToCopy > (hf->dwSectorSize - dwBytesInSector)) - dwBytesToCopy = (hf->dwSectorSize - dwBytesInSector); - - // Copy the data to the file sector - memcpy(hf->pbFileSector + dwBytesInSector, pbFileData, dwBytesToCopy); - dwBytesInSector += dwBytesToCopy; - pbFileData += dwBytesToCopy; - dwDataSize -= dwBytesToCopy; - - // Update the file position - hf->dwFilePos += dwBytesToCopy; - - // If the current sector is full, or if the file is already full, - // then write the data to the MPQ - if(dwBytesInSector >= hf->dwSectorSize || hf->dwFilePos >= pFileEntry->dwFileSize) - { - // Set the position in the file - ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize; - - // Update CRC32 and MD5 of the file - md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector); - hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector); - - // Compress the file sector, if needed - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) - { - int nOutBuffer = (int)dwBytesInSector; - int nInBuffer = (int)dwBytesInSector; - - // If the file is compressed, allocate buffer for the compressed data. - // Note that we allocate buffer that is a bit longer than sector size, - // for case if the compression method performs a buffer overrun - if(pbCompressed == NULL) - { - pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100); - if(pbCompressed == NULL) - { - nError = ERROR_NOT_ENOUGH_MEMORY; - break; - } - } - - // - // Note that both SCompImplode and SCompCompress give original buffer, - // if they are unable to comperss the data. - // - - if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) - { - SCompImplode((char *)pbCompressed, - &nOutBuffer, - (char *)hf->pbFileSector, - nInBuffer); - } - - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) - { - SCompCompress((char *)pbCompressed, - &nOutBuffer, - (char *)hf->pbFileSector, - nInBuffer, - (unsigned)dwCompression, - 0, - nCompressionLevel); - } - - // Update sector positions - dwBytesInSector = nOutBuffer; - if(hf->SectorOffsets != NULL) - hf->SectorOffsets[dwSectorIndex+1] = hf->SectorOffsets[dwSectorIndex] + dwBytesInSector; - - // We have to calculate sector CRC, if enabled - if(hf->SectorChksums != NULL) - hf->SectorChksums[dwSectorIndex] = adler32(0, pbCompressed, nOutBuffer); - } - - // Encrypt the sector, if necessary - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - BSWAP_ARRAY32_UNSIGNED(pbToWrite, dwBytesInSector); - EncryptMpqBlock(pbToWrite, dwBytesInSector, hf->dwFileKey + dwSectorIndex); - BSWAP_ARRAY32_UNSIGNED(pbToWrite, dwBytesInSector); - } - - // Write the file sector - if(!FileStream_Write(ha->pStream, &ByteOffset, pbToWrite, dwBytesInSector)) - { - nError = GetLastError(); - break; - } - - // Call the compact callback, if any - if(AddFileCB != NULL) - AddFileCB(pvUserData, hf->dwFilePos, hf->dwDataSize, false); - - // Update the compressed file size - pFileEntry->dwCmpSize += dwBytesInSector; - dwBytesInSector = 0; - dwSectorIndex++; - } - } - } - - // Cleanup - if(pbCompressed != NULL) - STORM_FREE(pbCompressed); - return nError; -} - -//----------------------------------------------------------------------------- -// Recrypts file data for file renaming - -static int RecryptFileData( - TMPQArchive * ha, - TMPQFile * hf, - const char * szFileName, - const char * szNewFileName) -{ - ULONGLONG RawFilePos; - TFileEntry * pFileEntry = hf->pFileEntry; - DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize; - DWORD dwOldKey; - DWORD dwNewKey; - int nError = ERROR_SUCCESS; - - // The file must be encrypted - assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); - - // File decryption key is calculated from the plain name - szNewFileName = GetPlainFileNameA(szNewFileName); - szFileName = GetPlainFileNameA(szFileName); - - // Calculate both file keys - dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); - dwNewKey = DecryptFileKey(szNewFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); - - // Incase the keys are equal, don't recrypt the file - if(dwNewKey == dwOldKey) - return ERROR_SUCCESS; - hf->dwFileKey = dwOldKey; - - // Calculate the raw position of the file in the archive - hf->MpqFilePos = pFileEntry->ByteOffset; - hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; - - // Allocate buffer for file transfer - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - return nError; - - // Also allocate buffer for sector offsets - // Note: Don't load sector checksums, we don't need to recrypt them - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS) - return nError; - - // If we have sector offsets, recrypt these as well - if(hf->SectorOffsets != NULL) - { - // Allocate secondary buffer for sectors copy - DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); - DWORD dwSectorOffsLen = hf->SectorOffsets[0]; - - if(SectorOffsetsCopy == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Recrypt the array of sector offsets - memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen); - EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwNewKey - 1); - BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen); - - // Write the recrypted array back - if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen)) - nError = GetLastError(); - STORM_FREE(SectorOffsetsCopy); - } - - // Now we have to recrypt all file sectors. We do it without - // recompression, because recompression is not necessary in this case - if(nError == ERROR_SUCCESS) - { - for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) - { - DWORD dwRawDataInSector = hf->dwSectorSize; - DWORD dwRawByteOffset = dwSector * hf->dwSectorSize; - - // Last sector: If there is not enough bytes remaining in the file, cut the raw size - if(dwRawDataInSector > dwBytesToRecrypt) - dwRawDataInSector = dwBytesToRecrypt; - - // Fix the raw data length if the file is compressed - if(hf->SectorOffsets != NULL) - { - dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector]; - dwRawByteOffset = hf->SectorOffsets[dwSector]; - } - - // Calculate the raw file offset of the file sector - CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); - - // Read the file sector - if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) - { - nError = GetLastError(); - break; - } - - // If necessary, re-encrypt the sector - // Note: Recompression is not necessary here. Unlike encryption, - // the compression does not depend on the position of the file in MPQ. - BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); - DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector); - EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwNewKey + dwSector); - BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); - - // Write the sector back - if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) - { - nError = GetLastError(); - break; - } - - // Decrement number of bytes remaining - dwBytesToRecrypt -= hf->dwSectorSize; - } - } - - return nError; -} - -//----------------------------------------------------------------------------- -// Support functions for adding files to the MPQ - -int SFileAddFile_Init( - TMPQArchive * ha, - const char * szFileName, - ULONGLONG FileTime, - DWORD dwFileSize, - LCID lcLocale, - DWORD dwFlags, - TMPQFile ** phf) -{ - TFileEntry * pFileEntry = NULL; - ULONGLONG TempPos; // For various file offset calculations - TMPQFile * hf = NULL; // File structure for newly added file - int nError = ERROR_SUCCESS; - - // - // Note: This is an internal function so no validity checks are done. - // It is the caller's responsibility to make sure that no invalid - // flags get to this point - // - - // Sestor CRC is not allowed with single unit files - if(dwFlags & MPQ_FILE_SINGLE_UNIT) - dwFlags &= ~MPQ_FILE_SECTOR_CRC; - - // Sector CRC is not allowed if the file is not compressed - if(!(dwFlags & MPQ_FILE_COMPRESSED)) - dwFlags &= ~MPQ_FILE_SECTOR_CRC; - - // Fix Key is not allowed if the file is not enrypted - if(!(dwFlags & MPQ_FILE_ENCRYPTED)) - dwFlags &= ~MPQ_FILE_FIX_KEY; - - // If the MPQ is of version 3.0 or higher, we ignore file locale. - // This is because HET and BET tables have no known support for it - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) - lcLocale = 0; - - // Allocate the TMPQFile entry for newly added file - hf = CreateMpqFile(ha); - if(hf == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - - // Find a free space in the MPQ, as well as free block table entry - if(nError == ERROR_SUCCESS) - { - // Find the position where the file will be stored - FindFreeMpqSpace(ha, &hf->MpqFilePos); - hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; - hf->bIsWriteHandle = true; - - // Sanity check: The MPQ must be marked as changed at this point - assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0); - - // When format V1, the size of the archive cannot exceed 4 GB - if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) - { - TempPos = hf->MpqFilePos + dwFileSize; - TempPos += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); - TempPos += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); - TempPos += ha->pHeader->dwBlockTableSize * sizeof(USHORT); - if((TempPos >> 32) != 0) - nError = ERROR_DISK_FULL; - } - } - - // Allocate file entry in the MPQ - if(nError == ERROR_SUCCESS) - { - // Check if the file already exists in the archive - pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale); - if(pFileEntry == NULL) - { - pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale); - if(pFileEntry == NULL) - nError = ERROR_DISK_FULL; - } - else - { - // If the file exists and "replace existing" is not set, fail it - if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0) - nError = ERROR_ALREADY_EXISTS; - - // If the file entry already contains a file - // and it is a pseudo-name, replace it - if(nError == ERROR_SUCCESS) - { - AllocateFileName(pFileEntry, szFileName); - } - } - } - - // - // At this point, the file name in file entry must be non-NULL - // - - // Create key for file encryption - if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) - { - hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); - } - - if(nError == ERROR_SUCCESS) - { - // Initialize the hash entry for the file - hf->pFileEntry = pFileEntry; - hf->dwDataSize = dwFileSize; - - // Initialize the block table entry for the file - pFileEntry->ByteOffset = hf->MpqFilePos; - pFileEntry->dwFileSize = dwFileSize; - pFileEntry->dwCmpSize = 0; - pFileEntry->dwFlags = dwFlags | MPQ_FILE_EXISTS; - pFileEntry->lcLocale = (USHORT)lcLocale; - - // Initialize the file time, CRC32 and MD5 - assert(sizeof(hf->hctx) >= sizeof(hash_state)); - memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); - md5_init((hash_state *)hf->hctx); - pFileEntry->dwCrc32 = crc32(0, Z_NULL, 0); - - // If the caller gave us a file time, use it. - pFileEntry->FileTime = FileTime; - - // Call the callback, if needed - if(AddFileCB != NULL) - AddFileCB(pvUserData, 0, hf->dwDataSize, false); - } - - // If an error occured, remember it - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; - *phf = hf; - return nError; -} - -int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression) -{ - TMPQArchive * ha; - TFileEntry * pFileEntry; - int nError = ERROR_SUCCESS; - - // Don't bother if the caller gave us zero size - if(pvData == NULL || dwSize == 0) - return ERROR_SUCCESS; - - // Get pointer to the MPQ archive - pFileEntry = hf->pFileEntry; - ha = hf->ha; - - // Allocate file buffers - if(hf->pbFileSector == NULL) - { - ULONGLONG RawFilePos = hf->RawFilePos; - - // Allocate buffer for file sector - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } - - // Allocate patch info, if the data is patch - if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize)) - { - // Set the MPQ_FILE_PATCH_FILE flag - hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; - - // Allocate the patch info - nError = AllocatePatchInfo(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } - } - - // Allocate sector offsets - if(hf->SectorOffsets == NULL) - { - nError = AllocateSectorOffsets(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } - } - - // Create array of sector checksums - if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)) - { - nError = AllocateSectorChecksums(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } - } - - // Pre-save the patch info, if any - if(hf->pPatchInfo != NULL) - { - if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength)) - nError = GetLastError(); - - pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength; - RawFilePos += hf->pPatchInfo->dwLength; - } - - // Pre-save the sector offset table, just to reserve space in the file. - // Note that we dont need to swap the sector positions, nor encrypt the table - // at the moment, as it will be written again after writing all file sectors. - if(hf->SectorOffsets != NULL) - { - if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, hf->SectorOffsets[0])) - nError = GetLastError(); - - pFileEntry->dwCmpSize += hf->SectorOffsets[0]; - RawFilePos += hf->SectorOffsets[0]; - } - } - - // Write the MPQ data to the file - if(nError == ERROR_SUCCESS) - nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression); - - // If it succeeded and we wrote all the file data, - // we need to re-save sector offset table - if(nError == ERROR_SUCCESS) - { - if(hf->dwFilePos >= pFileEntry->dwFileSize) - { - // Finish calculating CRC32 - hf->pFileEntry->dwCrc32 = hf->dwCrc32; - - // Finish calculating MD5 - md5_done((hash_state *)hf->hctx, hf->pFileEntry->md5); - - // If we also have sector checksums, write them to the file - if(hf->SectorChksums != NULL) - { - nError = WriteSectorChecksums(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; - } - - // Now write patch info - if(hf->pPatchInfo != NULL) - { - memcpy(hf->pPatchInfo->md5, hf->pFileEntry->md5, MD5_DIGEST_SIZE); - hf->pPatchInfo->dwDataSize = hf->pFileEntry->dwFileSize; - hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize; - nError = WritePatchInfo(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; - } - - // Now write sector offsets to the file - if(hf->SectorOffsets != NULL) - { - nError = WriteSectorOffsets(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; - } - - // Write the MD5 hashes of each file chunk, if required - if(ha->pHeader->dwRawChunkSize != 0) - { - nError = WriteMpqDataMD5(ha->pStream, - ha->MpqPos + hf->pFileEntry->ByteOffset, - hf->pFileEntry->dwCmpSize, - ha->pHeader->dwRawChunkSize); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; - } - } - } - else - { - hf->bErrorOccured = true; - } - - return nError; -} - -int SFileAddFile_Finish(TMPQFile * hf) -{ - TMPQArchive * ha = hf->ha; - TFileEntry * pFileEntry = hf->pFileEntry; - int nError = ERROR_SUCCESS; - - // If all previous operations succeeded, we can update the MPQ - if(!hf->bErrorOccured) - { - // Verify if the caller wrote the file properly - if(hf->pPatchInfo == NULL) - { - assert(pFileEntry != NULL); - if(hf->dwFilePos != pFileEntry->dwFileSize) - { - nError = ERROR_CAN_NOT_COMPLETE; - hf->bErrorOccured = true; - } - } - else - { - if(hf->dwFilePos != hf->pPatchInfo->dwDataSize) - { - nError = ERROR_CAN_NOT_COMPLETE; - hf->bErrorOccured = true; - } - } - } - - if(!hf->bErrorOccured) - { - // Call the user callback, if any - if(AddFileCB != NULL) - AddFileCB(pvUserData, hf->dwDataSize, hf->dwDataSize, true); - - // Update the size of the block table - ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; - } - else - { - // Free the file entry in MPQ tables - if(pFileEntry != NULL) - FreeFileEntry(ha, pFileEntry); - } - - // Clear the add file callback - FreeMPQFile(hf); - pvUserData = NULL; - AddFileCB = NULL; - return nError; -} - -//----------------------------------------------------------------------------- -// Adds data as file to the archive - -bool WINAPI SFileCreateFile( - HANDLE hMpq, - const char * szArchivedName, - ULONGLONG FileTime, - DWORD dwFileSize, - LCID lcLocale, - DWORD dwFlags, - HANDLE * phFile) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - int nError = ERROR_SUCCESS; - - // Check valid parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szArchivedName == NULL || *szArchivedName == 0) - nError = ERROR_INVALID_PARAMETER; - if(phFile == NULL) - nError = ERROR_INVALID_PARAMETER; - - // Don't allow to add file if the MPQ is open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; - - // Don't allow to add a file under pseudo-file name - if(IsPseudoFileName(szArchivedName, NULL)) - nError = ERROR_INVALID_PARAMETER; - - // Don't allow to add any of the internal files - if(IsInternalMpqFileName(szArchivedName)) - nError = ERROR_INTERNAL_FILE; - - // Perform validity check of the MPQ flags - if(nError == ERROR_SUCCESS) - { - // Mask all unsupported flags out - dwFlags &= MPQ_FILE_VALID_FLAGS; - - // Check for valid flag combinations - if((dwFlags & (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) == (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) - nError = ERROR_INVALID_PARAMETER; - } - - // Create the file in MPQ - if(nError == ERROR_SUCCESS) - { - // Invalidate the entries for (listfile) and (attributes) - // After we are done with MPQ changes, we need to re-create them anyway - InvalidateInternalFiles(ha); - - // Initiate the add file operation - nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile); - } - - // Deal with the errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -bool WINAPI SFileWriteFile( - HANDLE hFile, - const void * pvData, - DWORD dwSize, - DWORD dwCompression) -{ - TMPQFile * hf = (TMPQFile *)hFile; - int nError = ERROR_SUCCESS; - - // Check the proper parameters - if(!IsValidFileHandle(hf)) - nError = ERROR_INVALID_HANDLE; - if(hf->bIsWriteHandle == false) - nError = ERROR_INVALID_HANDLE; - - // Special checks for single unit files - if(nError == ERROR_SUCCESS && (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)) - { - // - // Note: Blizzard doesn't support single unit files - // that are stored as encrypted or imploded. We will allow them here, - // the calling application must ensure that such flag combination doesn't get here - // - -// if(dwFlags & MPQ_FILE_IMPLODE) -// nError = ERROR_INVALID_PARAMETER; -// -// if(dwFlags & MPQ_FILE_ENCRYPTED) -// nError = ERROR_INVALID_PARAMETER; - - // Lossy compression is not allowed on single unit files - if(dwCompression & LOSSY_COMPRESSION_MASK) - nError = ERROR_INVALID_PARAMETER; - } - - - // Write the data to the file - if(nError == ERROR_SUCCESS) - nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression); - - // Deal with errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -bool WINAPI SFileFinishFile(HANDLE hFile) -{ - TMPQFile * hf = (TMPQFile *)hFile; - int nError = ERROR_SUCCESS; - - // Check the proper parameters - if(!IsValidFileHandle(hf)) - nError = ERROR_INVALID_HANDLE; - if(hf->bIsWriteHandle == false) - nError = ERROR_INVALID_HANDLE; - - // Finish the file - if(nError == ERROR_SUCCESS) - nError = SFileAddFile_Finish(hf); - - // Deal with errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -//----------------------------------------------------------------------------- -// Adds a file to the archive - -bool WINAPI SFileAddFileEx( - HANDLE hMpq, - const TCHAR * szFileName, - const char * szArchivedName, - DWORD dwFlags, - DWORD dwCompression, // Compression of the first sector - DWORD dwCompressionNext) // Compression of next sectors -{ - ULONGLONG FileSize = 0; - ULONGLONG FileTime = 0; - TFileStream * pStream = NULL; - HANDLE hMpqFile = NULL; - LPBYTE pbFileData = NULL; - DWORD dwBytesRemaining = 0; - DWORD dwBytesToRead; - DWORD dwSectorSize = 0x1000; - DWORD dwChannels = 0; - bool bIsAdpcmCompression = false; - bool bIsFirstSector = true; - int nError = ERROR_SUCCESS; - - // Check parameters - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - - // Open added file - if(nError == ERROR_SUCCESS) - { - pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream == NULL) - nError = GetLastError(); - } - - // Get the file size and file time - if(nError == ERROR_SUCCESS) - { - FileStream_GetTime(pStream, &FileTime); - FileStream_GetSize(pStream, FileSize); - - // Files bigger than 4GB cannot be added to MPQ - if(FileSize >> 32) - nError = ERROR_DISK_FULL; - } - - // Allocate data buffer for reading from the source file - if(nError == ERROR_SUCCESS) - { - dwBytesRemaining = (DWORD)FileSize; - pbFileData = STORM_ALLOC(BYTE, dwSectorSize); - if(pbFileData == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Deal with various combination of compressions - if(nError == ERROR_SUCCESS) - { - // When the compression for next blocks is set to default, - // we will copy the compression for the first sector - if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME) - dwCompressionNext = dwCompression; - - // If the caller wants ADPCM compression, we make sure - // that the first sector is not compressed with lossy compression - if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) - { - // The first compression must not be WAVE - if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) - dwCompression = MPQ_COMPRESSION_PKWARE; - - dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); - bIsAdpcmCompression = true; - } - - // Initiate adding file to the MPQ - if(!SFileCreateFile(hMpq, szArchivedName, FileTime, (DWORD)FileSize, lcFileLocale, dwFlags, &hMpqFile)) - nError = GetLastError(); - } - - // Write the file data to the MPQ - while(nError == ERROR_SUCCESS && dwBytesRemaining != 0) - { - // Get the number of bytes remaining in the source file - dwBytesToRead = dwBytesRemaining; - if(dwBytesToRead > dwSectorSize) - dwBytesToRead = dwSectorSize; - - // Read data from the local file - if(!FileStream_Read(pStream, NULL, pbFileData, dwBytesToRead)) - { - nError = GetLastError(); - break; - } - - // If the file being added is a WAVE file, we check number of channels - if(bIsFirstSector && bIsAdpcmCompression) - { - // The file must really be a wave file, otherwise it's data corruption - if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels)) - { - nError = ERROR_BAD_FORMAT; - break; - } - - // Setup the compression according to number of channels - dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; - bIsFirstSector = false; - } - - // Add the file sectors to the MPQ - if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression)) - { - nError = GetLastError(); - break; - } - - // Set the next data compression - dwBytesRemaining -= dwBytesToRead; - dwCompression = dwCompressionNext; - } - - // Finish the file writing - if(hMpqFile != NULL) - { - if(!SFileFinishFile(hMpqFile)) - nError = GetLastError(); - } - - // Cleanup and exit - if(pbFileData != NULL) - STORM_FREE(pbFileData); - if(pStream != NULL) - FileStream_Close(pStream); - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -// Adds a data file into the archive -bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags) -{ - return SFileAddFileEx(hMpq, - szFileName, - szArchivedName, - dwFlags, - DefaultDataCompression, - DefaultDataCompression); -} - -// Adds a WAVE file into the archive -bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality) -{ - DWORD dwCompression = 0; - - // - // Note to wave compression level: - // The following conversion table applied: - // High quality: WaveCompressionLevel = -1 - // Medium quality: WaveCompressionLevel = 4 - // Low quality: WaveCompressionLevel = 2 - // - // Starcraft files are packed as Mono (0x41) on medium quality. - // Because this compression is not used anymore, our compression functions - // will default to WaveCompressionLevel = 4 when using ADPCM compression - // - - // Convert quality to data compression - switch(dwQuality) - { - case MPQ_WAVE_QUALITY_HIGH: -// WaveCompressionLevel = -1; - dwCompression = MPQ_COMPRESSION_PKWARE; - break; - - case MPQ_WAVE_QUALITY_MEDIUM: -// WaveCompressionLevel = 4; - dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; - break; - - case MPQ_WAVE_QUALITY_LOW: -// WaveCompressionLevel = 2; - dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; - break; - } - - return SFileAddFileEx(hMpq, - szFileName, - szArchivedName, - dwFlags, - MPQ_COMPRESSION_PKWARE, // First sector should be compressed as data - dwCompression); // Next sectors should be compressed as WAVE -} - -//----------------------------------------------------------------------------- -// bool SFileRemoveFile(HANDLE hMpq, char * szFileName) -// -// This function removes a file from the archive. The file content -// remains there, only the entries in the hash table and in the block -// table are updated. - -bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; // File entry of the file to be deleted - DWORD dwFileIndex = 0; - int nError = ERROR_SUCCESS; - - // Keep compiler happy - dwSearchScope = dwSearchScope; - - // Check the parameters - if(nError == ERROR_SUCCESS) - { - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - if(IsInternalMpqFileName(szFileName)) - nError = ERROR_INTERNAL_FILE; - } - - if(nError == ERROR_SUCCESS) - { - // Do not allow to remove files from MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; - } - - // Get hash entry belonging to this file - if(nError == ERROR_SUCCESS) - { - if(!IsPseudoFileName(szFileName, &dwFileIndex)) - { - if((pFileEntry = GetFileEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - else - { - if((pFileEntry = GetFileEntryByIndex(ha, dwFileIndex)) == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - } - - // Test if the file is not already deleted - if(nError == ERROR_SUCCESS) - { - if(!(pFileEntry->dwFlags & MPQ_FILE_EXISTS)) - nError = ERROR_FILE_NOT_FOUND; - } - - if(nError == ERROR_SUCCESS) - { - // Invalidate the entries for (listfile) and (attributes) - // After we are done with MPQ changes, we need to re-create them anyway - InvalidateInternalFiles(ha); - - // Mark the file entry as free - nError = FreeFileEntry(ha, pFileEntry); - } - - // Resolve error and exit - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -// Renames the file within the archive. -bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; - ULONGLONG RawDataOffs; - TMPQFile * hf; - int nError = ERROR_SUCCESS; - - // Test the valid parameters - if(nError == ERROR_SUCCESS) - { - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) - nError = ERROR_INVALID_PARAMETER; - } - - if(nError == ERROR_SUCCESS) - { - // Do not allow to rename files in MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; - - // Do not allow renaming anything to a pseudo-file name - if(IsPseudoFileName(szFileName, NULL) || IsPseudoFileName(szNewFileName, NULL)) - nError = ERROR_INVALID_PARAMETER; - - // Do not allow to rename any of the internal files - // Also do not allow to rename any of files to an internal file - if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) - nError = ERROR_INTERNAL_FILE; - } - - // Find the current file entry. - if(nError == ERROR_SUCCESS) - { - // Get the file entry - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - - // Also try to find file entry for the new file. - // This verifies if we are not overwriting an existing file - // (whose name we perhaps don't know) - if(nError == ERROR_SUCCESS) - { - if(GetFileEntryLocale(ha, szNewFileName, pFileEntry->lcLocale) != NULL) - nError = ERROR_ALREADY_EXISTS; - } - - // Now we rename the existing file entry. - if(nError == ERROR_SUCCESS) - { - // Rename the file entry - nError = RenameFileEntry(ha, pFileEntry, szNewFileName); - } - - // Now we copy the existing file entry to the new one - if(nError == ERROR_SUCCESS) - { - // If the file is encrypted, we have to re-crypt the file content - // with the new decryption key - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - hf = CreateMpqFile(ha); - if(hf != NULL) - { - // Recrypt the file data in the MPQ - hf->pFileEntry = pFileEntry; - hf->dwDataSize = pFileEntry->dwFileSize; - nError = RecryptFileData(ha, hf, szFileName, szNewFileName); - - // Update the MD5 - if(ha->pHeader->dwRawChunkSize != 0) - { - RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; - WriteMpqDataMD5(ha->pStream, - RawDataOffs, - pFileEntry->dwCmpSize, - ha->pHeader->dwRawChunkSize); - } - - FreeMPQFile(hf); - } - else - { - nError = ERROR_NOT_ENOUGH_MEMORY; - } - } - } - - // - // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry - // - - // Resolve error and return - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -//----------------------------------------------------------------------------- -// Sets default data compression for SFileAddFile - -bool WINAPI SFileSetDataCompression(DWORD DataCompression) -{ - unsigned int uValidMask = (MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_PKWARE | MPQ_COMPRESSION_BZIP2 | MPQ_COMPRESSION_SPARSE); - - if((DataCompression & uValidMask) != DataCompression) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - DefaultDataCompression = DataCompression; - return true; -} - -//----------------------------------------------------------------------------- -// Changes locale ID of a file - -bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) -{ - TMPQArchive * ha; - TFileEntry * pFileEntry; - TMPQFile * hf = (TMPQFile *)hFile; - - // Invalid handle => do nothing - if(!IsValidFileHandle(hf)) - { - SetLastError(ERROR_INVALID_HANDLE); - return false; - } - - // Do not allow unnamed access - if(hf->pFileEntry->szFileName == NULL) - { - SetLastError(ERROR_CAN_NOT_COMPLETE); - return false; - } - - // Do not allow to change locale of any internal file - if(IsInternalMpqFileName(hf->pFileEntry->szFileName)) - { - SetLastError(ERROR_INTERNAL_FILE); - return false; - } - - // Do not allow changing file locales in MPQs version 3 or higher - ha = hf->ha; - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) - { - SetLastError(ERROR_NOT_SUPPORTED); - return false; - } - - // Do not allow to rename files in MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - { - SetLastError(ERROR_ACCESS_DENIED); - return false; - } - - // If the file already has that locale, return OK - if(hf->pFileEntry->lcLocale == lcNewLocale) - return true; - - // We have to check if the file+locale is not already there - pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale); - if(pFileEntry != NULL) - { - SetLastError(ERROR_ALREADY_EXISTS); - return false; - } - - // Set the locale and return success - pFileEntry = hf->pFileEntry; - pFileEntry->lcLocale = (USHORT)lcNewLocale; - - // Save the new locale to the hash table, if any - if(ha->pHashTable != NULL) - ha->pHashTable[pFileEntry->dwHashIndex].lcLocale = (USHORT)lcNewLocale; - - // Remember that the MPQ tables have been changed - ha->dwFlags |= MPQ_FLAG_CHANGED; - return true; -} - -//----------------------------------------------------------------------------- -// Sets add file callback - -bool WINAPI SFileSetAddFileCallback(HANDLE /* hMpq */, SFILE_ADDFILE_CALLBACK aAddFileCB, void * pvData) -{ - pvUserData = pvData; - AddFileCB = aAddFileCB; - return true; -} |