diff options
author | Ladislav <Zezula> | 2013-12-15 10:29:50 +0100 |
---|---|---|
committer | Ladislav <Zezula> | 2013-12-15 10:29:50 +0100 |
commit | fe51da468be9ca0963192c5f3a1705562bc957ba (patch) | |
tree | 767edeed5f8cd2b4561317724efdb2d1b2547d7b | |
parent | 6961cd51b65e8df7f807e25ab6ffc4da8c205449 (diff) |
+ static analysis issues fixed
-rw-r--r-- | StormLib.xcodeproj/project.pbxproj | 2 | ||||
-rw-r--r-- | src/FileStream.cpp | 10 | ||||
-rw-r--r-- | src/SBaseCommon.cpp | 5 | ||||
-rw-r--r-- | src/SBaseFileTable.cpp | 9 | ||||
-rw-r--r-- | src/SFileAddFile.cpp | 2 | ||||
-rw-r--r-- | src/SFileCompactArchive.cpp | 5 | ||||
-rw-r--r-- | src/SFileGetFileInfo.cpp | 9 | ||||
-rw-r--r-- | src/SFileListFile.cpp | 20 | ||||
-rw-r--r-- | src/SFilePatchArchives.cpp | 1 | ||||
-rw-r--r-- | src/SFileReadFile.cpp | 2 | ||||
-rw-r--r-- | src/StormPort.h | 1 | ||||
-rw-r--r-- | test/TLogHelper.cpp | 20 | ||||
-rw-r--r-- | test/Test.cpp | 614 |
13 files changed, 463 insertions, 237 deletions
diff --git a/StormLib.xcodeproj/project.pbxproj b/StormLib.xcodeproj/project.pbxproj index 7ee4f29..2cf44e7 100644 --- a/StormLib.xcodeproj/project.pbxproj +++ b/StormLib.xcodeproj/project.pbxproj @@ -1855,7 +1855,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.6; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; - SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; }; name = Debug; @@ -1875,7 +1874,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; SDKROOT = macosx; - SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = YES; }; name = Release; diff --git a/src/FileStream.cpp b/src/FileStream.cpp index a081bd7..eaf85de 100644 --- a/src/FileStream.cpp +++ b/src/FileStream.cpp @@ -182,7 +182,7 @@ static bool BaseFile_Read( // we have to update the file position if(ByteOffset != pStream->Base.File.FilePos) { - lseek64((intptr_t)pStream->Base.File.hFile, (__off64_t)(ByteOffset), SEEK_SET); + lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET); pStream->Base.File.FilePos = ByteOffset; } @@ -271,7 +271,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const // we have to update the file position if(ByteOffset != pStream->Base.File.FilePos) { - lseek64((intptr_t)pStream->Base.File.hFile, (__off64_t)(ByteOffset), SEEK_SET); + lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET); pStream->Base.File.FilePos = ByteOffset; } @@ -346,7 +346,7 @@ static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) { - if(ftruncate64((intptr_t)pStream->Base.File.hFile, (__off64_t)NewFileSize) == -1) + if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1) { nLastError = errno; return false; @@ -1134,7 +1134,6 @@ static bool PartialStream_Read( DWORD dwPartIndex; DWORD dwBytesRead = 0; DWORD dwBlockSize = pStream->BlockSize; - bool bResult = false; int nFailReason = ERROR_HANDLE_EOF; // Why it failed if not enough bytes was read // If the byte offset is not entered, use the current position @@ -1172,7 +1171,6 @@ static bool PartialStream_Read( if((PartMap->Flags & 3) == 0) { nFailReason = ERROR_FILE_CORRUPT; - bResult = false; break; } @@ -1188,7 +1186,6 @@ static bool PartialStream_Read( if(RawByteOffset == 0) { nFailReason = ERROR_FILE_CORRUPT; - bResult = false; break; } @@ -1201,7 +1198,6 @@ static bool PartialStream_Read( if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) { nFailReason = ERROR_FILE_CORRUPT; - bResult = false; break; } diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index a0e789b..bb580b5 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -371,6 +371,7 @@ DWORD DetectFileKeyByKnownContent(void * pvFileContent, DWORD nDwords, ...) // We need at least two DWORDS to detect the file key if(nDwords < 0x02 || nDwords > 0x10) return 0; + memset(dwDecrypted, 0, sizeof(dwDecrypted)); va_start(argList, nDwords); for(i = 0; i < nDwords; i++) @@ -639,7 +640,7 @@ ULONGLONG FindFreeMpqSpace(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - TFileEntry * pFileEntry = ha->pFileTable; + TFileEntry * pFileEntry; ULONGLONG FreeSpacePos = ha->pHeader->dwHeaderSize; DWORD dwChunkCount; @@ -952,7 +953,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) __LoadSectorOffsets: // Allocate the sector offset table - hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen); + hf->SectorOffsets = STORM_ALLOC(DWORD, (dwSectorOffsLen / sizeof(DWORD))); if(hf->SectorOffsets == NULL) return ERROR_NOT_ENOUGH_MEMORY; diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index d4155bd..d88c5f9 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -499,7 +499,7 @@ int ConvertMpqHeaderToFormat4( if(pHeader->HetTablePos64) { pHeader->HetTableSize64 = ByteOffset - pHeader->HetTablePos64; - ByteOffset = pHeader->HetTablePos64; +// ByteOffset = pHeader->HetTablePos64; } break; @@ -1479,7 +1479,7 @@ static TMPQBetTable * TranslateBetTable( LengthInBytes = (pBetTable->pNameHashes->NumberOfBits + 7) / 8; if(pBetTable->pNameHashes != NULL) memcpy(pBetTable->pNameHashes->Elements, pbSrcData, LengthInBytes); - pbSrcData += pBetHeader->dwNameHashArraySize; +// pbSrcData += pBetHeader->dwNameHashArraySize; // Dump both tables // DumpHetAndBetTable(ha->pHetTable, pBetTable); @@ -1592,7 +1592,7 @@ TMPQExtHeader * TranslateBetTable( // Write the array of BET hashes LengthInBytes = (pBitArray->NumberOfBits + 7) / 8; memcpy(pbTrgData, pBitArray->Elements, LengthInBytes); - pbTrgData += LengthInBytes; +// pbTrgData += LengthInBytes; // Free the bit array STORM_FREE(pBitArray); @@ -2726,6 +2726,7 @@ int RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize, DWORD dwNewMaxF // Set the new tables to the MPQ archive ha->pFileTable = pFileTable; ha->pHashTable = pHashTable; + pFileTable = NULL; // Set the new limits to the MPQ archive ha->pHeader->dwHashTableSize = dwNewHashTableSize; @@ -2763,6 +2764,8 @@ int RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize, DWORD dwNewMaxF STORM_FREE(pOldFileTable); if(pOldHashTable != NULL) STORM_FREE(pOldHashTable); + if(pFileTable != NULL) + STORM_FREE(pFileTable); return nError; } diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index bb8f4f1..5246f89 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -276,7 +276,7 @@ static int RecryptFileData( if(hf->SectorOffsets != NULL) { // Allocate secondary buffer for sectors copy - DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD)); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; if(SectorOffsetsCopy == NULL) diff --git a/src/SFileCompactArchive.cpp b/src/SFileCompactArchive.cpp index ad9801b..5ca4065 100644 --- a/src/SFileCompactArchive.cpp +++ b/src/SFileCompactArchive.cpp @@ -118,7 +118,7 @@ static int CopyNonMpqData( DataSize -= dwToRead; } - return ERROR_SUCCESS; + return nError; } // Copies all file sectors into another archive. @@ -167,7 +167,7 @@ static int CopyMpqFileSectors( // If we have to save sector offset table, do it. if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL) { - DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD)); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0); @@ -311,7 +311,6 @@ static int CopyMpqFileSectors( // Include these extra data in the compressed size dwCmpSize += dwBytesToCopy; - dwBytesToCopy = 0; STORM_FREE(pbExtraData); } else diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 07b7722..da47fc9 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -143,6 +143,7 @@ bool WINAPI SFileGetFileInfo( TFileEntry * pFileEntry = NULL; ULONGLONG Int64Value = 0; ULONGLONG ByteOffset = 0; + TMPQHash * pHash; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; @@ -691,7 +692,8 @@ bool WINAPI SFileGetFileInfo( hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { - pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1; + pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; + pvSrcFileInfo = &pHash->dwName1; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } @@ -701,7 +703,8 @@ bool WINAPI SFileGetFileInfo( hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { - pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2; + pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; + pvSrcFileInfo = &pHash->dwName2; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } @@ -825,7 +828,7 @@ bool WINAPI SFileGetFileInfo( pcbLengthNeeded[0] = cbSrcFileInfo; // If the caller entered an output buffer, the output size must also be entered - if(pvFileInfo != NULL && cbFileInfo != 0) + if(pvSrcFileInfo != NULL && pvFileInfo != NULL && cbFileInfo != 0) { // Check if there is enough space in the output buffer if(cbSrcFileInfo <= cbFileInfo) diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp index 896b341..c1197c9 100644 --- a/src/SFileListFile.cpp +++ b/src/SFileListFile.cpp @@ -138,7 +138,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache) // Load the next data chunk to the cache SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN); - SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); + SFileReadFile(pCache->hFile, pCache->Buffer, dwBytesToRead, &dwBytesRead, NULL); // If we didn't read anything, it might mean that the block // of the file is not available (in case of partial MPQs). @@ -327,7 +327,6 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; - bool bNameEntryCreated = false; // If we have HET table, use that one if(ha->pHetTable != NULL) @@ -337,14 +336,13 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil { // Allocate file name for the file entry AllocateFileName(ha, pFileEntry, szFileName); - bNameEntryCreated = true; } return ERROR_SUCCESS; } // If we have hash table, we use it - if(bNameEntryCreated == false && ha->pHashTable != NULL) + if(ha->pHashTable != NULL) { // Look for the first hash table entry for the file pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); @@ -357,7 +355,6 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil { // Allocate file name for the file entry AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName); - bNameEntryCreated = true; } // Now find the next language version of the file @@ -599,17 +596,20 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const } } + // Close the listfile + if(hListFile != NULL) + SFileCloseFile(hListFile); + // Cleanup & exit if(nError != ERROR_SUCCESS) { + if(pCache != NULL) + FreeListFileCache(pCache); + pCache = NULL; + memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); SetLastError(nError); } - - if(pCache != NULL) - FreeListFileCache(pCache); - if(hListFile != NULL) - SFileCloseFile(hListFile); return (HANDLE)pCache; } diff --git a/src/SFilePatchArchives.cpp b/src/SFilePatchArchives.cpp index 7f67749..cc10726 100644 --- a/src/SFilePatchArchives.cpp +++ b/src/SFilePatchArchives.cpp @@ -65,7 +65,6 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p // Cut the initial DWORD from the compressed chunk pbCompressed += sizeof(DWORD); - cbCompressed -= sizeof(DWORD); // Pre-fill decompressed buffer with zeros memset(pbDecompressed, 0, cbDecompressed); diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp index ff4783c..1e1ae56 100644 --- a/src/SFileReadFile.cpp +++ b/src/SFileReadFile.cpp @@ -301,7 +301,7 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos } else { - if(pbRawData != hf->pbFileSector) + if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector) memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); } diff --git a/src/StormPort.h b/src/StormPort.h index 83d8624..38726ab 100644 --- a/src/StormPort.h +++ b/src/StormPort.h @@ -186,6 +186,7 @@ #define stat64 stat #define fstat64 fstat #define lseek64 lseek + #define ftruncate64 ftruncate #define off64_t off_t #define O_LARGEFILE 0 #endif diff --git a/test/TLogHelper.cpp b/test/TLogHelper.cpp index 8bfb5fd..229f9fb 100644 --- a/test/TLogHelper.cpp +++ b/test/TLogHelper.cpp @@ -38,6 +38,7 @@ class TLogHelper const char * UserString; unsigned int UserCount; unsigned int UserTotal; + bool bDontPrintResult; protected: @@ -78,14 +79,15 @@ TLogHelper::TLogHelper(const char * szNewTestTitle, const char * szNewSubTitle) szSubTitle = szNewSubTitle; nTextLength = 0; bMessagePrinted = false; + bDontPrintResult = false; // Print the initial information if(szMainTitle != NULL) { if(szSubTitle != NULL) - printf("Running test %s (%s) ...", szMainTitle, szSubTitle); + printf("Running %s (%s) ...", szMainTitle, szSubTitle); else - printf("Running test %s ...", szMainTitle); + printf("Running %s ...", szMainTitle); } } @@ -101,10 +103,18 @@ TLogHelper::~TLogHelper() // Print the final information if(szSaveMainTitle != NULL && bMessagePrinted == false) { - if(szSaveSubTitle != NULL) - PrintMessage("The test %s (%s) succeeded.", szSaveMainTitle, szSaveSubTitle); + if(bDontPrintResult == false) + { + if(szSaveSubTitle != NULL) + PrintMessage("%s (%s) succeeded.", szSaveMainTitle, szSaveSubTitle); + else + PrintMessage("%s succeeded.", szSaveMainTitle); + } else - PrintMessage("The test %s succeeded.", szSaveMainTitle); + { + PrintProgress(" "); + printf("\r"); + } } } diff --git a/test/Test.cpp b/test/Test.cpp index 061d432..7566fbe 100644 --- a/test/Test.cpp +++ b/test/Test.cpp @@ -44,10 +44,11 @@ #endif // Global for the work MPQ -static const char * szMpqSubDir = "1996 - Test MPQs"; -static const char * szMpqPatchDir = "1996 - Test MPQs\\patches"; +static const char * szMpqSubDir = "1995 - Test MPQs"; +static const char * szMpqPatchDir = "1995 - Test MPQs\\patches"; -typedef int (*ARCHIVE_TEST)(const char * szMpqName); +typedef int (*FIND_FILE_CALLBACK)(const char * szFullPath); +typedef int (*FIND_PAIR_CALLBACK)(const char * szFullPath1, const char * szFullPath2); //----------------------------------------------------------------------------- // Testing data @@ -195,20 +196,13 @@ static bool IsMpqExtension(const char * szFileName) return true; if(!_stricmp(szExtension, ".SC2Map")) return true; + if(!_stricmp(szExtension, ".link")) + return true; } return false; } -static bool IsUnicodeNameConvertableToAnsi(const TCHAR * szFileNameT, const char * szFileNameA) -{ - TCHAR szUnicodeName[MAX_PATH]; - - // Convert the ANSI to UNICODE and compare them - CopyFileName(szUnicodeName, szFileNameA, strlen(szFileNameA)); - return (_tcsicmp(szUnicodeName, szFileNameT) == 0); -} - static size_t ConvertSha1ToText(const unsigned char * sha1_digest, char * szSha1Text) { const char * szTable = "0123456789abcdef"; @@ -224,25 +218,119 @@ static size_t ConvertSha1ToText(const unsigned char * sha1_digest, char * szSha1 return (SHA1_DIGEST_SIZE * 2); } -static const char * GetShortPlainName(const char * szFileName) +static int GetPathSeparatorCount(const char * szPath) { - const char * szPlainName = szFileName; - const char * szPlainEnd = szFileName + strlen(szFileName); + int nSeparatorCount = 0; - // If there is terminating slash or backslash, move to it - while(szFileName < szPlainEnd) + while(szPath[0] != 0) { - if(szFileName[0] == '\\' || szFileName[0] == '/') - szPlainName = szFileName + 1; - szFileName++; + if(szPath[0] == '\\' || szPath[0] == '/') + nSeparatorCount++; + szPath++; } + return nSeparatorCount; +} + +static const char * FindNextPathPart(const char * szPath, size_t nPartCount) +{ + const char * szPathPart = szPath; + + while(szPath[0] != 0 && nPartCount > 0) + { + // Is there path separator? + if(szPath[0] == '\\' || szPath[0] == '/') + { + szPathPart = szPath + 1; + nPartCount--; + } + + // Move to the next letter + szPath++; + } + + return szPathPart; +} + +static const char * GetShortPlainName(const char * szFileName) +{ + const char * szPlainName = FindNextPathPart(szFileName, 1000); + const char * szPlainEnd = szFileName + strlen(szFileName); + // If the name is still too long, cut it if((szPlainEnd - szPlainName) > 50) szPlainName = szPlainEnd - 50; return szPlainName; } +static void CopyPathPart(char * szBuffer, const char * szPath) +{ + while(szPath[0] != 0) + { + szBuffer[0] = (szPath[0] == '\\' || szPath[0] == '/') ? '/' : szPath[0]; + szBuffer++; + szPath++; + } + + *szBuffer = 0; +} + +static void CalculateRelativePath(const char * szFullPath1, const char * szFullPath2, char * szBuffer) +{ + const char * szPathPart1 = szFullPath1; + const char * szPathPart2 = szFullPath2; + const char * szNextPart1; + const char * szNextPart2; + int nEqualParts = 0; + int nStepsUp = 0; + + // Parse both paths and find all path parts that are equal + for(;;) + { + // Find the next part of the first path + szNextPart1 = FindNextPathPart(szPathPart1, 1); + if(szNextPart1 == szPathPart1) + break; + + szNextPart2 = FindNextPathPart(szPathPart2, 1); + if(szNextPart2 == szPathPart2) + break; + + // Are these equal? + if((szNextPart2 - szPathPart2) != (szNextPart1 - szPathPart1)) + break; + if(_strnicmp(szPathPart1, szPathPart2, (szNextPart1 - szPathPart1 - 1))) + break; + + // Increment the number of path parts that are equal + szPathPart1 = szNextPart1; + szPathPart2 = szNextPart2; + nEqualParts++; + } + + // If we found at least one equal part, we can create relative path + if(nEqualParts != 0) + { + // Calculate how many steps up we need to go + nStepsUp = GetPathSeparatorCount(szPathPart2); + + // Append "../" nStepsUp-times + for(int i = 0; i < nStepsUp; i++) + { + *szBuffer++ = '.'; + *szBuffer++ = '.'; + *szBuffer++ = '/'; + } + + // Append the rest of the path. Also change DOS backslashes to slashes + CopyPathPart(szBuffer, szPathPart1); + return; + } + + // Failed. Just copy the source path as it is + strcpy(szBuffer, szFullPath1); +} + static void CreateFullPathName(char * szBuffer, const char * szSubDir, const char * szNamePart1, const char * szNamePart2 = NULL) { size_t nLength; @@ -299,6 +387,133 @@ static void CreateFullPathName(char * szBuffer, const char * szSubDir, const cha *szBuffer = 0; } +static int FindFilesInternal(FIND_FILE_CALLBACK pfnTest, char * szDirectory) +{ + char * szPlainName; + int nError = ERROR_SUCCESS; + + // Setup the search masks + strcat(szDirectory, "\\*"); + szPlainName = strrchr(szDirectory, '*'); + + if(szDirectory != NULL && szPlainName != NULL) + { +#ifdef PLATFORM_WINDOWS + WIN32_FIND_DATAA wf; + HANDLE hFind; + + // Initiate search. Use ANSI function only + hFind = FindFirstFileA(szDirectory, &wf); + if(hFind != INVALID_HANDLE_VALUE) + { + // Skip the first entry, since it's always "." or ".." + while(FindNextFileA(hFind, &wf) && nError == ERROR_SUCCESS) + { + // Found a directory? + if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if(wf.cFileName[0] != '.') + { + strcpy(szPlainName, wf.cFileName); + nError = FindFilesInternal(pfnTest, szDirectory); + } + } + else + { + if(pfnTest != NULL) + { + strcpy(szPlainName, wf.cFileName); + nError = pfnTest(szDirectory); + } + } + } + + FindClose(hFind); + } +#endif + } + + // Free the path buffer, if any + return nError; +} + +static int FindFiles(FIND_FILE_CALLBACK pfnFindFile, const char * szSubDirectory) +{ + char szWorkBuff[MAX_PATH]; + + CreateFullPathName(szWorkBuff, szSubDirectory, NULL); + return FindFilesInternal(pfnFindFile, szWorkBuff); +} + +static int FindFilePairsInternal( + FIND_PAIR_CALLBACK pfnFilePair, + char * szSource, + char * szTarget) +{ + char * szPlainName1; + char * szPlainName2; + int nError = ERROR_SUCCESS; + + // Setup the search masks + strcat(szSource, "\\*"); + szPlainName1 = strrchr(szSource, '*'); + strcat(szTarget, "\\*"); + szPlainName2 = strrchr(szTarget, '*'); + + // If both paths are OK, perform the search + if(szPlainName1 != NULL && szPlainName2 != NULL) + { +#ifdef PLATFORM_WINDOWS + WIN32_FIND_DATAA wf; + HANDLE hFind; + + // Search the second directory + hFind = FindFirstFileA(szTarget, &wf); + if(hFind != INVALID_HANDLE_VALUE) + { + // Skip the first entry, since it's always "." or ".." + while(FindNextFileA(hFind, &wf) && nError == ERROR_SUCCESS) + { + // Found a directory? + if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if(wf.cFileName[0] != '.') + { + strcpy(szPlainName1, wf.cFileName); + strcpy(szPlainName2, wf.cFileName); + nError = FindFilePairsInternal(pfnFilePair, szSource, szTarget); + } + } + else + { + if(pfnFilePair != NULL) + { + strcpy(szPlainName1, wf.cFileName); + strcpy(szPlainName2, wf.cFileName); + nError = pfnFilePair(szSource, szTarget); + } + } + } + + FindClose(hFind); + } +#endif + } + + return nError; +} + +static int FindFilePairs(FIND_PAIR_CALLBACK pfnFindPair, const char * szSourceSubDir, const char * szTargetSubDir) +{ + char szSource[MAX_PATH]; + char szTarget[MAX_PATH]; + + // Create the source search mask + CreateFullPathName(szSource, szSourceSubDir, NULL); + CreateFullPathName(szTarget, szTargetSubDir, NULL); + return FindFilePairsInternal(pfnFindPair, szSource, szTarget); +} + TFileStream * OpenLocalFile(const char * szFileName, DWORD dwStreamFlags) { TCHAR szFileNameT[MAX_PATH]; @@ -315,6 +530,76 @@ TFileStream * CreateLocalFile(const char * szFileName, DWORD dwStreamFlags) return FileStream_CreateFile(szFileNameT, dwStreamFlags); } +static int CalculateFileSha1(TLogHelper * pLogger, const char * szFullPath, char * szFileSha1) +{ + TFileStream * pStream; + unsigned char sha1_digest[SHA1_DIGEST_SIZE]; + const char * szShortPlainName = GetShortPlainName(szFullPath); + hash_state sha1_state; + ULONGLONG ByteOffset = 0; + ULONGLONG FileSize = 0; + BYTE * pbFileBlock; + DWORD cbBytesToRead; + DWORD cbFileBlock = 0x100000; + int nError = ERROR_SUCCESS; + + // Notify the user + pLogger->PrintProgress("Hashing file %s", szShortPlainName); + szFileSha1[0] = 0; + + // Open the file to be verified + pStream = OpenLocalFile(szFullPath, STREAM_FLAG_READ_ONLY); + if(pStream != NULL) + { + // Retrieve the size of the file + FileStream_GetSize(pStream, &FileSize); + + // Allocate the buffer for loading file parts + pbFileBlock = STORM_ALLOC(BYTE, cbFileBlock); + if(pbFileBlock != NULL) + { + // Initialize SHA1 calculation + sha1_init(&sha1_state); + + // Calculate the SHA1 of the file + while(ByteOffset < FileSize) + { + // Notify the user + pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize); + + // Load the file block + cbBytesToRead = ((FileSize - ByteOffset) > cbFileBlock) ? cbFileBlock : (DWORD)(FileSize - ByteOffset); + if(!FileStream_Read(pStream, &ByteOffset, pbFileBlock, cbBytesToRead)) + { + nError = GetLastError(); + break; + } + + // Add to SHA1 + sha1_process(&sha1_state, pbFileBlock, cbBytesToRead); + ByteOffset += cbBytesToRead; + } + + // Notify the user + pLogger->PrintProgress("Hashing file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize); + + // Finalize SHA1 + sha1_done(&sha1_state, sha1_digest); + + // Convert the SHA1 to ANSI text + ConvertSha1ToText(sha1_digest, szFileSha1); + STORM_FREE(pbFileBlock); + } + + FileStream_Close(pStream); + } + + // If we calculated something, return OK + if(nError == ERROR_SUCCESS && szFileSha1[0] == 0) + nError = ERROR_CAN_NOT_COMPLETE; + return nError; +} + static int InitializeMpqDirectory(char * argv[], int argc) { TLogHelper Logger("InitWorkDir"); @@ -714,7 +999,7 @@ static void WINAPI CompactCallback(void * pvUserData, DWORD dwWork, ULONGLONG By if(pLogger != NULL) pLogger->PrintProgress("%s (%I64u of %I64u) ...", szWork, BytesDone, TotalBytes); else - printf("%s (" I64u_a " of " I64u_a ") ... \r", szWork, (DWORD)BytesDone, (DWORD)TotalBytes); + printf("%s (" I64u_a " of " I64u_a ") ... \r", szWork, BytesDone, TotalBytes); } } @@ -1307,104 +1592,6 @@ static void TestGetFileInfo( pLogger->PrintMessage("Different error from SFileGetFileInfo (expected %u, returned %u)", nExpectedError, nError); } -static int TestVerifyFileChecksum(const char * szFullPath) -{ - const char * szShortPlainName = GetShortPlainName(szFullPath); - unsigned char sha1_digest[SHA1_DIGEST_SIZE]; - TFileStream * pStream; - TFileData * pFileData; - hash_state sha1_state; - ULONGLONG ByteOffset = 0; - ULONGLONG FileSize = 0; - char * szExtension; - LPBYTE pbFileBlock; - char szShaFileName[MAX_PATH]; - char Sha1Text[0x40]; - DWORD cbBytesToRead; - DWORD cbFileBlock = 0x10000; - size_t nLength; - int nError = ERROR_SUCCESS; - - // Try to load the file with the SHA extension - strcpy(szShaFileName, szFullPath); - szExtension = strrchr(szShaFileName, '.'); - if(szExtension == NULL) - return ERROR_SUCCESS; - - // Skip .SHA and .TXT files - if(!_stricmp(szExtension, ".sha") || !_stricmp(szExtension, ".txt")) - return ERROR_SUCCESS; - - // Load the local file to memory - strcpy(szExtension, ".sha"); - pFileData = LoadLocalFile(NULL, szShaFileName, false); - if(pFileData != NULL) - { - TLogHelper Logger("VerifyFileHash", szShortPlainName); - - // Open the file to be verified - pStream = OpenLocalFile(szFullPath, STREAM_FLAG_READ_ONLY); - if(pStream != NULL) - { - // Notify the user - Logger.PrintProgress("Verifying file %s", szShortPlainName); - - // Retrieve the size of the file - FileStream_GetSize(pStream, &FileSize); - - // Allocate the buffer for loading file parts - pbFileBlock = STORM_ALLOC(BYTE, cbFileBlock); - if(pbFileBlock != NULL) - { - // Initialize SHA1 calculation - sha1_init(&sha1_state); - - // Calculate the SHA1 of the file - while(ByteOffset < FileSize) - { - // Notify the user - Logger.PrintProgress("Verifying file %s (%I64u of %I64u)", szShortPlainName, ByteOffset, FileSize); - - // Load the file block - cbBytesToRead = ((FileSize - ByteOffset) > cbFileBlock) ? cbFileBlock : (DWORD)(FileSize - ByteOffset); - if(!FileStream_Read(pStream, &ByteOffset, pbFileBlock, cbBytesToRead)) - { - nError = GetLastError(); - break; - } - - // Add to SHA1 - sha1_process(&sha1_state, pbFileBlock, cbBytesToRead); - ByteOffset += cbBytesToRead; - } - - // Finalize SHA1 - sha1_done(&sha1_state, sha1_digest); - STORM_FREE(pbFileBlock); - - // Compare with what we loaded from the file - if(pFileData->dwFileSize >= (SHA1_DIGEST_SIZE * 2)) - { - // Compare the Sha1 - nLength = ConvertSha1ToText(sha1_digest, Sha1Text); - if(_strnicmp(Sha1Text, (char *)pFileData->FileData, nLength)) - { - Logger.PrintError("File CRC check failed: %s", szFullPath); - nError = ERROR_FILE_CORRUPT; - } - } - } - - // Close the file - FileStream_Close(pStream); - } - - STORM_FREE(pFileData); - } - - return nError; -} - // StormLib is able to open local files (as well as the original Storm.dll) // I want to keep this for occasional use static int TestOpenLocalFile(const char * szPlainName) @@ -1630,7 +1817,7 @@ static int TestOpenArchive_ReadOnly(const char * szPlainName, bool bReadOnly) { const char * szCopyName; TLogHelper Logger("ReadOnlyTest", szPlainName); - HANDLE hMpq; + HANDLE hMpq = NULL; char szFullPathName[MAX_PATH]; DWORD dwFlags = bReadOnly ? MPQ_OPEN_READ_ONLY : 0;; bool bMustSucceed; @@ -1872,9 +2059,56 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char return nError; } +static int ForEachFile_VerifyFileChecksum(const char * szFullPath) +{ + const char * szShortPlainName = GetShortPlainName(szFullPath); + TFileData * pFileData; + char * szExtension; + char szShaFileName[MAX_PATH]; + char szSha1Text[0x40]; + int nError = ERROR_SUCCESS; + + // Try to load the file with the SHA extension + strcpy(szShaFileName, szFullPath); + szExtension = strrchr(szShaFileName, '.'); + if(szExtension == NULL) + return ERROR_SUCCESS; + + // Skip .SHA and .TXT files + if(!_stricmp(szExtension, ".sha") || !_stricmp(szExtension, ".txt")) + return ERROR_SUCCESS; + + // Load the local file to memory + strcpy(szExtension, ".sha"); + pFileData = LoadLocalFile(NULL, szShaFileName, false); + if(pFileData != NULL) + { + TLogHelper Logger("VerifyFileHash", szShortPlainName); + + // Calculate SHA1 of the entire file + nError = CalculateFileSha1(&Logger, szFullPath, szSha1Text); + if(nError == ERROR_SUCCESS) + { + // Compare with what we loaded from the file + if(pFileData->dwFileSize >= (SHA1_DIGEST_SIZE * 2)) + { + // Compare the SHA1 + if(_strnicmp(szSha1Text, (char *)pFileData->FileData, (SHA1_DIGEST_SIZE * 2))) + { + Logger.PrintError("File CRC check failed: %s", szFullPath); + nError = ERROR_FILE_CORRUPT; + } + } + } + + STORM_FREE(pFileData); + } -// Searches a direcroty -static int TestOpenEachArchive_EachFile(const char * szFullPath) + return nError; +} + +// Opens a found archive +static int ForEachFile_OpenArchive(const char * szFullPath) { HANDLE hMpq = NULL; DWORD dwFileCount = 0; @@ -2342,7 +2576,8 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName) hMpq = NULL; // Try to reopen the archive - nError = OpenExistingArchive(&Logger, szFullPath, 0, NULL); + if(nError == ERROR_SUCCESS) + nError = OpenExistingArchive(&Logger, szFullPath, 0, NULL); return nError; } @@ -2547,90 +2782,67 @@ static int TestCreateArchive_BigArchive(const char * szPlainName) return nError; } -static int TestForEachArchive(ARCHIVE_TEST pfnTest, TCHAR * szSearchMask, TCHAR * szPlainName) +//----------------------------------------------------------------------------- +// Comparing two directories, creating links + +#define LINK_COMPARE_BLOCK_SIZE 0x200 + +static int CreateArchiveLinkFile(const char * szFullPath1, const char * szFullPath2, const char * szFileHash) { - TCHAR szPathBuffT[MAX_PATH]; - char szPathBuffA[MAX_PATH]; - char szFullPath[MAX_PATH]; - int nError = ERROR_SUCCESS; + TFileStream * pStream; + char szLinkData[MAX_PATH + 0x80]; + char szLinkFile[MAX_PATH]; + char szLinkPath[MAX_PATH]; + int nLength; - // If the name was not entered, construct new one - if(szSearchMask == NULL) - { - CreateFullPathName(szPathBuffA, szMpqSubDir, "*"); - CopyFileName(szPathBuffT, szPathBuffA, strlen(szPathBuffA)); - szSearchMask = szPathBuffT; - } + // Construct the link file name + CalculateRelativePath(szFullPath1, szFullPath2, szLinkPath); + sprintf(szLinkFile, "%s.link", szFullPath2); - // Get the position of the plain name - if(szPlainName == NULL) - { - szPlainName = _tcsrchr(szSearchMask, _T('*')); - if(szPlainName == NULL) - return ERROR_SUCCESS; - } + // Format the content of the link file + nLength = sprintf(szLinkData, "LINK:%s\x0D\x0ASHA1:%s", szLinkPath, szFileHash); - // At this point, both pointers must be valid - assert(szSearchMask != NULL && szPlainName != NULL); + // Create the link file + pStream = CreateLocalFile(szLinkFile, 0); + if(pStream == NULL) + return GetLastError(); - // Now both must be entered - if(szSearchMask != NULL && szPlainName != NULL) - { -#ifdef PLATFORM_WINDOWS - WIN32_FIND_DATA wf; - HANDLE hFind; + // Write the content of the link file + FileStream_Write(pStream, NULL, szLinkData, (DWORD)nLength); + FileStream_Close(pStream); + return ERROR_SUCCESS; +} - // Initiate search. Use ANSI function only - hFind = FindFirstFile(szSearchMask, &wf); - if(hFind != INVALID_HANDLE_VALUE) +static int ForEachFile_CreateArchiveLink(const char * szFullPath1, const char * szFullPath2) +{ + TLogHelper Logger("CreateMpqLink", GetShortPlainName(szFullPath2)); + char szFileHash1[0x40]; + char szFileHash2[0x40]; + int nError; + + // Prevent logger from witing any result messages + Logger.bDontPrintResult = true; + + // Create SHA1 of both files + nError = CalculateFileSha1(&Logger, szFullPath1, szFileHash1); + if(nError == ERROR_SUCCESS) + { + nError = CalculateFileSha1(&Logger, szFullPath2, szFileHash2); + if(nError == ERROR_SUCCESS) { - // Skip the first entry, since it's always "." or ".." - while(FindNextFile(hFind, &wf) && nError == ERROR_SUCCESS) + // If the hashes are identical, we can create link + if(!strcmp(szFileHash1, szFileHash2)) { - // Found a directory? - if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + nError = CreateArchiveLinkFile(szFullPath1, szFullPath2, szFileHash1); + if(nError == ERROR_SUCCESS) { - if(wf.cFileName[0] != '.') - { - _stprintf(szPlainName, _T("%s\\*"), wf.cFileName); - nError = TestForEachArchive(pfnTest, szSearchMask, NULL); - } - } - else - { - if(pfnTest != NULL) - { - // Create the full path as TCHAR - _tcscpy(szPlainName, wf.cFileName); - CopyFileName(szFullPath, szSearchMask, _tcslen(szSearchMask)); - - // Check for UNICODE names - if(IsUnicodeNameConvertableToAnsi(szSearchMask, szFullPath)) - nError = pfnTest(szFullPath); - } + Logger.PrintMessage("Created link to %s", szFullPath2); } } - - FindClose(hFind); } -#endif } - // Free the path buffer, if any - return nError; -} - -static int TestOpenArchive_EachArchive() -{ - TCHAR szSearchMaskT[MAX_PATH]; - char szSearchMaskA[MAX_PATH]; - - // Create the TCHAR name of search mask - CreateFullPathName(szSearchMaskA, NULL, "*"); - CopyFileName(szSearchMaskT, szSearchMaskA, strlen(szSearchMaskA)); - - // Invoke the searching function - return TestForEachArchive(TestOpenEachArchive_EachFile, szSearchMaskT, NULL); + return ERROR_SUCCESS; } //----------------------------------------------------------------------------- @@ -2648,10 +2860,14 @@ int main(int argc, char * argv[]) printf("==== Test Suite for StormLib version %s ====\n", STORMLIB_VERSION_STRING); nError = InitializeMpqDirectory(argv, argc); - // Search all testing archives and verify their SHA1 hash + // Not a test, but rather a tool for creating links to duplicated files if(nError == ERROR_SUCCESS) - nError = TestForEachArchive(TestVerifyFileChecksum, NULL, NULL); + nError = FindFilePairs(ForEachFile_CreateArchiveLink, "2004 - WoW\\16965", "2004 - WoW\\17658"); + // Search all testing archives and verify their SHA1 hash + if(nError == ERROR_SUCCESS) + nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqDirectory); + // Test opening local file with SFileOpenFileEx if(nError == ERROR_SUCCESS) nError = TestOpenLocalFile("ListFile_Blizzard.txt"); @@ -2720,6 +2936,10 @@ int main(int argc, char * argv[]) if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2010_v2_HashTableCompressed.MPQ.part"); + // Open every MPQ that we have in the storage + if(nError == ERROR_SUCCESS) + nError = FindFiles(ForEachFile_OpenArchive, szMpqDirectory); + // Open a patched archive if(nError == ERROR_SUCCESS) nError = TestOpenArchive_Patched(PatchList_WoW_OldWorld13286, "OldWorld\\World\\Model.blob", 2); @@ -2764,10 +2984,6 @@ int main(int argc, char * argv[]) if(nError == ERROR_SUCCESS) nError = TestOpenArchive_CraftedUserData("MPQ_2013_v4_expansion1.MPQ", "StormLibTest_CraftedMpq3_v4.mpq"); - // Open every MPQ that we have in the storage - if(nError == ERROR_SUCCESS) - nError = TestOpenArchive_EachArchive(); - // Test modifying file with no (listfile) and no (attributes) if(nError == ERROR_SUCCESS) nError = TestAddFile_ListFileTest("MPQ_1997_v1_Diablo1_DIABDAT.MPQ", false, false); |