diff options
Diffstat (limited to 'src/SFileAddFile.cpp')
-rw-r--r-- | src/SFileAddFile.cpp | 194 |
1 files changed, 89 insertions, 105 deletions
diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index 775a969..bb8f4f1 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -85,23 +85,17 @@ static int WriteDataToMpqFile( { 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) + LPBYTE pbCompressed = NULL; // Compressed (target) data + LPBYTE pbToWrite = hf->pbFileSector; // Data to write to the file + int nCompressionLevel; // 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) @@ -159,8 +153,8 @@ static int WriteDataToMpqFile( } // - // Note that both SCompImplode and SCompCompress give original buffer, - // if they are unable to comperss the data. + // Note that both SCompImplode and SCompCompress copy data as-is, + // if they are unable to compress the data. // if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) @@ -170,6 +164,21 @@ static int WriteDataToMpqFile( if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { + // If this is the first sector, we need to override the given compression + // by the first sector compression. This is because the entire sector must + // be compressed by the same compression. + // + // Test case: + // + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_PKWARE) // Write 0x10 bytes (sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + dwCompression = (dwSectorIndex == 0) ? hf->dwCompression0 : dwCompression; + + // If the caller wants ADPCM compression, we will set wave compression level to 4, + // which corresponds to medium quality + nCompressionLevel = (dwCompression & LOSSY_COMPRESSION_MASK) ? 4 : -1; SCompCompress(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer, (unsigned)dwCompression, 0, nCompressionLevel); } @@ -236,8 +245,8 @@ static int RecryptFileData( assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); // File decryption key is calculated from the plain name - szNewFileName = GetPlainFileNameA(szNewFileName); - szFileName = GetPlainFileNameA(szFileName); + szNewFileName = GetPlainFileName(szNewFileName); + szFileName = GetPlainFileName(szFileName); // Calculate both file keys dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); @@ -382,7 +391,7 @@ int SFileAddFile_Init( if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; - // Find a free space in the MPQ, as well as free block table entry + // Find a free space in the MPQ and verify if it's not over 4 GB on MPQs v1 if(nError == ERROR_SUCCESS) { // Find the position where the file will be stored @@ -390,9 +399,6 @@ int SFileAddFile_Init( 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) { @@ -418,38 +424,26 @@ int SFileAddFile_Init( } else { - // Only if the file really exists - if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) - { - // 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); - } - } + // If the caller didn't set MPQ_FILE_REPLACEEXISTING, fail it + if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0) + nError = ERROR_ALREADY_EXISTS; } } - // - // 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); - } - + // Fill the file entry and TMPQFile structure if(nError == ERROR_SUCCESS) { + // At this point, the file name in the file entry must be set + assert(pFileEntry->szFileName != NULL); + assert(_stricmp(pFileEntry->szFileName, szFileName) == 0); + // Initialize the hash entry for the file hf->pFileEntry = pFileEntry; hf->dwDataSize = dwFileSize; + + // Decrypt the file key + if(dwFlags & MPQ_FILE_ENCRYPTED) + hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); // Initialize the block table entry for the file pFileEntry->ByteOffset = hf->MpqFilePos; @@ -467,14 +461,17 @@ int SFileAddFile_Init( // If the caller gave us a file time, use it. pFileEntry->FileTime = FileTime; + // Mark the archive as modified + ha->dwFlags |= MPQ_FLAG_CHANGED; + // Call the callback, if needed if(ha->pfnAddFileCB != NULL) ha->pfnAddFileCB(ha->pvAddFileUserData, 0, hf->dwDataSize, false); } - // If an error occured, remember it - if(nError != ERROR_SUCCESS && hf != NULL) - hf->bErrorOccured = true; + // Store the error code from Add File operation + if(hf != NULL) + hf->nAddFileError = nError; *phf = hf; return nError; } @@ -499,12 +496,9 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d ULONGLONG RawFilePos = hf->RawFilePos; // Allocate buffer for file sector - nError = AllocateSectorBuffer(hf); + hf->nAddFileError = 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)) @@ -513,34 +507,25 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; // Allocate the patch info - nError = AllocatePatchInfo(hf, false); + hf->nAddFileError = nError = AllocatePatchInfo(hf, false); if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; return nError; - } } // Allocate sector offsets if(hf->SectorOffsets == NULL) { - nError = AllocateSectorOffsets(hf, false); + hf->nAddFileError = 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); + hf->nAddFileError = nError = AllocateSectorChecksums(hf, false); if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; return nError; - } } // Pre-save the patch info, if any @@ -568,7 +553,16 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d // Write the MPQ data to the file if(nError == ERROR_SUCCESS) + { + // Save the first sector compression to the file structure + // Note that the entire first file sector will be compressed + // by compression that was passed to the first call of SFileAddFile_Write + if(hf->dwFilePos == 0) + hf->dwCompression0 = dwCompression; + + // Write the data to the MPQ 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 @@ -586,8 +580,6 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d if(hf->SectorChksums != NULL) { nError = WriteSectorChecksums(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; } // Now write patch info @@ -597,16 +589,12 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d 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 @@ -616,16 +604,12 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d ha->MpqPos + hf->pFileEntry->ByteOffset, hf->pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; } } } - else - { - hf->bErrorOccured = true; - } + // Store the error code from the Write File operation + hf->nAddFileError = nError; return nError; } @@ -633,45 +617,43 @@ int SFileAddFile_Finish(TMPQFile * hf) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; - int nError = ERROR_SUCCESS; + int nError = hf->nAddFileError; // If all previous operations succeeded, we can update the MPQ - if(!hf->bErrorOccured) + if(nError == ERROR_SUCCESS) { // 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) + // Now we need to recreate the HET table, if exists + if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) + { + nError = RebuildHetTable(ha); + } + + // Update the block table size + if(nError == ERROR_SUCCESS) { // Call the user callback, if any if(ha->pfnAddFileCB != NULL) ha->pfnAddFileCB(ha->pvAddFileUserData, 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); + DeleteFileEntry(ha, pFileEntry); } // Clear the add file callback @@ -695,7 +677,7 @@ bool WINAPI SFileCreateFile( int nError = ERROR_SUCCESS; // Check valid parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(szArchivedName == NULL || *szArchivedName == 0) nError = ERROR_INVALID_PARAMETER; @@ -725,16 +707,9 @@ bool WINAPI SFileCreateFile( nError = ERROR_INVALID_PARAMETER; } - // Create the file in MPQ + // Initiate the add file operation 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) @@ -752,7 +727,7 @@ bool WINAPI SFileWriteFile( int nError = ERROR_SUCCESS; // Check the proper parameters - if(!IsValidFileHandle(hf)) + if(!IsValidFileHandle(hFile)) nError = ERROR_INVALID_HANDLE; if(hf->bIsWriteHandle == false) nError = ERROR_INVALID_HANDLE; @@ -794,7 +769,7 @@ bool WINAPI SFileFinishFile(HANDLE hFile) int nError = ERROR_SUCCESS; // Check the proper parameters - if(!IsValidFileHandle(hf)) + if(!IsValidFileHandle(hFile)) nError = ERROR_INVALID_HANDLE; if(hf->bIsWriteHandle == false) nError = ERROR_INVALID_HANDLE; @@ -881,6 +856,8 @@ bool WINAPI SFileAddFileEx( if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; + // Remove both flag mono and stereo flags. + // They will be re-added according to WAVE type dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); bIsAdpcmCompression = true; } @@ -908,7 +885,7 @@ bool WINAPI SFileAddFileEx( // 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 + // The file must really be a wave file, otherwise it will corrupt the file if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels)) { nError = ERROR_BAD_FORMAT; @@ -1024,7 +1001,7 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch // Check the parameters if(nError == ERROR_SUCCESS) { - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; @@ -1067,8 +1044,12 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch // 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); + // Delete the file entry in the file table and defragment the file table + DeleteFileEntry(ha, pFileEntry); + + // We also need to rebuild the HET table, if present + if(ha->pHetTable != NULL) + nError = RebuildHetTable(ha); } // Resolve error and exit @@ -1089,7 +1070,7 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s // Test the valid parameters if(nError == ERROR_SUCCESS) { - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) nError = ERROR_INVALID_PARAMETER; @@ -1136,6 +1117,10 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s nError = RenameFileEntry(ha, pFileEntry, szNewFileName); } + // Now we need to recreate the HET table, if we have one + if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) + nError = RebuildHetTable(ha); + // Now we copy the existing file entry to the new one if(nError == ERROR_SUCCESS) { @@ -1170,9 +1155,8 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s } } - // // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry - // + // assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0); // Resolve error and return if(nError != ERROR_SUCCESS) @@ -1207,7 +1191,7 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) TMPQFile * hf = (TMPQFile *)hFile; // Invalid handle => do nothing - if(!IsValidFileHandle(hf)) + if(!IsValidFileHandle(hFile)) { SetLastError(ERROR_INVALID_HANDLE); return false; @@ -1274,7 +1258,7 @@ bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileC { TMPQArchive * ha = (TMPQArchive *) hMpq; - if (!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) { SetLastError(ERROR_INVALID_HANDLE); return false; |