diff options
Diffstat (limited to 'dep/CascLib/src')
44 files changed, 23179 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascBuildCfg.cpp b/dep/CascLib/src/CascBuildCfg.cpp new file mode 100644 index 00000000000..effb74552c5 --- /dev/null +++ b/dep/CascLib/src/CascBuildCfg.cpp @@ -0,0 +1,922 @@ +/*****************************************************************************/ +/* CascBuildCfg.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Build configuration for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static bool inline IsValueSeparator(LPBYTE pbVarValue) +{ + return ((0 <= pbVarValue[0] && pbVarValue[0] <= 0x20) || (pbVarValue[0] == '|')); +} + +static bool IsCharDigit(BYTE OneByte) +{ + return ('0' <= OneByte && OneByte <= '9'); +} + +static void FreeCascBlob(PQUERY_KEY pBlob) +{ + if(pBlob != NULL) + { + if(pBlob->pbData != NULL) + CASC_FREE(pBlob->pbData); + + pBlob->pbData = NULL; + pBlob->cbData = 0; + } +} + +static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType) +{ + size_t nLength; + + // Check the variable name + nLength = strlen(szVarName); + if((size_t)(szLineEnd - szLineBegin) > nLength) + { + // Check the variable name + if(!_strnicmp(szLineBegin, szVarName, nLength)) + { + // Skip variable name and the exclamation mark + szLineBegin += nLength; + if(szLineBegin < szLineEnd && szLineBegin[0] == '!') + { + // Skip the exclamation mark + szLineBegin++; + + // Check the variable type + nLength = strlen(szVarType); + if((size_t)(szLineEnd - szLineBegin) > nLength) + { + // Check the variable name + if(!_strnicmp(szLineBegin, szVarType, nLength)) + { + // Skip variable type and the doublecolon + szLineBegin += nLength; + return (szLineBegin < szLineEnd && szLineBegin[0] == ':'); + } + } + } + } + } + + return false; +} + +static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd) +{ + while(szLineBegin < szLineEnd) + { + if(szLineBegin[0] == '|') + return szLineBegin + 1; + + szLineBegin++; + } + + return NULL; +} + +static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir) +{ + TCHAR * szIndexPath; + + // Cpmbine the index path + szIndexPath = CombinePath(hs->szDataPath, szSubDir); + if(DirectoryExists(szIndexPath)) + { + hs->szIndexPath = szIndexPath; + return hs->szIndexPath; + } + + delete [] szIndexPath; + return NULL; +} + +TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator) +{ + // Put the separator, if any + if(chSeparator != 0) + *szBuffer++ = chSeparator; + + // Copy the blob data as text + for(DWORD i = 0; i < cbData; i++) + { + *szBuffer++ = IntToHexChar[pbData[0] >> 0x04]; + *szBuffer++ = IntToHexChar[pbData[0] & 0x0F]; + pbData++; + } + + // Terminate the string + *szBuffer = 0; + + // Return new buffer position + return szBuffer; +} + +static int StringBlobToBinaryBlob( + PQUERY_KEY pBlob, + LPBYTE pbBlobBegin, + LPBYTE pbBlobEnd) +{ + // Sanity checks + assert(pBlob != NULL && pBlob->pbData != NULL); + + // Reset the blob length + pBlob->cbData = 0; + + // Convert the blob + while(pbBlobBegin < pbBlobEnd) + { + BYTE DigitOne; + BYTE DigitTwo; + + DigitOne = (BYTE)(AsciiToUpperTable[pbBlobBegin[0]] - '0'); + if(DigitOne > 9) + DigitOne -= 'A' - '9' - 1; + + DigitTwo = (BYTE)(AsciiToUpperTable[pbBlobBegin[1]] - '0'); + if(DigitTwo > 9) + DigitTwo -= 'A' - '9' - 1; + + if(DigitOne > 0x0F || DigitTwo > 0x0F || pBlob->cbData >= MAX_CASC_KEY_LENGTH) + return ERROR_BAD_FORMAT; + + pBlob->pbData[pBlob->cbData++] = (DigitOne << 0x04) | DigitTwo; + pbBlobBegin += 2; + } + + return ERROR_SUCCESS; +} + + +static LPBYTE FindNextSeparator(PQUERY_KEY pFileBlob, LPBYTE pbFilePtr) +{ + LPBYTE pbFileBegin = pFileBlob->pbData; + LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData; + + if(pbFileBegin <= pbFilePtr && pbFilePtr < pbFileEnd) + { + while(pbFilePtr < pbFileEnd && pbFilePtr[0] != '|') + pbFilePtr++; + + return pbFilePtr; + } + + return NULL; +} + +static bool GetNextFileLine(PQUERY_KEY pFileBlob, LPBYTE * ppbLineBegin, LPBYTE * ppbLineEnd) +{ + LPBYTE pbLineBegin = *ppbLineBegin; + LPBYTE pbLineEnd = *ppbLineEnd; + LPBYTE pbFileEnd = pFileBlob->pbData + pFileBlob->cbData; + + // If there was a previous line, skip all end-of-line chars + if(pbLineEnd != NULL) + { + // Go to the next line + while(pbLineEnd < pbFileEnd && (pbLineEnd[0] == 0x0A || pbLineEnd[0] == 0x0D)) + pbLineEnd++; + pbLineBegin = pbLineEnd; + + // If there is no more data, return false + if(pbLineEnd >= pbFileEnd) + return false; + } + + // Skip all spaces before the line begins + while(pbLineBegin < pbFileEnd && (pbLineBegin[0] == 0x09 || pbLineBegin[0] == 0x20)) + pbLineBegin++; + pbLineEnd = pbLineBegin; + + // Go to the end of the line + while(pbLineEnd < pbFileEnd && pbLineEnd[0] != 0x0A && pbLineEnd[0] != 0x0D) + pbLineEnd++; + + // Give the results to the caller + *ppbLineBegin = pbLineBegin; + *ppbLineEnd = pbLineEnd; + return true; +} + +static LPBYTE CheckLineVariable(LPBYTE pbLineBegin, LPBYTE pbLineEnd, const char * szVarName) +{ + size_t nLineLength = (size_t)(pbLineEnd - pbLineBegin); + size_t nNameLength = strlen(szVarName); + + // If the line longer than the variable name? + if(nLineLength > nNameLength) + { + if(!_strnicmp((const char *)pbLineBegin, szVarName, nNameLength)) + { + // Skip the variable name + pbLineBegin += nNameLength; + + // Skip the separator(s) + while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) + pbLineBegin++; + + // Check if there is "=" + if(pbLineBegin >= pbLineEnd || pbLineBegin[0] != '=') + return NULL; + pbLineBegin++; + + // Skip the separator(s) + while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) + pbLineBegin++; + + // Check if there is "=" + if(pbLineBegin >= pbLineEnd) + return NULL; + + // Return the begin of the variable + return pbLineBegin; + } + } + + return NULL; +} + +static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue) +{ + const char * szLinePtr = szLineBegin; + + // Sanity checks + assert(pVarBlob->pbData == NULL); + assert(pVarBlob->cbData == 0); + + // Check length of the variable + while(szLinePtr < szLineEnd && szLinePtr[0] != '|') + szLinePtr++; + + // Allocate space for the blob + if(bHexaValue) + { + // Initialize the blob + pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2); + return StringBlobToBinaryBlob(pVarBlob, (LPBYTE)szLineBegin, (LPBYTE)szLinePtr); + } + + // Initialize the blob + pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1); + pVarBlob->cbData = (size_t)(szLinePtr - szLineBegin); + + // Check for success + if(pVarBlob->pbData == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Copy the string + memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData); + pVarBlob->pbData[pVarBlob->cbData] = 0; + return ERROR_SUCCESS; +} + + +static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey) +{ + // Get to the end of the file name + szFileName = szFileName + _tcslen(szFileName); + + // Append the "config" directory + _tcscat(szFileName, _T("/config")); + szFileName += 7; + + // Append the first level directory + szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/')); + szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/')); + szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/')); +} + +static DWORD GetBlobCount(LPBYTE pbLineBegin, LPBYTE pbLineEnd) +{ + DWORD dwBlobCount = 0; + + // Until we find an end of the line + while(pbLineBegin < pbLineEnd) + { + // Skip the blob + while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin) == false) + pbLineBegin++; + + // Increment the number of blobs + dwBlobCount++; + + // Skip the separator + while(pbLineBegin < pbLineEnd && IsValueSeparator(pbLineBegin)) + pbLineBegin++; + } + + return dwBlobCount; +} + +static int LoadBlobArray( + PQUERY_KEY pBlob, + DWORD dwMaxBlobs, + LPBYTE pbLineBegin, + LPBYTE pbLineEnd, + LPBYTE pbBuffer, + DWORD dwBufferSize) +{ + LPBYTE pbBlobBegin = pbLineBegin; + LPBYTE pbBlobEnd = pbLineBegin; + int nError = ERROR_SUCCESS; + + // Sanity check + assert(pbBuffer != NULL); + + // Until we find an end of the line + while(pbBlobBegin < pbLineEnd) + { + // Convert the blob from string to binary + if(dwBufferSize < MAX_CASC_KEY_LENGTH) + return ERROR_NOT_ENOUGH_MEMORY; + + // Find the end of the text blob + while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd) == false) + pbBlobEnd++; + + // Convert the blob from ANSI to binary + pBlob->pbData = pbBuffer; + nError = StringBlobToBinaryBlob(pBlob, pbBlobBegin, pbBlobEnd); + if(nError != ERROR_SUCCESS || dwMaxBlobs == 1) + break; + + // Move the blob, buffer, and limits + dwBufferSize -= MAX_CASC_KEY_LENGTH; + pbBuffer += MAX_CASC_KEY_LENGTH; + dwMaxBlobs--; + pBlob++; + + // Skip the separator + while(pbBlobEnd < pbLineEnd && IsValueSeparator(pbBlobEnd)) + pbBlobEnd++; + pbBlobBegin = pbBlobEnd; + } + + return nError; +} + +static int LoadSingleBlob(PQUERY_KEY pBlob, LPBYTE pbBlobBegin, LPBYTE pbBlobEnd) +{ + LPBYTE pbBuffer; + size_t nLength = (pbBlobEnd - pbBlobBegin) / 2; + + // Check maximum size + if(nLength > MAX_CASC_KEY_LENGTH) + return ERROR_INVALID_PARAMETER; + + // Allocate the blob buffer + pbBuffer = CASC_ALLOC(BYTE, MAX_CASC_KEY_LENGTH); + if(pbBuffer == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + return LoadBlobArray(pBlob, 1, pbBlobBegin, pbBlobEnd, pbBuffer, MAX_CASC_KEY_LENGTH); +} + +static PQUERY_KEY LoadMultipleBlobs(LPBYTE pbLineBegin, LPBYTE pbLineEnd, DWORD * pdwBlobCount) +{ + PQUERY_KEY pBlobArray = NULL; + LPBYTE pbBuffer = NULL; + DWORD dwBlobCount = GetBlobCount(pbLineBegin, pbLineEnd); + int nError; + + // Only if there is at least 1 blob + if(dwBlobCount != 0) + { + // Allocate the array of blobs + pBlobArray = CASC_ALLOC(QUERY_KEY, dwBlobCount); + if(pBlobArray != NULL) + { + // Zero the blob array + memset(pBlobArray, 0, dwBlobCount * sizeof(QUERY_KEY)); + + // Allocate buffer for the blobs + pbBuffer = CASC_ALLOC(BYTE, dwBlobCount * MAX_CASC_KEY_LENGTH); + if(pbBuffer != NULL) + { + // Zero the buffer + memset(pbBuffer, 0, dwBlobCount * MAX_CASC_KEY_LENGTH); + + // Load the entire blob array + nError = LoadBlobArray(pBlobArray, dwBlobCount, pbLineBegin, pbLineEnd, pbBuffer, dwBlobCount * MAX_CASC_KEY_LENGTH); + if(nError == ERROR_SUCCESS) + { + *pdwBlobCount = dwBlobCount; + return pBlobArray; + } + + // Free the buffer + CASC_FREE(pbBuffer); + } + + // Free the array of blobs + CASC_FREE(pBlobArray); + pBlobArray = NULL; + } + + // Reset the blob count + dwBlobCount = 0; + } + + *pdwBlobCount = dwBlobCount; + return pBlobArray; +} + +static int LoadTextFile(const TCHAR * szFileName, PQUERY_KEY pFileBlob) +{ + TFileStream * pStream; + ULONGLONG FileSize = 0; + int nError = ERROR_SUCCESS; + + // Open the agent file + pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + if(pStream != NULL) + { + // Retrieve its size + FileStream_GetSize(pStream, &FileSize); + + // Load the file to memory + if(0 < FileSize && FileSize < 0x100000) + { + // Initialize the blob + pFileBlob->cbData = (DWORD)FileSize; + pFileBlob->pbData = CASC_ALLOC(BYTE, pFileBlob->cbData + 1); + + // Load the file data into the blob + if(pFileBlob->pbData != NULL) + { + FileStream_Read(pStream, NULL, pFileBlob->pbData, (DWORD)FileSize); + pFileBlob->pbData[pFileBlob->cbData] = 0; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + else + nError = ERROR_INVALID_PARAMETER; + + FileStream_Close(pStream); + } + else + nError = GetLastError(); + + return nError; +} + +static int GetGameType(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd) +{ + // Alpha build of Heroes of the Storm + if((pbLineEnd - pbVarBegin) == 4 && !_strnicmp((const char *)pbVarBegin, "Hero", 4)) + { + hs->dwGameInfo = CASC_GAME_HOTS; + return ERROR_SUCCESS; + } + + // Alpha build of World of Warcraft - Warlords of Draenor + if((pbLineEnd - pbVarBegin) == 3 && !_strnicmp((const char *)pbVarBegin, "WoW", 3)) + { + hs->dwGameInfo = CASC_GAME_WOW6; + return ERROR_SUCCESS; + } + + // An unknown game + assert(false); + return ERROR_BAD_FORMAT; +} + +// "B29049" +// "WOW-18125patch6.0.1" +static int GetBuildNumber(TCascStorage * hs, LPBYTE pbVarBegin, LPBYTE pbLineEnd) +{ + DWORD dwBuildNumber = 0; + + // Skip all non-digit characters + while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0]) == false) + pbVarBegin++; + + // Convert the build number + while(pbVarBegin < pbLineEnd && IsCharDigit(pbVarBegin[0])) + dwBuildNumber = (dwBuildNumber * 10) + (*pbVarBegin++ - '0'); + + assert(dwBuildNumber != 0); + hs->dwBuildNumber = dwBuildNumber; + return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; +} + +static int FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PQUERY_KEY pFileBlob) +{ + TCHAR * szFileName; + int nError; + + // Construct the local file name + szFileName = NewStr(hs->szDataPath, 8 + 3 + 3 + 32); + if(szFileName == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Add the part where the config file path is + AppendConfigFilePath(szFileName, pFileKey); + + // Load the config file + nError = LoadTextFile(szFileName, pFileBlob); + if(nError == ERROR_SUCCESS) + { + // Verify the blob's MD5 + if(!VerifyDataBlockHash(pFileBlob->pbData, pFileBlob->cbData, pFileKey->pbData)) + { + FreeCascBlob(pFileBlob); + nError = ERROR_BAD_FORMAT; + } + } + + CASC_FREE(szFileName); + return nError; +} + +static int ParseInfoFile(TCascStorage * hs, PQUERY_KEY pFileBlob) +{ + QUERY_KEY CdnHost = {NULL, 0}; + QUERY_KEY CdnPath = {NULL, 0}; + const char * szLineBegin1 = NULL; + const char * szLineBegin2 = NULL; + const char * szLineEnd1 = NULL; + const char * szLineEnd2 = NULL; + const char * szFileEnd = (const char *)(pFileBlob->pbData + pFileBlob->cbData); + const char * szFilePtr = (const char *)pFileBlob->pbData; + int nError = ERROR_BAD_FORMAT; + + // Find the first line + szLineBegin1 = szFilePtr; + while(szFilePtr < szFileEnd) + { + // Check for the end of the line + if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) + { + szLineEnd1 = szFilePtr; + break; + } + + szFilePtr++; + } + + // Skip the newline character(s) + while(szFilePtr < szFileEnd && (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A)) + szFilePtr++; + + // Find the second line + szLineBegin2 = szFilePtr; + while(szFilePtr < szFileEnd) + { + // Check for the end of the line + if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) + { + szLineEnd2 = szFilePtr; + break; + } + + szFilePtr++; + } + + // Find the build key, CDN config key and the URL path + while(szLineBegin1 < szLineEnd1) + { + // Check for variables we need + if(IsInfoVariable(szLineBegin1, szLineEnd1, "Build Key", "HEX")) + LoadInfoVariable(&hs->CdnBuildKey, szLineBegin2, szLineEnd2, true); + if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Key", "HEX")) + LoadInfoVariable(&hs->CdnConfigKey, szLineBegin2, szLineEnd2, true); + if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Hosts", "STRING")) + LoadInfoVariable(&CdnHost, szLineBegin2, szLineEnd2, false); + if(IsInfoVariable(szLineBegin1, szLineEnd1, "CDN Path", "STRING")) + LoadInfoVariable(&CdnPath, szLineBegin2, szLineEnd2, false); + + // Move both line pointers + szLineBegin1 = SkipInfoVariable(szLineBegin1, szLineEnd1); + if(szLineBegin1 == NULL) + break; + + szLineBegin2 = SkipInfoVariable(szLineBegin2, szLineEnd2); + if(szLineBegin2 == NULL) + break; + } + + // All four must be present + if(hs->CdnBuildKey.pbData != NULL && + hs->CdnConfigKey.pbData != NULL && + CdnHost.pbData != NULL && + CdnPath.pbData != NULL) + { + // Merge the CDN host and CDN path + hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1); + if(hs->szUrlPath != NULL) + { + CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData); + CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData); + nError = ERROR_SUCCESS; + } + } + + FreeCascBlob(&CdnHost); + FreeCascBlob(&CdnPath); + return nError; +} + +static int ParseAgentFile(TCascStorage * hs, PQUERY_KEY pFileBlob) +{ + LPBYTE pbBlobBegin = pFileBlob->pbData; + LPBYTE pbBlobEnd; + int nError = ERROR_SUCCESS; + + // Extract the CDN build hash + pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); + if(pbBlobEnd != NULL) + { + // Convert the string to a blob + nError = LoadSingleBlob(&hs->CdnBuildKey, pbBlobBegin, pbBlobEnd); + + // Move to the next part + if(pbBlobEnd[0] == _T('|')) + pbBlobEnd++; + pbBlobBegin = pbBlobEnd; + } + + // Extract the CDN config hash + pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); + if(pbBlobEnd != NULL) + { + // Convert the string to a blob + nError = LoadSingleBlob(&hs->CdnConfigKey, pbBlobBegin, pbBlobEnd); + + // Move to the next part + if(pbBlobEnd[0] == _T('|')) + pbBlobEnd++; + pbBlobBegin = pbBlobEnd; + } + + // Skip the intermediate part + pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); + if(pbBlobEnd != NULL) + { + // Move to the next part + if(pbBlobEnd[0] == _T('|')) + pbBlobEnd++; + pbBlobBegin = pbBlobEnd; + } + + // Extract the URL config hash + pbBlobEnd = FindNextSeparator(pFileBlob, pbBlobBegin); + if(pbBlobEnd != NULL) + { + // Convert the string to a blob + hs->szUrlPath = NewStrFromAnsi(pbBlobBegin, pbBlobEnd); + } + + // Verify all variables + if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL) + nError = ERROR_BAD_FORMAT; + return nError; +} + +static int LoadCdnConfigFile(TCascStorage * hs, PQUERY_KEY pFileBlob) +{ + LPBYTE pbLineBegin = pFileBlob->pbData; + LPBYTE pbVarBegin; + LPBYTE pbLineEnd = NULL; + int nError; + + while(pbLineBegin != NULL) + { + // Get the next line + if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd)) + break; + + // Archive group + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archive-group"); + if(pbVarBegin != NULL) + { + nError = LoadSingleBlob(&hs->ArchiveGroup, pbVarBegin, pbLineEnd); + if(nError != ERROR_SUCCESS) + return nError; + continue; + } + + // Archives + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "archives"); + if(pbVarBegin != NULL) + { + hs->pArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->ArchiveCount); + if(hs->pArchiveArray == NULL || hs->ArchiveCount == 0) + return ERROR_BAD_FORMAT; + continue; + } + + // Patch archive group + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archive-group"); + if(pbVarBegin != NULL) + { + LoadSingleBlob(&hs->PatchArchiveGroup, pbVarBegin, pbLineEnd); + continue; + } + + // Patch archives + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch-archives"); + if(pbVarBegin != NULL) + { + hs->pPatchArchiveArray = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->PatchArchiveCount); + continue; + } + } + + // Check if all required fields are present + if(hs->ArchiveGroup.pbData == NULL || hs->ArchiveGroup.cbData == 0 || hs->pArchiveArray == NULL || hs->ArchiveCount == 0) + return ERROR_BAD_FORMAT; + + return ERROR_SUCCESS; +} + +static int LoadCdnBuildFile(TCascStorage * hs, PQUERY_KEY pFileBlob) +{ + LPBYTE pbLineBegin = pFileBlob->pbData; + LPBYTE pbVarBegin; + LPBYTE pbLineEnd = NULL; + + while(pbLineBegin != NULL) + { + // Get the next line + if(!GetNextFileLine(pFileBlob, &pbLineBegin, &pbLineEnd)) + break; + + // Game name + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-product"); + if(pbVarBegin != NULL) + { + GetGameType(hs, pbVarBegin, pbLineEnd); + continue; + } + + // Game build number + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "build-name"); + if(pbVarBegin != NULL) + { + GetBuildNumber(hs, pbVarBegin, pbLineEnd); + continue; + } + + // Root + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "root"); + if(pbVarBegin != NULL) + { + LoadSingleBlob(&hs->RootKey, pbVarBegin, pbLineEnd); + continue; + } + + // Patch + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "patch"); + if(pbVarBegin != NULL) + { + LoadSingleBlob(&hs->PatchKey, pbVarBegin, pbLineEnd); + continue; + } + + // Download + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "download"); + if(pbVarBegin != NULL) + { + LoadSingleBlob(&hs->DownloadKey, pbVarBegin, pbLineEnd); + continue; + } + + // Install + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "install"); + if(pbVarBegin != NULL) + { + LoadSingleBlob(&hs->InstallKey, pbVarBegin, pbLineEnd); + continue; + } + + // Encoding keys + pbVarBegin = CheckLineVariable(pbLineBegin, pbLineEnd, "encoding"); + if(pbVarBegin != NULL) + { + hs->pEncodingKeys = LoadMultipleBlobs(pbVarBegin, pbLineEnd, &hs->EncodingKeys); + if(hs->pEncodingKeys == NULL || hs->EncodingKeys != 2) + return ERROR_BAD_FORMAT; + + hs->EncodingKey = hs->pEncodingKeys[0]; + hs->EncodingEKey = hs->pEncodingKeys[1]; + continue; + } + } + + // Check the encoding keys + if(hs->pEncodingKeys == NULL || hs->EncodingKeys == 0) + return ERROR_BAD_FORMAT; + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Public functions + +int LoadBuildConfiguration(TCascStorage * hs) +{ + QUERY_KEY InfoFile = {NULL, 0}; + QUERY_KEY FileData = {NULL, 0}; + TCHAR * szAgentFile; + TCHAR * szInfoFile; + bool bBuildConfigComplete = false; + int nError = ERROR_SUCCESS; + + // Since HOTS build 30027, the game uses build.info file for storage info + if(bBuildConfigComplete == false) + { + szInfoFile = CombinePath(hs->szRootPath, _T(".build.info")); + if(szInfoFile != NULL) + { + nError = LoadTextFile(szInfoFile, &InfoFile); + if(nError == ERROR_SUCCESS) + { + // Parse the info file + nError = ParseInfoFile(hs, &InfoFile); + if(nError == ERROR_SUCCESS) + bBuildConfigComplete = true; + + // Free the loaded blob + FreeCascBlob(&InfoFile); + } + + CASC_FREE(szInfoFile); + } + } + + // If the info file has not been loaded, try the legacy .build.db + if(bBuildConfigComplete == false) + { + szAgentFile = CombinePath(hs->szRootPath, _T(".build.db")); + if(szAgentFile != NULL) + { + nError = LoadTextFile(szAgentFile, &FileData); + if(nError == ERROR_SUCCESS) + { + nError = ParseAgentFile(hs, &FileData); + if(nError == ERROR_SUCCESS) + bBuildConfigComplete = true; + + FreeCascBlob(&FileData); + } + CASC_FREE(szAgentFile); + } + } + + // If the .build.info and .build.db file hasn't been loaded, + if(nError == ERROR_SUCCESS && bBuildConfigComplete == false) + { + nError = ERROR_FILE_CORRUPT; + } + + // Load the configuration file + if(nError == ERROR_SUCCESS) + { + nError = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey, &FileData); + if(nError == ERROR_SUCCESS) + { + nError = LoadCdnConfigFile(hs, &FileData); + FreeCascBlob(&FileData); + } + } + + // Load the build file + if(nError == ERROR_SUCCESS) + { + nError = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey, &FileData); + if(nError == ERROR_SUCCESS) + { + nError = LoadCdnBuildFile(hs, &FileData); + FreeCascBlob(&FileData); + } + } + + // Fill the index directory + if(nError == ERROR_SUCCESS) + { + // First, check for more common "data" subdirectory + if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) + return ERROR_SUCCESS; + + // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) + if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) + return ERROR_SUCCESS; + + nError = ERROR_FILE_NOT_FOUND; + } + + return nError; +} diff --git a/dep/CascLib/src/CascCommon.cpp b/dep/CascLib/src/CascCommon.cpp new file mode 100644 index 00000000000..8ad7d716b82 --- /dev/null +++ b/dep/CascLib/src/CascCommon.cpp @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Conversion of big-endian to integer + +// Read the 24-bit big-endian offset into ULONGLONG +DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes) +{ + DWORD Value = 0; + + Value = (Value << 0x08) | ValueAsBytes[0]; + Value = (Value << 0x08) | ValueAsBytes[1]; + Value = (Value << 0x08) | ValueAsBytes[2]; + + return Value; +} + +// Read the 32-bit big-endian offset into ULONGLONG +DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes) +{ + DWORD Value = 0; + + Value = (Value << 0x08) | ValueAsBytes[0]; + Value = (Value << 0x08) | ValueAsBytes[1]; + Value = (Value << 0x08) | ValueAsBytes[2]; + Value = (Value << 0x08) | ValueAsBytes[3]; + + return Value; +} + +DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes) +{ + DWORD Value = 0; + + Value = (Value << 0x08) | ValueAsBytes[3]; + Value = (Value << 0x08) | ValueAsBytes[2]; + Value = (Value << 0x08) | ValueAsBytes[1]; + Value = (Value << 0x08) | ValueAsBytes[0]; + + return Value; +} + +// Read the 40-bit big-endian offset into ULONGLONG +ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes) +{ + ULONGLONG Value = 0; + + Value = (Value << 0x08) | ValueAsBytes[0]; + Value = (Value << 0x08) | ValueAsBytes[1]; + Value = (Value << 0x08) | ValueAsBytes[2]; + Value = (Value << 0x08) | ValueAsBytes[3]; + Value = (Value << 0x08) | ValueAsBytes[4]; + + return Value; +} diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h new file mode 100644 index 00000000000..b2f6a156e39 --- /dev/null +++ b/dep/CascLib/src/CascCommon.h @@ -0,0 +1,374 @@ +/*****************************************************************************/ +/* CascCommon.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascCommon.h */ +/*****************************************************************************/ + +#ifndef __CASCCOMMON_H__ +#define __CASCCOMMON_H__ + +//----------------------------------------------------------------------------- +// Compression support + +// Include functions from zlib +#ifndef __SYS_ZLIB + #include "zlib/zlib.h" +#else + #include <zlib.h> +#endif + +#include "CascPort.h" +#include "common/Common.h" +#include "common/FileStream.h" +#include "common/ListFile.h" +#include "common/Map.h" + +// Headers from LibTomCrypt +#include "libtomcrypt/src/headers/tomcrypt.h" + +// For HashStringJenkins +#include "jenkins/lookup.h" + +//----------------------------------------------------------------------------- +// CascLib private defines + +#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm +#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor +#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID + +#define CASC_INDEX_COUNT 0x10 +#define CASC_FILE_KEY_SIZE 0x09 // Size of the file key +#define CASC_MAX_DATA_FILES 0x100 +#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported +#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX' + +#define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name + +// Prevent problems with CRT "min" and "max" functions, +// as they are not defined on all platforms +#define CASCLIB_MIN(a, b) ((a < b) ? a : b) +#define CASCLIB_MAX(a, b) ((a > b) ? a : b) +#define CASCLIB_UNUSED(p) ((void)(p)) + +#define CASC_PACKAGE_BUFFER 0x1000 + +//----------------------------------------------------------------------------- +// Structures + +struct TFileStream; +struct _MAR_FILE; + +typedef struct _CASC_INDEX_ENTRY +{ + BYTE IndexKey[CASC_FILE_KEY_SIZE]; // The first 9 bytes of the encoding key + BYTE FileOffset[5]; // + BYTE FileSize[4]; +} CASC_INDEX_ENTRY, *PCASC_INDEX_ENTRY; + +typedef struct _CASC_MAPPING_TABLE +{ + TCHAR * szFileName; // Name of the key mapping file + LPBYTE pbFileData; // Pointer to the file data + DWORD cbFileData; // Length of the file data + BYTE ExtraBytes; // (?) Extra bytes in the key record + BYTE SpanSizeBytes; // Size of field with file size + BYTE SpanOffsBytes; // Size of field with file offset + BYTE KeyBytes; // Size of the file key + BYTE SegmentBits; // Number of bits for the file offset (rest is archive index) + ULONGLONG MaxFileOffset; + + PCASC_INDEX_ENTRY pIndexEntries; // Sorted array of index entries + DWORD nIndexEntries; // Number of index entries + +} CASC_MAPPING_TABLE, *PCASC_MAPPING_TABLE; + +typedef struct _CASC_FILE_FRAME +{ + DWORD FrameArchiveOffset; // Archive file pointer corresponding to the begin of the frame + DWORD FrameFileOffset; // File pointer corresponding to the begin of the frame + DWORD CompressedSize; // Compressed size of the file + DWORD FrameSize; // Size of the frame + BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector +} CASC_FILE_FRAME, *PCASC_FILE_FRAME; + +typedef struct _CASC_ENCODING_HEADER +{ + BYTE Magic[2]; // "EN" + BYTE field_2; + BYTE field_3; + BYTE field_4; + BYTE field_5[2]; + BYTE field_7[2]; + BYTE NumSegments[4]; // Number of entries (big endian) + BYTE field_D[4]; + BYTE field_11; + BYTE SegmentsPos[4]; // Offset of encoding segments + +} CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER; + +typedef struct _CASC_ENCODING_ENTRY +{ + USHORT KeyCount; // Number of subitems + BYTE FileSizeBytes[4]; // File size as bytes (big-endian) + BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key + + // Followed by the index keys + // (number of items = KeyCount) + // Followed by the index keys (number of items = KeyCount) +} CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY; + +typedef struct _CASC_ROOT_LOCALE_BLOCK +{ + DWORD NumberOfFiles; // Number of entries + DWORD Flags; + DWORD FileLocales; // File locales (CASC_LOCALE_XXX) + + // Followed by a block of 32-bit integers (count: NumberOfFiles) + // Followed by the MD5 and file name hash (count: NumberOfFiles) + +} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK; + +typedef struct _CASC_ROOT_ENTRY +{ + BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key (MD5) + ULONGLONG FileNameHash; // Jenkins hash of the file name + DWORD Locales; // File locales (see CASC_LOCALE_XXX) + DWORD Flags; // File flags + +} CASC_ROOT_ENTRY, *PCASC_ROOT_ENTRY; + +typedef struct _CASC_ROOT_KEY_INFO +{ + BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file, obtained from root info + ULONGLONG FileSize; // Size of the file, in bytes + BYTE Flags; // File flags +} CASC_ROOT_KEY_INFO, *PCASC_ROOT_KEY_INFO; + +typedef struct _CASC_MNDX_ENTRY +{ + DWORD Flags; // High 8 bits: Flags, low 24 bits: package index + BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file + DWORD FileSize; // Size of the file, in bytes + +} CASC_MNDX_ENTRY, *PCASC_MNDX_ENTRY; + +typedef struct _CASC_MNDX_INFO +{ + bool bRootFileLoaded; // true if the root info file was properly loaded + BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file + DWORD HeaderVersion; // Must be <= 2 + DWORD FormatVersion; + DWORD field_1C; + DWORD field_20; + DWORD MarInfoOffset; // Offset of the first MAR entry info + DWORD MarInfoCount; // Number of the MAR info entries + DWORD MarInfoSize; // Size of the MAR info entry + DWORD MndxEntriesOffset; + DWORD MndxEntriesTotal; // Total number of MNDX root entries + DWORD MndxEntriesValid; // Number of valid MNDX root entries + DWORD MndxEntrySize; // Size of one MNDX root entry + struct _MAR_FILE * pMarFile1; // File name list for the packages + struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names + struct _MAR_FILE * pMarFile3; // File name list for complete names + PCASC_MNDX_ENTRY pMndxEntries; + PCASC_MNDX_ENTRY * ppValidEntries; + +} CASC_MNDX_INFO, *PCASC_MNDX_INFO; + +typedef struct _CASC_PACKAGE +{ + char * szFileName; // Pointer to file name + size_t nLength; // Length of the file name + +} CASC_PACKAGE, *PCASC_PACKAGE; + +typedef struct _CASC_PACKAGES +{ + char * szNameBuffer; // Pointer to the buffer for file names + size_t NameEntries; // Number of name entries in Names + size_t NameBufferUsed; // Number of bytes used in the name buffer + size_t NameBufferMax; // Total size of the name buffer + + CASC_PACKAGE Packages[1]; // List of packages + +} CASC_PACKAGES, *PCASC_PACKAGES; + +typedef struct _TCascStorage +{ + const char * szClassName; // "TCascStorage" + const TCHAR * szIndexFormat; // Format of the index file name + TCHAR * szRootPath; // This is the game directory + TCHAR * szDataPath; // This is the directory where data files are + TCHAR * szIndexPath; // This is the directory where index files are + TCHAR * szUrlPath; // URL to the Blizzard servers + DWORD dwRefCount; // Number of references + DWORD dwGameInfo; // Game type + DWORD dwBuildNumber; // Game build number + + QUERY_KEY CdnConfigKey; + QUERY_KEY CdnBuildKey; + + PQUERY_KEY pArchiveArray; // Array of the archives + QUERY_KEY ArchiveGroup; // Name of the group archive file + DWORD ArchiveCount; // Number of archives in the array + + PQUERY_KEY pPatchArchiveArray; // Array of the patch archives + QUERY_KEY PatchArchiveGroup; // Name of the patch group archive file + DWORD PatchArchiveCount; // Number of patch archives in the array + + QUERY_KEY RootKey; + QUERY_KEY PatchKey; + QUERY_KEY DownloadKey; + QUERY_KEY InstallKey; + + PQUERY_KEY pEncodingKeys; + QUERY_KEY EncodingKey; + QUERY_KEY EncodingEKey; + DWORD EncodingKeys; + + TFileStream * DataFileArray[CASC_MAX_DATA_FILES]; // Data file handles + + CASC_MAPPING_TABLE KeyMapping[CASC_INDEX_COUNT]; // Key mapping + PCASC_MAP pIndexEntryMap; // Map of index entries + + PCASC_ENCODING_HEADER pEncodingHeader; // The encoding file + PCASC_ENCODING_ENTRY * ppEncodingEntries; // Map of encoding entries + size_t nEncodingEntries; + + PCASC_ROOT_ENTRY * ppRootEntries; // Sorted array of root entries + PCASC_ROOT_ENTRY pRootEntries; // Linear array of root entries + size_t nRootEntries; // Number of root entries + + PCASC_MNDX_INFO pMndxInfo; // Used for storages which have MNDX/MAR file + PCASC_PACKAGES pPackages; // Linear list of present packages + +} TCascStorage; + +typedef struct _TCascFile +{ + TCascStorage * hs; // Pointer to storage structure + TFileStream * pStream; // An open data stream + const char * szClassName; // "TCascFile" + + DWORD FilePointer; // Current file pointer + + DWORD ArchiveIndex; // Index of the archive (data.###) + DWORD HeaderOffset; // Offset of the BLTE header, relative to the begin of the archive + DWORD FramesOffset; // Offset of the frame data, relative to the begin of the archive + DWORD CompressedSize; // Compressed size of the file (in bytes) + DWORD FileSize; // Size of file, in bytes + + PCASC_FILE_FRAME pFrames; // Array of file frames + DWORD FrameCount; // Number of the file frames + + LPBYTE pbFileCache; // Pointer to file cache + DWORD cbFileCache; // Size of the file cache + DWORD CacheStart; // Starting offset in the cache + DWORD CacheEnd; // Ending offset in the cache + +} TCascFile; + +class TMndxFindResult; + +typedef struct _TCascSearch +{ + TCascStorage * hs; // Pointer to the storage handle + const char * szClassName; + TCHAR * szListFile; + void * pCache; // Listfile cache + TMndxFindResult * pStruct1C; // Search structure for MNDX info + char * szMask; + char szFileName[MAX_PATH]; // Buffer for the file name + char szNormName[MAX_PATH]; // Buffer for normalized file name + ULONGLONG FileNameHash; // Name hash being searched right now + size_t RootIndex; // Root index of the previously found item + DWORD dwState; // Pointer to the state (0 = listfile, 1 = nameless, 2 = done) + BYTE BitArray[1]; // Bit array of already-reported files + +} TCascSearch; + +//----------------------------------------------------------------------------- +// Memory management +// +// We use our own macros for allocating/freeing memory. If you want +// to redefine them, please keep the following rules: +// +// - The memory allocation must return NULL if not enough memory +// (i.e not to throw exception) +// - The allocating function does not need to fill the allocated buffer with zeros +// - The reallocating function must support NULL as the previous block +// - Memory freeing function doesn't have to test the pointer to NULL +// + +#if defined(_MSC_VER) && defined(_DEBUG) +/* +void * DbgRealloc(void * ptr, size_t nSize); + +#define CASC_REALLOC(type, ptr, count) (type *)DbgRealloc(ptr, ((count) * sizeof(type))) +#define CASC_ALLOC(type, count) (type *)HeapAlloc(GetProcessHeap(), 0, ((count) * sizeof(type))) +#define CASC_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr) +*/ + +#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type)) +#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type)) +#define CASC_FREE(ptr) free(ptr) + +#else + +#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type)) +#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type)) +#define CASC_FREE(ptr) free(ptr) + +#endif + +//----------------------------------------------------------------------------- +// Big endian number manipulation + +DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes); +DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes); +DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes); +ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes); + +//----------------------------------------------------------------------------- +// Build configuration reading + +int LoadBuildConfiguration(TCascStorage * hs); + +//----------------------------------------------------------------------------- +// Internal file functions + +TCascStorage * IsValidStorageHandle(HANDLE hStorage); +TCascFile * IsValidFileHandle(HANDLE hFile); + +PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex); +PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex); +PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey); + +int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer); + +//----------------------------------------------------------------------------- +// Dump data + +#ifdef _DEBUG +void CascDumpSparseArray(const char * szFileName, void * pvSparseArray); +void CascDumpNameFragTable(const char * szFileName, void * pvMarFile); +void CascDumpFileNames(const char * szFileName, void * pvMarFile); +void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo); +void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs); +void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile); +void CascDumpFile(const char * szFileName, HANDLE hFile); +#else // _DEBUG +#define CascDumpSparseArray(n,a) /* */ +#define CascDumpNameFragTable(n, m) /* */ +#define CascDumpFileNames(n, m) /* */ +#define CascDumpMndxRoot(n,i) /* */ +#define CascDumpIndexEntries(n,h) /* */ +#define CascDumpStorage(n,h) /* */ +#define CascDumpFile(n,h) /* */ +#endif // _DEBUG + +#endif // __CASCCOMMON_H__ diff --git a/dep/CascLib/src/CascDecompress.cpp b/dep/CascLib/src/CascDecompress.cpp new file mode 100644 index 00000000000..2858bcec5ab --- /dev/null +++ b/dep/CascLib/src/CascDecompress.cpp @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* CascDecompress.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Decompression functions */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 02.05.14 1.00 Lad The first version of CascDecompress.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer) +{ + z_stream z; // Stream information for zlib + int nResult; + + // Fill the stream structure for zlib + z.next_in = pbInBuffer; + z.avail_in = cbInBuffer; + z.total_in = cbInBuffer; + z.next_out = pbOutBuffer; + z.avail_out = *pcbOutBuffer; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + // Initialize the decompression structure + if((nResult = inflateInit(&z)) == Z_OK) + { + // Call zlib to decompress the data + nResult = inflate(&z, Z_NO_FLUSH); + inflateEnd(&z); + + // Give the size of the uncompressed data + *pcbOutBuffer = z.total_out; + } + + // Return an error code + return (nResult == Z_OK || nResult == Z_STREAM_END) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; +} + +//----------------------------------------------------------------------------- +// Public functions + +int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer) +{ + LPBYTE pbOutBuffer = (LPBYTE)pvOutBuffer; + LPBYTE pbInBuffer = (LPBYTE)pvInBuffer; + DWORD cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer + BYTE uCompression; // Decompressions applied to the data + + // Verify buffer sizes + if(cbInBuffer <= 1) + return 0; + + // Get applied compression types and decrement data length + uCompression = *pbInBuffer++; + cbInBuffer--; + + // Perform the decompressions + switch(uCompression) + { + case 'N': // Uncompressed + + assert(cbOutBuffer == cbInBuffer); + memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + *pcbOutBuffer = cbOutBuffer; + return ERROR_SUCCESS; + + case 'Z': // ZLIB + + return Decompress_ZLIB(pbOutBuffer, pcbOutBuffer, pbInBuffer, cbInBuffer); + } + + assert(false); + return ERROR_NOT_SUPPORTED; +} diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp new file mode 100644 index 00000000000..d3871709fce --- /dev/null +++ b/dep/CascLib/src/CascDumpData.cpp @@ -0,0 +1,524 @@ +/*****************************************************************************/ +/* CascDumpData.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 07.05.14 1.00 Lad The first version of CascDumpData.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +#ifdef _DEBUG // The entire file is only valid for debug purposes + +//----------------------------------------------------------------------------- +// Local functions + +static char * StringFromIndexKey(LPBYTE md5, char * szBuffer) +{ + return StringFromBinary(md5, 9, szBuffer); +} + +static char * StringFromMD5(LPBYTE md5, char * szBuffer) +{ + return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer); +} + +static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, const void * pvIndexEntry2) +{ + PCASC_INDEX_ENTRY pIndexEntry1 = (PCASC_INDEX_ENTRY)pvIndexEntry1; + PCASC_INDEX_ENTRY pIndexEntry2 = (PCASC_INDEX_ENTRY)pvIndexEntry2; + ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffset); + ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffset); + DWORD ArchIndex1 = (DWORD)(FileOffset1 >> 0x1E); + DWORD ArchIndex2 = (DWORD)(FileOffset2 >> 0x1E); + + // First, compare the archive index + if(ArchIndex1 < ArchIndex2) + return -1; + if(ArchIndex1 > ArchIndex2) + return +1; + + // Second, compare the archive offset + FileOffset1 &= 0x3FFFFFFF; + FileOffset2 &= 0x3FFFFFFF; + if(FileOffset1 < FileOffset2) + return -1; + if(FileOffset1 > FileOffset2) + return +1; + + return 0; +} + + +static char ** CreateFileNameArray(TCascStorage * hs, const TCHAR * szListFile) +{ + PCASC_ROOT_ENTRY pRootEntry; + char ** FileNameArray = NULL; + void * pvListFile; + size_t nRootIndex; + char szFileName1[MAX_PATH+1]; + char szFileName2[MAX_PATH+1]; + + // Open the listfile stream and initialize the listfile cache + pvListFile = ListFile_OpenExternal(szListFile); + if(pvListFile != NULL) + { + // Allocate the array of file names + FileNameArray = CASC_ALLOC(char*, hs->nRootEntries); + if(FileNameArray != NULL) + { + // Zero the name array + memset(FileNameArray, 0, hs->nRootEntries * sizeof(char *)); + + // Perform search + while(ListFile_GetNext(pvListFile, "*", szFileName1, MAX_PATH)) + { + // Create normalized name + strcpy(szFileName2, szFileName1); + NormalizeFileName_UpperBkSlash(szFileName2); + + // Try to find the root entry + pRootEntry = FindFirstRootEntry(hs, szFileName2, &nRootIndex); + if(pRootEntry != NULL) + { + assert(nRootIndex < hs->nRootEntries); + if(FileNameArray[nRootIndex] == NULL) + FileNameArray[nRootIndex] = NewStr(szFileName1, 0); + } + } + } + + // Close the listfile cache + ListFile_Free(pvListFile); + } + + return FileNameArray; +} + +static void FreeFileNameArray(TCascStorage * hs, char ** FileNameArray) +{ + if(FileNameArray != NULL) + { + // Free all sub-entries + for(size_t i = 0; i < hs->nRootEntries; i++) + { + if(FileNameArray[i] != NULL) + CASC_FREE(FileNameArray[i]); + } + + // Free the array itself + CASC_FREE(FileNameArray); + } +} + +static void DumpIndexKey( + FILE * fp, + TCascStorage * hs, + LPBYTE pbEncodingKey, + PCASC_INDEX_ENTRY * ppIndexEntries) +{ + PCASC_INDEX_ENTRY pIndexEntry; + QUERY_KEY QueryKey; + size_t EntryIndex = 0; + char szMd5[MD5_STRING_SIZE]; + + QueryKey.pbData = pbEncodingKey; + QueryKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(hs, &QueryKey); + if(pIndexEntry != NULL) + { + ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset); + DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E); + DWORD FileSize = *(PDWORD)pIndexEntry->FileSize; + + // Mark the index entry as dumped + ppIndexEntries[EntryIndex] = NULL; + + // Mask the file offset + FileOffset &= 0x3FFFFFFF; + fprintf(fp, " Index: %s, ArchIdx: %02x FileOffset %08x FileSize: %lx\n", + StringFromIndexKey(pIndexEntry->IndexKey, szMd5), + ArchIndex, + (DWORD)FileOffset, + FileSize); + } + else + { + fprintf(fp, " NO INDEX ENTRY\n"); + } +} + +static void DumpEncodingEntry( + FILE * fp, + TCascStorage * hs, + PCASC_ENCODING_ENTRY pEncodingEntry, + PCASC_INDEX_ENTRY * ppIndexEntries) +{ + LPBYTE pbEncodingKey; + char szMd5[MD5_STRING_SIZE]; + + fprintf(fp, " Encoding Key: %s Key Count: %u Size: %lx\n", + StringFromMD5(pEncodingEntry->EncodingKey, szMd5), + pEncodingEntry->KeyCount, + ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes)); + + // If there is a file key + if(pEncodingEntry->KeyCount != 0) + { + // Get the first encoding key + pbEncodingKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + + // Dump all encoding keys + for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++) + { + fprintf(fp, " Index Key: %s\n", StringFromMD5(pbEncodingKey, szMd5)); + DumpIndexKey(fp, hs, pbEncodingKey, ppIndexEntries); + pbEncodingKey += MD5_HASH_SIZE; + } + } + else + { + fprintf(fp, " ZERO FILE KEYS\n"); + return; + } +} + +static void DumpEncodingEntry( + FILE * fp, + TCascStorage * hs, + PCASC_ROOT_ENTRY pRootEntry, + PCASC_ENCODING_ENTRY * ppEncodingKeys, + PCASC_INDEX_ENTRY * ppIndexEntries) +{ + PCASC_ENCODING_ENTRY pEncodingKey; + QUERY_KEY QueryKey; + size_t EntryIndex = 0; + + // Find the encoding key + QueryKey.pbData = pRootEntry->EncodingKey; + QueryKey.cbData = MD5_HASH_SIZE; + pEncodingKey = FindEncodingEntry(hs, &QueryKey, &EntryIndex); + if(pEncodingKey == NULL) + { + fprintf(fp, " NO ENCODING KEY\n"); + return; + } + + // Get the file key, clear the encoding key + ppEncodingKeys[EntryIndex] = NULL; + DumpEncodingEntry(fp, hs, pEncodingKey, ppIndexEntries); +} + +static void DumpRootEntries(FILE * fp, TCascStorage * hs, char ** FileNameArray) +{ + PCASC_ENCODING_ENTRY * ppEncodingEntries; // Array of encoding entries + PCASC_INDEX_ENTRY * ppIndexEntries; // Complete list of key entries for all files + const char * szFileName = NULL; + ULONGLONG PrevNameHash = (ULONGLONG)-1; + char szMd5[MD5_STRING_SIZE]; + + // Create copy of the encoding keys and file keys + ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, hs->nEncodingEntries); + ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, hs->pIndexEntryMap->ItemCount); + if(ppEncodingEntries && ppIndexEntries) + { + // Copy all pointers + memcpy(ppEncodingEntries, hs->ppEncodingEntries, hs->nEncodingEntries * sizeof(PCASC_ENCODING_ENTRY)); + Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries); + + // Parse all entries + for(size_t i = 0; i < hs->nRootEntries; i++) + { + PCASC_ROOT_ENTRY pRootEntry = hs->ppRootEntries[i]; + const char * szDuplicate = ""; + + // Check duplicates + if(pRootEntry->FileNameHash != PrevNameHash) + szFileName = FileNameArray[i]; + else + szDuplicate = "(DUPLICATE) "; + + // Dump the root entry + fprintf(fp, "NameHash: %016llx Locales: %08lx MD5: %s %sFileName: %s\n", + pRootEntry->FileNameHash, + pRootEntry->Locales, + StringFromMD5(pRootEntry->EncodingKey, szMd5), + szDuplicate, + szFileName); + + DumpEncodingEntry(fp, hs, pRootEntry, ppEncodingEntries, ppIndexEntries); + PrevNameHash = pRootEntry->FileNameHash; + fprintf(fp, "\n"); + } + + // Dump all orphaned encoding keys + for(size_t i = 0; i < hs->nEncodingEntries; i++) + { + if(ppEncodingEntries[i] != NULL) + { + fprintf(fp, "[NO ROOT KEY]\n"); + + DumpEncodingEntry(fp, hs, ppEncodingEntries[i], ppIndexEntries); + ppEncodingEntries[i] = NULL; + + fprintf(fp, "\n"); + } + } + + + CASC_FREE(ppIndexEntries); + CASC_FREE(ppEncodingEntries); + } +} + +//----------------------------------------------------------------------------- +// Public functions + +void CascDumpSparseArray(const char * szFileName, void * pvSparseArray) +{ + TSparseArray * pSparseArray = (TSparseArray *)pvSparseArray; + FILE * fp; + + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + // Write header + fprintf(fp, "## Value\n-- -----\n"); + + // Write the values + for(DWORD i = 0; i < pSparseArray->TotalItemCount; i++) + { + DWORD Value = 0; + + if(pSparseArray->IsItemPresent(i)) + { + Value = pSparseArray->GetItemValue(i); + fprintf(fp, "%02X %02X\n", i, Value); + } + else + { + fprintf(fp, "%02X --\n", i); + } + } + + fclose(fp); + } +} + +void CascDumpNameFragTable(const char * szFileName, void * pMarFile) +{ + TFileNameDatabase * pDB = ((PMAR_FILE)pMarFile)->pDatabasePtr->pDB; + FILE * fp; + + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + PNAME_FRAG pNameTable = pDB->NameFragTable.NameFragArray; + const char * szNames = pDB->IndexStruct_174.NameFragments.CharArray; + const char * szLastEntry; + char szMatchType[0x100]; + + // Dump the table header + fprintf(fp, "Indx ThisHash NextHash FragOffs\n"); + fprintf(fp, "---- -------- -------- --------\n"); + + // Dump all name entries + for(DWORD i = 0; i < pDB->NameFragTable.ItemCount; i++) + { + // Reset both match types + szMatchType[0] = 0; + szLastEntry = ""; + + // Only if the table entry is not empty + if(pNameTable->ItemIndex != 0xFFFFFFFF) + { + // Prepare the entry + if(IS_SINGLE_CHAR_MATCH(pDB->NameFragTable, i)) + sprintf(szMatchType, "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF)); + else + sprintf(szMatchType, "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs); + } + + // Dump the entry + fprintf(fp, "0x%02X %08x %08x %08x %s%s\n", i, pNameTable->ItemIndex, + pNameTable->NextIndex, + pNameTable->FragOffs, + szMatchType, + szLastEntry); + pNameTable++; + } + fclose(fp); + } +} + +void CascDumpFileNames(const char * szFileName, void * pvMarFile) +{ + TMndxFindResult Struct1C; + PMAR_FILE pMarFile = (PMAR_FILE)pvMarFile; + FILE * fp; + char szNameBuff[0x200]; + bool bFindResult; + + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + // Set an empty path as search mask (?) + Struct1C.SetSearchPath("", 0); + + // Keep searching + for(;;) + { + // Search the next file name + pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult); + + // Stop the search in case of failure + if(!bFindResult) + break; + + // Printf the found file name + memcpy(szNameBuff, Struct1C.szFoundPath, Struct1C.cchFoundPath); + szNameBuff[Struct1C.cchFoundPath] = 0; + fprintf(fp, "%s\n", szNameBuff); + } + + fclose(fp); + } + + // Free the search structures + Struct1C.FreeStruct40(); +} + +void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo) +{ + PCASC_MNDX_ENTRY pMndxEntry; + FILE * fp; + char szMd5[MD5_STRING_SIZE]; + + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n"); + for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++) + { + pMndxEntry = pMndxInfo->ppValidEntries[i]; + + fprintf(fp, "%04X %08X %s %08X\n", i, + pMndxEntry->Flags, + StringFromMD5(pMndxEntry->EncodingKey, szMd5), + pMndxEntry->FileSize); + } + fclose(fp); + } +} + +void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs) +{ + PCASC_INDEX_ENTRY * ppIndexEntries; + FILE * fp; + size_t nIndexEntries = hs->pIndexEntryMap->ItemCount; + char szIndexKey[0x40]; + + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + // Create linear aray + ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, nIndexEntries); + if(ppIndexEntries != NULL) + { + // Obtain the linear array of index entries + Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries); + + // Sort the array by archive number and archive offset + qsort_pointer_array((void **)ppIndexEntries, nIndexEntries, CompareIndexEntries_FilePos, NULL); + + // Parse the array + fprintf(fp, "ArNo ArOffset FileSize IndexKey\n==== ======== ======== ================================\n"); + for(size_t i = 0; i < nIndexEntries; i++) + { + PCASC_INDEX_ENTRY pIndexEntry = ppIndexEntries[i]; + ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset); + DWORD ArchIndex = (DWORD)(ArchOffset >> 0x1E); + DWORD FileSize; + + FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize); + ArchOffset &= 0x3FFFFFFF; + + fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey)); + } + + CASC_FREE(ppIndexEntries); + } + + fclose(fp); + } +} + +void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile) +{ + char ** FileNameArray = NULL; + FILE * fp; + + // Validate the storage handle + if(hs != NULL) + { + // Create the dump file + fp = fopen(szFileName, "wt"); + if(fp != NULL) + { + // If we also have listfile, open it + if(szListFile != NULL) + FileNameArray = CreateFileNameArray(hs, szListFile); + + // Dump all root keys + fprintf(fp, "Root Entries\n=========\n\n"); + DumpRootEntries(fp, hs, FileNameArray); + + FreeFileNameArray(hs, FileNameArray); + fclose(fp); + } + } +} + + +void CascDumpFile(const char * szFileName, HANDLE hFile) +{ + FILE * fp; + DWORD dwBytesRead = 1; + DWORD dwFilePos = CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN); + BYTE Buffer[0x1000]; + + // Create the dump file + fp = fopen(szFileName, "wb"); + if(fp != NULL) + { + // Read data as long as we can, write as long as we can + while(dwBytesRead != 0) + { + // Read from the source file + if(!CascReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead)) + break; + + // Write to the destination file + if(fwrite(Buffer, 1, dwBytesRead, fp) != dwBytesRead) + break; + } + + // Close the local file + fclose(fp); + + // Restore the file pointer + CascSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN); + } +} + +#endif // _DEBUG diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp new file mode 100644 index 00000000000..f1b14560bed --- /dev/null +++ b/dep/CascLib/src/CascFindFile.cpp @@ -0,0 +1,364 @@ +/*****************************************************************************/ +/* CascFindFile.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 10.05.14 1.00 Lad The first version of CascFindFile.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +//----------------------------------------------------------------------------- +// Local functions + +static TCascSearch * IsValidSearchHandle(HANDLE hFind) +{ + TCascSearch * pSearch = (TCascSearch *)hFind; + + return (pSearch != NULL && pSearch->szClassName != NULL && !strcmp(pSearch->szClassName, "TCascSearch") && pSearch->szMask != NULL) ? pSearch : NULL; +} + +static void FreeSearchHandle(TCascSearch * pSearch) +{ + // Only if the storage handle is valid + assert(pSearch != NULL); + + // Close (dereference) the archive handle + if(pSearch->hs != NULL) + CascCloseStorage((HANDLE)pSearch->hs); + pSearch->hs = NULL; + + // Free the file cache and frame array + if(pSearch->szMask != NULL) + CASC_FREE(pSearch->szMask); + if(pSearch->szListFile != NULL) + CASC_FREE(pSearch->szListFile); + if(pSearch->pStruct1C != NULL) + delete pSearch->pStruct1C; + if(pSearch->pCache != NULL) + ListFile_Free(pSearch->pCache); + + // Free the structure itself + pSearch->szClassName = NULL; + CASC_FREE(pSearch); +} +/* +DWORD dwRootEntries = 0; +DWORD dwEncoEntries = 0; +DWORD dwIndexEntries = 0; +*/ +static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry, PCASC_FIND_DATA pFindData, size_t nRootIndex) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + PCASC_INDEX_ENTRY pIndexEntry; + TCascStorage * hs = pSearch->hs; + QUERY_KEY QueryKey; + DWORD dwByteIndex = (DWORD)(nRootIndex / 0x08); + DWORD dwBitIndex = (DWORD)(nRootIndex & 0x07); + + // First of all, check if that entry has been reported before + // If the bit is set, then the file has already been reported + // by a previous search iteration + if(pSearch->BitArray[dwByteIndex] & (1 << dwBitIndex)) + return false; + pSearch->BitArray[dwByteIndex] |= (1 << dwBitIndex); + + // Increment the number of root entries +// dwRootEntries++; + + // Now try to find that encoding key in the array of encoding keys + QueryKey.pbData = pRootEntry->EncodingKey; + QueryKey.cbData = MD5_HASH_SIZE; + pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL); + if(pEncodingEntry == NULL) + return false; + +// dwEncoEntries++; + + // Now try to find the index entry. Note that we take the first key + QueryKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + QueryKey.cbData = MD5_HASH_SIZE; + pIndexEntry = FindIndexEntry(hs, &QueryKey); + if(pIndexEntry == NULL) + return false; + +// dwIndexEntries++; + + // Fill the name hash and the MD5 + memcpy(pFindData->EncodingKey, pRootEntry->EncodingKey, MD5_HASH_SIZE); + pFindData->FileNameHash = pRootEntry->FileNameHash; + pFindData->dwPackageIndex = 0; + pFindData->dwLocaleFlags = pRootEntry->Locales; + pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes); + return true; +} + +static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask) +{ + TCascSearch * pSearch; + size_t cbToAllocate; + + // When using the MNDX info, do not allocate the extra bit array + cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->nRootEntries / 8) : 0); + pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate); + if(pSearch != NULL) + { + // Initialize the structure + memset(pSearch, 0, cbToAllocate); + pSearch->szClassName = "TCascSearch"; + + // Save the search handle + pSearch->hs = hs; + hs->dwRefCount++; + + // If the mask was not given, use default + if(szMask == NULL) + szMask = "*"; + + // Save the other variables + if(szListFile != NULL) + { + pSearch->szListFile = NewStr(szListFile, 0); + if(pSearch->szListFile == NULL) + { + FreeSearchHandle(pSearch); + return NULL; + } + } + + // Allocate the search mask + pSearch->szMask = NewStr(szMask, 0); + if(pSearch->szMask == NULL) + { + FreeSearchHandle(pSearch); + return NULL; + } + } + + return pSearch; +} + +static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +{ + PCASC_ROOT_ENTRY pRootEntry; + TCascStorage * hs = pSearch->hs; + size_t RootIndex; + + for(;;) + { + // Shall we get a new file name from the listfile? + if(pSearch->FileNameHash == 0) + { + // Try to get next file from the listfile + if(!ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH)) + break; + +// if(!_stricmp(pSearch->szFileName, "Interface\\Glues\\MODELS\\UI_MainMenu_Warlords\\Caustic_MedFreq.blp")) +// DebugBreak(); + + // Normalize the file name + strcpy(pSearch->szNormName, pSearch->szFileName); + NormalizeFileName_UpperBkSlash(pSearch->szNormName); + + // Find the first root entry belonging to this file name + pRootEntry = FindFirstRootEntry(pSearch->hs, pSearch->szNormName, &RootIndex); + if(pRootEntry == NULL) + continue; + + // We have the name now, search all locales + pSearch->FileNameHash = pRootEntry->FileNameHash; + pSearch->RootIndex = RootIndex; + } + + // Is the root index in range? + while(pSearch->RootIndex < hs->nRootEntries) + { + // Get the next root entry. If name mismatches, stop searching + pRootEntry = hs->ppRootEntries[pSearch->RootIndex]; + if(pRootEntry->FileNameHash != pSearch->FileNameHash) + break; + + // Verify whether the file exists in the storage + if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex++)) + { + strcpy(pFindData->szFileName, pSearch->szFileName); + pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName); + return true; + } + } + + // Reset the name hash and root index and retry search + pSearch->FileNameHash = 0; + } + + // Listfile search ended + return false; +} + +static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +{ + PCASC_ROOT_ENTRY pRootEntry; + TCascStorage * hs = pSearch->hs; + + // Check if there is more files with the same name hash + while(pSearch->RootIndex < hs->nRootEntries) + { + // Get the pointer to the root entry + pRootEntry = hs->ppRootEntries[pSearch->RootIndex]; + + // Verify if that root entry exists in the CASC storage + // Note that the file name will be there from the previous search + if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex)) + { + pFindData->szFileName[0] = 0; + pFindData->szPlainName = NULL; + return true; + } + + // Move to the next entry + pSearch->RootIndex++; + } + + // Nameless search ended + return false; +} + +static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +{ + // Are we searching using the MNDX ? + if(pSearch->hs->pMndxInfo != NULL) + return DoStorageSearch_MNDX(pSearch, pFindData); + + // State 0: No search done yet + if(pSearch->dwState == 0) + { + // Does the search specify listfile? + if(pSearch->szListFile != NULL) + pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile); + + // Move the search phase to the listfile searching + pSearch->FileNameHash = 0; + pSearch->RootIndex = 0; + pSearch->dwState++; + + // If either file stream or listfile cache are invalid, + // move to the next phase + if(pSearch->pCache == NULL) + pSearch->dwState++; + } + + // State 1: Searching the list file + if(pSearch->dwState == 1) + { + if(DoStorageSearch_ListFile(pSearch, pFindData)) + return true; + + // Move to the nameless search state + pSearch->RootIndex = 0; + pSearch->dwState++; + } + + // State 2: Searching the remaining entries + if(pSearch->dwState == 2) + { + if(DoStorageSearch_Hash(pSearch, pFindData)) + return true; + + // Move to the final search state + pSearch->dwState++; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Public functions + +HANDLE WINAPI CascFindFirstFile( + HANDLE hStorage, + const char * szMask, + PCASC_FIND_DATA pFindData, + const TCHAR * szListFile) +{ + TCascStorage * hs; + TCascSearch * pSearch = NULL; + int nError = ERROR_SUCCESS; + + // Check parameters + if((hs = IsValidStorageHandle(hStorage)) == NULL) + nError = ERROR_INVALID_HANDLE; + if(szMask == NULL || pFindData == NULL) + nError = ERROR_INVALID_PARAMETER; + + // Allocate the structure for archive search + if(nError == ERROR_SUCCESS) + { + // Clear the entire search structure + memset(pFindData, 0, sizeof(CASC_FIND_DATA)); + + // We must have listfile for non-MNDX storages + if(hs->pMndxInfo == NULL && szListFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + // Allocate the search handle + pSearch = AllocateSearchHandle(hs, szListFile, szMask); + if(pSearch == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Perform search + if(nError == ERROR_SUCCESS) + { + if(!DoStorageSearch(pSearch, pFindData)) + nError = ERROR_NO_MORE_FILES; + } + + if(nError != ERROR_SUCCESS) + { + if(pSearch != NULL) + FreeSearchHandle(pSearch); + pSearch = NULL; + } + + return (HANDLE)pSearch; +} + +bool WINAPI CascFindNextFile( + HANDLE hFind, + PCASC_FIND_DATA pFindData) +{ + TCascSearch * pSearch; + + pSearch = IsValidSearchHandle(hFind); + if(pSearch == NULL || pFindData == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Perform search + return DoStorageSearch(pSearch, pFindData); +} + +bool WINAPI CascFindClose(HANDLE hFind) +{ + TCascSearch * pSearch; + + pSearch = IsValidSearchHandle(hFind); + if(pSearch == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + FreeSearchHandle(pSearch); + return true; +} diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h new file mode 100644 index 00000000000..a3960a558c2 --- /dev/null +++ b/dep/CascLib/src/CascLib.h @@ -0,0 +1,171 @@ +/*****************************************************************************/ +/* CascLib.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* CascLib library v 1.00 */ +/* */ +/* Author : Ladislav Zezula */ +/* E-mail : ladik@zezula.net */ +/* WWW : http://www.zezula.net */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __CASCLIB_H__ +#define __CASCLIB_H__ + +#ifdef _MSC_VER +#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' +#endif + +#include "CascPort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------------- +// Defines + +#define CASCLIB_VERSION 0x0100 // Current version of CascLib (1.0) +#define CASCLIB_VERSION_STRING "1.00" // String version of CascLib version + +#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by encrypted stream when can't find file key +#define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing + +// Values for CascOpenStorage +#define CASC_STOR_XXXXX 0x00000001 // Not used + +// Values for CascOpenFile +#define CASC_FILE_XXXXX 0x00000001 // Not used + +// Flags for file stream +#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file +#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file +#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server +#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value + +#define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping +#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part) +#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive +#define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file +#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value + +#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only +#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write +#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it +#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options + +#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers +#define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options) + +#define CASC_LOCALE_ALL 0xFFFFFFFF +#define CASC_LOCALE_NONE 0x00000000 +#define CASC_LOCALE_UNKNOWN1 0x00000001 +#define CASC_LOCALE_ENUS 0x00000002 +#define CASC_LOCALE_KOKR 0x00000004 +#define CASC_LOCALE_UNKNOWN8 0x00000008 +#define CASC_LOCALE_FRFR 0x00000010 +#define CASC_LOCALE_DEDE 0x00000020 +#define CASC_LOCALE_ZHCN 0x00000040 +#define CASC_LOCALE_ESES 0x00000080 +#define CASC_LOCALE_ZHTW 0x00000100 +#define CASC_LOCALE_ENGB 0x00000200 +#define CASC_LOCALE_ENCN 0x00000400 +#define CASC_LOCALE_ENTW 0x00000800 +#define CASC_LOCALE_ESMX 0x00001000 +#define CASC_LOCALE_RURU 0x00002000 +#define CASC_LOCALE_PTBR 0x00004000 +#define CASC_LOCALE_ITIT 0x00008000 +#define CASC_LOCALE_PTPT 0x00010000 + +#define MAX_CASC_KEY_LENGTH 0x10 // Maximum length of the key (equal to MD5 hash) + +#ifndef MD5_HASH_SIZE +#define MD5_HASH_SIZE 0x10 +#define MD5_STRING_SIZE 0x21 +#endif + +#ifndef SHA1_DIGEST_SIZE +#define SHA1_DIGEST_SIZE 0x14 // 160 bits +#endif + +#ifndef LANG_NEUTRAL +#define LANG_NEUTRAL 0x00 // Neutral locale +#endif + +// Return value for CascGetFileSize and CascSetFilePointer +#define CASC_INVALID_SIZE 0xFFFFFFFF +#define CASC_INVALID_POS 0xFFFFFFFF + +// Flags for CascGetStorageInfo +#define CASC_FEATURE_LISTFILE 0x00000001 // The storage supports listfile + +//----------------------------------------------------------------------------- +// Structures + +typedef enum _CASC_STORAGE_INFO_CLASS +{ + CascStorageFileCount, + CascStorageFeatures, + CascStorageInfoClassMax + +} CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS; + + +typedef struct _QUERY_KEY +{ + LPBYTE pbData; + DWORD cbData; +} QUERY_KEY, *PQUERY_KEY; + +// Structure for SFileFindFirstFile and SFileFindNextFile +typedef struct _CASC_FIND_DATA +{ + char szFileName[MAX_PATH]; // Full name of the found file + char * szPlainName; // Plain name of the found file + ULONGLONG FileNameHash; // File name hash + BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key + DWORD dwPackageIndex; // File package index (HOTS only) + DWORD dwLocaleFlags; // Locale flags (WoW only) + DWORD dwFileSize; // Size of the file + +} CASC_FIND_DATA, *PCASC_FIND_DATA; + +//----------------------------------------------------------------------------- +// Callback functions + +typedef struct TFileStream TFileStream; +typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); + +//----------------------------------------------------------------------------- +// We have our own qsort implementation, optimized for sorting array of pointers + +void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context); + +//----------------------------------------------------------------------------- +// Functions for storage manipulation + +bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * phStorage); +bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded); +bool WINAPI CascCloseStorage(HANDLE hStorage); + +bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile); +bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile); +bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile); +DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh); +DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); +bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead); +bool WINAPI CascCloseFile(HANDLE hFile); + +HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND_DATA pFindData, const TCHAR * szListFile); +bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData); +bool WINAPI CascFindClose(HANDLE hFind); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __CASCLIB_H__ diff --git a/dep/CascLib/src/CascMndxRoot.cpp b/dep/CascLib/src/CascMndxRoot.cpp new file mode 100644 index 00000000000..a788b652573 --- /dev/null +++ b/dep/CascLib/src/CascMndxRoot.cpp @@ -0,0 +1,3476 @@ +/*****************************************************************************/ +/* CascMndxRoot.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/* Note: "HOTS" refers to Play.exe, v2.5.0.29049 (Heroes of the Storm Alpha) */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 18.05.14 1.00 Lad The first version of CascMndxRoot.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +//----------------------------------------------------------------------------- +// Local defines + +#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0' + +//----------------------------------------------------------------------------- +// Local structures + +typedef struct _FILE_MNDX_HEADER +{ + DWORD Signature; // 'MNDX' + DWORD HeaderVersion; // Must be <= 2 + DWORD FormatVersion; + +} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER; + +typedef struct _FILE_MAR_INFO +{ + DWORD MarIndex; + DWORD MarDataSize; + DWORD MarDataSizeHi; + DWORD MarDataOffset; + DWORD MarDataOffsetHi; +} FILE_MAR_INFO, *PFILE_MAR_INFO; + +//----------------------------------------------------------------------------- +// Testing functions prototypes + +#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST) +extern "C" bool _cdecl sub_1958B00_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); +void TestMndxRootFile(PCASC_MNDX_INFO pMndxInfo); +#endif + +//----------------------------------------------------------------------------- +// Local variables + +unsigned char table_1BA1818[0x800] = +{ + 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 +}; + +//----------------------------------------------------------------------------- +// Local functions - Number of set bits in an integer + +// HOTS: inlined +DWORD GetNumberOfSetBits(DWORD Value32) +{ + Value32 = ((Value32 >> 1) & 0x55555555) + (Value32 & 0x55555555); + Value32 = ((Value32 >> 2) & 0x33333333) + (Value32 & 0x33333333); + Value32 = ((Value32 >> 4) & 0x0F0F0F0F) + (Value32 & 0x0F0F0F0F); + + return (Value32 * 0x01010101); +} + +#define GetNumbrOfSetBits32(x) (GetNumberOfSetBits(x) >> 0x18) + +//----------------------------------------------------------------------------- +// Local functions - common + +static bool RootFileRead(LPBYTE pbFilePointer, LPBYTE pbFileEnd, void * pvBuffer, size_t dwBytesToRead) +{ + if((size_t)(pbFileEnd - pbFilePointer) < dwBytesToRead) + return false; + + memcpy(pvBuffer, pbFilePointer, dwBytesToRead); + return true; +} + +//----------------------------------------------------------------------------- +// Local functions - TMndxFindResult + +// HOTS: 01956EE0 +TMndxFindResult::TMndxFindResult() +{ + szSearchMask = NULL; + cchSearchMask = 0; + field_8 = 0; + szFoundPath = NULL; + cchFoundPath = 0; + FileNameIndex = 0; + pStruct40 = NULL; +} + +// HOTS: 01956F00 +TMndxFindResult::~TMndxFindResult() +{ + FreeStruct40(); +} + +// HOTS: 01956F30 +int TMndxFindResult::CreateStruct40() +{ + if(pStruct40 != NULL) + return ERROR_INVALID_PARAMETER; + + pStruct40 = new TStruct40(); + return (pStruct40 != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; +} + +void TMndxFindResult::FreeStruct40() +{ + if(pStruct40 != NULL) + delete pStruct40; + pStruct40 = NULL; +} + +// HOTS: 01956E70 +int TMndxFindResult::SetSearchPath( + const char * szNewSearchMask, + size_t cchNewSearchMask) +{ + if(szSearchMask == NULL && cchSearchMask != 0) + return ERROR_INVALID_PARAMETER; + + if(pStruct40 != NULL) + pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING; + + szSearchMask = szNewSearchMask; + cchSearchMask = cchNewSearchMask; + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TByteStream functions + +// HOTS: 01959990 +TByteStream::TByteStream() +{ + pbByteData = NULL; + pvMappedFile = NULL; + cbByteData = 0; + field_C = 0; + hFile = 0; + hMap = 0; +} + +// HOTS: 19599F0 +void TByteStream::ExchangeWith(TByteStream & Target) +{ + TByteStream WorkBuff; + + WorkBuff = *this; + *this = Target; + Target = WorkBuff; +} + +// HOTS: 19599F0 +int TByteStream::GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray) +{ + if(cbByteData < cbByteCount) + return ERROR_BAD_FORMAT; + + // Give the buffer to the caller + PtrArray->Bytes = pbByteData; + + // Move pointers + pbByteData += cbByteCount; + cbByteData -= cbByteCount; + return ERROR_SUCCESS; +} + +// HOTS: 1957190 +int TByteStream::GetArray_DWORDs(PARRAY_POINTER PtrArray, DWORD ItemCount) +{ + if(PtrArray == NULL && ItemCount != 0) + return ERROR_INVALID_PARAMETER; + if(ItemCount > CASC_MAX_ENTRIES(DWORD)) + return ERROR_NOT_ENOUGH_MEMORY; + + return GetBytes(ItemCount * 4, PtrArray); +} + +// HOTS: 19571E0 +int TByteStream::GetArray_Triplets(PARRAY_POINTER PtrArray, DWORD ItemCount) +{ + if(PtrArray == NULL && ItemCount != 0) + return ERROR_INVALID_PARAMETER; + if(ItemCount > CASC_MAX_ENTRIES(TRIPLET)) + return ERROR_NOT_ENOUGH_MEMORY; + + return GetBytes(ItemCount * sizeof(TRIPLET), PtrArray); +} + +// HOTS: 1957230 +int TByteStream::GetArray_BYTES(PARRAY_POINTER PtrArray, DWORD ItemCount) +{ + if(PtrArray == NULL && ItemCount != 0) + return ERROR_INVALID_PARAMETER; + if(ItemCount > CASC_MAX_ENTRIES(BYTE)) + return ERROR_NOT_ENOUGH_MEMORY; + + return GetBytes(ItemCount, PtrArray); +} + +// HOTS: 1957280 +int TByteStream::GetArray_NameTable(PARRAY_POINTER PtrArray, DWORD ItemCount) +{ + if(PtrArray == NULL && ItemCount != 0) + return ERROR_INVALID_PARAMETER; + if(ItemCount > CASC_MAX_ENTRIES(NAME_FRAG)) + return ERROR_NOT_ENOUGH_MEMORY; + + return GetBytes(ItemCount * sizeof(NAME_FRAG), PtrArray); +} + +// HOTS: 1959A60 +int TByteStream::SkipBytes(DWORD cbByteCount) +{ + ARRAY_POINTER Dummy; + + return GetBytes(cbByteCount, &Dummy); +} + +// HOTS: 1959AF0 +int TByteStream::SetByteBuffer(LPBYTE pbNewByteData, DWORD cbNewByteData) +{ + if(pbNewByteData != NULL || cbNewByteData == 0) + { + pbByteData = pbNewByteData; + cbByteData = cbNewByteData; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; +} + + +// HOTS: 1957160 +int TByteStream::GetValue_DWORD(DWORD & Value) +{ + ARRAY_POINTER Pointer; + int nError; + + nError = GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + + Value = Pointer.Uint32s[0]; + return ERROR_SUCCESS; +} + +int TByteStream::GetValue_ItemCount(DWORD & NumberOfBytes, DWORD & ItemCount, DWORD ItemSize) +{ + ARRAY_POINTER Pointer; + ULONGLONG ByteCount; + int nError; + + // Verify if there is at least - 8 bytes + nError = GetBytes(sizeof(ULONGLONG), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + + // Extract the number of bytes + ByteCount = Pointer.Int64Ptr[0]; + if(ByteCount > 0xFFFFFFFF || (ByteCount % ItemSize) != 0) + return ERROR_BAD_FORMAT; + + // Give the result to the caller + NumberOfBytes = (DWORD)ByteCount; + ItemCount = (DWORD)(ByteCount / ItemSize); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TGenericArray functions + +TGenericArray::TGenericArray() +{ + DataBuffer.Bytes = NULL; + FirstValid.Bytes = NULL; + ArrayPointer.Bytes = NULL; + ItemCount = 0; + MaxItemCount = 0; + bIsValidArray = false; +} + +TGenericArray::~TGenericArray() +{ + if(DataBuffer.Bytes != NULL) + CASC_FREE(DataBuffer.Bytes); +} + +// HOTS: inlined +void TGenericArray::ExchangeWith(TGenericArray & Target) +{ + TGenericArray WorkBuff; + + WorkBuff = *this; + *this = Target; + Target = WorkBuff; +} + +// HOTS: Inlined +void TGenericArray::CopyFrom(TGenericArray & Source) +{ + if(DataBuffer.Bytes != NULL) + CASC_FREE(DataBuffer.Bytes); + *this = Source; +} + +// HOTS: 1957090 (SetDwordsValid) +// HOTS: 19570B0 (SetTripletsValid) +// HOTS: 19570D0 (? SetBitsValid ?) +// HOTS: 19570F0 (SetNameFragmentsValid) +int TGenericArray::SetArrayValid() +{ + if(bIsValidArray != 0) + return 1; + + bIsValidArray = true; + return ERROR_SUCCESS; +} + + +// HOTS: 19575A0 +void TGenericArray::SetMaxItems_CHARS(DWORD NewMaxItemCount) +{ + ARRAY_POINTER OldDataBuffer = DataBuffer; + ARRAY_POINTER NewDataBuffer; + + // Allocate new data buffer + NewDataBuffer.Chars = CASC_ALLOC(char, NewMaxItemCount); + if(NewDataBuffer.Chars != NULL) + { + // Copy the old items to the buffer + for(DWORD i = 0; i < ItemCount; i++) + { + NewDataBuffer.Chars[i] = FirstValid.Chars[i]; + } + } + + DataBuffer = NewDataBuffer; + FirstValid = NewDataBuffer; + ArrayPointer = NewDataBuffer; + MaxItemCount = NewMaxItemCount; + CASC_FREE(OldDataBuffer.Chars); +} + +// HOTS: 1957600 +void TGenericArray::SetMaxItems_PATH_STOP(DWORD NewMaxItemCount) +{ + ARRAY_POINTER OldDataBuffer = DataBuffer; + ARRAY_POINTER NewDataBuffer; + + // Allocate new data buffer + NewDataBuffer.PathStopPtr = CASC_ALLOC(PATH_STOP, NewMaxItemCount); + if(NewDataBuffer.PathStopPtr != NULL) + { + // Copy the old items to the buffer + for(DWORD i = 0; i < ItemCount; i++) + { + NewDataBuffer.PathStopPtr[i] = FirstValid.PathStopPtr[i]; + } + } + + DataBuffer = NewDataBuffer; + FirstValid = NewDataBuffer; + ArrayPointer = NewDataBuffer; + MaxItemCount = NewMaxItemCount; + CASC_FREE(OldDataBuffer.PathStopPtr); +} + +// HOTS: inline +void TGenericArray::InsertOneItem_CHAR(char NewItem) +{ + DWORD NewMaxItemCount; + DWORD NewItemCount; + + NewItemCount = ItemCount + 1; + if(NewItemCount > MaxItemCount) + { + NewMaxItemCount = NewItemCount; + + if(MaxItemCount > (NewItemCount / 2)) + { + if(MaxItemCount <= (CASC_MAX_ENTRIES(BYTE) / 2)) + NewMaxItemCount = MaxItemCount + MaxItemCount; + else + NewMaxItemCount = CASC_MAX_ENTRIES(BYTE); + } + + SetMaxItems_CHARS(NewMaxItemCount); + } + + // Put the character to the slot that has been reserved + FirstValid.Chars[ItemCount++] = NewItem; +} + +// HOTS: 1958330, inline +void TGenericArray::InsertOneItem_PATH_STOP(PATH_STOP & NewItem) +{ + DWORD NewMaxItemCount; + DWORD NewItemCount; + + NewItemCount = ItemCount + 1; + if(NewItemCount > MaxItemCount) + { + NewMaxItemCount = NewItemCount; + + if(MaxItemCount > (NewItemCount / 2)) + { + if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2)) + NewMaxItemCount = MaxItemCount + MaxItemCount; + else + NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP); + } + + SetMaxItems_PATH_STOP(NewMaxItemCount); + } + + // Put the structure to the slot that has been reserved + FirstValid.PathStopPtr[ItemCount++] = NewItem; +} + +// HOTS: 19583A0 +void TGenericArray::sub_19583A0(DWORD NewItemCount) +{ + DWORD OldMaxItemCount = MaxItemCount; + + if(NewItemCount > MaxItemCount) + { + DWORD NewMaxItemCount = NewItemCount; + + if(MaxItemCount > (NewItemCount / 2)) + { + if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2)) + NewMaxItemCount = MaxItemCount + MaxItemCount; + else + NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP); + } + + SetMaxItems_PATH_STOP(NewMaxItemCount); + } + + // Initialize the newly inserted items + for(DWORD i = OldMaxItemCount; i < NewItemCount; i++) + { + FirstValid.PathStopPtr[i].ItemIndex = 0; + FirstValid.PathStopPtr[i].field_4 = 0; + FirstValid.PathStopPtr[i].field_8 = 0; + FirstValid.PathStopPtr[i].field_C = 0xFFFFFFFF; + FirstValid.PathStopPtr[i].field_10 = 0xFFFFFFFF; + } + + ItemCount = NewItemCount; +} + +// HOTS: 1957440 +int TGenericArray::LoadDwordsArray(TByteStream & InStream) +{ + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(DWORD)); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetArray_DWORDs(&ArrayPointer, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); +} + +// HOTS: 19574E0 +int TGenericArray::LoadTripletsArray(TByteStream & InStream) +{ + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(TRIPLET)); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetArray_Triplets(&ArrayPointer, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); +} + +// HOTS: 1957690 +int TGenericArray::LoadByteArray(TByteStream & InStream) +{ + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(BYTE)); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); +} + +// HOTS: 1957700 +int TGenericArray::LoadFragmentInfos(TByteStream & InStream) +{ + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(NAME_FRAG)); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetArray_NameTable(&ArrayPointer, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); +} + +// HOTS: 195A220 +int TGenericArray::LoadStrings(TByteStream & InStream) +{ + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(char)); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); +} + +// HOTS: 19581C0 +int TGenericArray::LoadDwordsArray_Copy(TByteStream & InStream) +{ + TGenericArray TempArray; + int nError; + + nError = TempArray.LoadDwordsArray(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempArray); + return ERROR_SUCCESS; +} + +// HOTS: 1958250 +int TGenericArray::LoadTripletsArray_Copy(TByteStream & InStream) +{ + TGenericArray TempArray; + int nError; + + nError = TempArray.LoadTripletsArray(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempArray); + return ERROR_SUCCESS; +} + +// HOTS: 1958420 +int TGenericArray::LoadBytes_Copy(TByteStream & InStream) +{ + TGenericArray TempArray; + int nError; + + nError = TempArray.LoadByteArray(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempArray); + return 0; +} + +// HOTS: 19584F0 +int TGenericArray::LoadFragmentInfos_Copy(TByteStream & InStream) +{ + TGenericArray TempArray; + int nError; + + nError = TempArray.LoadFragmentInfos(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempArray); + return ERROR_SUCCESS; +} + +// HOTS: 195A360 +int TGenericArray::LoadStringsWithCopy(TByteStream & InStream) +{ + TGenericArray TempArray; + int nError; + + nError = TempArray.LoadStrings(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempArray); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TBitEntryArray functions + +TBitEntryArray::TBitEntryArray() +{ + BitsPerEntry = 0; + EntryBitMask = 0; + TotalEntries = 0; +} + +TBitEntryArray::~TBitEntryArray() +{} + +// HOTS: 01957D20 +void TBitEntryArray::ExchangeWith(TBitEntryArray & Target) +{ + TBitEntryArray WorkBuff; + + WorkBuff = *this; + *this = Target; + Target = WorkBuff; +} + +// HOTS: 1958580 +int TBitEntryArray::LoadFromStream(TByteStream & InStream) +{ + ARRAY_POINTER Pointer; + ULONGLONG Value = 0; + int nError; + + nError = LoadDwordsArray_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + + BitsPerEntry = Pointer.Uint32s[0]; + if(BitsPerEntry > 0x20) + return ERROR_BAD_FORMAT; + + nError = InStream.GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + EntryBitMask = Pointer.Uint32s[0]; + + nError = InStream.GetBytes(sizeof(ULONGLONG), &Pointer); + if(nError == ERROR_SUCCESS) + Value = Pointer.Int64Ptr[0]; + if(Value > 0xFFFFFFFF) + return ERROR_BAD_FORMAT; + TotalEntries = (DWORD)Value; + + assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount); + return ERROR_SUCCESS; +} + +// HOTS: 1959300 +int TBitEntryArray::LoadFromStream_Exchange(TByteStream & InStream) +{ + TBitEntryArray TempArray; + int nError; + + nError = TempArray.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + ExchangeWith(TempArray); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TStruct40 functions + +TStruct40::TStruct40() +{ + ItemIndex = 0; + CharIndex = 0; + ItemCount = 0; + SearchPhase = CASC_SEARCH_INITIALIZING; +} + +// HOTS: 19586B0 +void TStruct40::InitSearchBuffers() +{ + DWORD NewMaxItemCount; + + array_00.ItemCount = 0; + + // HOTS: 19586BD + if(array_00.MaxItemCount < 0x40) + { + // HOTS: 19586C2 + NewMaxItemCount = 0x40; + + if(array_00.MaxItemCount > 0x20) + { + if(array_00.MaxItemCount <= 0x7FFFFFFF) + NewMaxItemCount = array_00.MaxItemCount + array_00.MaxItemCount; + else + NewMaxItemCount = CASC_MAX_ENTRIES(BYTE); + } + + array_00.SetMaxItems_CHARS(NewMaxItemCount); + } + + // HOTS: 19586E1 + // Set the new item count + PathStops.sub_19583A0(0); + + if(PathStops.MaxItemCount < 4) + { + // HOTS: 19586F2 + NewMaxItemCount = 4; + + // HOTS: 19586EA + if(PathStops.MaxItemCount > 2) + { + if(PathStops.MaxItemCount <= 0x6666666) + NewMaxItemCount = PathStops.MaxItemCount + PathStops.MaxItemCount; + else + NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP); + } + + // HOTS: 195870B + PathStops.SetMaxItems_PATH_STOP(NewMaxItemCount); + } + + ItemIndex = 0; + CharIndex = 0; + ItemCount = 0; + SearchPhase = CASC_SEARCH_SEARCHING; +} + +//----------------------------------------------------------------------------- +// TSparseArray functions + +TSparseArray::TSparseArray() +{ + TotalItemCount = 0; + ValidItemCount = 0; +} + +// HOTS: 1957DA0 +void TSparseArray::ExchangeWith(TSparseArray & Target) +{ + TSparseArray WorkBuff; + + WorkBuff = *this; + *this = Target; + Target = WorkBuff; +} + +// HOTS: 1958630 +int TSparseArray::LoadFromStream(TByteStream & InStream) +{ + ARRAY_POINTER Pointer; + int nError; + + nError = ItemBits.LoadDwordsArray_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + TotalItemCount = Pointer.Uint32s[0]; + + nError = InStream.GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + ValidItemCount = Pointer.Uint32s[0]; + + if(ValidItemCount > TotalItemCount) + return ERROR_FILE_CORRUPT; + + nError = BaseValues.LoadTripletsArray_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = ArrayDwords_38.LoadDwordsArray_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = ArrayDwords_50.LoadDwordsArray_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + return ERROR_SUCCESS; +} + +// HOTS: 1959380 +int TSparseArray::LoadFromStream_Exchange(TByteStream & InStream) +{ + TSparseArray NewStruct68; + int nError; + + nError = NewStruct68.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + ExchangeWith(NewStruct68); + return ERROR_SUCCESS; +} + +// HOTS: 1959B60 +DWORD TSparseArray::GetItemValue(DWORD ItemIndex) +{ + PTRIPLET pTriplet; + DWORD DwordIndex; + DWORD BaseValue; + DWORD BitMask; + + // + // Divide the low-8-bits index to four parts: + // + // |-----------------------|---|------------| + // | A (23 bits) | B | C | + // |-----------------------|---|------------| + // + // A (23-bits): Index to the table (60 bits per entry) + // + // Layout of the table entry: + // |--------------------------------|-------|--------|--------|---------|---------|---------|---------|-----| + // | Base Value | val[0]| val[1] | val[2] | val[3] | val[4] | val[5] | val[6] | - | + // | 32 bits | 7 bits| 8 bits | 8 bits | 9 bits | 9 bits | 9 bits | 9 bits |5bits| + // |--------------------------------|-------|--------|--------|---------|---------|---------|---------|-----| + // + // B (3 bits) : Index of the variable-bit value in the array (val[#], see above) + // + // C (32 bits): Number of bits to be checked (up to 0x3F bits). + // Number of set bits is then added to the values obtained from A and B + + // Upper 23 bits contain index to the table + pTriplet = BaseValues.TripletArray + (ItemIndex >> 0x09); + BaseValue = pTriplet->BaseValue; + + // Next 3 bits contain the index to the VBR + switch(((ItemIndex >> 0x06) & 0x07) - 1) + { + case 0: // Add the 1st value (7 bits) + BaseValue += (pTriplet->Value2 & 0x7F); + break; + + case 1: // Add the 2nd value (8 bits) + BaseValue += (pTriplet->Value2 >> 0x07) & 0xFF; + break; + + case 2: // Add the 3rd value (8 bits) + BaseValue += (pTriplet->Value2 >> 0x0F) & 0xFF; + break; + + case 3: // Add the 4th value (9 bits) + BaseValue += (pTriplet->Value2 >> 0x17); + break; + + case 4: // Add the 5th value (9 bits) + BaseValue += (pTriplet->Value3 & 0x1FF); + break; + + case 5: // Add the 6th value (9 bits) + BaseValue += (pTriplet->Value3 >> 0x09) & 0x1FF; + break; + + case 6: // Add the 7th value (9 bits) + BaseValue += (pTriplet->Value3 >> 0x12) & 0x1FF; + break; + } + + // + // Take the upper 27 bits as an index to DWORD array, take lower 5 bits + // as number of bits to mask. Then calculate number of set bits in the value + // masked value. + // + + // Get the index into the array of DWORDs + DwordIndex = (ItemIndex >> 0x05); + + // Add number of set bits in the masked value up to 0x3F bits + if(ItemIndex & 0x20) + BaseValue += GetNumbrOfSetBits32(ItemBits.Uint32Array[DwordIndex - 1]); + + BitMask = (1 << (ItemIndex & 0x1F)) - 1; + return BaseValue + GetNumbrOfSetBits32(ItemBits.Uint32Array[DwordIndex] & BitMask); +} + +//----------------------------------------------------------------------------- +// TNameIndexStruct functions + +// HOTS: 0195A290 +TNameIndexStruct::TNameIndexStruct() +{} + +// HOTS: inlined +TNameIndexStruct::~TNameIndexStruct() +{} + +// HOTS: 195A180 +bool TNameIndexStruct::CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + const char * szPathFragment; + const char * szSearchMask; + + if(!Struct68.TotalItemCount) + { + // Get the offset of the fragment to compare. For convenience with pStruct40->CharIndex, + // subtract the CharIndex from the fragment offset + szPathFragment = (NameFragments.CharArray + dwFragOffs - pStruct40->CharIndex); + szSearchMask = pStruct1C->szSearchMask; + + // Keep searching as long as the name matches with the fragment + while(szPathFragment[pStruct40->CharIndex] == szSearchMask[pStruct40->CharIndex]) + { + // Move to the next character + pStruct40->CharIndex++; + + // Is it the end of the fragment or end of the path? + if(szPathFragment[pStruct40->CharIndex] == 0) + return true; + if(pStruct40->CharIndex >= pStruct1C->cchSearchMask) + return false; + } + + return false; + } + else + { + // Get the offset of the fragment to compare. + szPathFragment = (const char *)(NameFragments.CharArray); + szSearchMask = pStruct1C->szSearchMask; + + // Keep searching as long as the name matches with the fragment + while(szPathFragment[dwFragOffs] == szSearchMask[pStruct40->CharIndex]) + { + // Move to the next character + pStruct40->CharIndex++; + + // Is it the end of the fragment or end of the path? + if(Struct68.IsItemPresent(dwFragOffs++)) + return true; + if(dwFragOffs >= pStruct1C->cchSearchMask) + return false; + } + + return false; + } +} + +// HOTS: 195A570 +bool TNameIndexStruct::CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + const char * szPathFragment; + const char * szSearchMask; + + if(!Struct68.TotalItemCount) + { + // Get the offset of the fragment to compare. For convenience with pStruct40->CharIndex, + // subtract the CharIndex from the fragment offset + szPathFragment = (const char *)(NameFragments.CharArray + dwFragOffs - pStruct40->CharIndex); + szSearchMask = pStruct1C->szSearchMask; + + // Keep copying as long as we don't reach the end of the search mask + while(pStruct40->CharIndex < pStruct1C->cchSearchMask) + { + // HOTS: 195A5A0 + if(szPathFragment[pStruct40->CharIndex] != szSearchMask[pStruct40->CharIndex]) + return false; + + // HOTS: 195A5B7 + pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[pStruct40->CharIndex]); + pStruct40->CharIndex++; + + if(szPathFragment[pStruct40->CharIndex] == 0) + return true; + } + + // Fixup the address of the fragment + szPathFragment += pStruct40->CharIndex; + + // HOTS: 195A660 + // Now we need to copy the rest of the fragment + while(szPathFragment[0] != 0) + { + pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[0]); + szPathFragment++; + } + } + else + { + // Get the offset of the fragment to compare + // HOTS: 195A6B7 + szPathFragment = NameFragments.CharArray; + szSearchMask = pStruct1C->szSearchMask; + + // Keep copying as long as we don't reach the end of the search mask + while(dwFragOffs < pStruct1C->cchSearchMask) + { + if(szPathFragment[dwFragOffs] != szSearchMask[pStruct40->CharIndex]) + return false; + + pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[dwFragOffs]); + pStruct40->CharIndex++; + + // Keep going as long as the given bit is not set + if(Struct68.IsItemPresent(dwFragOffs++)) + return true; + } + + // Fixup the address of the fragment + szPathFragment += dwFragOffs; + + // Now we need to copy the rest of the fragment + while(Struct68.IsItemPresent(dwFragOffs++) == 0) + { + // HOTS: 195A7A6 + pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[0]); + szPathFragment++; + } + } + + return true; +} + +// HOTS: 195A3F0 +void TNameIndexStruct::CopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + const char * szPathFragment; + + // HOTS: 195A3FA + if(!Struct68.TotalItemCount) + { + // HOTS: 195A40C + szPathFragment = NameFragments.CharArray + dwFragOffs; + while(szPathFragment[0] != 0) + { + // Insert the character to the path being built + pStruct40->array_00.InsertOneItem_CHAR(*szPathFragment++); + } + } + else + { + // HOTS: 195A4B3 + for(;;) + { + // Insert the character to the path being built + pStruct40->array_00.InsertOneItem_CHAR(NameFragments.CharArray[dwFragOffs]); + + // Keep going as long as the given bit is not set + if(Struct68.IsItemPresent(dwFragOffs++)) + break; + } + } +} + +// HOTS: 0195A300 +void TNameIndexStruct::ExchangeWith(TNameIndexStruct & Target) +{ + TNameIndexStruct WorkBuff; + + WorkBuff = *this; + *this = Target; + Target = WorkBuff; +} + +// HOTS: 0195A820 +int TNameIndexStruct::LoadFromStream(TByteStream & InStream) +{ + int nError; + + nError = NameFragments.LoadStringsWithCopy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + return Struct68.LoadFromStream_Exchange(InStream); +} + +// HOTS: 195A850 +int TNameIndexStruct::LoadFromStream_Exchange(TByteStream & InStream) +{ + TNameIndexStruct TempIndexStruct; + int nError; + + nError = TempIndexStruct.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + ExchangeWith(TempIndexStruct); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TStruct10 functions + +TStruct10::TStruct10() +{ + field_0 = 0x03; + field_4 = 0x200; + field_8 = 0x1000; + field_C = 0x20000; +} + +// HOTS: inline +void TStruct10::CopyFrom(TStruct10 & Target) +{ + field_0 = Target.field_0; + field_4 = Target.field_4; + field_8 = Target.field_8; + field_C = Target.field_C; +} + +// HOTS: 1956FD0 +int TStruct10::sub_1956FD0(DWORD dwBitMask) +{ + switch(dwBitMask & 0xF80) + { + case 0x00: + field_4 = 0x200; + return ERROR_SUCCESS; + + case 0x80: + field_4 = 0x80; + return ERROR_SUCCESS; + + case 0x100: + field_4 = 0x100; + return ERROR_SUCCESS; + + case 0x200: + field_4 = 0x200; + return ERROR_SUCCESS; + + case 0x400: + field_4 = 0x400; + return ERROR_SUCCESS; + + case 0x800: + field_4 = 0x800; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; +} + +// HOTS: 1957050 +int TStruct10::sub_1957050(DWORD dwBitMask) +{ + switch(dwBitMask & 0xF0000) + { + case 0x00: + field_C = 0x20000; + return ERROR_SUCCESS; + + case 0x10000: + field_C = 0x10000; + return ERROR_SUCCESS; + + case 0x20000: + field_C = 0x20000; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; +} + +// HOTS: 19572E0 +int TStruct10::sub_19572E0(DWORD dwBitMask) +{ + DWORD dwSubMask; + int nError; + + if(dwBitMask & 0xFFF00000) + return ERROR_INVALID_PARAMETER; + + dwSubMask = dwBitMask & 0x7F; + if(dwSubMask) + field_0 = dwSubMask; + + nError = sub_1956FD0(dwBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + dwSubMask = dwBitMask & 0xF000; + if(dwSubMask == 0 || dwSubMask == 0x1000) + { + field_8 = 0x1000; + return sub_1957050(dwBitMask); + } + + if(dwSubMask == 0x2000) + { + field_8 = 0x2000; + return sub_1957050(dwBitMask); + } + + return ERROR_INVALID_PARAMETER; +} + +// HOTS: 1957800 +int TStruct10::sub_1957800(DWORD dwBitMask) +{ + TStruct10 TempStruct; + int nError; + + nError = TempStruct.sub_19572E0(dwBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + CopyFrom(TempStruct); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TFileNameDatabase functions + +// HOTS: 01958730 +TFileNameDatabase::TFileNameDatabase() +{ + NameFragIndexMask = 0; + field_214 = 0; +} + +// HOTS: inlined +void TFileNameDatabase::ExchangeWith(TFileNameDatabase & Target) +{ + TFileNameDatabasePtr TempPtr; + DWORD dwTemp; + + Struct68_00.ExchangeWith(Target.Struct68_00); + FileNameIndexes.ExchangeWith(Target.FileNameIndexes); + Struct68_D0.ExchangeWith(Target.Struct68_D0); + + FrgmDist_LoBits.ExchangeWith(Target.FrgmDist_LoBits); + FrgmDist_HiBits.ExchangeWith(Target.FrgmDist_HiBits); + + IndexStruct_174.ExchangeWith(Target.IndexStruct_174); + + TempPtr = NextDB; + NextDB = Target.NextDB; + Target.NextDB = TempPtr; + + NameFragTable.ExchangeWith(Target.NameFragTable); + + dwTemp = NameFragIndexMask; + NameFragIndexMask = Target.NameFragIndexMask; + Target.NameFragIndexMask = dwTemp; + + dwTemp = field_214; + field_214 = Target.field_214; + Target.field_214 = dwTemp; + + Struct10.CopyFrom(Target.Struct10); +} + +// HOTS: 1959CB0 +DWORD TFileNameDatabase::sub_1959CB0(DWORD dwItemIndex) +{ + PTRIPLET pTriplet; + DWORD dwKeyShifted = (dwItemIndex >> 9); + DWORD eax, ebx, ecx, edx, esi, edi; + + // If lower 9 is zero + edx = dwItemIndex; + if((edx & 0x1FF) == 0) + return Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted]; + + eax = Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted] >> 9; + esi = (Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted + 1] + 0x1FF) >> 9; + dwItemIndex = esi; + + if((eax + 0x0A) >= esi) + { + // HOTS: 1959CF7 + pTriplet = Struct68_00.BaseValues.TripletArray + eax + 1; + edi = (eax << 0x09); + ebx = edi - pTriplet->BaseValue + 0x200; + while(edx >= ebx) + { + // HOTS: 1959D14 + edi += 0x200; + pTriplet++; + + ebx = edi - pTriplet->BaseValue + 0x200; + eax++; + } + } + else + { + // HOTS: 1959D2E + while((eax + 1) < esi) + { + // HOTS: 1959D38 + // ecx = Struct68_00.BaseValues.TripletArray; + esi = (esi + eax) >> 1; + ebx = (esi << 0x09) - Struct68_00.BaseValues.TripletArray[esi].BaseValue; + if(edx < ebx) + { + // HOTS: 01959D4B + dwItemIndex = esi; + } + else + { + // HOTS: 1959D50 + eax = esi; + esi = dwItemIndex; + } + } + } + + // HOTS: 1959D5F + pTriplet = Struct68_00.BaseValues.TripletArray + eax; + edx += pTriplet->BaseValue - (eax << 0x09); + edi = (eax << 4); + + eax = pTriplet->Value2; + ecx = (eax >> 0x17); + ebx = 0x100 - ecx; + if(edx < ebx) + { + // HOTS: 1959D8C + ecx = ((eax >> 0x07) & 0xFF); + esi = 0x80 - ecx; + if(edx < esi) + { + // HOTS: 01959DA2 + eax = eax & 0x7F; + ecx = 0x40 - eax; + if(edx >= ecx) + { + // HOTS: 01959DB7 + edi += 2; + edx = edx + eax - 0x40; + } + } + else + { + // HOTS: 1959DC0 + eax = (eax >> 0x0F) & 0xFF; + esi = 0xC0 - eax; + if(edx < esi) + { + // HOTS: 1959DD3 + edi += 4; + edx = edx + ecx - 0x80; + } + else + { + // HOTS: 1959DD3 + edi += 6; + edx = edx + eax - 0xC0; + } + } + } + else + { + // HOTS: 1959DE8 + esi = pTriplet->Value3; + eax = ((esi >> 0x09) & 0x1FF); + ebx = 0x180 - eax; + if(edx < ebx) + { + // HOTS: 01959E00 + esi = esi & 0x1FF; + eax = (0x140 - esi); + if(edx < eax) + { + // HOTS: 1959E11 + edi = edi + 8; + edx = edx + ecx - 0x100; + } + else + { + // HOTS: 1959E1D + edi = edi + 0x0A; + edx = edx + esi - 0x140; + } + } + else + { + // HOTS: 1959E29 + esi = (esi >> 0x12) & 0x1FF; + ecx = (0x1C0 - esi); + if(edx < ecx) + { + // HOTS: 1959E3D + edi = edi + 0x0C; + edx = edx + eax - 0x180; + } + else + { + // HOTS: 1959E49 + edi = edi + 0x0E; + edx = edx + esi - 0x1C0; + } + } + } + + // HOTS: 1959E53: + // Calculate the number of bits set in the value of "ecx" + ecx = ~Struct68_00.ItemBits.Uint32Array[edi]; + eax = GetNumberOfSetBits(ecx); + esi = eax >> 0x18; + + if(edx >= esi) + { + // HOTS: 1959ea4 + ecx = ~Struct68_00.ItemBits.Uint32Array[++edi]; + edx = edx - esi; + eax = GetNumberOfSetBits(ecx); + } + + // HOTS: 1959eea + // ESI gets the number of set bits in the lower 16 bits of ECX + esi = (eax >> 0x08) & 0xFF; + edi = edi << 0x05; + if(edx < esi) + { + // HOTS: 1959EFC + eax = eax & 0xFF; + if(edx >= eax) + { + // HOTS: 1959F05 + ecx >>= 0x08; + edi += 0x08; + edx -= eax; + } + } + else + { + // HOTS: 1959F0D + eax = (eax >> 0x10) & 0xFF; + if(edx < eax) + { + // HOTS: 1959F19 + ecx >>= 0x10; + edi += 0x10; + edx -= esi; + } + else + { + // HOTS: 1959F23 + ecx >>= 0x18; + edi += 0x18; + edx -= eax; + } + } + + // HOTS: 1959f2b + edx = edx << 0x08; + ecx = ecx & 0xFF; + + // BUGBUG: Possible buffer overflow here. Happens when dwItemIndex >= 0x9C. + // The same happens in Heroes of the Storm (build 29049), so I am not sure + // if this is a bug or a case that never happens + assert((ecx + edx) < sizeof(table_1BA1818)); + return table_1BA1818[ecx + edx] + edi; +} + +DWORD TFileNameDatabase::sub_1959F50(DWORD arg_0) +{ + PTRIPLET pTriplet; + PDWORD ItemArray; + DWORD eax, ebx, ecx, edx, esi, edi; + + edx = arg_0; + eax = arg_0 >> 0x09; + if((arg_0 & 0x1FF) == 0) + return Struct68_00.ArrayDwords_50.Uint32Array[eax]; + + ItemArray = Struct68_00.ArrayDwords_50.Uint32Array + eax; + eax = (ItemArray[0] >> 0x09); + edi = (ItemArray[1] + 0x1FF) >> 0x09; + + if((eax + 0x0A) > edi) + { + // HOTS: 01959F94 + pTriplet = Struct68_00.BaseValues.TripletArray + eax + 1; + while(edx >= pTriplet->BaseValue) + { + // HOTS: 1959FA3 + pTriplet++; + eax++; + } + } + else + { + // Binary search + // HOTS: 1959FAD + if((eax + 1) < edi) + { + // HOTS: 1959FB4 + esi = (edi + eax) >> 1; + if(edx < Struct68_00.BaseValues.TripletArray[esi].BaseValue) + { + // HOTS: 1959FC4 + edi = esi; + } + else + { + // HOTS: 1959FC8 + eax = esi; + } + } + } + + // HOTS: 1959FD4 + pTriplet = Struct68_00.BaseValues.TripletArray + eax; + edx = edx - pTriplet->BaseValue; + edi = eax << 0x04; + eax = pTriplet->Value2; + ebx = (eax >> 0x17); + if(edx < ebx) + { + // HOTS: 1959FF1 + esi = (eax >> 0x07) & 0xFF; + if(edx < esi) + { + // HOTS: 0195A000 + eax = eax & 0x7F; + if(edx >= eax) + { + // HOTS: 195A007 + edi = edi + 2; + edx = edx - eax; + } + } + else + { + // HOTS: 195A00E + eax = (eax >> 0x0F) & 0xFF; + if(edx < eax) + { + // HOTS: 195A01A + edi += 4; + edx = edx - esi; + } + else + { + // HOTS: 195A01F + edi += 6; + edx = edx - eax; + } + } + } + else + { + // HOTS: 195A026 + esi = pTriplet->Value3; + eax = (pTriplet->Value3 >> 0x09) & 0x1FF; + if(edx < eax) + { + // HOTS: 195A037 + esi = esi & 0x1FF; + if(edx < esi) + { + // HOTS: 195A041 + edi = edi + 8; + edx = edx - ebx; + } + else + { + // HOTS: 195A048 + edi = edi + 0x0A; + edx = edx - esi; + } + } + else + { + // HOTS: 195A04D + esi = (esi >> 0x12) & 0x1FF; + if(edx < esi) + { + // HOTS: 195A05A + edi = edi + 0x0C; + edx = edx - eax; + } + else + { + // HOTS: 195A061 + edi = edi + 0x0E; + edx = edx - esi; + } + } + } + + // HOTS: 195A066 + esi = Struct68_00.ItemBits.Uint32Array[edi]; + eax = GetNumberOfSetBits(esi); + ecx = eax >> 0x18; + + if(edx >= ecx) + { + // HOTS: 195A0B2 + esi = Struct68_00.ItemBits.Uint32Array[++edi]; + edx = edx - ecx; + eax = GetNumberOfSetBits(esi); + } + + // HOTS: 195A0F6 + ecx = (eax >> 0x08) & 0xFF; + + edi = (edi << 0x05); + if(edx < ecx) + { + // HOTS: 195A111 + eax = eax & 0xFF; + if(edx >= eax) + { + // HOTS: 195A111 + edi = edi + 0x08; + esi = esi >> 0x08; + edx = edx - eax; + } + } + else + { + // HOTS: 195A119 + eax = (eax >> 0x10) & 0xFF; + if(edx < eax) + { + // HOTS: 195A125 + esi = esi >> 0x10; + edi = edi + 0x10; + edx = edx - ecx; + } + else + { + // HOTS: 195A12F + esi = esi >> 0x18; + edi = edi + 0x18; + edx = edx - eax; + } + } + + esi = esi & 0xFF; + edx = edx << 0x08; + + // BUGBUG: Potential buffer overflow + // Happens in Heroes of the Storm when arg_0 == 0x5B + assert((esi + edx) < sizeof(table_1BA1818)); + return table_1BA1818[esi + edx] + edi; +} + +// HOTS: 1957970 +bool TFileNameDatabase::CheckNextPathFragment(TMndxFindResult * pStruct1C) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + LPBYTE pbPathName = (LPBYTE)pStruct1C->szSearchMask; + DWORD CollisionIndex; + DWORD NameFragIndex; + DWORD SaveCharIndex; + DWORD HiBitsIndex; + DWORD FragOffs; + + // Calculate index of the next name fragment in the name fragment table + NameFragIndex = ((pStruct40->ItemIndex << 0x05) ^ pStruct40->ItemIndex ^ pbPathName[pStruct40->CharIndex]) & NameFragIndexMask; + + // Does the hash value match? + if(NameFragTable.NameFragArray[NameFragIndex].ItemIndex == pStruct40->ItemIndex) + { + // Check if there is single character match + if(IS_SINGLE_CHAR_MATCH(NameFragTable, NameFragIndex)) + { + pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex; + pStruct40->CharIndex++; + return true; + } + + // Check if there is a name fragment match + if(NextDB.pDB != NULL) + { + if(!NextDB.pDB->sub_1957B80(pStruct1C, NameFragTable.NameFragArray[NameFragIndex].FragOffs)) + return false; + } + else + { + if(!IndexStruct_174.CheckNameFragment(pStruct1C, NameFragTable.NameFragArray[NameFragIndex].FragOffs)) + return false; + } + + pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex; + return true; + } + + // + // Conflict: Multiple hashes give the same table index + // + + // HOTS: 1957A0E + CollisionIndex = sub_1959CB0(pStruct40->ItemIndex) + 1; + if(!Struct68_00.IsItemPresent(CollisionIndex)) + return false; + + pStruct40->ItemIndex = (CollisionIndex - pStruct40->ItemIndex - 1); + HiBitsIndex = 0xFFFFFFFF; + +// CascDumpSparseArray("E:\\casc-array-68.txt", &FileNameIndexes); +// CascDumpSparseArray("E:\\casc-array-D0.txt", &Struct68_D0); + + // HOTS: 1957A41: + do + { + // HOTS: 1957A41 + // Check if the low 8 bits if the fragment offset contain a single character + // or an offset to a name fragment + if(Struct68_D0.IsItemPresent(pStruct40->ItemIndex)) + { + if(HiBitsIndex == 0xFFFFFFFF) + { + // HOTS: 1957A6C + HiBitsIndex = Struct68_D0.GetItemValue(pStruct40->ItemIndex); + } + else + { + // HOTS: 1957A7F + HiBitsIndex++; + } + + // HOTS: 1957A83 + SaveCharIndex = pStruct40->CharIndex; + + // Get the name fragment offset as combined value from lower 8 bits and upper bits + FragOffs = GetNameFragmentOffsetEx(pStruct40->ItemIndex, HiBitsIndex); + + // Compare the string with the fragment name database + if(NextDB.pDB != NULL) + { + // HOTS: 1957AEC + if(NextDB.pDB->sub_1957B80(pStruct1C, FragOffs)) + return true; + } + else + { + // HOTS: 1957AF7 + if(IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs)) + return true; + } + + // HOTS: 1957B0E + // If there was partial match with the fragment, end the search + if(pStruct40->CharIndex != SaveCharIndex) + return false; + } + else + { + // HOTS: 1957B1C + if(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex] == pStruct1C->szSearchMask[pStruct40->CharIndex]) + { + pStruct40->CharIndex++; + return true; + } + } + + // HOTS: 1957B32 + pStruct40->ItemIndex++; + CollisionIndex++; + } + while(Struct68_00.IsItemPresent(CollisionIndex)); + return false; +} + +// HOTS: 1957B80 +bool TFileNameDatabase::sub_1957B80(TMndxFindResult * pStruct1C, DWORD arg_4) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + PNAME_FRAG pNameEntry; + DWORD FragOffs; + DWORD eax, edi; + + edi = arg_4; + + // HOTS: 1957B95 + for(;;) + { + pNameEntry = NameFragTable.NameFragArray + (edi & NameFragIndexMask); + if(edi == pNameEntry->NextIndex) + { + // HOTS: 01957BB4 + if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00) + { + // HOTS: 1957BC7 + if(NextDB.pDB != NULL) + { + // HOTS: 1957BD3 + if(!NextDB.pDB->sub_1957B80(pStruct1C, pNameEntry->FragOffs)) + return false; + } + else + { + // HOTS: 1957BE0 + if(!IndexStruct_174.CheckNameFragment(pStruct1C, pNameEntry->FragOffs)) + return false; + } + } + else + { + // HOTS: 1957BEE + if(pStruct1C->szSearchMask[pStruct40->CharIndex] != (char)pNameEntry->FragOffs) + return false; + pStruct40->CharIndex++; + } + + // HOTS: 1957C05 + edi = pNameEntry->ItemIndex; + if(edi == 0) + return true; + + if(pStruct40->CharIndex >= pStruct1C->cchSearchMask) + return false; + } + else + { + // HOTS: 1957C30 + if(Struct68_D0.IsItemPresent(edi)) + { + // HOTS: 1957C4C + if(NextDB.pDB != NULL) + { + // HOTS: 1957C58 + FragOffs = GetNameFragmentOffset(edi); + if(!NextDB.pDB->sub_1957B80(pStruct1C, FragOffs)) + return false; + } + else + { + // HOTS: 1957350 + FragOffs = GetNameFragmentOffset(edi); + if(!IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs)) + return false; + } + } + else + { + // HOTS: 1957C8E + if(FrgmDist_LoBits.ByteArray[edi] != pStruct1C->szSearchMask[pStruct40->CharIndex]) + return false; + + pStruct40->CharIndex++; + } + + // HOTS: 1957CB2 + if(edi <= field_214) + return true; + + if(pStruct40->CharIndex >= pStruct1C->cchSearchMask) + return false; + + eax = sub_1959F50(edi); + edi = (eax - edi - 1); + } + } +} + +// HOTS: 1958D70 +void TFileNameDatabase::sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + PNAME_FRAG pNameEntry; + + // HOTS: 1958D84 + for(;;) + { + pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask); + if(arg_4 == pNameEntry->NextIndex) + { + // HOTS: 1958DA6 + if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00) + { + // HOTS: 1958DBA + if(NextDB.pDB != NULL) + { + NextDB.pDB->sub_1958D70(pStruct1C, pNameEntry->FragOffs); + } + else + { + IndexStruct_174.CopyNameFragment(pStruct1C, pNameEntry->FragOffs); + } + } + else + { + // HOTS: 1958DE7 + // Insert the low 8 bits to the path being built + pStruct40->array_00.InsertOneItem_CHAR((char)(pNameEntry->FragOffs & 0xFF)); + } + + // HOTS: 1958E71 + arg_4 = pNameEntry->ItemIndex; + if(arg_4 == 0) + return; + } + else + { + // HOTS: 1958E8E + if(Struct68_D0.IsItemPresent(arg_4)) + { + DWORD FragOffs; + + // HOTS: 1958EAF + FragOffs = GetNameFragmentOffset(arg_4); + if(NextDB.pDB != NULL) + { + NextDB.pDB->sub_1958D70(pStruct1C, FragOffs); + } + else + { + IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs); + } + } + else + { + // HOTS: 1958F50 + // Insert one character to the path being built + pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[arg_4]); + } + + // HOTS: 1958FDE + if(arg_4 <= field_214) + return; + + arg_4 = 0xFFFFFFFF - arg_4 + sub_1959F50(arg_4); + } + } +} + +// HOTS: 1959010 +bool TFileNameDatabase::sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + PNAME_FRAG pNameEntry; + + // HOTS: 1959024 + for(;;) + { + pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask); + if(arg_4 == pNameEntry->NextIndex) + { + // HOTS: 1959047 + if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00) + { + // HOTS: 195905A + if(NextDB.pDB != NULL) + { + if(!NextDB.pDB->sub_1959010(pStruct1C, pNameEntry->FragOffs)) + return false; + } + else + { + if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, pNameEntry->FragOffs)) + return false; + } + } + else + { + // HOTS: 1959092 + if((char)(pNameEntry->FragOffs & 0xFF) != pStruct1C->szSearchMask[pStruct40->CharIndex]) + return false; + + // Insert the low 8 bits to the path being built + pStruct40->array_00.InsertOneItem_CHAR((char)(pNameEntry->FragOffs & 0xFF)); + pStruct40->CharIndex++; + } + + // HOTS: 195912E + arg_4 = pNameEntry->ItemIndex; + if(arg_4 == 0) + return true; + } + else + { + // HOTS: 1959147 + if(Struct68_D0.IsItemPresent(arg_4)) + { + DWORD FragOffs; + + // HOTS: 195917C + FragOffs = GetNameFragmentOffset(arg_4); + if(NextDB.pDB != NULL) + { + if(!NextDB.pDB->sub_1959010(pStruct1C, FragOffs)) + return false; + } + else + { + if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragOffs)) + return false; + } + } + else + { + // HOTS: 195920E + if(FrgmDist_LoBits.CharArray[arg_4] != pStruct1C->szSearchMask[pStruct40->CharIndex]) + return false; + + // Insert one character to the path being built + pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[arg_4]); + pStruct40->CharIndex++; + } + + // HOTS: 19592B6 + if(arg_4 <= field_214) + return true; + + arg_4 = 0xFFFFFFFF - arg_4 + sub_1959F50(arg_4); + } + + // HOTS: 19592D5 + if(pStruct40->CharIndex >= pStruct1C->cchSearchMask) + break; + } + + sub_1958D70(pStruct1C, arg_4); + return true; +} + +// HOTS: 1959460 +bool TFileNameDatabase::sub_1959460(TMndxFindResult * pStruct1C) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + PPATH_STOP pPathStop; + PATH_STOP PathStop; + DWORD NewMaxItemCount; + DWORD FragOffs; + DWORD edi; + + if(pStruct40->SearchPhase == CASC_SEARCH_FINISHED) + return false; + + if(pStruct40->SearchPhase != CASC_SEARCH_SEARCHING) + { + // HOTS: 1959489 + pStruct40->InitSearchBuffers(); + + // If the caller passed a part of the search path, we need to find that one + while(pStruct40->CharIndex < pStruct1C->cchSearchMask) + { + if(!sub_1958B00(pStruct1C)) + { + pStruct40->SearchPhase = CASC_SEARCH_FINISHED; + return false; + } + } + + // HOTS: 19594b0 + PathStop.ItemIndex = pStruct40->ItemIndex; + PathStop.field_4 = 0; + PathStop.field_8 = pStruct40->array_00.ItemCount; + PathStop.field_C = 0xFFFFFFFF; + PathStop.field_10 = 0xFFFFFFFF; + pStruct40->PathStops.InsertOneItem_PATH_STOP(PathStop); + pStruct40->ItemCount = 1; + + if(FileNameIndexes.IsItemPresent(pStruct40->ItemIndex)) + { + pStruct1C->szFoundPath = pStruct40->array_00.FirstValid.Chars; + pStruct1C->cchFoundPath = pStruct40->array_00.ItemCount; + pStruct1C->FileNameIndex = FileNameIndexes.GetItemValue(pStruct40->ItemIndex); + return true; + } + } + + // HOTS: 1959522 + for(;;) + { + // HOTS: 1959530 + if(pStruct40->ItemCount == pStruct40->PathStops.ItemCount) + { + PPATH_STOP pLastStop; + DWORD CollisionIndex; + + pLastStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->PathStops.ItemCount - 1; + CollisionIndex = sub_1959CB0(pLastStop->ItemIndex) + 1; + + // Insert a new structure + PathStop.ItemIndex = CollisionIndex - pLastStop->ItemIndex - 1;; + PathStop.field_4 = CollisionIndex; + PathStop.field_8 = 0; + PathStop.field_C = 0xFFFFFFFF; + PathStop.field_10 = 0xFFFFFFFF; + pStruct40->PathStops.InsertOneItem_PATH_STOP(PathStop); + } + + // HOTS: 19595BD + pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount; + + // HOTS: 19595CC + if(Struct68_00.IsItemPresent(pPathStop->field_4++)) + { + // HOTS: 19595F2 + pStruct40->ItemCount++; + + if(Struct68_D0.IsItemPresent(pPathStop->ItemIndex)) + { + // HOTS: 1959617 + if(pPathStop->field_C == 0xFFFFFFFF) + pPathStop->field_C = Struct68_D0.GetItemValue(pPathStop->ItemIndex); + else + pPathStop->field_C++; + + // HOTS: 1959630 + FragOffs = GetNameFragmentOffsetEx(pPathStop->ItemIndex, pPathStop->field_C); + if(NextDB.pDB != NULL) + { + // HOTS: 1959649 + NextDB.pDB->sub_1958D70(pStruct1C, FragOffs); + } + else + { + // HOTS: 1959654 + IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs); + } + } + else + { + // HOTS: 1959665 + // Insert one character to the path being built + pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[pPathStop->ItemIndex]); + } + + // HOTS: 19596AE + pPathStop->field_8 = pStruct40->array_00.ItemCount; + + // HOTS: 19596b6 + if(FileNameIndexes.IsItemPresent(pPathStop->ItemIndex)) + { + // HOTS: 19596D1 + if(pPathStop->field_10 == 0xFFFFFFFF) + { + // HOTS: 19596D9 + pPathStop->field_10 = FileNameIndexes.GetItemValue(pPathStop->ItemIndex); + } + else + { + pPathStop->field_10++; + } + + // HOTS: 1959755 + pStruct1C->szFoundPath = pStruct40->array_00.FirstValid.Chars; + pStruct1C->cchFoundPath = pStruct40->array_00.ItemCount; + pStruct1C->FileNameIndex = pPathStop->field_10; + return true; + } + } + else + { + // HOTS: 19596E9 + if(pStruct40->ItemCount == 1) + { + pStruct40->SearchPhase = CASC_SEARCH_FINISHED; + return false; + } + + // HOTS: 19596F5 + pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount - 1; + pPathStop->ItemIndex++; + + pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount - 2; + edi = pPathStop->field_8; + + if(edi > pStruct40->array_00.MaxItemCount) + { + // HOTS: 1959717 + NewMaxItemCount = edi; + + if(pStruct40->array_00.MaxItemCount > (edi / 2)) + { + if(pStruct40->array_00.MaxItemCount > 0x7FFFFFFF) + { + NewMaxItemCount = 0xFFFFFFFF; + } + else + { + NewMaxItemCount = pStruct40->array_00.MaxItemCount + pStruct40->array_00.MaxItemCount; + } + } + + pStruct40->array_00.SetMaxItems_CHARS(NewMaxItemCount); + } + + // HOTS: 1959749 + pStruct40->array_00.ItemCount = edi; + pStruct40->ItemCount--; + } + } +} + +// HOTS: 1958B00 +bool TFileNameDatabase::sub_1958B00(TMndxFindResult * pStruct1C) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + LPBYTE pbPathName = (LPBYTE)pStruct1C->szSearchMask; + DWORD CollisionIndex; + DWORD FragmentOffset; + DWORD SaveCharIndex; + DWORD ItemIndex; + DWORD FragOffs; + DWORD var_4; + + ItemIndex = pbPathName[pStruct40->CharIndex] ^ (pStruct40->ItemIndex << 0x05) ^ pStruct40->ItemIndex; + ItemIndex = ItemIndex & NameFragIndexMask; + if(pStruct40->ItemIndex == NameFragTable.NameFragArray[ItemIndex].ItemIndex) + { + // HOTS: 1958B45 + FragmentOffset = NameFragTable.NameFragArray[ItemIndex].FragOffs; + if((FragmentOffset & 0xFFFFFF00) == 0xFFFFFF00) + { + // HOTS: 1958B88 + pStruct40->array_00.InsertOneItem_CHAR((char)FragmentOffset); + pStruct40->ItemIndex = NameFragTable.NameFragArray[ItemIndex].NextIndex; + pStruct40->CharIndex++; + return true; + } + + // HOTS: 1958B59 + if(NextDB.pDB != NULL) + { + if(!NextDB.pDB->sub_1959010(pStruct1C, FragmentOffset)) + return false; + } + else + { + if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragmentOffset)) + return false; + } + + // HOTS: 1958BCA + pStruct40->ItemIndex = NameFragTable.NameFragArray[ItemIndex].NextIndex; + return true; + } + + // HOTS: 1958BE5 + CollisionIndex = sub_1959CB0(pStruct40->ItemIndex) + 1; + if(!Struct68_00.IsItemPresent(CollisionIndex)) + return false; + + pStruct40->ItemIndex = (CollisionIndex - pStruct40->ItemIndex - 1); + var_4 = 0xFFFFFFFF; + + // HOTS: 1958C20 + for(;;) + { + if(Struct68_D0.IsItemPresent(pStruct40->ItemIndex)) + { + // HOTS: 1958C0E + if(var_4 == 0xFFFFFFFF) + { + // HOTS: 1958C4B + var_4 = Struct68_D0.GetItemValue(pStruct40->ItemIndex); + } + else + { + var_4++; + } + + // HOTS: 1958C62 + SaveCharIndex = pStruct40->CharIndex; + + FragOffs = GetNameFragmentOffsetEx(pStruct40->ItemIndex, var_4); + if(NextDB.pDB != NULL) + { + // HOTS: 1958CCB + if(NextDB.pDB->sub_1959010(pStruct1C, FragOffs)) + return true; + } + else + { + // HOTS: 1958CD6 + if(IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragOffs)) + return true; + } + + // HOTS: 1958CED + if(SaveCharIndex != pStruct40->CharIndex) + return false; + } + else + { + // HOTS: 1958CFB + if(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex] == pStruct1C->szSearchMask[pStruct40->CharIndex]) + { + // HOTS: 1958D11 + pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex]); + pStruct40->CharIndex++; + return true; + } + } + + // HOTS: 1958D11 + pStruct40->ItemIndex++; + CollisionIndex++; + + if(!Struct68_00.IsItemPresent(CollisionIndex)) + break; + } + + return false; +} + +// HOTS: 1957EF0 +bool TFileNameDatabase::FindFileInDatabase(TMndxFindResult * pStruct1C) +{ + TStruct40 * pStruct40 = pStruct1C->pStruct40; + + pStruct40->ItemIndex = 0; + pStruct40->CharIndex = 0; + pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING; + + if(pStruct1C->cchSearchMask > 0) + { + while(pStruct40->CharIndex < pStruct1C->cchSearchMask) + { + // HOTS: 01957F12 + if(!CheckNextPathFragment(pStruct1C)) + return false; + } + } + + // HOTS: 1957F26 + if(!FileNameIndexes.IsItemPresent(pStruct40->ItemIndex)) + return false; + + pStruct1C->szFoundPath = pStruct1C->szSearchMask; + pStruct1C->cchFoundPath = pStruct1C->cchSearchMask; + pStruct1C->FileNameIndex = FileNameIndexes.GetItemValue(pStruct40->ItemIndex); + return true; +} + +// HOTS: 1959790 +int TFileNameDatabase::LoadFromStream(TByteStream & InStream) +{ + DWORD dwBitMask; + int nError; + + nError = Struct68_00.LoadFromStream_Exchange(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = FileNameIndexes.LoadFromStream_Exchange(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = Struct68_D0.LoadFromStream_Exchange(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 019597CD + nError = FrgmDist_LoBits.LoadBytes_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = FrgmDist_HiBits.LoadFromStream_Exchange(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 019597F5 + nError = IndexStruct_174.LoadFromStream_Exchange(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 0195980A + if(Struct68_D0.ValidItemCount != 0 && IndexStruct_174.NameFragments.ItemCount == 0) + { + TFileNameDatabase * pNextDB = new TFileNameDatabase; + + nError = NextDB.SetDatabase(pNextDB); + if(nError != ERROR_SUCCESS) + return nError; + + if(NextDB.pDB == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + nError = NextDB.pDB->LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + } + + // HOTS: 0195986B + nError = NameFragTable.LoadFragmentInfos_Copy(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + NameFragIndexMask = NameFragTable.ItemCount - 1; + + nError = InStream.GetValue_DWORD(field_214); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetValue_DWORD(dwBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + return Struct10.sub_1957800(dwBitMask); +} + +// HOTS: 19598D0 +int TFileNameDatabase::LoadFromStream_Exchange(TByteStream & InStream) +{ + TFileNameDatabase TempDatabase; + ARRAY_POINTER Pointer; + DWORD dwSignature; + int nError; + + // Get pointer to MAR signature + nError = InStream.GetBytes(sizeof(DWORD), &Pointer); + if(nError != ERROR_SUCCESS) + return nError; + + // Verify the signature + dwSignature = Pointer.Uint32s[0]; + if(dwSignature != CASC_MAR_SIGNATURE) + return ERROR_BAD_FORMAT; + + nError = TempDatabase.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + MarStream.ExchangeWith(InStream); + ExchangeWith(TempDatabase); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// TFileNameDatabasePtr functions + +// HOTS: 01956D70 +TFileNameDatabasePtr::TFileNameDatabasePtr() +{ + pDB = NULL; +} + +TFileNameDatabasePtr::~TFileNameDatabasePtr() +{ + delete pDB; +} + +// HOTS: 1956C60 +int TFileNameDatabasePtr::FindFileInDatabase(TMndxFindResult * pStruct1C) +{ + int nError = ERROR_SUCCESS; + + if(pDB == NULL) + return ERROR_INVALID_PARAMETER; + + nError = pStruct1C->CreateStruct40(); + if(nError != ERROR_SUCCESS) + return nError; + + if(!pDB->FindFileInDatabase(pStruct1C)) + nError = ERROR_FILE_NOT_FOUND; + + pStruct1C->FreeStruct40(); + return nError; +} + +// HOTS: 1956CE0 +int TFileNameDatabasePtr::sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult) +{ + int nError = ERROR_SUCCESS; + + if(pDB == NULL) + return ERROR_INVALID_PARAMETER; + + // Create the pStruct40, if not initialized yet + if(pStruct1C->pStruct40 == NULL) + { + nError = pStruct1C->CreateStruct40(); + if(nError != ERROR_SUCCESS) + return nError; + } + + *pbFindResult = pDB->sub_1959460(pStruct1C); + return nError; +} + +// HOTS: 1956D20 +int TFileNameDatabasePtr::GetFileNameCount(PDWORD PtrFileNameCount) +{ + if(pDB == NULL) + return ERROR_INVALID_PARAMETER; + + PtrFileNameCount[0] = pDB->FileNameIndexes.ValidItemCount; + return ERROR_SUCCESS; +} + +// HOTS: 1956DA0 +int TFileNameDatabasePtr::CreateDatabase(LPBYTE pbMarData, DWORD cbMarData) +{ + TFileNameDatabase * pDatabase; + TByteStream ByteStream; + int nError; + + if(pbMarData == NULL && cbMarData != 0) + return ERROR_INVALID_PARAMETER; + + pDatabase = new TFileNameDatabase; + if(pDatabase == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + nError = ByteStream.SetByteBuffer(pbMarData, cbMarData); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 1956E11 + nError = pDatabase->LoadFromStream_Exchange(ByteStream); + if(nError != ERROR_SUCCESS) + return nError; + + pDB = pDatabase; + return ERROR_SUCCESS; +} + +// HOTS: 19584B0 +int TFileNameDatabasePtr::SetDatabase(TFileNameDatabase * pNewDB) +{ + if(pNewDB != NULL && pDB == pNewDB) + return ERROR_INVALID_PARAMETER; + + if(pDB != NULL) + delete pDB; + pDB = pNewDB; + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Local functions - MAR file + +// HOTS: 00E94180 +static void MAR_FILE_CreateDatabase(PMAR_FILE pMarFile) +{ + pMarFile->pDatabasePtr = new TFileNameDatabasePtr; + if(pMarFile->pDatabasePtr != NULL) + pMarFile->pDatabasePtr->CreateDatabase(pMarFile->pbMarData, pMarFile->cbMarData); +} + +static int MAR_FILE_SearchFile(PMAR_FILE pMarFile, TMndxFindResult * pStruct1C) +{ + return pMarFile->pDatabasePtr->FindFileInDatabase(pStruct1C); +} + +static void MAR_FILE_Destructor(PMAR_FILE pMarFile) +{ + if(pMarFile != NULL) + { + if(pMarFile->pDatabasePtr != NULL) + delete pMarFile->pDatabasePtr; + if(pMarFile->pbMarData != NULL) + CASC_FREE(pMarFile->pbMarData); + + CASC_FREE(pMarFile); + } +} + +//----------------------------------------------------------------------------- +// Package functions + +// TODO: When working, increment these values to lower number of (re)allocations +#define CASC_PACKAGES_INIT 0x10 +#define CASC_PACKAGES_DELTA 0x10 + +static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax) +{ + PCASC_PACKAGES pPackages; + size_t cbToAllocate; + + // Allocate space + cbToAllocate = sizeof(CASC_PACKAGES) + (nNameEntries * sizeof(CASC_PACKAGE)) + nNameBufferMax; + pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); + if(pPackages != NULL) + { + // Fill the structure + memset(pPackages, 0, cbToAllocate); + + // Init the list entries + pPackages->szNameBuffer = (char *)(&pPackages->Packages[nNameEntries]); + pPackages->NameEntries = nNameEntries; + pPackages->NameBufferUsed = 0; + pPackages->NameBufferMax = nNameBufferMax; + } + + return pPackages; +} + +static PCASC_PACKAGES InsertToPackageList( + PCASC_PACKAGES pPackages, + const char * szFileName, + size_t cchFileName, + size_t nPackageIndex) +{ + size_t nNewNameEntries = pPackages->NameEntries; + size_t nNewNameBufferMax = pPackages->NameBufferMax; + size_t cbToAllocate; + char * szNameBuffer; + + // Need to reallocate? + while(nPackageIndex >= nNewNameEntries) + nNewNameEntries = nNewNameEntries + CASC_PACKAGES_DELTA; + if((pPackages->NameBufferUsed + cchFileName + 1) > nNewNameBufferMax) + nNewNameBufferMax = nNewNameBufferMax + 0x1000; + + // If any of the two variables overflowed, we need to reallocate the name list + if(nNewNameEntries > pPackages->NameEntries || nNewNameBufferMax > pPackages->NameBufferMax) + { + PCASC_PACKAGES pOldPackages = pPackages; + + // Allocate new name list + cbToAllocate = sizeof(CASC_PACKAGES) + (nNewNameEntries * sizeof(CASC_PACKAGE)) + nNewNameBufferMax; + pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate); + if(pPackages == NULL) + return NULL; + + // Copy the old entries + memset(pPackages, 0, cbToAllocate); + pPackages->szNameBuffer = szNameBuffer = (char *)(&pPackages->Packages[nNewNameEntries]); + memcpy(pPackages->szNameBuffer, pOldPackages->szNameBuffer, pOldPackages->NameBufferUsed); + + // Copy the old entries + for(size_t i = 0; i < pOldPackages->NameEntries; i++) + { + if(pOldPackages->Packages[i].szFileName != NULL) + { + pPackages->Packages[i].szFileName = pPackages->szNameBuffer + (pOldPackages->Packages[i].szFileName - pOldPackages->szNameBuffer); + pPackages->Packages[i].nLength = pOldPackages->Packages[i].nLength; + } + } + + // Fill the limits + pPackages->NameEntries = nNewNameEntries; + pPackages->NameBufferUsed = pOldPackages->NameBufferUsed; + pPackages->NameBufferMax = nNewNameBufferMax; + + // Switch the name lists + CASC_FREE(pOldPackages); + } + + // The slot is expected to be empty at the moment + assert(pPackages->Packages[nPackageIndex].szFileName == NULL); + assert(pPackages->Packages[nPackageIndex].nLength == 0); + + // Set the file name entry + szNameBuffer = pPackages->szNameBuffer + pPackages->NameBufferUsed; + pPackages->Packages[nPackageIndex].szFileName = szNameBuffer; + pPackages->Packages[nPackageIndex].nLength = cchFileName; + memcpy(szNameBuffer, szFileName, cchFileName); + pPackages->NameBufferUsed += (cchFileName + 1); + return pPackages; +} + +static int LoadPackageNames(TCascStorage * hs) +{ + TMndxFindResult Struct1C; + PCASC_PACKAGES pPackages = NULL; + PMAR_FILE pMarFile; + + // Sanity checks + assert(hs->pMndxInfo != NULL); + + // Prepare the file name search in the top level directory + pMarFile = hs->pMndxInfo->pMarFile1; + Struct1C.SetSearchPath("", 0); + + // Allocate initial name list structure + pPackages = AllocatePackages(CASC_PACKAGES_INIT, 0x1000); + if(pPackages == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Keep searching as long as we find something + for(;;) + { + bool bFindResult = false; + + // Search the next file name + pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult); + if(bFindResult == false) + break; + + // Insert the found name to the top level directory list + pPackages = InsertToPackageList(pPackages, Struct1C.szFoundPath, Struct1C.cchFoundPath, Struct1C.FileNameIndex); + if(pPackages == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Set the name list to the CASC storage structure + hs->pPackages = pPackages; + return ERROR_SUCCESS; +} + +PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName) +{ + PCASC_PACKAGE pMatching = NULL; + PCASC_PACKAGE pPackage; + size_t nMaxLength = 0; + size_t nLength = strlen(szFileName); + + // Packages must be loaded + assert(hs->pPackages != NULL); + pPackage = hs->pPackages->Packages; + + // Find the longest matching name + for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++) + { + if(pPackage->szFileName != NULL && pPackage->nLength < nLength && pPackage->nLength > nMaxLength) + { + // Compare the package name + if(!strncmp(szFileName, pPackage->szFileName, pPackage->nLength)) + { + pMatching = pPackage; + nMaxLength = pPackage->nLength; + } + } + } + + // Give the package pointer or NULL if not found + return pMatching; +} + +static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndxFindResult * pStruct1C) +{ + CASC_ROOT_KEY_INFO RootKeyInfo; + TCascStorage * hs = pSearch->hs; + PCASC_PACKAGE pPackage; + char * szStrippedName; + int nError; + + // Sanity check + assert(pStruct1C->cchFoundPath < MAX_PATH); + + // Fill the file name + memcpy(pFindData->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath); + pFindData->szFileName[pStruct1C->cchFoundPath] = 0; + pFindData->dwFileSize = CASC_INVALID_SIZE; + + // Fill the file size + pPackage = FindMndxPackage(hs, pFindData->szFileName); + if(pPackage != NULL) + { + // Cut the package name off the full path + szStrippedName = pFindData->szFileName + pPackage->nLength; + while(szStrippedName[0] == '/') + szStrippedName++; + + nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &RootKeyInfo); + if(nError == ERROR_SUCCESS) + { + pFindData->dwFileSize = (DWORD)RootKeyInfo.FileSize; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Public functions - MNDX info + +int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +{ + PFILE_MNDX_HEADER pMndxHeader = (PFILE_MNDX_HEADER)pbRootFile; + PCASC_MNDX_INFO pMndxInfo; + FILE_MAR_INFO MarInfo; + PMAR_FILE pMarFile; + LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; + DWORD cbToAllocate; + DWORD dwFilePointer = 0; + DWORD i; + int nError = ERROR_SUCCESS; + + // Check signature and the other variables + if(pMndxHeader->Signature != CASC_MNDX_SIGNATURE || pMndxHeader->FormatVersion > 2 || pMndxHeader->FormatVersion < 1) + return ERROR_BAD_FORMAT; + + // Allocate space for the CASC_MNDX_INFO structure + pMndxInfo = CASC_ALLOC(CASC_MNDX_INFO, 1); + if(pMndxInfo == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Copy the header into the MNDX info + memset(pMndxInfo, 0, sizeof(CASC_MNDX_INFO)); + pMndxInfo->HeaderVersion = pMndxHeader->HeaderVersion; + pMndxInfo->FormatVersion = pMndxHeader->FormatVersion; + dwFilePointer += sizeof(FILE_MNDX_HEADER); + + // Header version 2 has 2 extra fields that we need to load + if(pMndxInfo->HeaderVersion == 2) + { + if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &pMndxInfo->field_1C, sizeof(DWORD) + sizeof(DWORD))) + return ERROR_FILE_CORRUPT; + dwFilePointer += sizeof(DWORD) + sizeof(DWORD); + } + + // Load the rest of the file header + if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &pMndxInfo->MarInfoOffset, 0x1C)) + return ERROR_FILE_CORRUPT; + + // Verify the structure + if(pMndxInfo->MarInfoCount > CASC_MAX_MAR_FILES || pMndxInfo->MarInfoSize != sizeof(FILE_MAR_INFO)) + return ERROR_FILE_CORRUPT; + + // Load all MAR infos + for(i = 0; i < pMndxInfo->MarInfoCount; i++) + { + // Load the n-th MAR info + dwFilePointer = pMndxInfo->MarInfoOffset + (pMndxInfo->MarInfoSize * i); + if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &MarInfo, sizeof(FILE_MAR_INFO))) + return ERROR_FILE_CORRUPT; + + // Allocate MAR_FILE structure + pMarFile = CASC_ALLOC(MAR_FILE, 1); + if(pMarFile == NULL) + { + nError = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + // Allocate space for the MAR data + pMarFile->pDatabasePtr = NULL; + pMarFile->pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize); + pMarFile->cbMarData = MarInfo.MarDataSize; + if(pMarFile->pbMarData == NULL) + { + nError = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + // Read the MAR data + if(!RootFileRead(pbRootFile + MarInfo.MarDataOffset, pbRootFileEnd, pMarFile->pbMarData, pMarFile->cbMarData)) + { + nError = ERROR_FILE_CORRUPT; + break; + } + + // HOTS: 00E94FF1 + MAR_FILE_CreateDatabase(pMarFile); + if(i == 0) + pMndxInfo->pMarFile1 = pMarFile; + if(i == 1) + pMndxInfo->pMarFile2 = pMarFile; + if(i == 2) + pMndxInfo->pMarFile3 = pMarFile; + } + + // All three MAR files must be loaded + // HOTS: 00E9503B + if(nError == ERROR_SUCCESS) + { + if(pMndxInfo->pMarFile1 == NULL || pMndxInfo->pMarFile2 == NULL || pMndxInfo->pMarFile3 == NULL) + nError = ERROR_BAD_FORMAT; + if(pMndxInfo->MndxEntrySize != sizeof(CASC_MNDX_ENTRY)) + nError = ERROR_BAD_FORMAT; + } + + // Load the complete array of MNDX entries + if(nError == ERROR_SUCCESS) + { + TFileNameDatabasePtr * pDbPtr = pMndxInfo->pMarFile2->pDatabasePtr; + DWORD FileNameCount; + + nError = pDbPtr->GetFileNameCount(&FileNameCount); + if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid) + { + cbToAllocate = pMndxInfo->MndxEntriesTotal * pMndxInfo->MndxEntrySize; + pMndxInfo->pMndxEntries = (PCASC_MNDX_ENTRY)CASC_ALLOC(BYTE, cbToAllocate); + if(pMndxInfo->pMndxEntries != NULL) + { + if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pMndxInfo->pMndxEntries, cbToAllocate)) + nError = ERROR_FILE_CORRUPT; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + else + nError = ERROR_FILE_CORRUPT; + } + + // Pick the valid MNDX entries and put them to a separate array + if(nError == ERROR_SUCCESS) + { + assert(pMndxInfo->MndxEntriesValid <= pMndxInfo->MndxEntriesTotal); + pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_MNDX_ENTRY, pMndxInfo->MndxEntriesValid + 1); + if(pMndxInfo->ppValidEntries != NULL) + { + PCASC_MNDX_ENTRY pMndxEntry = pMndxInfo->pMndxEntries; + DWORD ValidEntryCount = 1; // edx + DWORD nIndex1 = 0; + + // The first entry is always valid + pMndxInfo->ppValidEntries[nIndex1++] = pMndxInfo->pMndxEntries; + + // Put the remaining entries + for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pMndxEntry++) + { + if(ValidEntryCount > pMndxInfo->MndxEntriesValid) + break; + + if(pMndxEntry->Flags & 0x80000000) + { + pMndxInfo->ppValidEntries[nIndex1++] = pMndxEntry + 1; + ValidEntryCount++; + } + } + + // Verify the final number of valid entries + if((ValidEntryCount - 1) != pMndxInfo->MndxEntriesValid) + nError = ERROR_BAD_FORMAT; + + // Mark the MNDX info as fully loaded + pMndxInfo->bRootFileLoaded = true; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Save the MNDX info to the archive storage + if(nError == ERROR_SUCCESS) + { + // Store the MNDX database into the archive + hs->pMndxInfo = pMndxInfo; + pMndxInfo = NULL; + +#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST) +// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", hs->pMndxInfo->pMarFile1); +// CascDumpFileNames("E:\\casc-listfile.txt", hs->pMndxInfo->pMarFile1); + TestMndxRootFile(hs->pMndxInfo); +#endif + // Load the top level entries + nError = LoadPackageNames(hs); + } + + // If anything failed, free the memory remaining allocated + if(nError != ERROR_SUCCESS) + { + if(pMndxInfo != NULL) + FreeMndxInfo(pMndxInfo); + pMndxInfo = NULL; + } + + return nError; +} + +int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo) +{ + PCASC_MNDX_ENTRY pMndxEntry; + TMndxFindResult Struct1C; + + // Search the database for the file name + if(pMndxInfo->bRootFileLoaded) + { + Struct1C.SetSearchPath(szFileName, strlen(szFileName)); + + // Search the file name in the second MAR info (the one with stripped package names) + if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS) + return ERROR_FILE_NOT_FOUND; + + // The found MNDX index must fall into range of valid MNDX entries + if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid) + { + // HOTS: E945F4 + pMndxEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex]; + while((pMndxEntry->Flags & 0x00FFFFFF) != dwPackage) + { + // The highest bit serves as a terminator if set + if(pMndxEntry->Flags & 0x80000000) + return ERROR_FILE_NOT_FOUND; + + pMndxEntry++; + } + + // Fill the root info + memcpy(pFoundInfo->EncodingKey, pMndxEntry->EncodingKey, MD5_HASH_SIZE); + pFoundInfo->FileSize = pMndxEntry->FileSize; + pFoundInfo->Flags = (BYTE)((pMndxEntry->Flags >> 0x18) & 0x3F); + return ERROR_SUCCESS; + } + } + + return ERROR_FILE_NOT_FOUND; +} + +bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData) +{ + TMndxFindResult * pStruct1C = NULL; + PCASC_MNDX_INFO pMndxInfo = pSearch->hs->pMndxInfo; + PMAR_FILE pMarFile = pMndxInfo->pMarFile3; + bool bFindResult = false; + + // Sanity checks + assert(pMndxInfo != NULL); + + // If the first time, allocate the structure for the search result + if(pSearch->pStruct1C == NULL) + { + // Create the new search structure + pSearch->pStruct1C = pStruct1C = new TMndxFindResult; + if(pSearch->pStruct1C == NULL) + return false; + + // Setup the search mask + pStruct1C->SetSearchPath("", 0); + } + + // Make shortcut for the search structure + assert(pSearch->pStruct1C != NULL); + pStruct1C = (TMndxFindResult *)pSearch->pStruct1C; + + // Search the next file name (our code) + pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult); + if(bFindResult) + return FillFindData(pSearch, pFindData, pStruct1C); + + return false; +} + +void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo) +{ + if(pMndxInfo != NULL) + { + if(pMndxInfo->pMarFile1 != NULL) + MAR_FILE_Destructor(pMndxInfo->pMarFile1); + if(pMndxInfo->pMarFile2 != NULL) + MAR_FILE_Destructor(pMndxInfo->pMarFile2); + if(pMndxInfo->pMarFile3 != NULL) + MAR_FILE_Destructor(pMndxInfo->pMarFile3); + if(pMndxInfo->ppValidEntries != NULL) + CASC_FREE(pMndxInfo->ppValidEntries); + if(pMndxInfo->pMndxEntries != NULL) + CASC_FREE(pMndxInfo->pMndxEntries); + CASC_FREE(pMndxInfo); + } +} + +//---------------------------------------------------------------------------- +// Unit tests + +#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST) + +extern "C" { + DWORD _cdecl sub_19573D0_x86(TFileNameDatabase * pDB, DWORD arg_0, DWORD arg_4); + DWORD _cdecl sub_1957EF0_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); + bool _cdecl sub_1959460_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); + DWORD _cdecl GetItemValue_x86(TSparseArray * pStruct, DWORD dwKey); + DWORD _cdecl sub_1959CB0_x86(TFileNameDatabase * pDB, DWORD dwKey); + DWORD _cdecl sub_1959F50_x86(TFileNameDatabase * pDB, DWORD arg_0); +} + +extern "C" void * allocate_zeroed_memory_x86(size_t bytes) +{ + return calloc(bytes, 1); +} + +extern "C" void free_memory_x86(void * ptr) +{ + if(ptr != NULL) + { + free(ptr); + } +} + +static int sub_1956CE0_x86(TFileNameDatabasePtr * pDatabasePtr, TMndxFindResult * pStruct1C, bool * pbFindResult) +{ + int nError = ERROR_SUCCESS; + + if(pDatabasePtr->pDB == NULL) + return ERROR_INVALID_PARAMETER; + + // Create the pStruct40, if not initialized yet + if(pStruct1C->pStruct40 == NULL) + { + nError = pStruct1C->CreateStruct40(); + if(nError != ERROR_SUCCESS) + return nError; + } + + *pbFindResult = sub_1959460_x86(pDatabasePtr->pDB, pStruct1C); + return nError; +} +/* +static void TestFileSearch_SubStrings(PMAR_FILE pMarFile, char * szFileName, size_t nLength) +{ + TMndxFindResult Struct1C_1; + TMndxFindResult Struct1C_2; + +// if(strcmp(szFileName, "mods/heroes.stormmod/base.stormassets/assets/textures/storm_temp_war3_btnstatup.dds")) +// return; + + // Perform search on anything, that is longer than 4 chars + while(nLength >= 4) + { + // Set a substring as search name + Struct1C_1.SetSearchPath(szFileName, nLength); + Struct1C_2.SetSearchPath(szFileName, nLength); + szFileName[nLength] = 0; + + // Keep searching + for(;;) + { + bool bFindResult1 = false; + bool bFindResult2 = false; + + // Search the next file name (orig HOTS code) + sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1); + + // Search the next file name (our code) + pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C_2, &bFindResult2); + + // Check the result + assert(bFindResult1 == bFindResult2); + assert(Struct1C_1.cchFoundPath == Struct1C_1.cchFoundPath); + assert(Struct1C_1.FileNameIndex == Struct1C_2.FileNameIndex); + assert(strncmp(Struct1C_1.szFoundPath, Struct1C_2.szFoundPath, Struct1C_1.cchFoundPath) == 0); + assert(Struct1C_1.cchFoundPath < MAX_PATH); + + // Stop the search in case of failure + if(bFindResult1 == false || bFindResult2 == false) + break; + } + + // Free the search structures + Struct1C_1.FreeStruct40(); + Struct1C_2.FreeStruct40(); + nLength--; + } +} +*/ + +static void TestFindPackage(PMAR_FILE pMarFile, const char * szPackageName) +{ + TMndxFindResult Struct1C; + + // Search the database for the file name + Struct1C.SetSearchPath(szPackageName, strlen(szPackageName)); + + // Search the file name in the second MAR info (the one with stripped package names) + MAR_FILE_SearchFile(pMarFile, &Struct1C); +} + +static void TestFileSearch(PMAR_FILE pMarFile, const char * szFileName) +{ + TMndxFindResult Struct1C_1; + TMndxFindResult Struct1C_2; + size_t nLength = strlen(szFileName); + char szNameBuff[MAX_PATH + 1]; + + // Set an empty path as search mask (?) + Struct1C_1.SetSearchPath(szFileName, nLength); + Struct1C_2.SetSearchPath(szFileName, nLength); + + // Keep searching + for(;;) + { + bool bFindResult1 = false; + bool bFindResult2 = false; + + // Search the next file name (orig HOTS code) + sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1); + + // Search the next file name (our code) + pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C_2, &bFindResult2); + + assert(bFindResult1 == bFindResult2); + assert(Struct1C_1.cchFoundPath == Struct1C_1.cchFoundPath); + assert(Struct1C_1.FileNameIndex == Struct1C_2.FileNameIndex); + assert(strncmp(Struct1C_1.szFoundPath, Struct1C_2.szFoundPath, Struct1C_1.cchFoundPath) == 0); + assert(Struct1C_1.cchFoundPath < MAX_PATH); + + // Stop the search in case of failure + if(bFindResult1 == false || bFindResult2 == false) + break; + + // Printf the found file name + memcpy(szNameBuff, Struct1C_2.szFoundPath, Struct1C_2.cchFoundPath); + szNameBuff[Struct1C_2.cchFoundPath] = 0; +// printf("%s \r", szNameBuff); + + // Perform sub-searches on this string and its substrings that are longer than 4 chars +// TestFileSearch_SubStrings(pMarFile, szNameBuff, Struct1C_2.cchFoundPath); + } + + // Free the search structures + Struct1C_1.FreeStruct40(); + Struct1C_2.FreeStruct40(); +} + +static void TestMarFile(PMAR_FILE pMarFile, const char * szFileName, size_t nLength) +{ + TFileNameDatabase * pDB = pMarFile->pDatabasePtr->pDB; + DWORD dwFileNameIndex1 = 0xFFFFFFFF; + DWORD dwFileNameIndex2 = 0xFFFFFFFF; + + // Perform the search using original HOTS code + { + TMndxFindResult Struct1C; + + Struct1C.CreateStruct40(); + Struct1C.SetSearchPath(szFileName, nLength); + + // Call the original HOTS function + sub_1957EF0_x86(pDB, &Struct1C); + dwFileNameIndex1 = Struct1C.FileNameIndex; + } + + // Perform the search using our code + { + TMndxFindResult Struct1C; + + Struct1C.CreateStruct40(); + Struct1C.SetSearchPath(szFileName, nLength); + + // Call our function + pDB->FindFileInDatabase(&Struct1C); + dwFileNameIndex2 = Struct1C.FileNameIndex; + } + + // Compare both + assert(dwFileNameIndex1 == dwFileNameIndex2); +} + +static void TestMndxFunctions(PMAR_FILE pMarFile) +{ + TFileNameDatabase * pDB = pMarFile->pDatabasePtr->pDB; + + // Exercise function sub_19573D0 + for(DWORD arg_0 = 0; arg_0 < 0x100; arg_0++) + { + for(DWORD arg_4 = 0; arg_4 < 0x100; arg_4++) + { + DWORD dwResult1 = sub_19573D0_x86(pDB, arg_0, arg_4); + DWORD dwResult2 = pDB->GetNameFragmentOffsetEx(arg_0, arg_4); + + assert(dwResult1 == dwResult2); + } + } + + // Exercise function GetItemValue + for(DWORD i = 0; i < 0x10000; i++) + { + DWORD dwResult1 = GetItemValue_x86(&pDB->Struct68_D0, i); + DWORD dwResult2 = pDB->Struct68_D0.GetItemValue(i); + + assert(dwResult1 == dwResult2); + } + + // Exercise function sub_1959CB0 + for(DWORD i = 0; i < 0x9C; i++) + { + DWORD dwResult1 = sub_1959CB0_x86(pDB, i); + DWORD dwResult2 = pDB->sub_1959CB0(i); + + assert(dwResult1 == dwResult2); + } + + // Exercise function sub_1959F50 + for(DWORD i = 0; i < 0x40; i++) + { + DWORD dwResult1 = sub_1959F50_x86(pDB, i); + DWORD dwResult2 = pDB->sub_1959F50(i); + + assert(dwResult1 == dwResult2); + } +} + +void TestMndxRootFile(PCASC_MNDX_INFO pMndxInfo) +{ + size_t nLength; + char szFileName[MAX_PATH+1]; + void * pvListFile; + + // Exercise low level functions and compare their results + // with original code from Heroes of the Storm + TestMndxFunctions(pMndxInfo->pMarFile1); + TestMndxFunctions(pMndxInfo->pMarFile2); + TestMndxFunctions(pMndxInfo->pMarFile3); + + // Find a "mods" in the package array + TestFindPackage(pMndxInfo->pMarFile3, "mods/heroes.stormmod/base.stormassets/assets/textures/glow_green2.dds"); + TestMarFile(pMndxInfo->pMarFile3, "mods/heroes.stormmod/base.stormassets/assets/textures/glow_green2.dds", 69); + + // Search the package MAR file aith a path shorter than a fragment + TestFileSearch(pMndxInfo->pMarFile1, "mods/heroes.s"); + + // Test the file search + TestFileSearch(pMndxInfo->pMarFile1, ""); + TestFileSearch(pMndxInfo->pMarFile2, ""); + TestFileSearch(pMndxInfo->pMarFile3, ""); + + // False file search + TestFileSearch(pMndxInfo->pMarFile2, "assets/textures/storm_temp_hrhu"); + + // Open the listfile stream and initialize the listfile cache + pvListFile = ListFile_OpenExternal(_T("e:\\Ladik\\Appdir\\CascLib\\listfile\\listfile-hots-29049.txt")); + if(pvListFile != NULL) + { + // Check every file in the database + while((nLength = ListFile_GetNext(pvListFile, "*", szFileName, MAX_PATH)) != 0) + { + // Normalize the file name: ToLower + BackSlashToSlash + NormalizeFileName_LowerSlash(szFileName); + + // Check the file with all three MAR files + TestMarFile(pMndxInfo->pMarFile1, szFileName, nLength); + TestMarFile(pMndxInfo->pMarFile2, szFileName, nLength); + TestMarFile(pMndxInfo->pMarFile3, szFileName, nLength); + } + + ListFile_Free(pvListFile); + } +} +#endif // defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST) diff --git a/dep/CascLib/src/CascMndxRoot.h b/dep/CascLib/src/CascMndxRoot.h new file mode 100644 index 00000000000..23017642ff9 --- /dev/null +++ b/dep/CascLib/src/CascMndxRoot.h @@ -0,0 +1,365 @@ +/*****************************************************************************/ +/* CascMndxRoot.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Interface file for MNDX structures */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 15.05.14 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __CASC_MNDX_ROOT__ +#define __CASC_MNDX_ROOT__ + +class TFileNameDatabase; + +#define CASC_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type)) + +#define CASC_SEARCH_INITIALIZING 0 +#define CASC_SEARCH_SEARCHING 2 +#define CASC_SEARCH_FINISHED 4 + +typedef struct _TRIPLET +{ + DWORD BaseValue; + DWORD Value2; + DWORD Value3; +} TRIPLET, *PTRIPLET; + +typedef struct _NAME_FRAG +{ + DWORD ItemIndex; // Back index to various tables + DWORD NextIndex; // The following item index + DWORD FragOffs; // Higher 24 bits are 0xFFFFFF00 --> A single matching character + // Otherwise --> Offset to the name fragment table +} NAME_FRAG, *PNAME_FRAG; + +typedef struct _PATH_STOP +{ + DWORD ItemIndex; + DWORD field_4; + DWORD field_8; + DWORD field_C; + DWORD field_10; +} PATH_STOP, *PPATH_STOP; + +typedef union _ARRAY_POINTER +{ + LPBYTE Bytes; // Pointer to an octet + char * Chars; // Pointer to a character + PDWORD Uint32s; // Pointer to a DWORD + PTRIPLET Triplets; // Pointer to TRIPLET + PNAME_FRAG NameFrags; // Pointer to name fragment entry + PPATH_STOP PathStopPtr; // Pointer to path checkpoint + PULONGLONG Int64Ptr; // Pointer to 64-bit integer + +} ARRAY_POINTER, *PARRAY_POINTER; + +// Simple access to various tables within TGenericArray +#define ByteArray ArrayPointer.Bytes +#define CharArray ArrayPointer.Chars +#define Uint32Array ArrayPointer.Uint32s +#define TripletArray ArrayPointer.Triplets +#define NameFragArray ArrayPointer.NameFrags + +class TByteStream +{ + public: + + TByteStream(); + + void ExchangeWith(TByteStream & Target); + int GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray); + int SkipBytes(DWORD cbByteCount); + int SetByteBuffer(LPBYTE pbNewMarData, DWORD cbNewMarData); + int GetValue_DWORD(DWORD & Value); + int GetValue_ItemCount(DWORD & NumberOfBytes, DWORD & ItemCount, DWORD ItemSize); + int GetArray_DWORDs(PARRAY_POINTER PtrArray, DWORD ItemCount); + int GetArray_Triplets(PARRAY_POINTER PtrArray, DWORD ItemCount); + int GetArray_NameTable(PARRAY_POINTER PtrArray, DWORD ItemCount); + int GetArray_BYTES(PARRAY_POINTER PtrArray, DWORD ItemCount); + + LPBYTE pbByteData; + void * pvMappedFile; + DWORD cbByteData; + DWORD field_C; + HANDLE hFile; + HANDLE hMap; +}; + +class TGenericArray +{ + public: + + TGenericArray(); + ~TGenericArray(); + + int SetArrayValid(); + + void ExchangeWith(TGenericArray & Target); + void CopyFrom(TGenericArray & Source); + + void SetMaxItems_CHARS(DWORD NewMaxItemCount); + void SetMaxItems_PATH_STOP(DWORD NewMaxItemCount); + + void InsertOneItem_CHAR(char OneChar); + void InsertOneItem_PATH_STOP(PATH_STOP & NewItem); + + void sub_19583A0(DWORD NewItemCount); + + int LoadDwordsArray(TByteStream & InStream); + int LoadTripletsArray(TByteStream & InStream); + int LoadByteArray(TByteStream & InStream); + int LoadFragmentInfos(TByteStream & InStream); + int LoadStrings(TByteStream & InStream); + + int LoadDwordsArray_Copy(TByteStream & InStream); + int LoadTripletsArray_Copy(TByteStream & InStream); + int LoadBytes_Copy(TByteStream & InStream); + int LoadFragmentInfos_Copy(TByteStream & InStream); + int LoadStringsWithCopy(TByteStream & InStream); + + ARRAY_POINTER DataBuffer; + ARRAY_POINTER FirstValid; + + ARRAY_POINTER ArrayPointer; + DWORD ItemCount; // Number of items in the array + DWORD MaxItemCount; // Capacity of the array + bool bIsValidArray; +}; + +class TBitEntryArray : public TGenericArray +{ + public: + + TBitEntryArray(); + ~TBitEntryArray(); + + DWORD GetBitEntry(DWORD EntryIndex) + { + DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05; + DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F; + DWORD dwEndBit = dwStartBit + BitsPerEntry; + DWORD dwResult; + + // If the end bit index is greater than 32, + // we also need to load from the next 32-bit item + if(dwEndBit > 0x20) + { + dwResult = (Uint32Array[dwItemIndex + 1] << (0x20 - dwStartBit)) | (Uint32Array[dwItemIndex] >> dwStartBit); + } + else + { + dwResult = Uint32Array[dwItemIndex] >> dwStartBit; + } + + // Now we also need to mask the result by the bit mask + return dwResult & EntryBitMask; + } + + void ExchangeWith(TBitEntryArray & Target); + int LoadFromStream(TByteStream & InStream); + int LoadFromStream_Exchange(TByteStream & InStream); + + DWORD BitsPerEntry; + DWORD EntryBitMask; + DWORD TotalEntries; +}; + +class TStruct40 +{ + public: + + TStruct40(); + + void InitSearchBuffers(); + + TGenericArray array_00; + TGenericArray PathStops; // Array of path checkpoints + DWORD ItemIndex; // Current name fragment: Index to various tables + DWORD CharIndex; + DWORD ItemCount; + DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished +}; + +class TMndxFindResult +{ + public: + + TMndxFindResult(); + ~TMndxFindResult(); + + int CreateStruct40(); + void FreeStruct40(); + + int SetSearchPath(const char * szNewSearchPath, size_t cchNewSearchPath); + + const char * szSearchMask; // Search mask without wioldcards + size_t cchSearchMask; // Length of the search mask + DWORD field_8; + const char * szFoundPath; // Found path name + size_t cchFoundPath; // Length of the found path name + DWORD FileNameIndex; // Index of the file name + TStruct40 * pStruct40; +}; + +class TSparseArray +{ + public: + + TSparseArray(); + + void ExchangeWith(TSparseArray & TargetObject); + int LoadFromStream(TByteStream & InStream); + int LoadFromStream_Exchange(TByteStream & InStream); + + // Returns true if the item at n-th position is present + DWORD IsItemPresent(DWORD ItemIndex) + { + return (ItemBits.Uint32Array[ItemIndex >> 0x05] & (1 << (ItemIndex & 0x1F))); + } + + DWORD GetItemValue(DWORD ItemIndex); + + TGenericArray ItemBits; // Bit array for each item (1 = item is present) + DWORD TotalItemCount; // Total number of items in the array + DWORD ValidItemCount; // Number of present items in the array + TGenericArray BaseValues; // Array of base values for item indexes >= 0x200 + TGenericArray ArrayDwords_38; + TGenericArray ArrayDwords_50; +}; + +class TNameIndexStruct +{ + public: + + TNameIndexStruct(); + ~TNameIndexStruct(); + + bool CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs); + bool CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs); + void CopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs); + + void ExchangeWith(TNameIndexStruct & Target); + int LoadFromStream(TByteStream & InStream); + int LoadFromStream_Exchange(TByteStream & InStream); + + TGenericArray NameFragments; + TSparseArray Struct68; +}; + +class TStruct10 +{ + public: + + TStruct10(); + + void CopyFrom(TStruct10 & Target); + int sub_1956FD0(DWORD dwBitMask); + int sub_1957050(DWORD dwBitMask); + int sub_19572E0(DWORD dwBitMask); + int sub_1957800(DWORD dwBitMask); + + DWORD field_0; + DWORD field_4; + DWORD field_8; + DWORD field_C; +}; + +class TFileNameDatabasePtr +{ + public: + + TFileNameDatabasePtr(); + ~TFileNameDatabasePtr(); + + int FindFileInDatabase(TMndxFindResult * pStruct1C); + int sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult); + + int GetFileNameCount(PDWORD PtrFileNameCount); + int CreateDatabase(LPBYTE pbMarData, DWORD cbMarData); + int SetDatabase(TFileNameDatabase * pNewDB); + + TFileNameDatabase * pDB; +}; + +class TFileNameDatabase +{ + public: + + TFileNameDatabase(); + + void ExchangeWith(TFileNameDatabase & Target); + int LoadFromStream(TByteStream & InStream); + int LoadFromStream_Exchange(TByteStream & InStream); + + DWORD sub_1959CB0(DWORD dwHashValue); + DWORD sub_1959F50(DWORD arg_0); + + // Retrieves the name fragment distance + // HOTS: 19573D0/inlined + DWORD GetNameFragmentOffsetEx(DWORD LoBitsIndex, DWORD HiBitsIndex) + { + return (FrgmDist_HiBits.GetBitEntry(HiBitsIndex) << 0x08) | FrgmDist_LoBits.ByteArray[LoBitsIndex]; + } + + // HOTS: 1957350, inlined + DWORD GetNameFragmentOffset(DWORD LoBitsIndex) + { + return GetNameFragmentOffsetEx(LoBitsIndex, Struct68_D0.GetItemValue(LoBitsIndex)); + } + + bool sub_1957B80(TMndxFindResult * pStruct1C, DWORD dwKey); + bool CheckNextPathFragment(TMndxFindResult * pStruct1C); + bool FindFileInDatabase(TMndxFindResult * pStruct1C); + + void sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4); + bool sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4); + bool sub_1958B00(TMndxFindResult * pStruct1C); + bool sub_1959460(TMndxFindResult * pStruct1C); + + TSparseArray Struct68_00; + TSparseArray FileNameIndexes; // Array of file name indexes + TSparseArray Struct68_D0; + + // This pair of arrays serves for fast conversion from name hash to fragment offset + TGenericArray FrgmDist_LoBits; // Array of lower 8 bits of name fragment offset + TBitEntryArray FrgmDist_HiBits; // Array of upper x bits of name fragment offset + + TNameIndexStruct IndexStruct_174; + TFileNameDatabasePtr NextDB; + + TGenericArray NameFragTable; + + DWORD NameFragIndexMask; + DWORD field_214; + TStruct10 Struct10; + TByteStream MarStream; +}; + +typedef struct _MAR_FILE +{ + TFileNameDatabasePtr * pDatabasePtr; + LPBYTE pbMarData; + DWORD cbMarData; +} MAR_FILE, *PMAR_FILE; + +//----------------------------------------------------------------------------- +// Macros + +// Returns nonzero if the name fragment match is a single-char match +inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex) +{ + return ((Table.NameFragArray[ItemIndex].FragOffs & 0xFFFFFF00) == 0xFFFFFF00); +} + +//----------------------------------------------------------------------------- +// CASC functions related to MNDX + +int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); +PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName); +int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo); +bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData); +void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo); + +#endif // __CASC_MNDX_ROOT__ diff --git a/dep/CascLib/src/CascMndxRoot_x86.asm b/dep/CascLib/src/CascMndxRoot_x86.asm new file mode 100644 index 00000000000..c0e787d0872 --- /dev/null +++ b/dep/CascLib/src/CascMndxRoot_x86.asm @@ -0,0 +1,4369 @@ +.686P +.MODEL FLAT +ASSUME FS: NOTHING + +; --------------------------------------------------------------------------- + +TMndxFindResult struc ; (sizeof=0x1C) ; XREF: GAME_OBJECT_03F7E848::sub_E94500_r +szSearchMask dd ? ; XREF: TMndxFindResult__SetPath+2D_w + ; TMndxFindResult__Constructor+4_w ... +cchSearchMask dd ? ; XREF: TMndxFindResult__SetPath:loc_1956E9A_w + ; TMndxFindResult__Constructor+6_w ... +field_8 dd ? ; XREF: TMndxFindResult__Constructor+9_w +szFoundPath dd ? ; XREF: TMndxFindResult__Constructor+C_w + ; TFileNameDatabase__FindFileInDatabase+55_w +cchFoundPath dd ? ; XREF: TMndxFindResult__Constructor+F_w + ; TFileNameDatabase__FindFileInDatabase+5B_w +FileNameIndex dd ? ; XREF: TMndxFindResult__Constructor+12_w + ; TFileNameDatabase__FindFileInDatabase+6B_w +pStruct40 dd ? ; XREF: MAR_FILE__FindFileInDatabase+19_r + ; TMndxFindResult__SetPath:loc_1956E8C_r ... +TMndxFindResult ends + +; --------------------------------------------------------------------------- + +TRIPLET struc ; (sizeof=0xC) +BitIndex dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+39_r +NextKey dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+8C_r + ; TSparseArray__GetItemValue:loc_1959B8F_r ... +Distance dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+3E_r + ; TSparseArray__GetItemValue:loc_1959BBE_r ... +TRIPLET ends + +; --------------------------------------------------------------------------- + +NAME_ENTRY struc ; (sizeof=0xC) +ExpectedHashModifier dd ? +NextHashModifier dd ? ; XREF: TArchiveDatabase__sub_1958B00+D3r + ; sub_1958D70+2Cr +FragmentOffset dd ? ; XREF: TArchiveDatabase__sub_1958B00+45r + ; sub_1958D70+36r +NAME_ENTRY ends + +; --------------------------------------------------------------------------- + +TStruct14 struc ; (sizeof=0x14) ; XREF: TFileNameDatabase::sub_1959460r +HashValue dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+44r + ; TGenericArray__InsertItem_STRUCT14+46w ... +field_4 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+48r + ; TGenericArray__InsertItem_STRUCT14+4Bw ... +field_8 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+4Er + ; TGenericArray__InsertItem_STRUCT14+51w ... +field_C dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+54r + ; TGenericArray__InsertItem_STRUCT14+57w ... +field_10 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+5Ar + ; TGenericArray__InsertItem_STRUCT14+5Dw ... +TStruct14 ends + +; --------------------------------------------------------------------------- + +TGenericArray struc ; (sizeof=0x15) ; XREF: TGenericArray::LoadDwordsArrayWithCopyr + ; TFileNameDatabase::LoadBytesr ... +DataBuffer dd ? ; XREF: TMndxFindResult__CreateStruct40+24w + ; TMndxFindResult__CreateStruct40+35w ... +field_4 dd ? ; XREF: TMndxFindResult__CreateStruct40+26w + ; TMndxFindResult__CreateStruct40+38w ... +ItemArray dd ? ; XREF: TMndxFindResult__CreateStruct40+29w + ; TMndxFindResult__CreateStruct40+3Bw ... +ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+2Cw + ; TMndxFindResult__CreateStruct40+3Ew ... +MaxItemCount dd ? ; XREF: TFileNameDatabasePtr__CreateDatabase+27o + ; TMndxFindResult__CreateStruct40+2Fw ... +bIsValidArray db ? ; XREF: LoadAndVerifyIndexFileHeader+31w + ; TMndxFindResult__CreateStruct40+32w ... +TGenericArray ends + +; --------------------------------------------------------------------------- + +TBitEntryArray struc ; (sizeof=0x24) ; XREF: TGenericArrayEx::LoadFromStream_Exchanger + ; TFileNameDatabaser +DataBuffer dd ? ; XREF: TArchiveDatabase__Destructor+64r + ; TArchiveDatabase__Constructor+1A3w +field_4 dd ? ; XREF: TArchiveDatabase__Constructor+1A9w +ItemArray dd ? ; XREF: sub_1957350+31r + ; sub_19573D0+1Fr ... +ItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1B5w +MaxItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1BBw +bIsValidArray db ? ; XREF: TArchiveDatabase__Constructor+1C1w + db ? ; undefined + db ? ; undefined + db ? ; undefined +BitsPerEntry dd ? ; XREF: sub_1957350+1Fr + ; sub_19573D0+6r ... +EntryBitMask dd ? ; XREF: sub_1957350:loc_19573B1r + ; sub_19573D0:loc_1957419r ... +TotalEntries dd ? ; XREF: TGenericArrayEx__LoadFromStream:loc_195861Bw + ; TArchiveDatabase__Constructor+1D3w +TBitEntryArray ends + ; TFileNameDatabase__Constructor+1D3_w +; --------------------------------------------------------------------------- + +TStruct40 struc ; (sizeof=0x40) +array_00 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+24w + ; TMndxFindResult__CreateStruct40+26w ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +array_18 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+35w + ; TMndxFindResult__CreateStruct40+38w ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +HashValue dd ? ; XREF: TMndxFindResult__CreateStruct40+47w + ; TFileNameDatabase__CheckNextPathFragment+1Ar ... +CharIndex dd ? ; XREF: TMndxFindResult__CreateStruct40+4Aw + ; TFileNameDatabase__CheckNextPathFragment+11r ... +ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+4Dw + ; TStruct40__InitSearchBuffers+6Bw ... +SearchPhase dd ? ; XREF: TMndxFindResult__CreateStruct40+50w + ; TFileNameDatabase__FindFileInDatabase+17w ... +TStruct40 ends + +; --------------------------------------------------------------------------- + +TSparseArray struc ; (sizeof=0x65) ; XREF: TSparseArray::LoadFromStream_Exchange_r + ; TNameIndexStruct_r ... +ItemIsPresent TGenericArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w + ; TFileNameDatabase__Destructor+4C_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +TotalItemCount dd ? ; XREF: TSparseArray__ExchangeWith+4D_r + ; TSparseArray__ExchangeWith+56_w ... +ValidItemCount dd ? ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r + ; TSparseArray__ExchangeWith+59_r ... +ArrayTriplets_20 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+40_r + ; TFileNameDatabase__Destructor+94_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +ArrayDwords_38 TGenericArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o + ; TFileNameDatabase__Destructor+34_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +ArrayDwords_50 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+28_r + ; TFileNameDatabase__Destructor+7C_r ... +TSparseArray ends + +; --------------------------------------------------------------------------- + +TNameIndexStruct struc ; (sizeof=0x7D) ; XREF: TNameIndexStruct::LoadFromStream_Exchange_r + ; TFileNameDatabaser +NameFragments TGenericArray <> ; XREF: TFileNameDatabase__Destructor+58_r + ; TFileNameDatabase__LoadFromStream+82_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +Struct68 TSparseArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w + ; TFileNameDatabase__Destructor+28_r ... +TNameIndexStruct ends + +; --------------------------------------------------------------------------- + +TByteStream struc ; (sizeof=0x18) ; XREF: TFileNameDatabasePtr::CreateDatabase_r + ; TFileNameDatabase_r +pbData dd ? ; XREF: TByteStream__Constructor+4_w + ; TByteStream__IsMarDataValid+2_r ... +pvMappedFile dd ? ; XREF: TByteStream__Constructor+6_w + ; TByteStream__Destructor+3_r +cbData dd ? ; XREF: TByteStream__Constructor+9_w + ; TByteStream__GetBytes:loc_1959A05_r ... +field_C dd ? ; XREF: TByteStream__Constructor+C_w +hFile dd ? ; XREF: TByteStream__Constructor+F_w + ; TByteStream__Destructor:loc_19599D2_r +hMap dd ? ; XREF: TByteStream__Constructor+12_w + ; TByteStream__Destructor:loc_19599C2_r +TByteStream ends + +; --------------------------------------------------------------------------- + +TStruct10 struc ; (sizeof=0x10) ; XREF: TStruct10::sub_1957800_r + ; TFileNameDatabase_r +field_0 dd ? ; XREF: sub_19572E0+24_w + ; TFileNameDatabase__Constructor+21A_w +field_4 dd ? ; XREF: sub_1956FD0+28_w + ; sub_1956FD0:loc_1957001_w ... +field_8 dd ? ; XREF: sub_19572E0+4A_w + ; sub_19572E0+5B_w ... +field_C dd ? ; XREF: sub_1957050:loc_1957074_w + ; sub_1957050:loc_1957081_w ... +TStruct10 ends + +; --------------------------------------------------------------------------- + +TFileNameDatabasePtr struc ; (sizeof=0x4) ; XREF: TFileNameDatabase_r + ; MAR_FILE_r +pDatabase dd ? ; XREF: MAR_FILE__Constructor+1D_w + ; MAR_FILE__CreateDatabase+22_w ... +TFileNameDatabasePtr ends + +; --------------------------------------------------------------------------- + +TFileNameDatabase struc ; (sizeof=0x240) ; XREF: TFileNameDatabase::LoadFromStream_Exchange_r +Struct68_00 TSparseArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o + ; TFileNameDatabase__Destructor+D9_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +FileNameIndexes TSparseArray <> ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r + ; TFileNameDatabase__Destructor+AC_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +Struct68_D0 TSparseArray <> ; XREF: TFileNameDatabase__Destructor+7C_r + ; TFileNameDatabase__Destructor+88_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +FrgmDist_LoBits TGenericArray <> ; XREF: TFileNameDatabase__Destructor+70_r + ; TFileNameDatabase__CheckNextPathFragment:loc_1957AC9_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +FrgmDist_HiBits TBitEntryArray <> ; XREF: TFileNameDatabase__Destructor+64_r + ; TFileNameDatabase__CheckNextPathFragment+119_r ... +IndexStruct_174 TNameIndexStruct <> ; XREF: TFileNameDatabase__Destructor+28_r + ; TFileNameDatabase__Destructor+34_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +NextDB TFileNameDatabasePtr <> ; XREF: TFileNameDatabase__Destructor+1D_o + ; TFileNameDatabase__CheckNextPathFragment+52_r ... +NameFragTable TGenericArray <> ; XREF: TFileNameDatabase__Destructor+E_r + ; TFileNameDatabase__CheckNextPathFragment+2F_r ... + db ? ; undefined + db ? ; undefined + db ? ; undefined +NameFragIndexMask dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+26_r + ; sub_1957B80:loc_1957B95_r ... +field_214 dd ? ; XREF: TFileNameDatabase__Constructor+20E_w + ; TFileNameDatabase__LoadFromStream+110_w +Struct10 TStruct10 <> ; XREF: TFileNameDatabase__Constructor+21A_w + ; TFileNameDatabase__Constructor+224_w ... +MarStream TByteStream <> ; XREF: TFileNameDatabase__Destructor+3_o + ; TFileNameDatabase__Constructor+214_o +TFileNameDatabase ends + +; --------------------------------------------------------------------------- + +MAR_FILE struc ; (sizeof=0xC) +pDatabasePtr TFileNameDatabasePtr <> ; XREF: MAR_FILE__Constructor+1D_w + ; MAR_FILE__CreateDatabase+22_w ... +pbMarFileData dd ? ; XREF: MAR_FILE__Constructor+1A_w + ; MAR_FILE__CreateDatabase+1B_r ... +cbMarFileData dd ? ; XREF: MAR_FILE__Constructor+12_w + ; MAR_FILE__CreateDatabase+18_r ... +MAR_FILE ends + +.DATA +table_1BA1818 db 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0 + db 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0 + db 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0 + db 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0 + db 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0 + db 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0 + db 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0 + db 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 + db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0 + db 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0 + db 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0 + db 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0 + db 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0 + db 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0 + db 2, 0, 1, 0, 3 dup(7), 1, 7, 2 dup(2), 1, 7, 2 dup(3) + db 1, 3, 2 dup(2), 1, 7, 2 dup(4), 1, 4, 2 dup(2), 1, 4 + db 2 dup(3), 1, 3, 2 dup(2), 1, 7, 2 dup(5), 1, 5, 2 dup(2) + db 1, 5, 2 dup(3), 1, 3, 2 dup(2), 1, 5, 2 dup(4), 1, 4 + db 2 dup(2), 1, 4, 2 dup(3), 1, 3, 2 dup(2), 1, 7, 2 dup(6) + db 1, 6, 2 dup(2), 1, 6, 2 dup(3), 1, 3, 2 dup(2), 1, 6 + db 2 dup(4), 1, 4, 2 dup(2), 1, 4, 2 dup(3), 1, 3, 2 dup(2) + db 1, 6, 2 dup(5), 1, 5, 2 dup(2), 1, 5, 2 dup(3), 1, 3 + db 2 dup(2), 1, 5, 2 dup(4), 1, 4, 2 dup(2), 1, 4, 2 dup(3) + db 1, 3, 2 dup(2), 1, 3 dup(7), 1, 7, 2 dup(2), 1, 7, 2 dup(3) + db 1, 3, 2 dup(2), 1, 7, 2 dup(4), 1, 4, 2 dup(2), 1, 4 + db 2 dup(3), 1, 3, 2 dup(2), 1, 7, 2 dup(5), 1, 5, 2 dup(2) + db 1, 5, 2 dup(3), 1, 3, 2 dup(2), 1, 5, 2 dup(4), 1, 4 + db 2 dup(2), 1, 4, 2 dup(3), 1, 3, 2 dup(2), 1, 7, 2 dup(6) + db 1, 6, 2 dup(2), 1, 6, 2 dup(3), 1, 3, 2 dup(2), 1, 6 + db 2 dup(4), 1, 4, 2 dup(2), 1, 4, 2 dup(3), 1, 3, 2 dup(2) + db 1, 6, 2 dup(5), 1, 5, 2 dup(2), 1, 5, 2 dup(3), 1, 3 + db 2 dup(2), 1, 5, 2 dup(4), 1, 4, 2 dup(2), 1, 4, 2 dup(3) + db 1, 3, 2 dup(2), 1, 7 dup(7), 2, 3 dup(7), 3, 7, 2 dup(3) + db 2, 3 dup(7), 4, 7, 2 dup(4), 2, 7, 2 dup(4), 3, 4, 2 dup(3) + db 2, 3 dup(7), 5, 7, 2 dup(5), 2, 7, 2 dup(5), 3, 5, 2 dup(3) + db 2, 7, 2 dup(5), 4, 5, 2 dup(4), 2, 5, 2 dup(4), 3, 4 + db 2 dup(3), 2, 3 dup(7), 6, 7, 2 dup(6), 2, 7, 2 dup(6) + db 3, 6, 2 dup(3), 2, 7, 2 dup(6), 4, 6, 2 dup(4), 2, 6 + db 2 dup(4), 3, 4, 2 dup(3), 2, 7, 2 dup(6), 5, 6, 2 dup(5) + db 2, 6, 2 dup(5), 3, 5, 2 dup(3), 2, 6, 2 dup(5), 4, 5 + db 2 dup(4), 2, 5, 2 dup(4), 3, 4, 2 dup(3), 2, 7 dup(7) + db 2, 3 dup(7), 3, 7, 2 dup(3), 2, 3 dup(7), 4, 7, 2 dup(4) + db 2, 7, 2 dup(4), 3, 4, 2 dup(3), 2, 3 dup(7), 5, 7, 2 dup(5) + db 2, 7, 2 dup(5), 3, 5, 2 dup(3), 2, 7, 2 dup(5), 4, 5 + db 2 dup(4), 2, 5, 2 dup(4), 3, 4, 2 dup(3), 2, 3 dup(7) + db 6, 7, 2 dup(6), 2, 7, 2 dup(6), 3, 6, 2 dup(3), 2, 7 + db 2 dup(6), 4, 6, 2 dup(4), 2, 6, 2 dup(4), 3, 4, 2 dup(3) + db 2, 7, 2 dup(6), 5, 6, 2 dup(5), 2, 6, 2 dup(5), 3, 5 + db 2 dup(3), 2, 6, 2 dup(5), 4, 5, 2 dup(4), 2, 5, 2 dup(4) + db 3, 4, 2 dup(3), 2, 0Fh dup(7), 3, 7 dup(7), 4, 3 dup(7) + db 4, 7, 2 dup(4), 3, 7 dup(7), 5, 3 dup(7), 5, 7, 2 dup(5) + db 3, 3 dup(7), 5, 7, 2 dup(5), 4, 7, 2 dup(5), 4, 5, 2 dup(4) + db 3, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6), 3, 3 dup(7) + db 6, 7, 2 dup(6), 4, 7, 2 dup(6), 4, 6, 2 dup(4), 3, 3 dup(7) + db 6, 7, 2 dup(6), 5, 7, 2 dup(6), 5, 6, 2 dup(5), 3, 7 + db 2 dup(6), 5, 6, 2 dup(5), 4, 6, 2 dup(5), 4, 5, 2 dup(4) + db 3, 0Fh dup(7), 3, 7 dup(7), 4, 3 dup(7), 4, 7, 2 dup(4) + db 3, 7 dup(7), 5, 3 dup(7), 5, 7, 2 dup(5), 3, 3 dup(7) + db 5, 7, 2 dup(5), 4, 7, 2 dup(5), 4, 5, 2 dup(4), 3, 7 dup(7) + db 6, 3 dup(7), 6, 7, 2 dup(6), 3, 3 dup(7), 6, 7, 2 dup(6) + db 4, 7, 2 dup(6), 4, 6, 2 dup(4), 3, 3 dup(7), 6, 7, 2 dup(6) + db 5, 7, 2 dup(6), 5, 6, 2 dup(5), 3, 7, 2 dup(6), 5, 6 + db 2 dup(5), 4, 6, 2 dup(5), 4, 5, 2 dup(4), 3, 1Fh dup(7) + db 4, 0Fh dup(7), 5, 7 dup(7), 5, 3 dup(7), 5, 7, 2 dup(5) + db 4, 0Fh dup(7), 6, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6) + db 4, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6), 5, 3 dup(7) + db 6, 7, 2 dup(6), 5, 7, 2 dup(6), 5, 6, 2 dup(5), 4, 1Fh dup(7) + db 4, 0Fh dup(7), 5, 7 dup(7), 5, 3 dup(7), 5, 7, 2 dup(5) + db 4, 0Fh dup(7), 6, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6) + db 4, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6), 5, 3 dup(7) + db 6, 7, 2 dup(6), 5, 7, 2 dup(6), 5, 6, 2 dup(5), 4, 3Fh dup(7) + db 5, 1Fh dup(7), 6, 0Fh dup(7), 6, 7 dup(7), 6, 3 dup(7) + db 6, 7, 2 dup(6), 5, 3Fh dup(7), 5, 1Fh dup(7), 6, 0Fh dup(7) + db 6, 7 dup(7), 6, 3 dup(7), 6, 7, 2 dup(6), 5, 7Fh dup(7) + db 6, 7Fh dup(7), 6, 100h dup(7) + +unk_53ABE00 db 0 + +.CODE + +; +; void * operator_new_safe(size_t ByteCount, void * unk_53ABE00); +; + +EXTERN _allocate_zeroed_memory_x86:PROC +EXTERN _free_memory_x86:PROC + +j_operator_new_safe PROC + push ebp + mov ebp, esp + push [ebp+08] + call _allocate_zeroed_memory_x86 + add esp, 4 + mov esp, ebp + pop ebp + ret +j_operator_new_safe ENDP + +operator_delete PROC + push ebp + mov ebp, esp + push [ebp+08] + call _free_memory_x86 + add esp, 4 + mov esp, ebp + pop ebp + ret +operator_delete ENDP + + +TSparseArray__GetItemValue proc near ; CODE XREF: sub_1957350+1A_p + ; TFileNameDatabase__CheckNextPathFragment+103_p ... + +arg_0 = dword ptr 8 + + push ebp + mov ebp, esp + mov edx, [ecx+28h] + push ebx + push esi + push edi + mov edi, [ebp+arg_0] + mov eax, edi + shr eax, 9 + lea eax, [eax+eax*2] + mov esi, [edx+eax*4] + lea eax, [edx+eax*4] + mov edx, edi + shr edx, 6 + and edx, 7 + dec edx + cmp edx, 6 ; switch 7 cases + ja short loc_1959BE0 ; jumptable 01959B88 default case + jmp ds:off_1959C90[edx*4] ; switch jump + +loc_1959B8F: ; DATA XREF: .text:off_1959C90o + mov eax, [eax+4] ; jumptable 01959B88 case 0 + and eax, 7Fh + jmp short loc_1959BDE +; --------------------------------------------------------------------------- + +loc_1959B97: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov edx, [eax+4] ; jumptable 01959B88 case 1 + shr edx, 7 + and edx, 0FFh + add esi, edx + jmp short loc_1959BE0 ; jumptable 01959B88 default case +; --------------------------------------------------------------------------- + +loc_1959BA7: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov eax, [eax+4] ; jumptable 01959B88 case 2 + shr eax, 0Fh + and eax, 0FFh + jmp short loc_1959BDE +; --------------------------------------------------------------------------- + +loc_1959BB4: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov edx, [eax+4] ; jumptable 01959B88 case 3 + shr edx, 17h + add esi, edx + jmp short loc_1959BE0 ; jumptable 01959B88 default case +; --------------------------------------------------------------------------- + +loc_1959BBE: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov eax, [eax+8] ; jumptable 01959B88 case 4 + jmp short loc_1959BD9 +; --------------------------------------------------------------------------- + +loc_1959BC3: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov edx, [eax+8] ; jumptable 01959B88 case 5 + shr edx, 9 + and edx, 1FFh + add esi, edx + jmp short loc_1959BE0 ; jumptable 01959B88 default case +; --------------------------------------------------------------------------- + +loc_1959BD3: ; CODE XREF: TSparseArray__GetItemValue+28_j + ; DATA XREF: .text:off_1959C90o + mov eax, [eax+8] ; jumptable 01959B88 case 6 + shr eax, 12h + +loc_1959BD9: ; CODE XREF: TSparseArray__GetItemValue+61_j + and eax, 1FFh + +loc_1959BDE: ; CODE XREF: TSparseArray__GetItemValue+35_j + ; TSparseArray__GetItemValue+52_j + add esi, eax + +loc_1959BE0: ; CODE XREF: TSparseArray__GetItemValue+26_j + ; TSparseArray__GetItemValue+45_j ... + mov ebx, edi ; jumptable 01959B88 default case + shr ebx, 5 + test bl, 1 + jz short loc_1959C31 + mov edx, [ecx+8] + mov edx, [edx+ebx*4-4] + mov eax, edx + shr eax, 1 + and eax, 55555555h + and edx, 55555555h + add eax, edx + mov edx, eax + and eax, 33333333h + shr edx, 2 + and edx, 33333333h + add edx, eax + mov eax, edx + shr eax, 4 + and eax, 0F0F0F0Fh + and edx, 0F0F0F0Fh + add eax, edx + imul eax, 1010101h + shr eax, 18h + add esi, eax + +loc_1959C31: ; CODE XREF: TSparseArray__GetItemValue+88_j + mov edx, [ecx+8] + mov ecx, edi + and ecx, 1Fh + mov eax, 1 + shl eax, cl + mov ecx, [edx+ebx*4] + pop edi + dec eax + and ecx, eax + mov eax, ecx + shr eax, 1 + and eax, 55555555h + and ecx, 55555555h + add eax, ecx + mov ecx, eax + and eax, 33333333h + shr ecx, 2 + and ecx, 33333333h + add ecx, eax + mov eax, ecx + shr eax, 4 + and eax, 0F0F0F0Fh + and ecx, 0F0F0F0Fh + add eax, ecx + imul eax, 1010101h + shr eax, 18h + add eax, esi + pop esi + pop ebx + pop ebp + retn 4 + +off_1959C90 dd offset loc_1959B8F ; DATA XREF: TSparseArray__GetItemValue+28_r + dd offset loc_1959B97 ; jump table for switch statement + dd offset loc_1959BA7 + dd offset loc_1959BB4 + dd offset loc_1959BBE + dd offset loc_1959BC3 + dd offset loc_1959BD3 + +TSparseArray__GetItemValue endp + +;------------------------------------------------------------------------------ +; TArchiveDatabase__sub_1959CB0 + +TArchiveDatabase__sub_1959CB0 proc near + ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1_p + ; sub_1958B00+E8_p ... + +pThis = dword ptr -4 +dwKey = dword ptr 8 + + push ebp + mov ebp, esp + push ecx + mov edx, [ebp+dwKey] + mov eax, edx + shr eax, 9 + mov [ebp+pThis], ecx + test edx, 1FFh ; if(dwKey & 0x01FF) + jnz short loc_1959CD3 + mov ecx, [ecx+40h] + mov eax, [ecx+eax*4] ; EAX = pDatabase->NextKeyTable[dwKey >> 0x09] + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1959CD3: ; CODE XREF: TFileNameDatabase__FindNextMatch+15_j + push ebx + push esi + mov esi, [ecx+40h] + lea esi, [esi+eax*4] + mov eax, [esi] + mov esi, [esi+4] + shr eax, 9 + add esi, 1FFh + push edi + shr esi, 9 + lea edi, [eax+0Ah] + mov [ebp+dwKey], esi + cmp edi, esi + jb short loc_1959D2E + mov edi, [ecx+28h] + lea esi, [eax+eax*2+3] + lea esi, [edi+esi*4] + mov edi, eax + shl edi, 9 + mov ebx, edi + sub ebx, [esi] + add ebx, 200h + cmp edx, ebx + jb short loc_1959D5F + +loc_1959D14: ; CODE XREF: TFileNameDatabase__FindNextMatch+7Aj + add edi, 200h + add esi, 0Ch + mov ebx, edi + sub ebx, [esi] + inc eax + add ebx, 200h + cmp edx, ebx + jnb short loc_1959D14 + jmp short loc_1959D5F +; --------------------------------------------------------------------------- + +loc_1959D2E: ; CODE XREF: TFileNameDatabase__FindNextMatch+45_j + lea edi, [eax+1] + cmp edi, esi + jnb short loc_1959D5F + mov ecx, [ecx+28h] + +loc_1959D38: ; CODE XREF: TFileNameDatabase__FindNextMatch+AAj + add esi, eax + shr esi, 1 + mov ebx, esi + shl ebx, 9 + lea edi, [esi+esi*2] + sub ebx, [ecx+edi*4] + cmp edx, ebx + jnb short loc_1959D50 + mov [ebp+dwKey], esi + jmp short loc_1959D55 +; --------------------------------------------------------------------------- + +loc_1959D50: ; CODE XREF: TFileNameDatabase__FindNextMatch+99_j + mov eax, esi + mov esi, [ebp+dwKey] + +loc_1959D55: ; CODE XREF: TFileNameDatabase__FindNextMatch+9E_j + lea edi, [eax+1] + cmp edi, esi + jb short loc_1959D38 + mov ecx, [ebp+pThis] + +loc_1959D5F: ; CODE XREF: TFileNameDatabase__FindNextMatch+62_j + ; TFileNameDatabase__FindNextMatch+7C_j ... + mov ecx, [ecx+28h] + lea esi, [eax+eax*2] + mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax] + lea esi, [ecx+esi*4] ; ESI = Struct68_00.ArrayTriplets_20.ItemArray + eax + mov ecx, eax + shl ecx, 9 + sub edi, ecx + shl eax, 4 + add edx, edi + mov edi, eax + mov eax, [esi+4] + mov ecx, eax + shr ecx, 17h + mov ebx, 100h + sub ebx, ecx + cmp edx, ebx + jnb short loc_1959DE8 + mov ecx, eax + shr ecx, 7 + and ecx, 0FFh + mov esi, 80h + sub esi, ecx + cmp edx, esi + jnb short loc_1959DC0 + and eax, 7Fh + mov ecx, 40h + sub ecx, eax + cmp edx, ecx + jb loc_1959E53 + add edi, 2 + lea edx, [edx+eax-40h] + jmp loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DC0: ; CODE XREF: TFileNameDatabase__FindNextMatch+F0_j + shr eax, 0Fh + and eax, 0FFh + mov esi, 0C0h + sub esi, eax + cmp edx, esi + jnb short loc_1959DDC + add edi, 4 + lea edx, [edx+ecx-80h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DDC: ; CODE XREF: TFileNameDatabase__FindNextMatch+121_j + add edi, 6 + lea edx, [edx+eax-0C0h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DE8: ; CODE XREF: TFileNameDatabase__FindNextMatch+DA_j + mov esi, [esi+8] + mov eax, esi + shr eax, 9 + and eax, 1FFh + mov ebx, 180h + sub ebx, eax + cmp edx, ebx + jnb short loc_1959E29 + and esi, 1FFh + mov eax, 140h + sub eax, esi + cmp edx, eax + jnb short loc_1959E1D + add edi, 8 + lea edx, [edx+ecx-100h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E1D: ; CODE XREF: TFileNameDatabase__FindNextMatch+15F_j + add edi, 0Ah + lea edx, [edx+esi-140h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E29: ; CODE XREF: TFileNameDatabase__FindNextMatch+14E_j + shr esi, 12h + and esi, 1FFh + mov ecx, 1C0h + sub ecx, esi + cmp edx, ecx + jnb short loc_1959E49 + add edi, 0Ch + lea edx, [edx+eax-180h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E49: ; CODE XREF: TFileNameDatabase__FindNextMatch+18B_j + add edi, 0Eh + lea edx, [edx+esi-1C0h] + +loc_1959E53: ; CODE XREF: TFileNameDatabase__FindNextMatch+FE_j + ; TFileNameDatabase__FindNextMatch+10B_j ... + mov eax, [ebp+pThis] + mov ebx, [eax+8] + mov ecx, [ebx+edi*4] + not ecx + mov eax, ecx + shr eax, 1 + and eax, 55555555h + mov esi, ecx + and esi, 55555555h + add eax, esi + mov esi, eax + shr esi, 2 + and eax, 33333333h + and esi, 33333333h + add esi, eax + mov eax, esi + shr eax, 4 + and esi, 0F0F0F0Fh + and eax, 0F0F0F0Fh + add eax, esi + imul eax, 1010101h + mov esi, eax + shr esi, 18h + cmp edx, esi + jb short loc_1959EEA + mov ecx, [ebx+edi*4+4] + inc edi + sub edx, esi + not ecx + mov eax, ecx + shr eax, 1 + and eax, 55555555h + mov esi, ecx + and esi, 55555555h + add eax, esi + mov esi, eax + shr esi, 2 + and eax, 33333333h + and esi, 33333333h + add esi, eax + mov eax, esi + shr eax, 4 + and eax, 0F0F0F0Fh + and esi, 0F0F0F0Fh + add eax, esi + imul eax, 1010101h + +loc_1959EEA: ; CODE XREF: TFileNameDatabase__FindNextMatch+1F2_j + mov esi, eax + shr esi, 8 + and esi, 0FFh + shl edi, 5 + cmp edx, esi + jnb short loc_1959F0D + and eax, 0FFh + cmp edx, eax + jb short loc_1959F2B + add edi, 8 + shr ecx, 8 + jmp short loc_1959F29 +; --------------------------------------------------------------------------- + +loc_1959F0D: ; CODE XREF: TFileNameDatabase__FindNextMatch+24A_j + shr eax, 10h + and eax, 0FFh + cmp edx, eax + jnb short loc_1959F23 + add edi, 10h + shr ecx, 10h + sub edx, esi + jmp short loc_1959F2B +; --------------------------------------------------------------------------- + +loc_1959F23: ; CODE XREF: TFileNameDatabase__FindNextMatch+267_j + add edi, 18h + shr ecx, 18h + +loc_1959F29: ; CODE XREF: TFileNameDatabase__FindNextMatch+25B_j + sub edx, eax + +loc_1959F2B: ; CODE XREF: TFileNameDatabase__FindNextMatch+253_j + ; TFileNameDatabase__FindNextMatch+271_j + and ecx, 0FFh + shl edx, 8 + movzx eax, byte ptr ds:table_1BA1818[ecx+edx] + add eax, edi + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 4 +TArchiveDatabase__sub_1959CB0 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TNameIndexStruct__CheckNameFragment proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+6B_p + ; TFileNameDatabase__CheckNextPathFragment+191_p ... + +pThis = dword ptr -4 +pUnknownStruct1C= dword ptr 8 +dwDistance = dword ptr 0Ch + + push ebp + mov ebp, esp + push ecx + push ebx + push esi + push edi + mov edi, ecx + cmp [edi+TNameIndexStruct.Struct68.TotalItemCount], 0 + mov ecx, [ebp+pUnknownStruct1C] + mov edx, [ecx+TMndxFindResult.pStruct40] + mov [ebp+pThis], edi + jnz short loc_195A1C8 + mov edi, [edi+TNameIndexStruct.NameFragments.ItemArray] ; Pointer to name table + sub edi, [edx+TStruct40.CharIndex] + add edi, [ebp+dwDistance] ; EDI = szPathFragment + +loc_195A1A1: ; CODE XREF: TNameIndexStruct__CheckNameFragment+3Bj + mov eax, [edx+TStruct40.CharIndex] + mov esi, [ecx+TMndxFindResult.szSearchMask] + mov bl, [eax+edi] + cmp bl, [eax+esi] + jnz short loc_195A1BD + inc eax + mov [edx+TStruct40.CharIndex], eax + cmp byte ptr [eax+edi], 0 + jz short loc_195A215 + cmp eax, [ecx+TMndxFindResult.cchSearchMask] + jb short loc_195A1A1 + +loc_195A1BD: ; CODE XREF: TNameIndexStruct__CheckNameFragment+2C_j + ; TNameIndexStruct__CheckNameFragment+5Ej ... + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_195A1C8: ; CODE XREF: TNameIndexStruct__CheckNameFragment+16_j + mov eax, [ebp+dwDistance] + jmp short loc_195A1D0 +; --------------------------------------------------------------------------- + align 10h + +loc_195A1D0: ; CODE XREF: TNameIndexStruct__CheckNameFragment+4B_j + ; TNameIndexStruct__CheckNameFragment+93j + mov ebx, [edi+TNameIndexStruct.NameFragments.ItemArray] + mov esi, [edx+TStruct40.CharIndex] + mov ecx, [ecx+TMndxFindResult.szSearchMask] + mov bl, [eax+ebx] ; pNameList[dwNameOffset] == szPathName[nCharIndex] + cmp bl, [esi+ecx] + jnz short loc_195A1BD + lea ecx, [esi+1] + mov [edx+TStruct40.CharIndex], ecx + mov edi, [edi+TNameIndexStruct.Struct68.ItemIsPresent.ItemArray] + mov [ebp+dwDistance], ecx + mov ecx, eax + and ecx, 1Fh ; ecx = (dwNameOffset & 0x1F) + mov esi, eax + mov ebx, 1 + shl ebx, cl ; ebx = 1 << (dwNameOffset & 0x1F); + shr esi, 5 ; esi = (dwNameOffset >> 0x05) + mov ecx, [edi+esi*4] + inc eax + and ecx, ebx + jnz short loc_195A215 + mov esi, [ebp+dwDistance] + mov ecx, [ebp+pUnknownStruct1C] + cmp esi, [ecx+4] + jnb short loc_195A1BD + mov edi, [ebp+pThis] + jmp short loc_195A1D0 +; --------------------------------------------------------------------------- + +loc_195A215: ; CODE XREF: TNameIndexStruct__CheckNameFragment+36_j + ; TNameIndexStruct__CheckNameFragment+83_j + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 8 +TNameIndexStruct__CheckNameFragment endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_1957350 proc near ; CODE XREF: sub_1957B80+D8p + ; sub_1957B80:loc_1957C73p ... + +arg_0 = dword ptr 8 + + push ebp + mov ebp, esp + mov eax, [ebp+arg_0] + push ebx + push esi + mov esi, ecx + mov ebx, [esi+140h] + push edi + push eax + lea ecx, [esi+0D0h] + add ebx, eax + call TSparseArray__GetItemValue + mov edi, [esi+168h] + mov ecx, edi + imul ecx, eax + mov eax, ecx + and ecx, 1Fh + mov edx, ecx + mov ecx, [esi+158h] + add edi, edx + shr eax, 5 + cmp edi, 20h + ja short loc_195739A + mov eax, [ecx+eax*4] + mov ecx, edx + shr eax, cl + jmp short loc_19573B1 +; --------------------------------------------------------------------------- + +loc_195739A: ; CODE XREF: sub_1957350+3F_j + lea edi, [ecx+eax*4] + mov eax, [edi+4] + mov edi, [edi] + mov ecx, 20h + sub ecx, edx + shl eax, cl + mov ecx, edx + shr edi, cl + or eax, edi + +loc_19573B1: ; CODE XREF: sub_1957350+48_j + and eax, [esi+16Ch] + movzx edx, byte ptr [ebx] + pop edi + shl eax, 8 + pop esi + or eax, edx + pop ebx + pop ebp + retn 4 +sub_1957350 endp + +; --------------------------------------------------------------------------- + + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_1959F50 proc near ; CODE XREF: sub_1957B80+14C_p + ; sub_1958980+62_p ... + +var_4 = dword ptr -4 +arg_0 = dword ptr 8 + + push ebp + mov ebp, esp + push ecx + mov edx, [ebp+arg_0] + mov eax, edx + shr eax, 9 + mov [ebp+var_4], ecx + test edx, 1FFh + jnz short loc_1959F73 + mov ecx, [ecx+58h] + mov eax, [ecx+eax*4] + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1959F73: ; CODE XREF: sub_1959F50+15_j + push ebx + push esi + mov esi, [ecx+58h] + lea esi, [esi+eax*4] + mov eax, [esi] + push edi + mov edi, [esi+4] + shr eax, 9 + add edi, 1FFh + shr edi, 9 + lea esi, [eax+0Ah] + cmp esi, edi + jb short loc_1959FAD + mov edi, [ecx+28h] + lea esi, [eax+eax*2+3] + cmp edx, [edi+esi*4] + lea esi, [edi+esi*4] + jb short loc_1959FD4 + +loc_1959FA3: ; CODE XREF: sub_1959F50+59j + add esi, 0Ch + inc eax + cmp edx, [esi] + jnb short loc_1959FA3 + jmp short loc_1959FD4 +; --------------------------------------------------------------------------- + +loc_1959FAD: ; CODE XREF: sub_1959F50+42_j + lea esi, [eax+1] + cmp esi, edi + jnb short loc_1959FD4 + mov ecx, [ecx+28h] + +loc_1959FB7: ; CODE XREF: sub_1959F50+7Fj + lea esi, [edi+eax] + shr esi, 1 + lea ebx, [esi+esi*2] + cmp edx, [ecx+ebx*4] + jnb short loc_1959FC8 + mov edi, esi + jmp short loc_1959FCA +; --------------------------------------------------------------------------- + +loc_1959FC8: ; CODE XREF: sub_1959F50+72_j + mov eax, esi + +loc_1959FCA: ; CODE XREF: sub_1959F50+76_j + lea esi, [eax+1] + cmp esi, edi + jb short loc_1959FB7 + mov ecx, [ebp+var_4] + +loc_1959FD4: ; CODE XREF: sub_1959F50+51_j + ; sub_1959F50+5B_j ... + mov edi, [ecx+28h] + lea esi, [eax+eax*2] + sub edx, [edi+esi*4] + shl eax, 4 + lea esi, [edi+esi*4] + mov edi, eax + mov eax, [esi+4] + mov ebx, eax + shr ebx, 17h + cmp edx, ebx + jnb short loc_195A026 + mov esi, eax + shr esi, 7 + and esi, 0FFh + cmp edx, esi + jnb short loc_195A00E + and eax, 7Fh + cmp edx, eax + jb short loc_195A066 + add edi, 2 + sub edx, eax + jmp short loc_195A066 +; --------------------------------------------------------------------------- + +loc_195A00E: ; CODE XREF: sub_1959F50+AE_j + shr eax, 0Fh + and eax, 0FFh + cmp edx, eax + jnb short loc_195A01F + add edi, 4 + jmp short loc_195A064 +; --------------------------------------------------------------------------- + +loc_195A01F: ; CODE XREF: sub_1959F50+C8_j + add edi, 6 + sub edx, eax + jmp short loc_195A066 +; --------------------------------------------------------------------------- + +loc_195A026: ; CODE XREF: sub_1959F50+9F_j + mov esi, [esi+8] + mov eax, esi + shr eax, 9 + and eax, 1FFh + cmp edx, eax + jnb short loc_195A04D + and esi, 1FFh + cmp edx, esi + jnb short loc_195A048 + add edi, 8 + sub edx, ebx + jmp short loc_195A066 +; --------------------------------------------------------------------------- + +loc_195A048: ; CODE XREF: sub_1959F50+EF_j + add edi, 0Ah + jmp short loc_195A064 +; --------------------------------------------------------------------------- + +loc_195A04D: ; CODE XREF: sub_1959F50+E5_j + shr esi, 12h + and esi, 1FFh + cmp edx, esi + jnb short loc_195A061 + add edi, 0Ch + sub edx, eax + jmp short loc_195A066 +; --------------------------------------------------------------------------- + +loc_195A061: ; CODE XREF: sub_1959F50+108_j + add edi, 0Eh + +loc_195A064: ; CODE XREF: sub_1959F50+CD_j + ; sub_1959F50+FB_j + sub edx, esi + +loc_195A066: ; CODE XREF: sub_1959F50+B5_j + ; sub_1959F50+BC_j ... + mov ebx, [ecx+8] + mov esi, [ebx+edi*4] + mov eax, esi + shr eax, 1 + and eax, 55555555h + mov ecx, esi + and ecx, 55555555h + add eax, ecx + mov ecx, eax + shr ecx, 2 + and eax, 33333333h + and ecx, 33333333h + add ecx, eax + mov eax, ecx + shr eax, 4 + and ecx, 0F0F0F0Fh + and eax, 0F0F0F0Fh + add eax, ecx + imul eax, 1010101h + mov ecx, eax + shr ecx, 18h + cmp edx, ecx + jb short loc_195A0F6 + mov esi, [ebx+edi*4+4] + sub edx, ecx + inc edi + mov eax, esi + shr eax, 1 + and eax, 55555555h + mov ecx, esi + and ecx, 55555555h + add eax, ecx + mov ecx, eax + shr ecx, 2 + and eax, 33333333h + and ecx, 33333333h + add ecx, eax + mov eax, ecx + shr eax, 4 + and eax, 0F0F0F0Fh + and ecx, 0F0F0F0Fh + add eax, ecx + imul eax, 1010101h + +loc_195A0F6: ; CODE XREF: sub_1959F50+160_j + mov ecx, eax + shr ecx, 8 + and ecx, 0FFh + shl edi, 5 + cmp edx, ecx + jnb short loc_195A119 + and eax, 0FFh + cmp edx, eax + jb short loc_195A137 + add edi, 8 + shr esi, 8 + jmp short loc_195A135 +; --------------------------------------------------------------------------- + +loc_195A119: ; CODE XREF: sub_1959F50+1B6_j + shr eax, 10h + and eax, 0FFh + cmp edx, eax + jnb short loc_195A12F + add edi, 10h + shr esi, 10h + sub edx, ecx + jmp short loc_195A137 +; --------------------------------------------------------------------------- + +loc_195A12F: ; CODE XREF: sub_1959F50+1D3_j + add edi, 18h + shr esi, 18h + +loc_195A135: ; CODE XREF: sub_1959F50+1C7_j + sub edx, eax + +loc_195A137: ; CODE XREF: sub_1959F50+1BF_j + ; sub_1959F50+1DD_j + and esi, 0FFh + shl edx, 8 + movzx eax, ds:table_1BA1818[esi+edx] + add eax, edi + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 4 +sub_1959F50 endp + +; --------------------------------------------------------------------------- + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_1957B80 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+5E_p + ; TFileNameDatabase__CheckNextPathFragment+180_p ... + +pStruct40 = dword ptr -4 +arg_0 = dword ptr 8 +arg_4 = dword ptr 0Ch + + push ebp + mov ebp, esp + push ecx + push ebx + mov ebx, [ebp+arg_0] + mov eax, [ebx+18h] + push esi + push edi + mov edi, [ebp+arg_4] + mov esi, ecx + mov [ebp+pStruct40], eax + +loc_1957B95: ; CODE XREF: sub_1957B80+9Fj + ; sub_1957B80+156j + mov eax, [esi+TFileNameDatabase.NameFragIndexMask] + and eax, edi + lea ecx, [eax+eax*2] + mov eax, [esi+TFileNameDatabase.NameFragTable.ItemArray] + add ecx, ecx + add ecx, ecx + add eax, ecx + mov [ebp+arg_4], ecx + cmp edi, [eax+4] + jnz short loc_1957C30 + mov edx, [eax+8] + mov edi, edx + and edi, 0FFFFFF00h + cmp edi, 0FFFFFF00h + jz short loc_1957BEE + mov ecx, [esi+TFileNameDatabase.NextDB.pDatabase] + push edx + push ebx + test ecx, ecx + jz short loc_1957BDA + call sub_1957B80 + jmp short loc_1957BE5 +; --------------------------------------------------------------------------- + +loc_1957BDA: ; CODE XREF: sub_1957B80+51_j + lea ecx, [esi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckNameFragment + +loc_1957BE5: ; CODE XREF: sub_1957B80+58_j + test al, al + jz short loc_1957C25 + mov ecx, [ebp+arg_4] + jmp short loc_1957C05 +; --------------------------------------------------------------------------- + +loc_1957BEE: ; CODE XREF: sub_1957B80+45_j + mov edx, [ebp+pStruct40] + mov edi, [edx+34h] + mov edx, [ebx] + mov al, [eax+8] + cmp al, [edi+edx] + jnz short loc_1957C25 + mov edx, [ebp+pStruct40] + inc edi + mov [edx+34h], edi + +loc_1957C05: ; CODE XREF: sub_1957B80+6C_j + mov eax, [esi+200h] + mov edi, [ecx+eax] + test edi, edi + jz loc_1957CDB + mov ecx, [ebp+pStruct40] + mov edx, [ecx+34h] + cmp edx, [ebx+4] + jb loc_1957B95 + +loc_1957C25: ; CODE XREF: sub_1957B80+67_j + ; sub_1957B80+7C_j ... + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_1957C30: ; CODE XREF: sub_1957B80+32_j + mov edx, [esi+0D8h] + mov ecx, edi + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + mov eax, edi + shr eax, 5 + test [edx+eax*4], ebx + jz short loc_1957C8E + cmp dword ptr [esi+1F4h], 0 + push edi + mov ecx, esi + jz short loc_1957C73 + call sub_1957350 + mov ecx, [esi+1F4h] + mov ebx, [ebp+arg_0] + push eax + push ebx + test ecx, ecx + jz short loc_1957C7D + call sub_1957B80 + jmp short loc_1957C88 +; --------------------------------------------------------------------------- + +loc_1957C73: ; CODE XREF: sub_1957B80+D6_j + call sub_1957350 + mov ebx, [ebp+arg_0] + push eax + push ebx + +loc_1957C7D: ; CODE XREF: sub_1957B80+EA_j + lea ecx, [esi+174h] + call TNameIndexStruct__CheckNameFragment + +loc_1957C88: ; CODE XREF: sub_1957B80+F1_j + test al, al + jz short loc_1957C25 + jmp short loc_1957CB2 +; --------------------------------------------------------------------------- + +loc_1957C8E: ; CODE XREF: sub_1957B80+CA_j + mov eax, [ebp+pStruct40] + mov ecx, [esi+140h] + mov ebx, [ebp+arg_0] + mov eax, [eax+34h] + mov edx, [ebx] + mov cl, [edi+ecx] + cmp cl, [eax+edx] + jnz loc_1957C25 + mov edx, [ebp+pStruct40] + inc eax + mov [edx+34h], eax + +loc_1957CB2: ; CODE XREF: sub_1957B80+10C_j + cmp edi, [esi+214h] + jbe short loc_1957CDB + mov eax, [ebp+pStruct40] + mov ecx, [eax+34h] + cmp ecx, [ebx+4] + jnb loc_1957C25 + push edi + mov ecx, esi + call sub_1959F50 + sub eax, edi + lea edi, [eax-1] + jmp loc_1957B95 +; --------------------------------------------------------------------------- + +loc_1957CDB: ; CODE XREF: sub_1957B80+90_j + ; sub_1957B80+138_j + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 8 +sub_1957B80 endp + + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_19573D0 proc near ; CODE XREF: TFileNameDatabase__sub_1959460+1D9p + +arg_0 = dword ptr 8 +arg_4 = dword ptr 0Ch + + push ebp + mov ebp, esp + push esi + mov esi, ecx + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + mov eax, edx + imul eax, [ebp+arg_4] + mov ecx, eax + and eax, 1Fh + add edx, eax + shr ecx, 5 + cmp edx, 20h + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.ItemArray] + ja short loc_1957400 + mov edx, [edx+ecx*4] + mov ecx, eax + shr edx, cl + jmp short loc_1957419 +; --------------------------------------------------------------------------- + +loc_1957400: ; CODE XREF: sub_19573D0+25j + push edi + lea edi, [edx+ecx*4] + mov edx, [edi+4] + mov edi, [edi] + mov ecx, 20h + sub ecx, eax + shl edx, cl + mov ecx, eax + shr edi, cl + or edx, edi + pop edi + +loc_1957419: ; CODE XREF: sub_19573D0+2Ej + and edx, [esi+TFileNameDatabase.FrgmDist_HiBits.EntryBitMask] + mov eax, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov ecx, [ebp+arg_0] + movzx eax, byte ptr [eax+ecx] + shl edx, 8 + or eax, edx + pop esi + pop ebp + retn 8 +sub_19573D0 endp + +; --------------------------------------------------------------------------- + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TFileNameDatabase__CheckNextPathFragment proc near ; CODE XREF: TFileNameDatabase__FindFileInDatabase+25p + +var_C = dword ptr -0Ch +CollisionIndex = dword ptr -8 +var_4 = dword ptr -4 +pStruct1C = dword ptr 8 + + push ebp + mov ebp, esp + mov edx, [ebp+pStruct1C] + sub esp, 0Ch + push ebx + mov ebx, [edx+TMndxFindResult.szSearchMask] + push esi + push edi + mov edi, [edx+TMndxFindResult.pStruct40] + mov eax, [edi+TStruct40.CharIndex] + movzx eax, byte ptr [eax+ebx] ; EAX = szPathName[nCharIndex] + mov esi, ecx + mov ecx, [edi+TStruct40.HashValue] + mov ebx, ecx + shl ebx, 5 + xor eax, ebx + xor eax, ecx + and eax, [esi+TFileNameDatabase.NameFragIndexMask] ; (000000ff) - Mask value + lea ebx, [eax+eax*2] + mov eax, [esi+TFileNameDatabase.NameFragTable.ItemArray] ; (7f3ae128) - Array of 256 triplets + add ebx, ebx + add ebx, ebx ; EBX = Offset of the triplet we want to know + cmp ecx, [eax+ebx+TRIPLET.BitIndex] + jnz short loc_1957A0E + mov eax, [eax+ebx+TRIPLET.Distance] + mov ecx, eax + and ecx, 0FFFFFF00h + cmp ecx, 0FFFFFF00h + jz short loc_19579EF + mov ecx, [esi+TFileNameDatabase.NextDB.pDatabase] + push eax + push edx + test ecx, ecx + jz short loc_19579D5 + call sub_1957B80 + jmp short loc_19579E0 +; --------------------------------------------------------------------------- + +loc_19579D5: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+5C_j + lea ecx, [esi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckNameFragment + +loc_19579E0: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+63_j + test al, al + jnz short loc_19579F6 + +loc_19579E4: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+C1j + ; TFileNameDatabase__CheckNextPathFragment+1A4j + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_19579EF: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+50_j + mov eax, [edi+TStruct40.CharIndex] ; nCharIndex = nCharIndex + 1 + inc eax + mov [edi+TStruct40.CharIndex], eax + +loc_19579F6: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+72_j + mov edx, [esi+TFileNameDatabase.NameFragTable.ItemArray] ; EDX = pTripletArray + mov eax, [edx+ebx+TRIPLET.NextKey] + mov [edi+TStruct40.HashValue], eax + +loc_1957A03: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+198j + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1957A0E: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+3C_j + push ecx ; dwKey + mov ecx, esi ; pDatabase + call TArchiveDatabase__sub_1959CB0 + mov ebx, [esi+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + inc eax + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, eax + shr ecx, 5 + mov [ebp+CollisionIndex], eax + test [ebx+ecx*4], edx + jz short loc_19579E4 + sub eax, [edi+TStruct40.HashValue] + mov [ebp+var_4], 0FFFFFFFFh + dec eax + mov [edi+TStruct40.HashValue], eax + +loc_1957A41: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+1E1j + mov eax, [edi+TStruct40.HashValue] + mov ebx, [esi+TFileNameDatabase.Struct68_D0.ItemIsPresent.ItemArray] + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, eax + shr ecx, 5 + test [ebx+ecx*4], edx + jz loc_1957B1C + mov ecx, [ebp+var_4] + cmp ecx, 0FFFFFFFFh + jnz short loc_1957A7F + push eax + lea ecx, [esi+TFileNameDatabase.Struct68_D0] + call TSparseArray__GetItemValue + mov ecx, eax + mov [ebp+var_4], eax + jmp short loc_1957A83 +; --------------------------------------------------------------------------- + +loc_1957A7F: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+FA_j + inc ecx + mov [ebp+var_4], ecx + +loc_1957A83: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+10D_j + mov edx, [edi+TStruct40.CharIndex] + mov [ebp+var_C], edx + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + mov eax, edx + imul eax, ecx + mov ecx, eax + and eax, 1Fh + add edx, eax + shr ecx, 5 + cmp edx, 20h + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.ItemArray] + ja short loc_1957AB2 + mov edx, [edx+ecx*4] + mov ecx, eax + shr edx, cl + jmp short loc_1957AC9 +; --------------------------------------------------------------------------- + +loc_1957AB2: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+137_j + lea ebx, [edx+ecx*4] + mov edx, [ebx+4] + mov ebx, [ebx] + mov ecx, 20h + sub ecx, eax + shl edx, cl + mov ecx, eax + shr ebx, cl + or edx, ebx + +loc_1957AC9: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+140_j + mov ecx, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + and edx, [esi+TFileNameDatabase.FrgmDist_HiBits.EntryBitMask] + mov eax, [edi+TStruct40.HashValue] + movzx eax, byte ptr [ecx+eax] + mov ecx, [esi+TFileNameDatabase.NextDB.pDatabase] + shl edx, 8 + or eax, edx + push eax + test ecx, ecx + jz short loc_1957AF7 + mov edx, [ebp+pStruct1C] + push edx + call sub_1957B80 + jmp short loc_1957B06 +; --------------------------------------------------------------------------- + +loc_1957AF7: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+17A_j + mov eax, [ebp+pStruct1C] + push eax + lea ecx, [esi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckNameFragment + +loc_1957B06: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+185_j + test al, al + jnz loc_1957A03 + mov ecx, [ebp+var_C] + cmp [edi+TStruct40.CharIndex], ecx + jnz loc_19579E4 + jmp short loc_1957B32 +; --------------------------------------------------------------------------- + +loc_1957B1C: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+EE_j + mov edx, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov ebx, [ebp+pStruct1C] + mov ecx, [edi+TStruct40.CharIndex] + mov ebx, [ebx+TMndxFindResult.szSearchMask] + mov dl, [eax+edx] + cmp dl, [ecx+ebx] + jz short loc_1957B62 + +loc_1957B32: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+1AA_j + mov eax, [ebp+CollisionIndex] + inc [edi+TStruct40.HashValue] + inc eax + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, [esi+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + mov [ebp+CollisionIndex], eax + shr eax, 5 + test [ecx+eax*4], edx + jnz loc_1957A41 + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1957B62: ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+1C0_j + mov eax, 1 + add [edi+TStruct40.CharIndex], eax + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 4 +TFileNameDatabase__CheckNextPathFragment endp + + +TFileNameDatabase__FindFileInDatabase proc near + ; CODE XREF: MAR_FILE__FindFileInDatabase+2D_p + +pThis = dword ptr -4 +pStruct1C = dword ptr 8 + + push ebp ; ecx = Pointer to ARCHIVE_DATABASE + mov ebp, esp + push ecx + push ebx + push esi + mov esi, [ebp+pStruct1C] + xor eax, eax + push edi + mov edi, [esi+TMndxFindResult.pStruct40] + mov ebx, ecx + mov [edi+TStruct40.HashValue], eax + mov [edi+TStruct40.CharIndex], eax + mov [edi+TStruct40.SearchPhase], eax + mov [ebp+pThis], ebx + cmp [esi+TMndxFindResult.cchSearchMask], eax + jbe short loc_1957F26 + +label_process_all_characters: ; CODE XREF: TFileNameDatabase__FindFileInDatabase+34j + push esi + mov ecx, ebx + call TFileNameDatabase__CheckNextPathFragment + test al, al + jz short label_return_false + mov eax, [edi+TStruct40.CharIndex] + cmp eax, [esi+TMndxFindResult.cchSearchMask] + jb short label_process_all_characters + +loc_1957F26: ; CODE XREF: TFileNameDatabase__FindFileInDatabase+20_j + mov ecx, [edi+TStruct40.HashValue] + mov eax, [ebx+TFileNameDatabase.FileNameIndexes.ItemIsPresent.ItemArray] + mov edx, ecx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr edx, 5 + test [eax+edx*4], ebx + jz short label_return_false + mov ecx, [esi+TMndxFindResult.szSearchMask] + mov eax, [esi+TMndxFindResult.cchSearchMask] + mov [esi+TMndxFindResult.szFoundPath], ecx + mov ecx, [ebp+pThis] + mov [esi+TMndxFindResult.cchFoundPath], eax + mov edi, [edi+TStruct40.HashValue] + push edi + add ecx, TFileNameDatabase.FileNameIndexes + call TSparseArray__GetItemValue + pop edi + mov [esi+TMndxFindResult.FileNameIndex], eax + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +label_return_false: ; CODE XREF: TFileNameDatabase__FindFileInDatabase+2C_j + ; TFileNameDatabase__FindFileInDatabase+4E_j + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +TFileNameDatabase__FindFileInDatabase endp + + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TGenericArray__SetMaxItems_BYTE proc near ; CODE XREF: sub_19582E0+2Cp + ; TStruct40__InitSearchBuffers+2Cp ... + +ByteCount = dword ptr 8 + + push ebp + mov ebp, esp + push ebx + mov ebx, [ebp+ByteCount] + push esi + push edi + push offset unk_53ABE00 + push ebx + mov esi, ecx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + mov edi, eax + cmp [esi+TGenericArray.ItemCount], ecx + jbe short loc_19575D7 + +loc_19575C2: ; CODE XREF: TGenericArray__SetMaxItems_BYTE+35j + lea edx, [ecx+edi] + test edx, edx + jz short loc_19575D1 + mov eax, [esi+TGenericArray.field_4] + mov al, [ecx+eax] + mov [edx], al + +loc_19575D1: ; CODE XREF: TGenericArray__SetMaxItems_BYTE+27j + inc ecx + cmp ecx, [esi+TGenericArray.ItemCount] + jb short loc_19575C2 + +loc_19575D7: ; CODE XREF: TGenericArray__SetMaxItems_BYTE+20j + mov eax, [esi+TGenericArray.DataBuffer] + push eax + mov [esi+TGenericArray.DataBuffer], edi + mov [esi+TGenericArray.field_4], edi + mov [esi+TGenericArray.ItemArray], edi + mov [esi+TGenericArray.MaxItemCount], ebx + call operator_delete + add esp, 4 + pop edi + pop esi + pop ebx + pop ebp + retn 4 +TGenericArray__SetMaxItems_BYTE endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TGenericArray__SetMaxItems_STRUCT14 proc near + ; CODE XREF: TGenericArray__InsertItem_STRUCT14+2Cp + ; TGenericArray__sub_19583A0+2Dp ... + +var_4 = dword ptr -4 +ItemCount = dword ptr 8 + + push ebp + mov ebp, esp + push ecx + push esi + push edi + mov edi, [ebp+ItemCount] + lea eax, [edi+edi*4] ; EAX = (ItemCount * 5) + add eax, eax ; EAX = (ItemCount * 10) + add eax, eax ; EAX = (ItemCount * 20) + push offset unk_53ABE00 + push eax + mov esi, ecx + call j_operator_new_safe + mov ecx, eax + xor eax, eax + add esp, 8 + mov [ebp+var_4], ecx + cmp [esi+0Ch], eax + jbe short loc_195766A + xor edi, edi + mov edx, ecx + push ebx + +loc_1957631: ; CODE XREF: TGenericArray__SetMaxItems_STRUCT14+64j + test edx, edx + jz short loc_195765A + mov ecx, [esi+4] + mov ebx, [ecx+edi] + add ecx, edi + mov [edx], ebx + mov ebx, [ecx+4] + mov [edx+4], ebx + mov ebx, [ecx+8] + mov [edx+8], ebx + mov ebx, [ecx+0Ch] + mov [edx+0Ch], ebx + mov ecx, [ecx+10h] + mov [edx+10h], ecx + mov ecx, [ebp+var_4] + +loc_195765A: ; CODE XREF: TGenericArray__SetMaxItems_STRUCT14+33j + inc eax + add edi, 14h + add edx, 14h + cmp eax, [esi+0Ch] + jb short loc_1957631 + mov edi, [ebp+ItemCount] + pop ebx + +loc_195766A: ; CODE XREF: TGenericArray__SetMaxItems_STRUCT14+2Aj + mov eax, [esi+TGenericArray.DataBuffer] + push eax + mov [esi+TGenericArray.DataBuffer], ecx + mov [esi+TGenericArray.field_4], ecx + mov [esi+TGenericArray.ItemArray], ecx + mov [esi+TGenericArray.MaxItemCount], edi + call operator_delete + add esp, 4 + pop edi + pop esi + mov esp, ebp + pop ebp + retn 4 +TGenericArray__SetMaxItems_STRUCT14 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TGenericArray__sub_19583A0 proc near ; CODE XREF: TStruct40__InitSearchBuffers+35p + +NewItemCount = dword ptr 8 + + push ebp + mov ebp, esp + push esi + mov esi, ecx + mov eax, [esi+TGenericArray.MaxItemCount] + push edi + mov edi, [ebp+NewItemCount] + cmp edi, eax + jbe short loc_19583D2 + mov edx, edi + shr edx, 1 + mov ecx, edi + cmp eax, edx + jbe short loc_19583CA + mov ecx, 0CCCCCCCh + cmp eax, 6666666h + ja short loc_19583CA + lea ecx, [eax+eax] + +loc_19583CA: ; CODE XREF: TGenericArray__sub_19583A0+19j + ; TGenericArray__sub_19583A0+25j + push ecx + mov ecx, esi + call TGenericArray__SetMaxItems_STRUCT14 + +loc_19583D2: ; CODE XREF: TGenericArray__sub_19583A0+Fj + mov eax, [esi+TGenericArray.ItemCount] + cmp eax, edi + jnb short loc_1958410 + lea ecx, [eax+eax*4] + add ecx, ecx + mov edx, edi + push ebx + add ecx, ecx + sub edx, eax + or ebx, 0FFFFFFFFh + +loc_19583E8: ; CODE XREF: TGenericArray__sub_19583A0+6Dj + mov eax, [esi+TGenericArray.field_4] + add eax, ecx + jz short loc_1958409 + mov dword ptr [eax], 0 + mov dword ptr [eax+4], 0 + mov dword ptr [eax+8], 0 + mov [eax+0Ch], ebx + mov [eax+10h], ebx + +loc_1958409: ; CODE XREF: TGenericArray__sub_19583A0+4Dj + add ecx, 14h + dec edx + jnz short loc_19583E8 + pop ebx + +loc_1958410: ; CODE XREF: TGenericArray__sub_19583A0+37j + mov [esi+TGenericArray.ItemCount], edi + pop edi + pop esi + pop ebp + retn 4 +TGenericArray__sub_19583A0 endp + +; =============== S U B R O U T I N E ======================================= + + +TStruct40__InitSearchBuffers proc near ; CODE XREF: TFileNameDatabase__sub_1959460+2Bp + push ebx + push esi + mov esi, ecx + mov eax, [esi+TStruct40.array_00.MaxItemCount] + xor ebx, ebx + push edi + mov [esi+TStruct40.array_00.ItemCount], ebx + cmp eax, 40h + jnb short loc_19586E1 + lea ecx, [ebx+40h] ; ECX = 0x40 + cmp eax, 20h + jbe short loc_19586D9 + cmp eax, 7FFFFFFFh + jbe short loc_19586D6 + or ecx, 0FFFFFFFFh + jmp short loc_19586D9 +; --------------------------------------------------------------------------- + +loc_19586D6: ; CODE XREF: TStruct40__InitSearchBuffers+1Fj + lea ecx, [eax+eax] + +loc_19586D9: ; CODE XREF: TStruct40__InitSearchBuffers+18j + ; TStruct40__InitSearchBuffers+24j + push ecx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_19586E1: ; CODE XREF: TStruct40__InitSearchBuffers+10j + push ebx + lea ecx, [esi+TStruct40.array_18] + call TGenericArray__sub_19583A0 + mov eax, [esi+TStruct40.array_18.MaxItemCount] + cmp eax, 4 + jnb short loc_1958714 + mov ecx, 4 + cmp eax, 2 + jbe short loc_195870B + mov ecx, 0CCCCCCCh + cmp eax, 6666666h + ja short loc_195870B + lea ecx, [eax+eax] + +loc_195870B: ; CODE XREF: TStruct40__InitSearchBuffers+4Aj + ; TStruct40__InitSearchBuffers+56j + push ecx + lea ecx, [esi+TStruct40.array_18] + call TGenericArray__SetMaxItems_STRUCT14 + +loc_1958714: ; CODE XREF: TStruct40__InitSearchBuffers+40j + pop edi + mov [esi+TStruct40.HashValue], ebx + mov [esi+TStruct40.CharIndex], ebx + mov [esi+TStruct40.ItemCount], ebx + mov [esi+TStruct40.SearchPhase], 2 + pop esi + pop ebx + retn +TStruct40__InitSearchBuffers endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TGenericArray__InsertItem_STRUCT14 proc near + ; CODE XREF: TFileNameDatabase__sub_1959460+73p + +pStruct14 = dword ptr 8 + + push ebp + mov ebp, esp + push esi + mov esi, ecx + mov eax, [esi+TGenericArray.ItemCount] + mov ecx, [esi+TGenericArray.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_1958361 + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_1958359 + mov edx, 0CCCCCCCh + cmp ecx, 6666666h + ja short loc_1958359 + lea edx, [ecx+ecx] + +loc_1958359: ; CODE XREF: TGenericArray__InsertItem_STRUCT14+17j + ; TGenericArray__InsertItem_STRUCT14+24j + push edx + mov ecx, esi + call TGenericArray__SetMaxItems_STRUCT14 + +loc_1958361: ; CODE XREF: TGenericArray__InsertItem_STRUCT14+Fj + mov eax, [esi+TGenericArray.ItemCount] + mov ecx, [esi+TGenericArray.field_4] + lea eax, [eax+eax*4] + lea eax, [ecx+eax*4] + test eax, eax + jz short loc_1958390 + mov ecx, [ebp+pStruct14] + mov edx, [ecx+TStruct14.HashValue] + mov [eax+TStruct14.HashValue], edx + mov edx, [ecx+TStruct14.field_4] + mov [eax+TStruct14.field_4], edx + mov edx, [ecx+TStruct14.field_8] + mov [eax+TStruct14.field_8], edx + mov edx, [ecx+TStruct14.field_C] + mov [eax+TStruct14.field_C], edx + mov ecx, [ecx+TStruct14.field_10] + mov [eax+TStruct14.field_10], ecx + +loc_1958390: ; CODE XREF: TGenericArray__InsertItem_STRUCT14+3Fj + inc [esi+TGenericArray.ItemCount] + pop esi + pop ebp + retn 4 +TGenericArray__InsertItem_STRUCT14 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +CopyNameFragment proc near ; CODE XREF: sub_1958980+DAp + ; sub_1958D70+6Dp ... + +pThis = dword ptr -4 +pStruct1C = dword ptr 8 +dwDistance = dword ptr 0Ch + + push ebp + mov ebp, esp + push ecx + mov eax, [ebp+pStruct1C] + push ebx + mov ebx, ecx + cmp [ebx+TNameIndexStruct.Struct68.TotalItemCount], 0 + push esi + mov esi, [eax+TMndxFindResult.pStruct40] + push edi + mov [ebp+pThis], ebx + jnz loc_195A4B3 + mov ebx, [ebx+TNameIndexStruct.NameFragments.ItemArray] + add ebx, [ebp+dwDistance] + cmp byte ptr [ebx], 0 + mov [ebp+dwDistance], ebx + jz loc_195A55E + mov edi, edi + +loc_195A420: ; CODE XREF: CopyNameFragment+B4j + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_195A48E + mov ebx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A443 + cmp ecx, 7FFFFFFFh + jbe short loc_195A440 + or ebx, 0FFFFFFFFh + jmp short loc_195A443 +; --------------------------------------------------------------------------- + +loc_195A440: ; CODE XREF: CopyNameFragment+49j + lea ebx, [ecx+ecx] + +loc_195A443: ; CODE XREF: CopyNameFragment+41j + ; CopyNameFragment+4Ej + push offset unk_53ABE00 + push ebx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + mov edi, eax + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_195A475 + lea ebx, [ebx+0] + +loc_195A460: ; CODE XREF: CopyNameFragment+83j + lea edx, [ecx+edi] + test edx, edx + jz short loc_195A46F + mov eax, [esi+TStruct40.array_00.field_4] + mov al, [ecx+eax] + mov [edx], al + +loc_195A46F: ; CODE XREF: CopyNameFragment+75j + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_195A460 + +loc_195A475: ; CODE XREF: CopyNameFragment+68j + mov eax, [esi+TStruct40.array_00.DataBuffer] + push eax + mov [esi+TStruct40.array_00.DataBuffer], edi + mov [esi+TStruct40.array_00.field_4], edi + mov [esi+TStruct40.array_00.ItemArray], edi + mov [esi+TStruct40.array_00.MaxItemCount], ebx + call operator_delete + mov ebx, [ebp+dwDistance] + add esp, 4 + +loc_195A48E: ; CODE XREF: CopyNameFragment+39j + mov eax, [esi+TStruct40.array_00.ItemCount] + add eax, [esi+TStruct40.array_00.field_4] + jz short loc_195A49A + mov cl, [ebx] + mov [eax], cl + +loc_195A49A: ; CODE XREF: CopyNameFragment+A4j + inc [esi+TStruct40.array_00.ItemCount] + inc ebx + cmp byte ptr [ebx], 0 + mov [ebp+dwDistance], ebx + jnz loc_195A420 + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_195A4B3: ; CODE XREF: CopyNameFragment+16j + ; CopyNameFragment+168j + mov eax, [esi+TStruct40.array_00.ItemCount] + mov edx, [ebx+TNameIndexStruct.NameFragments.ItemArray] + mov edi, [ebp+dwDistance] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + add edx, edi + inc eax + mov [ebp+pStruct1C], edx + cmp eax, ecx + jbe short loc_195A52C + mov ebx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A4E1 + cmp ecx, 7FFFFFFFh + jbe short loc_195A4DE + or ebx, 0FFFFFFFFh + jmp short loc_195A4E1 +; --------------------------------------------------------------------------- + +loc_195A4DE: ; CODE XREF: CopyNameFragment+E7j + lea ebx, [ecx+ecx] + +loc_195A4E1: ; CODE XREF: CopyNameFragment+DFj + ; CopyNameFragment+ECj + push offset unk_53ABE00 + push ebx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + mov edi, eax + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_195A50D + +loc_195A4F8: ; CODE XREF: CopyNameFragment+11Bj + lea edx, [ecx+edi] + test edx, edx + jz short loc_195A507 + mov eax, [esi+TStruct40.array_00.field_4] + mov al, [ecx+eax] + mov [edx], al + +loc_195A507: ; CODE XREF: CopyNameFragment+10Dj + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_195A4F8 + +loc_195A50D: ; CODE XREF: CopyNameFragment+106j + mov eax, [esi+TStruct40.array_00.DataBuffer] + push eax + mov [esi+TStruct40.array_00.DataBuffer], edi + mov [esi+TStruct40.array_00.field_4], edi + mov [esi+TStruct40.array_00.ItemArray], edi + mov [esi+TStruct40.array_00.MaxItemCount], ebx + call operator_delete + mov edx, [ebp+pStruct1C] + mov edi, [ebp+dwDistance] + mov ebx, [ebp+pThis] + add esp, 4 + +loc_195A52C: ; CODE XREF: CopyNameFragment+D7j + mov eax, [esi+TStruct40.array_00.ItemCount] + add eax, [esi+TStruct40.array_00.field_4] + jz short loc_195A538 + mov cl, [edx] + mov [eax], cl + +loc_195A538: ; CODE XREF: CopyNameFragment+142j + inc [esi+TStruct40.array_00.ItemCount] + mov ecx, edi + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, [ebx+TNameIndexStruct.Struct68.ItemIsPresent.ItemArray] + mov eax, edi + shr eax, 5 + and edx, [ecx+eax*4] + inc edi + mov [ebp+dwDistance], edi + test edx, edx + jz loc_195A4B3 + +loc_195A55E: ; CODE XREF: CopyNameFragment+28j + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 8 +CopyNameFragment endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_1958D70 proc near ; CODE XREF: sub_1958980+C9p + ; sub_1958D70+59p ... + +var_C = dword ptr -0Ch +var_8 = dword ptr -8 +var_1 = byte ptr -1 +pStruct1C = dword ptr 8 +arg_4 = dword ptr 0Ch + + push ebp + mov ebp, esp + mov eax, [ebp+pStruct1C] + sub esp, 0Ch + push ebx + push esi + mov esi, [eax+TMndxFindResult.pStruct40] + push edi + mov edi, ecx + mov ecx, [ebp+arg_4] + +loc_1958D84: ; CODE XREF: sub_1958D70+10Fj + ; sub_1958D70+28Fj + mov eax, [edi+TFileNameDatabase.NameFragIndexMask] + and eax, ecx + lea ebx, [eax+eax*2] + mov eax, [edi+TFileNameDatabase.NameFragTable.ItemArray] + add ebx, ebx + add ebx, ebx + mov [ebp+var_C], ebx + cmp ecx, [eax+ebx+4] + jnz loc_1958E8E + mov edx, [eax+ebx+8] + mov ecx, edx + and ecx, 0FFFFFF00h + cmp ecx, 0FFFFFF00h + jz short loc_1958DE7 + mov ecx, [edi+TFileNameDatabase.NextDB.pDatabase] + push edx + test ecx, ecx + jz short loc_1958DD3 + mov edx, [ebp+pStruct1C] + push edx + call sub_1958D70 + jmp loc_1958E71 +; --------------------------------------------------------------------------- + +loc_1958DD3: ; CODE XREF: sub_1958D70+53j + mov eax, [ebp+pStruct1C] + push eax + lea ecx, [edi+TFileNameDatabase.IndexStruct_174] + call CopyNameFragment + jmp loc_1958E71 +; --------------------------------------------------------------------------- + +loc_1958DE7: ; CODE XREF: sub_1958D70+48j + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + mov byte ptr [ebp+arg_4+3], dl + cmp eax, ecx + jbe short loc_1958E61 + mov [ebp+var_8], eax + shr eax, 1 + cmp ecx, eax + jbe short loc_1958E15 + cmp ecx, 7FFFFFFFh + jbe short loc_1958E0F + mov [ebp+var_8], 0FFFFFFFFh + jmp short loc_1958E15 +; --------------------------------------------------------------------------- + +loc_1958E0F: ; CODE XREF: sub_1958D70+94j + lea edx, [ecx+ecx] + mov [ebp+var_8], edx + +loc_1958E15: ; CODE XREF: sub_1958D70+8Cj + ; sub_1958D70+9Dj + mov eax, [ebp+var_8] + push offset unk_53ABE00 + push eax + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_1958E48 + lea ecx, [ecx+0] + +loc_1958E30: ; CODE XREF: sub_1958D70+D6j + lea edx, [ecx+eax] + test edx, edx + jz short loc_1958E42 + mov ebx, [esi+4] + mov bl, [ecx+ebx] + mov [edx], bl + mov ebx, [ebp+var_C] + +loc_1958E42: ; CODE XREF: sub_1958D70+C5j + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_1958E30 + +loc_1958E48: ; CODE XREF: sub_1958D70+BBj + mov ecx, [esi] + mov edx, [ebp+var_8] + push ecx + mov [esi+TStruct40.array_00.DataBuffer], eax + mov [esi+TStruct40.array_00.field_4], eax + mov [esi+TStruct40.array_00.ItemArray], eax + mov [esi+TStruct40.array_00.MaxItemCount], edx + call operator_delete + add esp, 4 + +loc_1958E61: ; CODE XREF: sub_1958D70+83j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_1958E6E + mov cl, byte ptr [ebp+arg_4+3] + mov [eax], cl + +loc_1958E6E: ; CODE XREF: sub_1958D70+F7j + inc [esi+TStruct40.array_00.ItemCount] + +loc_1958E71: ; CODE XREF: sub_1958D70+5Ej + ; sub_1958D70+72j + mov edx, [edi+TFileNameDatabase.NameFragTable.ItemArray] + mov ecx, [ebx+edx] + mov [ebp+arg_4], ecx + test ecx, ecx + jnz loc_1958D84 + +loc_1958E85: ; CODE XREF: sub_1958D70+277j + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_1958E8E: ; CODE XREF: sub_1958D70+30j + mov edx, [edi+TFileNameDatabase.Struct68_D0.ItemIsPresent.ItemArray] + mov eax, ecx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr eax, 5 + test [edx+eax*4], ebx + mov eax, [ebp+arg_4] + jz loc_1958F50 + mov ebx, [edi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + push eax + lea ecx, [edi+TFileNameDatabase.Struct68_D0] + add ebx, eax + call TSparseArray__GetItemValue + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + imul ecx, eax + mov eax, ecx + and ecx, 1Fh + mov edx, ecx + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + add ecx, edx + shr eax, 5 + cmp ecx, 20h + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.ItemArray] + ja short loc_1958EF2 + mov eax, [ecx+eax*4] + mov ecx, edx + shr eax, cl + jmp short loc_1958F15 +; --------------------------------------------------------------------------- + +loc_1958EF2: ; CODE XREF: sub_1958D70+177j + lea eax, [ecx+eax*4] + mov [ebp+var_C], eax + mov eax, [eax+4] + mov ecx, 20h + sub ecx, edx + shl eax, cl + mov ecx, [ebp+var_C] + mov ecx, [ecx] + mov [ebp+var_C], ecx + mov ecx, edx + mov edx, [ebp+var_C] + shr edx, cl + or eax, edx + +loc_1958F15: ; CODE XREF: sub_1958D70+180j + and eax, [edi+TFileNameDatabase.FrgmDist_HiBits.EntryBitMask] + movzx edx, byte ptr [ebx] + mov ecx, [edi+TFileNameDatabase.NextDB.pDatabase] + shl eax, 8 + or eax, edx + push eax + test ecx, ecx + jz short loc_1958F3C + mov eax, [ebp+pStruct1C] + push eax + call sub_1958D70 + jmp loc_1958FDE +; --------------------------------------------------------------------------- + +loc_1958F3C: ; CODE XREF: sub_1958D70+1BCj + mov ecx, [ebp+pStruct1C] + push ecx + lea ecx, [edi+TFileNameDatabase.IndexStruct_174] + call CopyNameFragment + jmp loc_1958FDE +; --------------------------------------------------------------------------- + +loc_1958F50: ; CODE XREF: sub_1958D70+139j + mov edx, [edi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov cl, [eax+edx] + mov eax, [esi+TStruct40.array_00.ItemCount] + mov [ebp+var_1], cl + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_1958FCE + mov ebx, eax + shr eax, 1 + mov [ebp+var_8], ebx + cmp ecx, eax + jbe short loc_1958F85 + cmp ecx, 7FFFFFFFh + jbe short loc_1958F7F + or ebx, 0FFFFFFFFh + jmp short loc_1958F82 +; --------------------------------------------------------------------------- + +loc_1958F7F: ; CODE XREF: sub_1958D70+208j + lea ebx, [ecx+ecx] + +loc_1958F82: ; CODE XREF: sub_1958D70+20Dj + mov [ebp+var_8], ebx + +loc_1958F85: ; CODE XREF: sub_1958D70+200j + push offset unk_53ABE00 + push ebx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_1958FB8 + lea ebx, [ebx+0] + +loc_1958FA0: ; CODE XREF: sub_1958D70+243j + lea edx, [ecx+eax] + test edx, edx + jz short loc_1958FAF + mov ebx, [esi+TStruct40.array_00.field_4] + mov bl, [ecx+ebx] + mov [edx], bl + +loc_1958FAF: ; CODE XREF: sub_1958D70+235j + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_1958FA0 + mov ebx, [ebp+var_8] + +loc_1958FB8: ; CODE XREF: sub_1958D70+228j + mov ecx, [esi+TStruct40.array_00.DataBuffer] + push ecx + mov [esi+TStruct40.array_00.DataBuffer], eax + mov [esi+TStruct40.array_00.field_4], eax + mov [esi+TStruct40.array_00.ItemArray], eax + mov [esi+TStruct40.array_00.MaxItemCount], ebx + call operator_delete + add esp, 4 + +loc_1958FCE: ; CODE XREF: sub_1958D70+1F5j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_1958FDB + mov dl, [ebp+var_1] + mov [eax], dl + +loc_1958FDB: ; CODE XREF: sub_1958D70+264j + inc [esi+TStruct40.array_00.ItemCount] + +loc_1958FDE: ; CODE XREF: sub_1958D70+1C7j + ; sub_1958D70+1DBj + mov ebx, [ebp+arg_4] + cmp ebx, [edi+TFileNameDatabase.field_214] + jbe loc_1958E85 + push ebx + mov ecx, edi + call sub_1959F50 + or ecx, 0FFFFFFFFh + sub ecx, ebx + add ecx, eax + mov [ebp+arg_4], ecx + jmp loc_1958D84 +sub_1958D70 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TFileNameDatabase__sub_1959CB0 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1p + ; TFileNameDatabase__sub_1958B00+E8p ... + +pThis = dword ptr -4 +dwKey = dword ptr 8 + + push ebp + mov ebp, esp + push ecx + mov edx, [ebp+dwKey] + mov eax, edx + shr eax, 9 + mov [ebp+pThis], ecx + test edx, 1FFh ; if(dwKey & 0x01FF) + jnz short loc_1959CD3 + mov ecx, [ecx+TFileNameDatabase.Struct68_00.ArrayDwords_38.ItemArray] + mov eax, [ecx+eax*4] ; EAX = pDatabase->NextKeyTable[dwKey >> 0x09] + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1959CD3: ; CODE XREF: TFileNameDatabase__sub_1959CB0+15j + push ebx + push esi + mov esi, [ecx+TFileNameDatabase.Struct68_00.ArrayDwords_38.ItemArray] + lea esi, [esi+eax*4] + mov eax, [esi] + mov esi, [esi+4] + shr eax, 9 + add esi, 1FFh + push edi + shr esi, 9 + lea edi, [eax+0Ah] + mov [ebp+dwKey], esi + cmp edi, esi + jb short loc_1959D2E + mov edi, [ecx+TFileNameDatabase.Struct68_00.ArrayTriplets_20.ItemArray] + lea esi, [eax+eax*2+3] + lea esi, [edi+esi*4] + mov edi, eax + shl edi, 9 + mov ebx, edi + sub ebx, [esi] + add ebx, 200h + cmp edx, ebx + jb short loc_1959D5F + +loc_1959D14: ; CODE XREF: TFileNameDatabase__sub_1959CB0+7Aj + add edi, 200h + add esi, 0Ch + mov ebx, edi + sub ebx, [esi] + inc eax + add ebx, 200h + cmp edx, ebx + jnb short loc_1959D14 + jmp short loc_1959D5F +; --------------------------------------------------------------------------- + +loc_1959D2E: ; CODE XREF: TFileNameDatabase__sub_1959CB0+45j + lea edi, [eax+1] + cmp edi, esi + jnb short loc_1959D5F + mov ecx, [ecx+TFileNameDatabase.Struct68_00.ArrayTriplets_20.ItemArray] + +loc_1959D38: ; CODE XREF: TFileNameDatabase__sub_1959CB0+AAj + add esi, eax + shr esi, 1 + mov ebx, esi + shl ebx, 9 + lea edi, [esi+esi*2] + sub ebx, [ecx+edi*4] + cmp edx, ebx + jnb short loc_1959D50 + mov [ebp+dwKey], esi + jmp short loc_1959D55 +; --------------------------------------------------------------------------- + +loc_1959D50: ; CODE XREF: TFileNameDatabase__sub_1959CB0+99j + mov eax, esi + mov esi, [ebp+dwKey] + +loc_1959D55: ; CODE XREF: TFileNameDatabase__sub_1959CB0+9Ej + lea edi, [eax+1] + cmp edi, esi + jb short loc_1959D38 + mov ecx, [ebp+pThis] + +loc_1959D5F: ; CODE XREF: TFileNameDatabase__sub_1959CB0+62j + ; TFileNameDatabase__sub_1959CB0+7Cj ... + mov ecx, [ecx+TFileNameDatabase.Struct68_00.ArrayTriplets_20.ItemArray] + lea esi, [eax+eax*2] + mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax] + lea esi, [ecx+esi*4] ; ESI = Struct68_00.ArrayTriplets_20.ItemArray + eax + mov ecx, eax + shl ecx, 9 + sub edi, ecx + shl eax, 4 + add edx, edi + mov edi, eax + mov eax, [esi+TRIPLET.NextKey] + mov ecx, eax + shr ecx, 17h + mov ebx, 100h + sub ebx, ecx + cmp edx, ebx + jnb short loc_1959DE8 + mov ecx, eax + shr ecx, 7 + and ecx, 0FFh + mov esi, 80h + sub esi, ecx + cmp edx, esi + jnb short loc_1959DC0 + and eax, 7Fh + mov ecx, 40h + sub ecx, eax + cmp edx, ecx + jb loc_1959E53 + add edi, 2 + lea edx, [edx+eax-40h] + jmp loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DC0: ; CODE XREF: TFileNameDatabase__sub_1959CB0+F0j + shr eax, 0Fh + and eax, 0FFh + mov esi, 0C0h + sub esi, eax + cmp edx, esi + jnb short loc_1959DDC + add edi, 4 + lea edx, [edx+ecx-80h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DDC: ; CODE XREF: TFileNameDatabase__sub_1959CB0+121j + add edi, 6 + lea edx, [edx+eax-0C0h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959DE8: ; CODE XREF: TFileNameDatabase__sub_1959CB0+DAj + mov esi, [esi+TRIPLET.Distance] + mov eax, esi + shr eax, 9 + and eax, 1FFh + mov ebx, 180h + sub ebx, eax + cmp edx, ebx + jnb short loc_1959E29 + and esi, 1FFh + mov eax, 140h + sub eax, esi + cmp edx, eax + jnb short loc_1959E1D + add edi, 8 + lea edx, [edx+ecx-100h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E1D: ; CODE XREF: TFileNameDatabase__sub_1959CB0+15Fj + add edi, 0Ah + lea edx, [edx+esi-140h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E29: ; CODE XREF: TFileNameDatabase__sub_1959CB0+14Ej + shr esi, 12h + and esi, 1FFh + mov ecx, 1C0h + sub ecx, esi + cmp edx, ecx + jnb short loc_1959E49 + add edi, 0Ch + lea edx, [edx+eax-180h] + jmp short loc_1959E53 +; --------------------------------------------------------------------------- + +loc_1959E49: ; CODE XREF: TFileNameDatabase__sub_1959CB0+18Bj + add edi, 0Eh + lea edx, [edx+esi-1C0h] + +loc_1959E53: ; CODE XREF: TFileNameDatabase__sub_1959CB0+FEj + ; TFileNameDatabase__sub_1959CB0+10Bj ... + mov eax, [ebp+pThis] + mov ebx, [eax+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + mov ecx, [ebx+edi*4] + not ecx + mov eax, ecx + shr eax, 1 + and eax, 55555555h + mov esi, ecx + and esi, 55555555h + add eax, esi + mov esi, eax + shr esi, 2 + and eax, 33333333h + and esi, 33333333h + add esi, eax + mov eax, esi + shr eax, 4 + and esi, 0F0F0F0Fh + and eax, 0F0F0F0Fh + add eax, esi + imul eax, 1010101h + mov esi, eax + shr esi, 18h + cmp edx, esi + jb short loc_1959EEA + mov ecx, [ebx+edi*4+4] + inc edi + sub edx, esi + not ecx + mov eax, ecx + shr eax, 1 + and eax, 55555555h + mov esi, ecx + and esi, 55555555h + add eax, esi + mov esi, eax + shr esi, 2 + and eax, 33333333h + and esi, 33333333h + add esi, eax + mov eax, esi + shr eax, 4 + and eax, 0F0F0F0Fh + and esi, 0F0F0F0Fh + add eax, esi + imul eax, 1010101h + +loc_1959EEA: ; CODE XREF: TFileNameDatabase__sub_1959CB0+1F2j + mov esi, eax + shr esi, 8 + and esi, 0FFh + shl edi, 5 + cmp edx, esi + jnb short loc_1959F0D + and eax, 0FFh + cmp edx, eax + jb short loc_1959F2B + add edi, 8 + shr ecx, 8 + jmp short loc_1959F29 +; --------------------------------------------------------------------------- + +loc_1959F0D: ; CODE XREF: TFileNameDatabase__sub_1959CB0+24Aj + shr eax, 10h + and eax, 0FFh + cmp edx, eax + jnb short loc_1959F23 + add edi, 10h + shr ecx, 10h + sub edx, esi + jmp short loc_1959F2B +; --------------------------------------------------------------------------- + +loc_1959F23: ; CODE XREF: TFileNameDatabase__sub_1959CB0+267j + add edi, 18h + shr ecx, 18h + +loc_1959F29: ; CODE XREF: TFileNameDatabase__sub_1959CB0+25Bj + sub edx, eax + +loc_1959F2B: ; CODE XREF: TFileNameDatabase__sub_1959CB0+253j + ; TFileNameDatabase__sub_1959CB0+271j + and ecx, 0FFh + shl edx, 8 + movzx eax, ds:table_1BA1818[ecx+edx] + add eax, edi + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 4 +TFileNameDatabase__sub_1959CB0 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TNameIndexStruct__CheckAndCopyNameFragment proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+74p + ; TArchiveDatabase__sub_1958B00+1E0p ... + +var_C = dword ptr -0Ch +pThis = dword ptr -8 +var_4 = dword ptr -4 +pStruct1C = dword ptr 8 +dwDistance = dword ptr 0Ch + + push ebp + mov ebp, esp + sub esp, 0Ch + mov eax, [ebp+pStruct1C] + push ebx + mov edx, ecx + cmp [edx+TNameIndexStruct.Struct68.TotalItemCount], 0 + push esi + mov esi, [eax+TMndxFindResult.pStruct40] + push edi + mov [ebp+pThis], edx + jnz loc_195A6B7 + mov edi, [edx+TNameIndexStruct.NameFragments.ItemArray] + sub edi, [esi+TStruct40.CharIndex] + add edi, [ebp+dwDistance] + mov [ebp+pThis], edi + lea ebx, [ebx+0] + +loc_195A5A0: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+E6j + mov edx, [esi+TStruct40.CharIndex] + mov ecx, [ebp+pStruct1C] + mov eax, [ecx+TMndxFindResult.szSearchMask] + mov cl, [edx+edi] + mov [ebp+dwDistance], edx + cmp cl, [edx+eax] + jnz loc_195A67D + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + mov ebx, 1 + add eax, ebx + cmp eax, ecx + jbe short loc_195A62D + mov ebx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A5E0 + cmp ecx, 7FFFFFFFh + jbe short loc_195A5DD + or ebx, 0FFFFFFFFh + jmp short loc_195A5E0 +; --------------------------------------------------------------------------- + +loc_195A5DD: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+66j + lea ebx, [ecx+ecx] + +loc_195A5E0: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+5Ej + ; TNameIndexStruct__CheckAndCopyNameFragment+6Bj + push offset unk_53ABE00 + push ebx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + mov edi, eax + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_195A60C + +loc_195A5F7: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+9Aj + lea edx, [ecx+edi] + test edx, edx + jz short loc_195A606 + mov eax, [esi+TStruct40.array_00.field_4] + mov al, [ecx+eax] + mov [edx], al + +loc_195A606: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+8Cj + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_195A5F7 + +loc_195A60C: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+85j + mov eax, [esi+TStruct40.array_00.DataBuffer] + push eax + mov [esi+TStruct40.array_00.DataBuffer], edi + mov [esi+TStruct40.array_00.field_4], edi + mov [esi+TStruct40.array_00.ItemArray], edi + mov [esi+TStruct40.array_00.MaxItemCount], ebx + call operator_delete + mov edx, [ebp+dwDistance] + mov edi, [ebp+pThis] + add esp, 4 + mov ebx, 1 + +loc_195A62D: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+56j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_195A63A + mov cl, [edx+edi] + mov [eax], cl + +loc_195A63A: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+C3j + add [esi+TStruct40.CharIndex], ebx + add [esi+TStruct40.array_00.ItemCount], ebx + mov ecx, [esi+TStruct40.CharIndex] + cmp byte ptr [ecx+edi], 0 + mov eax, [esi+TStruct40.array_00.ItemCount] + jz loc_195A806 + mov edx, [ebp+pStruct1C] + cmp ecx, [edx+TMndxFindResult.cchSearchMask] + jb loc_195A5A0 + add edi, ecx + mov edi, edi + +loc_195A660: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+13Aj + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_195A693 + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A68B + cmp ecx, 7FFFFFFFh + jbe short loc_195A688 + or edx, 0FFFFFFFFh + jmp short loc_195A68B +; --------------------------------------------------------------------------- + +loc_195A67D: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+41j + ; TNameIndexStruct__CheckAndCopyNameFragment+16Bj + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_195A688: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+106j + lea edx, [ecx+ecx] + +loc_195A68B: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+FEj + ; TNameIndexStruct__CheckAndCopyNameFragment+10Bj + push edx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_195A693: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+F6j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_195A69F + mov cl, [edi] + mov [eax], cl + +loc_195A69F: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+129j + add [esi+TStruct40.array_00.ItemCount], ebx + mov eax, [esi+TStruct40.array_00.ItemCount] + add edi, ebx + cmp byte ptr [edi], 0 + jnz short loc_195A660 + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_195A6B7: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+18j + mov ebx, [ebp+dwDistance] + lea ebx, [ebx+0] + +loc_195A6C0: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+231j + mov edi, [edx+TNameIndexStruct.NameFragments.ItemArray] + mov eax, [ebp+pStruct1C] + mov ecx, [esi+TStruct40.CharIndex] + mov eax, [eax] + add edi, ebx + mov [ebp+var_4], edx + mov dl, [edi] + cmp dl, [ecx+eax] + mov edx, [ebp+var_4] + mov [ebp+var_C], edi + jnz short loc_195A67D + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_195A752 + mov [ebp+var_4], eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A707 + cmp ecx, 7FFFFFFFh + jbe short loc_195A702 + mov [ebp+var_4], 0FFFFFFFFh + jmp short loc_195A707 +; --------------------------------------------------------------------------- + +loc_195A702: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+187j + add ecx, ecx + mov [ebp+var_4], ecx + +loc_195A707: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+17Fj + ; TNameIndexStruct__CheckAndCopyNameFragment+190j + mov edx, [ebp+var_4] + push offset unk_53ABE00 + push edx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + mov edi, eax + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_195A736 + +loc_195A721: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+1C4j + lea edx, [ecx+edi] + test edx, edx + jz short loc_195A730 + mov eax, [esi+TStruct40.array_00.field_4] + mov al, [ecx+eax] + mov [edx], al + +loc_195A730: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+1B6j + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_195A721 + +loc_195A736: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+1AFj + mov eax, [esi+TStruct40.array_00.DataBuffer] + mov ecx, [ebp+var_4] + push eax + mov [esi+TStruct40.array_00.DataBuffer], edi + mov [esi+TStruct40.array_00.field_4], edi + mov [esi+TStruct40.array_00.ItemArray], edi + mov [esi+TStruct40.array_00.MaxItemCount], ecx + call operator_delete + mov edx, [ebp+pThis] + add esp, 4 + +loc_195A752: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+176j + mov edi, [esi+TStruct40.array_00.field_4] + add edi, [esi+TStruct40.array_00.ItemCount] + jz short loc_195A761 + mov eax, [ebp+var_C] + mov cl, [eax] + mov [edi], cl + +loc_195A761: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+1E8j + mov ecx, 1 + add [esi+TStruct40.array_00.ItemCount], ecx + add [esi+TStruct40.CharIndex], ecx + mov edx, [edx+20h] + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, ebx + mov edi, ebx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr edi, 5 + mov ecx, [edx+edi*4] + and ecx, ebx + mov ebx, [ebp+dwDistance] + inc ebx + mov [ebp+dwDistance], ebx + test ecx, ecx + jnz short loc_195A806 + mov ecx, [esi+TStruct40.CharIndex] + mov edx, [ebp+pStruct1C] + cmp ecx, [edx+4] + jnb short loc_195A7A6 + mov edx, [ebp+pThis] + jmp loc_195A6C0 +; --------------------------------------------------------------------------- + +loc_195A7A6: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+22Cj + ; TNameIndexStruct__CheckAndCopyNameFragment+294j + mov ecx, [ebp+pThis] + mov edi, [ecx+8] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_195A7D4 + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_195A7CC + cmp ecx, 7FFFFFFFh + jbe short loc_195A7C9 + or edx, 0FFFFFFFFh + jmp short loc_195A7CC +; --------------------------------------------------------------------------- + +loc_195A7C9: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+252j + lea edx, [ecx+ecx] + +loc_195A7CC: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+24Aj + ; TNameIndexStruct__CheckAndCopyNameFragment+257j + push edx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_195A7D4: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+242j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_195A7E1 + mov dl, [edi+ebx] + mov [eax], dl + +loc_195A7E1: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+26Aj + inc [esi+TStruct40.array_00.ItemCount] + mov edi, [ebp+pThis] + mov edi, [edi+20h] + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, ebx + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, ebx + shr ecx, 5 + inc ebx + and edx, [edi+ecx*4] + test edx, edx + jz short loc_195A7A6 + +loc_195A806: ; CODE XREF: TNameIndexStruct__CheckAndCopyNameFragment+DAj + ; TNameIndexStruct__CheckAndCopyNameFragment+221j + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 8 +TNameIndexStruct__CheckAndCopyNameFragment endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_1959010 proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+67p + ; TArchiveDatabase__sub_1958B00+1CFp ... + +pFragmentInfo = dword ptr -0Ch +var_8 = dword ptr -8 +var_1 = byte ptr -1 +pStruct1C = dword ptr 8 +arg_4 = dword ptr 0Ch + + push ebp + mov ebp, esp + mov eax, [ebp+pStruct1C] + sub esp, 0Ch + push ebx + push esi + mov esi, [eax+TMndxFindResult.pStruct40] + push edi + mov edi, ecx + mov ecx, [ebp+arg_4] + +loc_1959024: ; CODE XREF: sub_1959010+2CEj + mov eax, [edi+TFileNameDatabase.NameFragIndexMask] + and eax, ecx + lea ebx, [eax+eax*2] + mov eax, [edi+TFileNameDatabase.NameFragTable.ItemArray] + add ebx, ebx + add ebx, ebx + add eax, ebx + mov [ebp+pFragmentInfo], ebx + cmp ecx, [eax+NAME_ENTRY.NextHashModifier] + jnz loc_1959147 + mov edx, [eax+NAME_ENTRY.FragmentOffset] + mov ecx, edx + and ecx, 0FFFFFF00h + cmp ecx, 0FFFFFF00h + jz short loc_1959092 + mov ecx, [edi+TFileNameDatabase.NextDB.pDatabase] + push edx + test ecx, ecx + jz short loc_1959070 + mov edx, [ebp+pStruct1C] + push edx + call sub_1959010 + jmp short loc_195907F +; --------------------------------------------------------------------------- + +loc_1959070: ; CODE XREF: sub_1959010+53j + mov eax, [ebp+pStruct1C] + push eax + lea ecx, [edi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckAndCopyNameFragment + +loc_195907F: ; CODE XREF: sub_1959010+5Ej + test al, al + jnz loc_195912E + +loc_1959087: ; CODE XREF: sub_1959010+90j + ; sub_1959010+1F3j ... + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 8 +; --------------------------------------------------------------------------- + +loc_1959092: ; CODE XREF: sub_1959010+48j + mov ecx, [ebp+pStruct1C] + mov eax, [ecx+TMndxFindResult.szSearchMask] + mov ecx, [esi+TStruct40.CharIndex] + mov byte ptr [ebp+arg_4+3], dl + cmp dl, [eax+ecx] + jnz short loc_1959087 + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_1959119 + mov [ebp+var_8], eax + shr eax, 1 + cmp ecx, eax + jbe short loc_19590CD + cmp ecx, 7FFFFFFFh + jbe short loc_19590C7 + mov [ebp+var_8], 0FFFFFFFFh + jmp short loc_19590CD +; --------------------------------------------------------------------------- + +loc_19590C7: ; CODE XREF: sub_1959010+ACj + lea edx, [ecx+ecx] + mov [ebp+var_8], edx + +loc_19590CD: ; CODE XREF: sub_1959010+A4j + ; sub_1959010+B5j + mov eax, [ebp+var_8] + push offset unk_53ABE00 + push eax + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_19590FD + +loc_19590E5: ; CODE XREF: sub_1959010+EBj + lea edx, [ecx+eax] + test edx, edx + jz short loc_19590F7 + mov ebx, [esi+TStruct40.array_00.field_4] + mov bl, [ecx+ebx] + mov [edx], bl + mov ebx, [ebp+pFragmentInfo] + +loc_19590F7: ; CODE XREF: sub_1959010+DAj + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_19590E5 + +loc_19590FD: ; CODE XREF: sub_1959010+D3j + mov ecx, [esi+TStruct40.array_00.DataBuffer] + mov edx, [ebp+var_8] + push ecx + mov [esi+TStruct40.array_00.DataBuffer], eax + mov [esi+TStruct40.array_00.field_4], eax + mov [esi+TStruct40.array_00.ItemArray], eax + mov [esi+TStruct40.array_00.MaxItemCount], edx + call operator_delete + mov dl, byte ptr [ebp+arg_4+3] + add esp, 4 + +loc_1959119: ; CODE XREF: sub_1959010+9Bj + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_1959123 + mov [eax], dl + +loc_1959123: ; CODE XREF: sub_1959010+10Fj + mov eax, 1 + add [esi+TStruct40.array_00.ItemCount], eax + add [esi+TStruct40.CharIndex], eax + +loc_195912E: ; CODE XREF: sub_1959010+71j + mov eax, [edi+TFileNameDatabase.NameFragTable.ItemArray] + mov ecx, [ebx+eax] + mov [ebp+arg_4], ecx + test ecx, ecx + jz loc_19592ED + jmp loc_19592D5 +; --------------------------------------------------------------------------- + +loc_1959147: ; CODE XREF: sub_1959010+31j + mov eax, [edi+TFileNameDatabase.Struct68_D0.ItemIsPresent.ItemArray] + mov edx, ecx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr edx, 5 + test [eax+edx*4], ebx + mov eax, [ebp+arg_4] + jz loc_195920E + mov ebx, [edi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + push eax + lea ecx, [edi+TFileNameDatabase.Struct68_D0] + add ebx, eax + call TSparseArray__GetItemValue + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + imul ecx, eax + mov eax, ecx + and ecx, 1Fh + mov edx, ecx + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + add ecx, edx + shr eax, 5 + cmp ecx, 20h + mov ecx, [edi+TFileNameDatabase.FrgmDist_HiBits.ItemArray] + ja short loc_19591AB + mov eax, [ecx+eax*4] + mov ecx, edx + shr eax, cl + jmp short loc_19591CE +; --------------------------------------------------------------------------- + +loc_19591AB: ; CODE XREF: sub_1959010+190j + lea eax, [ecx+eax*4] + mov [ebp+pFragmentInfo], eax + mov eax, [eax+4] + mov ecx, 20h + sub ecx, edx + shl eax, cl + mov ecx, [ebp+pFragmentInfo] + mov ecx, [ecx] + mov [ebp+pFragmentInfo], ecx + mov ecx, edx + mov edx, [ebp+pFragmentInfo] + shr edx, cl + or eax, edx + +loc_19591CE: ; CODE XREF: sub_1959010+199j + and eax, [edi+TFileNameDatabase.FrgmDist_HiBits.EntryBitMask] + movzx edx, byte ptr [ebx] + mov ecx, [edi+TFileNameDatabase.NextDB.pDatabase] + shl eax, 8 + or eax, edx + push eax + test ecx, ecx + jz short loc_19591F2 + mov eax, [ebp+pStruct1C] + push eax + call sub_1959010 + jmp short loc_1959201 +; --------------------------------------------------------------------------- + +loc_19591F2: ; CODE XREF: sub_1959010+1D5j + mov ecx, [ebp+pStruct1C] + push ecx + lea ecx, [edi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckAndCopyNameFragment + +loc_1959201: ; CODE XREF: sub_1959010+1E0j + test al, al + jz loc_1959087 + jmp loc_19592B6 +; --------------------------------------------------------------------------- + +loc_195920E: ; CODE XREF: sub_1959010+152j + mov edx, [edi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov dl, [eax+edx] + mov ecx, [ebp+pStruct1C] + mov eax, [ecx+TMndxFindResult.szSearchMask] + mov ecx, [esi+TStruct40.CharIndex] + mov [ebp+var_1], dl + cmp dl, [eax+ecx] + jnz loc_1959087 + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_19592A1 + mov ebx, eax + shr eax, 1 + mov [ebp+var_8], ebx + cmp ecx, eax + jbe short loc_1959254 + cmp ecx, 7FFFFFFFh + jbe short loc_195924E + or ebx, 0FFFFFFFFh + jmp short loc_1959251 +; --------------------------------------------------------------------------- + +loc_195924E: ; CODE XREF: sub_1959010+237j + lea ebx, [ecx+ecx] + +loc_1959251: ; CODE XREF: sub_1959010+23Cj + mov [ebp+var_8], ebx + +loc_1959254: ; CODE XREF: sub_1959010+22Fj + push offset unk_53ABE00 + push ebx + call j_operator_new_safe + xor ecx, ecx + add esp, 8 + cmp [esi+TStruct40.array_00.ItemCount], ecx + jbe short loc_1959288 + lea esp, [esp+0] + +loc_1959270: ; CODE XREF: sub_1959010+273j + lea edx, [ecx+eax] + test edx, edx + jz short loc_195927F + mov ebx, [esi+TStruct40.array_00.field_4] + mov bl, [ecx+ebx] + mov [edx], bl + +loc_195927F: ; CODE XREF: sub_1959010+265j + inc ecx + cmp ecx, [esi+TStruct40.array_00.ItemCount] + jb short loc_1959270 + mov ebx, [ebp+var_8] + +loc_1959288: ; CODE XREF: sub_1959010+257j + mov ecx, [esi+TStruct40.array_00.DataBuffer] + push ecx + mov [esi+TStruct40.array_00.DataBuffer], eax + mov [esi+TStruct40.array_00.field_4], eax + mov [esi+TStruct40.array_00.ItemArray], eax + mov [esi+TStruct40.array_00.MaxItemCount], ebx + call operator_delete + mov dl, [ebp+var_1] + add esp, 4 + +loc_19592A1: ; CODE XREF: sub_1959010+224j + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_19592AB + mov [eax], dl + +loc_19592AB: ; CODE XREF: sub_1959010+297j + mov eax, 1 + add [esi+TStruct40.array_00.ItemCount], eax + add [esi+TStruct40.CharIndex], eax + +loc_19592B6: ; CODE XREF: sub_1959010+1F9j + mov ebx, [ebp+arg_4] + cmp ebx, [edi+TFileNameDatabase.field_214] + jbe short loc_19592ED + push ebx + mov ecx, edi + call sub_1959F50 + or edx, 0FFFFFFFFh + sub edx, ebx + add eax, edx + mov ecx, eax + mov [ebp+arg_4], eax + +loc_19592D5: ; CODE XREF: sub_1959010+132j + mov edx, [esi+TStruct40.CharIndex] + mov eax, [ebp+pStruct1C] + cmp edx, [eax+TMndxFindResult.cchSearchMask] + jb loc_1959024 + push ecx + push eax + mov ecx, edi + call sub_1958D70 + +loc_19592ED: ; CODE XREF: sub_1959010+12Cj + ; sub_1959010+2AFj + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 8 +sub_1959010 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +sub_19582E0 proc near ; CODE XREF: TFileNameDatabase__sub_1958B00+253p + +arg_0 = dword ptr 8 + + push ebp + mov ebp, esp + push esi + mov esi, ecx + mov eax, [esi+0Ch] + mov ecx, [esi+10h] + inc eax + cmp eax, ecx + jbe short loc_1958311 + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_1958309 + cmp ecx, 7FFFFFFFh + jbe short loc_1958306 + or edx, 0FFFFFFFFh + jmp short loc_1958309 +; --------------------------------------------------------------------------- + +loc_1958306: ; CODE XREF: sub_19582E0+1Fj + lea edx, [ecx+ecx] + +loc_1958309: ; CODE XREF: sub_19582E0+17j + ; sub_19582E0+24j + push edx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_1958311: ; CODE XREF: sub_19582E0+Fj + mov eax, [esi+4] + add eax, [esi+0Ch] + jz short loc_1958320 + mov ecx, [ebp+arg_0] + mov dl, [ecx] + mov [eax], dl + +loc_1958320: ; CODE XREF: sub_19582E0+37j + inc dword ptr [esi+0Ch] + pop esi + pop ebp + retn 4 +sub_19582E0 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TFileNameDatabase__sub_1958B00 proc near ; CODE XREF: TFileNameDatabase__sub_1959460+3Bp + +var_C = dword ptr -0Ch +ItemArrayOffset = dword ptr -8 +var_4 = dword ptr -4 +arg_0 = dword ptr 8 + + push ebp + mov ebp, esp + sub esp, 0Ch + mov edx, [ebp+pStruct1C] + push ebx + mov ebx, [edx+TMndxFindResult.szSearchMask] + push esi + push edi + mov edi, [edx+TMndxFindResult.pStruct40] + mov eax, [edi+TStruct40.CharIndex] + movzx eax, byte ptr [eax+ebx] + mov esi, ecx + mov ecx, [edi+TStruct40.HashValue] + mov ebx, ecx + shl ebx, 5 + xor eax, ebx + mov ebx, [esi+TFileNameDatabase.NameFragTable.ItemArray] + xor eax, ecx + and eax, [esi+TFileNameDatabase.NameFragIndexMask] + lea eax, [eax+eax*2] + add eax, eax + add eax, eax + mov [ebp+ItemArrayOffset], eax + cmp ecx, [eax+ebx] + jnz loc_1958BE5 + mov ecx, [eax+ebx+NAME_ENTRY.FragmentOffset] + mov ebx, ecx + and ebx, 0FFFFFF00h + cmp ebx, 0FFFFFF00h + jz short loc_1958B88 + mov eax, [esi+TFileNameDatabase.NextDB.pDatabase] + push ecx + push edx + test eax, eax + jz short loc_1958B6E + mov ecx, eax + call sub_1959010 + jmp short loc_1958B79 +; --------------------------------------------------------------------------- + +loc_1958B6E: ; CODE XREF: TArchiveDatabase__sub_1958B00+63j + lea ecx, [esi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckAndCopyNameFragment + +loc_1958B79: ; CODE XREF: TArchiveDatabase__sub_1958B00+6Cj + test al, al + jnz short loc_1958BCA + +loc_1958B7D: ; CODE XREF: TArchiveDatabase__sub_1958B00+108j + ; TArchiveDatabase__sub_1958B00+1F3j + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1958B88: ; CODE XREF: TArchiveDatabase__sub_1958B00+57j + mov eax, [edi+TStruct40.array_00.ItemCount] + mov bl, cl + mov ecx, [edi+TStruct40.array_00.MaxItemCount] + inc eax + cmp eax, ecx + jbe short loc_1958BB5 + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_1958BAD + cmp ecx, 7FFFFFFFh + jbe short loc_1958BAA + or edx, 0FFFFFFFFh + jmp short loc_1958BAD +; --------------------------------------------------------------------------- + +loc_1958BAA: ; CODE XREF: TArchiveDatabase__sub_1958B00+A3j + lea edx, [ecx+ecx] + +loc_1958BAD: ; CODE XREF: TArchiveDatabase__sub_1958B00+9Bj + ; TArchiveDatabase__sub_1958B00+A8j + push edx + mov ecx, edi + call TGenericArray__SetMaxItems_BYTE + +loc_1958BB5: ; CODE XREF: TArchiveDatabase__sub_1958B00+93j + mov eax, [edi+TStruct40.array_00.field_4] + add eax, [edi+TStruct40.array_00.ItemCount] + jz short loc_1958BBF + mov [eax], bl + +loc_1958BBF: ; CODE XREF: TArchiveDatabase__sub_1958B00+BBj + mov eax, 1 + add [edi+TStruct40.array_00.ItemCount], eax + add [edi+TStruct40.CharIndex], eax + +loc_1958BCA: ; CODE XREF: TArchiveDatabase__sub_1958B00+7Bj + mov ecx, [esi+TFileNameDatabase.NameFragTable.ItemArray] + mov edx, [ebp+ItemArrayOffset] + mov eax, [ecx+edx+NAME_ENTRY.NextHashModifier] + mov [edi+TStruct40.HashValue], eax + +loc_1958BDA: ; CODE XREF: TArchiveDatabase__sub_1958B00+1E7j + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1958BE5: ; CODE XREF: TArchiveDatabase__sub_1958B00+3Fj + push ecx + mov ecx, esi + call TArchiveDatabase__sub_1959CB0 + mov ebx, [esi+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + inc eax + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, eax + shr ecx, 5 + mov [ebp+ItemArrayOffset], eax + test [ebx+ecx*4], edx + jz loc_1958B7D + sub eax, [edi+TStruct40.HashValue] + mov [ebp+var_4], 0FFFFFFFFh + dec eax + mov [edi+TStruct40.HashValue], eax + lea esp, [esp+0] + +loc_1958C20: ; CODE XREF: TArchiveDatabase__sub_1958B00+230j + mov eax, [edi+TStruct40.HashValue] + mov ebx, [esi+TFileNameDatabase.Struct68_D0.ItemIsPresent.ItemArray] + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, eax + shr ecx, 5 + test [ebx+ecx*4], edx + jz loc_1958CFB + mov ecx, [ebp+var_4] + cmp ecx, 0FFFFFFFFh + jnz short loc_1958C5E + push eax + lea ecx, [esi+TFileNameDatabase.Struct68_D0] + call TSparseArray__GetItemValue + mov ecx, eax + mov [ebp+var_4], eax + jmp short loc_1958C62 +; --------------------------------------------------------------------------- + +loc_1958C5E: ; CODE XREF: TArchiveDatabase__sub_1958B00+149j + inc ecx + mov [ebp+var_4], ecx + +loc_1958C62: ; CODE XREF: TArchiveDatabase__sub_1958B00+15Cj + mov edx, [edi+TStruct40.CharIndex] + mov [ebp+var_C], edx + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.BitsPerEntry] + mov eax, edx + imul eax, ecx + mov ecx, eax + and eax, 1Fh + add edx, eax + shr ecx, 5 + cmp edx, 20h + mov edx, [esi+TFileNameDatabase.FrgmDist_HiBits.ItemArray] + ja short loc_1958C91 + mov edx, [edx+ecx*4] + mov ecx, eax + shr edx, cl + jmp short loc_1958CA8 +; --------------------------------------------------------------------------- + +loc_1958C91: ; CODE XREF: TArchiveDatabase__sub_1958B00+186j + lea ebx, [edx+ecx*4] + mov edx, [ebx+4] + mov ebx, [ebx] + mov ecx, 20h + sub ecx, eax + shl edx, cl + mov ecx, eax + shr ebx, cl + or edx, ebx + +loc_1958CA8: ; CODE XREF: TArchiveDatabase__sub_1958B00+18Fj + mov ecx, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + and edx, [esi+TFileNameDatabase.FrgmDist_HiBits.EntryBitMask] + mov eax, [edi+TStruct40.HashValue] + movzx eax, byte ptr [ecx+eax] + mov ecx, [esi+TFileNameDatabase.NextDB.pDatabase] + shl edx, 8 + or eax, edx + push eax + test ecx, ecx + jz short loc_1958CD6 + mov edx, [ebp+pStruct1C] + push edx + call sub_1959010 + jmp short loc_1958CE5 +; --------------------------------------------------------------------------- + +loc_1958CD6: ; CODE XREF: TArchiveDatabase__sub_1958B00+1C9j + mov eax, [ebp+pStruct1C] + push eax + lea ecx, [esi+TFileNameDatabase.IndexStruct_174] + call TNameIndexStruct__CheckAndCopyNameFragment + +loc_1958CE5: ; CODE XREF: TArchiveDatabase__sub_1958B00+1D4j + test al, al + jnz loc_1958BDA + mov ecx, [ebp+var_C] + cmp [edi+TStruct40.CharIndex], ecx + jnz loc_1958B7D + jmp short loc_1958D11 +; --------------------------------------------------------------------------- + +loc_1958CFB: ; CODE XREF: TArchiveDatabase__sub_1958B00+13Dj + mov edx, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov ebx, [ebp+pStruct1C] + mov ecx, [edi+TStruct40.CharIndex] + mov ebx, [ebx+TMndxFindResult.szSearchMask] + mov dl, [eax+edx] + cmp dl, [ecx+ebx] + jz short loc_1958D41 + +loc_1958D11: ; CODE XREF: TArchiveDatabase__sub_1958B00+1F9j + mov eax, [ebp+ItemArrayOffset] + inc [edi+TStruct40.HashValue] + inc eax + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, [esi+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + mov [ebp+ItemArrayOffset], eax + shr eax, 5 + test [ecx+eax*4], edx + jnz loc_1958C20 + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1958D41: ; CODE XREF: TArchiveDatabase__sub_1958B00+20Fj + mov edx, [esi+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov cl, [edx+eax] + lea edx, [ebp+pStruct1C+3] + mov byte ptr [ebp+pStruct1C+3], cl + push edx + mov ecx, edi + call sub_19582E0 + mov eax, 1 + add [edi+TStruct40.CharIndex], eax + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn 4 +TFileNameDatabase__sub_1958B00 endp + +; =============== S U B R O U T I N E ======================================= + +; Attributes: bp-based frame + +TFileNameDatabase__sub_1959460 proc near ; CODE XREF: TFileNameDatabasePtr__sub_1956CE0+2Dp + +Struct14 = dword ptr -20h +var_1C = dword ptr -1Ch +var_18 = dword ptr -18h +var_14 = dword ptr -14h +var_10 = dword ptr -10h +pStruct14 = dword ptr -0Ch +pThis = dword ptr -8 +OneChar = byte ptr -1 +pStruct1C = dword ptr 8 + + push ebp + mov ebp, esp + sub esp, 20h + push ebx + push esi + push edi + mov edi, [ebp+pStruct1C] + mov esi, [edi+TMndxFindResult.pStruct40] + mov eax, [esi+TStruct40.SearchPhase] + mov ebx, ecx + mov [ebp+pThis], ebx + cmp eax, 4 + jz return_false + cmp eax, 2 + jz loc_1959530 + mov ecx, esi + call TStruct40__InitSearchBuffers + mov eax, [esi+TStruct40.CharIndex] + cmp eax, [edi+TMndxFindResult.cchSearchMask] + jnb short loc_19594B0 + +loc_1959498: ; CODE XREF: TFileNameDatabase__sub_1959460+4Ej + push edi + mov ecx, ebx + call TFileNameDatabase__sub_1958B00 + test al, al + jz loc_1959778 + mov ecx, [esi+TStruct40.CharIndex] + cmp ecx, [edi+TMndxFindResult.cchSearchMask] + jb short loc_1959498 + +loc_19594B0: ; CODE XREF: TFileNameDatabase__sub_1959460+36j + mov edx, [esi+TStruct40.HashValue] + or eax, 0FFFFFFFFh + mov [ebp+Struct14], edx + mov [ebp+var_14], eax + mov [ebp+var_10], eax + mov eax, [esi+TStruct40.array_00.ItemCount] + lea edx, [ebp+Struct14] + lea ecx, [esi+TStruct40.array_18] + push edx + mov [ebp+var_1C], 0 + mov [ebp+var_18], eax + call TGenericArray__InsertItem_STRUCT14 + mov ecx, [esi+TStruct40.HashValue] + mov eax, ecx + and ecx, 1Fh + mov edi, 1 + shl edi, cl + mov [esi+TStruct40.ItemCount], 1 + mov edx, [ebx+TFileNameDatabase.FileNameIndexes.ItemIsPresent.ItemArray] + shr eax, 5 + test [edx+eax*4], edi + jz short loc_1959530 + mov ecx, [esi+TStruct40.array_00.field_4] + mov eax, [esi+TStruct40.array_00.ItemCount] + mov edi, [ebp+pStruct1C] + mov [edi+TMndxFindResult.szFoundPath], ecx + mov [edi+TMndxFindResult.cchFoundPath], eax + mov esi, [esi+TStruct40.HashValue] + push esi + lea ecx, [ebx+TFileNameDatabase.FileNameIndexes] + call TSparseArray__GetItemValue + mov [edi+TMndxFindResult.FileNameIndex], eax + pop edi + pop esi + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1959522: ; CODE XREF: TFileNameDatabase__sub_1959460+26Bj + ; TFileNameDatabase__sub_1959460+2D9j ... + mov ebx, [ebp+pThis] + jmp short loc_1959530 +; --------------------------------------------------------------------------- + align 10h + +loc_1959530: ; CODE XREF: TFileNameDatabase__sub_1959460+23j + ; TFileNameDatabase__sub_1959460+97j ... + mov edx, [esi+TStruct40.ItemCount] + cmp edx, [esi+TStruct40.array_18.ItemCount] + jnz loc_19595BD + mov eax, [esi+TStruct40.array_18.ItemCount] + mov ecx, [esi+TStruct40.array_18.field_4] + lea eax, [eax+eax*4] + lea eax, [ecx+eax*4-14h] + mov [ebp+pStruct14], eax + mov eax, [eax+TStruct14.HashValue] + push eax + mov ecx, ebx + call TFileNameDatabase__sub_1959CB0 + mov edx, [ebp+pStruct14] + mov ecx, [esi+TStruct40.array_18.ItemCount] + inc eax + mov ebx, eax + sub ebx, [edx+TStruct14.HashValue] + mov edx, [esi+TStruct40.array_18.MaxItemCount] + inc ecx + dec ebx + mov [ebp+var_1C], eax + cmp ecx, edx + jbe short loc_1959591 + mov eax, ecx + shr ecx, 1 + cmp edx, ecx + jbe short loc_1959585 + mov eax, 0CCCCCCCh + cmp edx, 6666666h + ja short loc_1959585 + lea eax, [edx+edx] + +loc_1959585: ; CODE XREF: TFileNameDatabase__sub_1959460+113j + ; TFileNameDatabase__sub_1959460+120j + push eax + lea ecx, [esi+TStruct40.array_18] + call TGenericArray__SetMaxItems_STRUCT14 + mov eax, [ebp+var_1C] + +loc_1959591: ; CODE XREF: TFileNameDatabase__sub_1959460+10Bj + mov ecx, [esi+TStruct40.array_18.ItemCount] + mov edx, [esi+TStruct40.array_18.field_4] + lea ecx, [ecx+ecx*4] + lea ecx, [edx+ecx*4] + test ecx, ecx + jz short loc_19595B7 + mov [ecx+TStruct14.HashValue], ebx + mov [ecx+TStruct14.field_4], eax + xor eax, eax + mov [ecx+TStruct14.field_8], eax + or eax, 0FFFFFFFFh + mov [ecx+TStruct14.field_C], eax + or eax, 0FFFFFFFFh + mov [ecx+TStruct14.field_10], eax + +loc_19595B7: ; CODE XREF: TFileNameDatabase__sub_1959460+13Fj + inc [esi+TStruct40.array_18.ItemCount] + mov ebx, [ebp+pThis] + +loc_19595BD: ; CODE XREF: TFileNameDatabase__sub_1959460+D6j + mov eax, [esi+TStruct40.ItemCount] + mov ecx, [esi+TStruct40.array_18.field_4] + mov ebx, [ebx+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray] + lea eax, [eax+eax*4] + lea edi, [ecx+eax*4] + mov eax, [edi+TStruct14.field_4] + mov ecx, eax + and ecx, 1Fh + mov edx, 1 + shl edx, cl + mov ecx, eax + shr ecx, 5 + test [ebx+ecx*4], edx + setnz cl + inc eax + mov [edi+TStruct14.field_4], eax + test cl, cl + jz loc_19596E9 + inc [esi+TStruct40.ItemCount] + mov ecx, [edi+TStruct14.HashValue] + mov eax, [ebp+pThis] + mov eax, [eax+TFileNameDatabase.Struct68_D0.ItemIsPresent.ItemArray] + mov edx, ecx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr edx, 5 + test [eax+edx*4], ebx + mov ebx, [ebp+pThis] + jz short loc_1959665 + mov eax, [edi+TStruct14.field_C] + cmp eax, 0FFFFFFFFh + jnz short loc_195962F + mov eax, [edi+TStruct14.HashValue] + push eax + lea ecx, [ebx+TFileNameDatabase.Struct68_D0] + call TSparseArray__GetItemValue + jmp short loc_1959630 +; --------------------------------------------------------------------------- + +loc_195962F: ; CODE XREF: TFileNameDatabase__sub_1959460+1BDj + inc eax + +loc_1959630: ; CODE XREF: TFileNameDatabase__sub_1959460+1CDj + mov ecx, [edi+TStruct14.HashValue] + push eax + push ecx + mov ecx, ebx + mov [edi+TStruct14.field_C], eax + call sub_19573D0 + mov ecx, [ebx+TFileNameDatabase.NextDB.pDatabase] + push eax + test ecx, ecx + jz short loc_1959654 + mov edx, [ebp+pStruct1C] + push edx + call sub_1958D70 + jmp short loc_19596AE +; --------------------------------------------------------------------------- + +loc_1959654: ; CODE XREF: TFileNameDatabase__sub_1959460+1E7j + mov eax, [ebp+pStruct1C] + push eax + lea ecx, [ebx+TFileNameDatabase.IndexStruct_174] + call CopyNameFragment + jmp short loc_19596AE +; --------------------------------------------------------------------------- + +loc_1959665: ; CODE XREF: TFileNameDatabase__sub_1959460+1B5j + mov ecx, [ebx+TFileNameDatabase.FrgmDist_LoBits.ItemArray] + mov eax, [edi+TStruct14.HashValue] + mov dl, [eax+ecx] + mov eax, [esi+TStruct40.array_00.ItemCount] + mov ecx, [esi+TStruct40.array_00.MaxItemCount] + inc eax + mov [ebp+OneChar], dl + cmp eax, ecx + jbe short loc_195969E + mov edx, eax + shr eax, 1 + cmp ecx, eax + jbe short loc_1959696 + cmp ecx, 7FFFFFFFh + jbe short loc_1959693 + or edx, 0FFFFFFFFh + jmp short loc_1959696 +; --------------------------------------------------------------------------- + +loc_1959693: ; CODE XREF: TFileNameDatabase__sub_1959460+22Cj + lea edx, [ecx+ecx] + +loc_1959696: ; CODE XREF: TFileNameDatabase__sub_1959460+224j + ; TFileNameDatabase__sub_1959460+231j + push edx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_195969E: ; CODE XREF: TFileNameDatabase__sub_1959460+21Cj + mov eax, [esi+TStruct40.array_00.field_4] + add eax, [esi+TStruct40.array_00.ItemCount] + jz short loc_19596AB + mov cl, [ebp+OneChar] + mov [eax], cl + +loc_19596AB: ; CODE XREF: TFileNameDatabase__sub_1959460+244j + inc [esi+TStruct40.array_00.ItemCount] + +loc_19596AE: ; CODE XREF: TFileNameDatabase__sub_1959460+1F2j + ; TFileNameDatabase__sub_1959460+203j + mov edx, [esi+TStruct40.array_00.ItemCount] + mov ecx, [edi+TStruct14.HashValue] + mov [edi+TStruct14.field_8], edx + mov edx, [ebx+TFileNameDatabase.FileNameIndexes.ItemIsPresent.ItemArray] + mov eax, ecx + and ecx, 1Fh + mov ebx, 1 + shl ebx, cl + shr eax, 5 + test [edx+eax*4], ebx + jz loc_1959522 + mov eax, [edi+TStruct14.field_10] + cmp eax, 0FFFFFFFFh + jnz short loc_1959754 + mov eax, [edi+TStruct14.HashValue] + mov ecx, [ebp+pThis] + push eax + add ecx, TFileNameDatabase.FileNameIndexes + call TSparseArray__GetItemValue + jmp short loc_1959755 +; --------------------------------------------------------------------------- + +loc_19596E9: ; CODE XREF: TFileNameDatabase__sub_1959460+18Cj + mov eax, [esi+TStruct40.ItemCount] + cmp eax, 1 + jz loc_1959778 + mov ecx, [esi+TStruct40.array_18.field_4] + lea eax, [eax+eax*4] + inc dword ptr [ecx+eax*4-14h] + lea eax, [ecx+eax*4-14h] + mov eax, [esi+TStruct40.ItemCount] + lea edx, [eax+eax*4] + mov eax, [esi+TStruct40.array_18.field_4] + mov edi, [eax+edx*4-20h] + mov eax, [esi+TStruct40.array_00.MaxItemCount] + cmp edi, eax + jbe short loc_1959749 + mov edx, edi + shr edx, 1 + mov ecx, edi + cmp eax, edx + jbe short loc_1959741 + cmp eax, 7FFFFFFFh + jbe short loc_195973E + or ecx, 0FFFFFFFFh + push ecx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + dec [esi+TStruct40.ItemCount] + mov [esi+TStruct40.array_00.ItemCount], edi + jmp loc_1959522 +; --------------------------------------------------------------------------- + +loc_195973E: ; CODE XREF: TFileNameDatabase__sub_1959460+2C6j + lea ecx, [eax+eax] + +loc_1959741: ; CODE XREF: TFileNameDatabase__sub_1959460+2BFj + push ecx + mov ecx, esi + call TGenericArray__SetMaxItems_BYTE + +loc_1959749: ; CODE XREF: TFileNameDatabase__sub_1959460+2B5j + dec [esi+TStruct40.ItemCount] + mov [esi+TStruct40.array_00.ItemCount], edi + jmp loc_1959522 +; --------------------------------------------------------------------------- + +loc_1959754: ; CODE XREF: TFileNameDatabase__sub_1959460+277j + inc eax + +loc_1959755: ; CODE XREF: TFileNameDatabase__sub_1959460+287j + mov [edi+TStruct14.field_10], eax + mov ecx, [esi+TStruct40.array_00.ItemCount] + mov edx, [esi+TStruct40.array_00.field_4] + mov eax, [ebp+pStruct1C] + mov [eax+TMndxFindResult.szFoundPath], edx + mov [eax+TMndxFindResult.cchFoundPath], ecx + mov ecx, [edi+TStruct14.field_10] + pop edi + pop esi + mov [eax+TMndxFindResult.FileNameIndex], ecx + mov al, 1 + pop ebx + mov esp, ebp + pop ebp + retn 4 +; --------------------------------------------------------------------------- + +loc_1959778: ; CODE XREF: TFileNameDatabase__sub_1959460+42j + ; TFileNameDatabase__sub_1959460+28Fj + mov [esi+TStruct40.SearchPhase], 4 + +return_false: ; CODE XREF: TFileNameDatabase__sub_1959460+1Aj + pop edi + pop esi + xor al, al + pop ebx + mov esp, ebp + pop ebp + retn 4 +TFileNameDatabase__sub_1959460 endp + +; --------------------------------------------------------------------------- + +;------------------------------------------------------------------------------ +; Public functions callable from C++ + +; +; DWORD _cdecl sub_19573D0_x86(TFileNameDatabase * pDB, DWORD arg_0, DWORD arg_4); +; + +_sub_19573D0_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+10h] ; arg_4 + push [ebp+0Ch] ; arg_0 + call sub_19573D0 + mov esp, ebp + pop ebp + ret + +_sub_19573D0_x86 ENDP + +; +; DWORD _cdecl sub_1957EF0_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); +; + +_sub_1957EF0_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+0Ch] ; pStruct1C + call TFileNameDatabase__FindFileInDatabase + mov esp, ebp + pop ebp + ret + +_sub_1957EF0_x86 ENDP + +; +; bool _cdecl _sub_1959460_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); +; + +_sub_1959460_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+0Ch] ; pStruct1C + call TFileNameDatabase__sub_1959460 + mov esp, ebp + pop ebp + ret + +_sub_1959460_x86 ENDP + +; +; bool _cdecl sub_1958B00_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C); +; + +_sub_1958B00_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+0Ch] ; pStruct1C + call TFileNameDatabase__sub_1959460 + mov esp, ebp + pop ebp + ret + +_sub_1958B00_x86 ENDP + +; +; DWORD _cdecl GetItemValue_x86(TSparseArray * pStruct, DWORD dwKey); +; + +_GetItemValue_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pStruct68 + push [ebp+0Ch] ; dwKey + call TSparseArray__GetItemValue + mov esp, ebp + pop ebp + ret + +_GetItemValue_x86 ENDP + +; +; DWORD _cdecl sub_1959CB0_x86(TFileNameDatabase * pDB, DWORD dwKey); +; + +_sub_1959CB0_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+0Ch] ; dwKey + call TArchiveDatabase__sub_1959CB0 + mov esp, ebp + pop ebp + ret + +_sub_1959CB0_x86 ENDP + +; +; DWORD _cdecl sub_1959F50_x86(TFileNameDatabase * pDB, DWORD arg_0); +; + +_sub_1959F50_x86 PROC + + push ebp + mov ebp, esp + mov ecx, [ebp+8] ; pDB + push [ebp+0Ch] ; dwKey + call sub_1959F50 + mov esp, ebp + pop ebp + ret + +_sub_1959F50_x86 ENDP + +END diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp new file mode 100644 index 00000000000..e7cdb9aa107 --- /dev/null +++ b/dep/CascLib/src/CascOpenFile.cpp @@ -0,0 +1,440 @@ +/*****************************************************************************/ +/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +//----------------------------------------------------------------------------- +// Local structures + +//----------------------------------------------------------------------------- +// Local functions + +TCascFile * IsValidFileHandle(HANDLE hFile) +{ + TCascFile * hf = (TCascFile *)hFile; + + return (hf != NULL && hf->hs != NULL && hf->szClassName != NULL && !strcmp(hf->szClassName, "TCascFile")) ? hf : NULL; +} + +PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey) +{ + PCASC_INDEX_ENTRY pIndexEntry = NULL; + + if(hs->pIndexEntryMap != NULL) + pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData); + + return pIndexEntry; +} + +PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + size_t StartEntry = 0; + size_t MidlEntry; + size_t EndEntry = hs->nEncodingEntries; + int nResult; + + // Perform binary search + while(StartEntry < EndEntry) + { + // Calculate the middle of the interval + MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2); + pEncodingEntry = hs->ppEncodingEntries[MidlEntry]; + + // Did we find it? + nResult = memcmp(pEncodingKey->pbData, pEncodingEntry->EncodingKey, MD5_HASH_SIZE); + if(nResult == 0) + { + if(PtrIndex != NULL) + PtrIndex[0] = MidlEntry; + return pEncodingEntry; + } + + // Move the interval to the left or right + (nResult < 0) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1; + } + + // Not found, sorry + return NULL; +} + +// Also used in CascSearchFile +PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex) +{ + PCASC_ROOT_ENTRY pFoundEntry = NULL; + PCASC_ROOT_ENTRY pRootEntry; + ULONGLONG FileNameHash; + uint32_t dwHashHigh = 0; + uint32_t dwHashLow = 0; + size_t StartEntry = 0; + size_t MidlEntry = 0; + size_t EndEntry = hs->nRootEntries; + + // Calculate the HASH value of the normalized file name + hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow); + FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; + + // Perform binary search + while(StartEntry < EndEntry) + { + // Calculate the middle of the interval + MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2); + pRootEntry = hs->ppRootEntries[MidlEntry]; + + // Did we find it? + if(pRootEntry->FileNameHash == FileNameHash) + { + pFoundEntry = pRootEntry; + break; + } + + // Move the interval to the left or right + (FileNameHash < pRootEntry->FileNameHash) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1; + } + + // Move the pointer back to the first entry with that hash + if(pFoundEntry != NULL) + { + while(MidlEntry > 0 && hs->ppRootEntries[MidlEntry - 1]->FileNameHash == FileNameHash) + { + pFoundEntry = hs->ppRootEntries[MidlEntry - 1]; + MidlEntry--; + } + } + + // Return what we found + if(PtrIndex != NULL) + *PtrIndex = MidlEntry; + return pFoundEntry; +} + +// Check the root directory for that hash +PCASC_ROOT_ENTRY FindRootEntryLocale(TCascStorage * hs, char * szFileName, DWORD Locale) +{ + PCASC_ROOT_ENTRY pThatEntry = NULL; + PCASC_ROOT_ENTRY pENUSEntry = NULL; + PCASC_ROOT_ENTRY pENGBEntry = NULL; + PCASC_ROOT_ENTRY pAnyEntry = NULL; + PCASC_ROOT_ENTRY pEndEntry = NULL; + PCASC_ROOT_ENTRY pRootEntry = NULL; + ULONGLONG FileNameHash; + size_t EntryIndex = 0; + size_t EndEntry = hs->nRootEntries; + + // Find a root entry with the given name hash + pRootEntry = FindFirstRootEntry(hs, szFileName, &EntryIndex); + if(pRootEntry != NULL) + { + // Rememeber the file name hash + pEndEntry = hs->pRootEntries + hs->nRootEntries; + FileNameHash = pRootEntry->FileNameHash; + + // Find all suitable root entries + while(EntryIndex < EndEntry) + { + // Get the root entry + pRootEntry = hs->ppRootEntries[EntryIndex++]; + if(pRootEntry->FileNameHash != FileNameHash) + break; + + // If a locale has been given, check it + if(pThatEntry == NULL && Locale != 0 && (Locale & pRootEntry->Locales)) + pThatEntry = pRootEntry; + if(pENUSEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENUS)) + pENUSEntry = pRootEntry; + if(pENGBEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENGB)) + pENGBEntry = pRootEntry; + if(pAnyEntry == NULL) + pAnyEntry = pRootEntry; + + // Move to the next one + pRootEntry++; + } + + // Return the key by priority + if(pThatEntry != NULL) + return pThatEntry; + if(pENGBEntry != NULL) + return pENGBEntry; + if(pENUSEntry != NULL) + return pENUSEntry; + } + + // Return whatever we got + return pAnyEntry; +} + +static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry) +{ + ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1; + ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset); + TCascFile * hf; + + // Allocate the CASC file structure + hf = (TCascFile *)CASC_ALLOC(TCascFile, 1); + if(hf != NULL) + { + // Initialize the structure + memset(hf, 0, sizeof(TCascFile)); + hf->ArchiveIndex = (DWORD)(FileOffset >> hs->KeyMapping[0].SegmentBits); + hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask); + hf->szClassName = "TCascFile"; + + // Copy the compressed file size + hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize) - 0x1E; + + // For now, we set the file size to be equal to compressed size + // This is used when loading the "encoding" file, which does not + // have entry in the encoding itself + hf->FileSize = hf->CompressedSize; + + // Increment the number of references to the archive + hs->dwRefCount++; + hf->hs = hs; + } + + return hf; +} + +static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile) +{ + PCASC_INDEX_ENTRY pIndexEntry; + TCascFile * hf = NULL; + int nError = ERROR_SUCCESS; + + CASCLIB_UNUSED(dwFlags); + + // Find the key entry in the array of file keys + pIndexEntry = FindIndexEntry(hs, pIndexKey); + if(pIndexEntry == NULL) + nError = ERROR_FILE_NOT_FOUND; + + // Create the file handle structure + if(nError == ERROR_SUCCESS) + { + hf = CreateFileHandle(hs, pIndexEntry); + *phFile = (HANDLE)hf; + if(hf == NULL) + nError = ERROR_FILE_NOT_FOUND; + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + QUERY_KEY IndexKey; + TCascFile * hf = NULL; + int nError = ERROR_SUCCESS; + + // Find the encoding entry + pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL); + if(pEncodingEntry == NULL) + nError = ERROR_FILE_NOT_FOUND; + + // Prepare the file index and open the file by index + // Note: We don't know what to do if there is more than just one index key + // We always take the first file present. Is that correct? +// IndexKey.pbData = pEncodingEntry->EncodingKey + (MD5_HASH_SIZE * pEncodingEntry->KeyCount); +// assert(pEncodingEntry->KeyCount == 1); + IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + IndexKey.cbData = MD5_HASH_SIZE; + if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, phFile)) + { + // Fix the file size from the encoding key + hf = IsValidFileHandle(*phFile); + if(hf != NULL) + { + hf->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Public functions + +bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile) +{ + TCascStorage * hs; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(pIndexKey == NULL || pIndexKey->pbData == NULL || pIndexKey->cbData == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Use the internal function to open the file + return OpenFileByIndexKey(hs, pIndexKey, dwFlags, phFile); +} + +bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile) +{ + TCascStorage * hs; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(pEncodingKey == NULL || pEncodingKey->pbData == NULL || pEncodingKey->cbData == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Use the internal function fo open the file + return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, phFile); +} + +bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile) +{ + CASC_ROOT_KEY_INFO EncodingKeyInfo; + PCASC_ROOT_ENTRY pRootEntry; + PCASC_PACKAGE pPackage; + TCascStorage * hs; + QUERY_KEY EncodingKey; + char * szStrippedName; + char * szFileName2; + int nError = ERROR_SUCCESS; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(szFileName == NULL || szFileName[0] == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Create the copy of the file name + szFileName2 = NewStr(szFileName, 0); + if(szFileName2 != NULL) + { + // If the storage has a MNDX root directory, use it to search the entry + if(hs->pMndxInfo != NULL) + { + // Convert the file name to lowercase + slashes + NormalizeFileName_LowerSlash(szFileName2); + + // Find the package number + pPackage = FindMndxPackage(hs, szFileName2); + if(pPackage != NULL) + { + // Cut the package name off the full path + szStrippedName = szFileName2 + pPackage->nLength; + while(szStrippedName[0] == '/') + szStrippedName++; + + nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &EncodingKeyInfo); + if(nError == ERROR_SUCCESS) + { + // Prepare the encoding key + EncodingKey.pbData = EncodingKeyInfo.EncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + } + } + else + { + nError = ERROR_FILE_NOT_FOUND; + } + } + else + { + // Convert the file name to lowercase + slashes + NormalizeFileName_UpperBkSlash(szFileName2); + + // Check the root directory for that hash + pRootEntry = FindRootEntryLocale(hs, szFileName2, dwLocale); + nError = (pRootEntry != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; + if(pRootEntry != NULL) + { + // Prepare the root key + EncodingKey.pbData = pRootEntry->EncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + } + } + + // Use the root key to find the file in the encoding table entry + if(nError == ERROR_SUCCESS) + { + if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, phFile)) + { + assert(GetLastError() != ERROR_SUCCESS); + nError = GetLastError(); + } + } + + // Delete the file name copy + delete [] szFileName2; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +bool WINAPI CascCloseFile(HANDLE hFile) +{ + TCascFile * hf; + + hf = IsValidFileHandle(hFile); + if(hf != NULL) + { + // Close (dereference) the archive handle + if(hf->hs != NULL) + CascCloseStorage((HANDLE)hf->hs); + hf->hs = NULL; + + // Free the file cache and frame array + if(hf->pbFileCache != NULL) + CASC_FREE(hf->pbFileCache); + if(hf->pFrames != NULL) + CASC_FREE(hf->pFrames); + + // Free the structure itself + hf->szClassName = NULL; + CASC_FREE(hf); + return true; + } + + SetLastError(ERROR_INVALID_HANDLE); + return false; +} + diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp new file mode 100644 index 00000000000..f9e6ba6f4d4 --- /dev/null +++ b/dep/CascLib/src/CascOpenStorage.cpp @@ -0,0 +1,1226 @@ +/*****************************************************************************/ +/* CascOpenStorage.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Storage functions for CASC */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascOpenStorage.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +//----------------------------------------------------------------------------- +// Local structures + +#define CASC_ENCODING_SEGMENT_SIZE 0x1000 + +typedef struct _BLOCK_SIZE_AND_HASH +{ + DWORD cbBlockSize; + DWORD dwBlockHash; + +} BLOCK_SIZE_AND_HASH, *PBLOCK_SIZE_AND_HASH; + +typedef struct _FILE_INDEX_HEADER_V1 +{ + USHORT field_0; + BYTE KeyIndex; // Key index (0 for data.i0x, 1 for data.i1x, 2 for data.i2x etc.) + BYTE align_3; + DWORD field_4; + ULONGLONG field_8; + ULONGLONG MaxFileOffset; + BYTE SpanSizeBytes; + BYTE SpanOffsBytes; + BYTE KeyBytes; + BYTE SegmentBits; // Number of bits for file offset + DWORD KeyCount1; + DWORD KeyCount2; + DWORD KeysHash1; + DWORD KeysHash2; + DWORD dwHeaderHash; +} FILE_INDEX_HEADER_V1, *PFILE_INDEX_HEADER_V1; + +typedef struct _FILE_INDEX_HEADER_V2 +{ + USHORT IndexVersion; // Must be 0x07 + BYTE KeyIndex; // Must be equal to the file key index + BYTE ExtraBytes; // (?) Extra bytes in the key record + BYTE SpanSizeBytes; // Size of field with file size + BYTE SpanOffsBytes; // Size of field with file offset + BYTE KeyBytes; // Size of the file key (bytes) + BYTE SegmentBits; // Number of bits for the file offset (rest is archive index) + ULONGLONG MaxFileOffset; + +} FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2; + +typedef struct _FILE_ENCODING_HEADER +{ + BYTE Magic[2]; // "EN" + BYTE field_2; + BYTE field_3; + BYTE field_4; + BYTE field_5[2]; + BYTE field_7[2]; + BYTE NumSegments[4]; // Number of entries (big endian) + BYTE field_D[4]; + BYTE field_11; + BYTE SegmentsPos[4]; // Offset of encoding segments + +} FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER; + +typedef struct _FILE_ENCODING_SEGMENT +{ + BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment + BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment + +} FILE_ENCODING_SEGMENT, *PFILE_ENCODING_SEGMENT; + +typedef struct _FILE_LOCALE_BLOCK +{ + DWORD NumberOfFiles; // Number of entries + DWORD Flags; + DWORD Locales; // File locale mask (CASC_LOCALE_XXX) + + // Followed by a block of 32-bit integers (count: NumberOfFiles) + // Followed by the MD5 and file name hash (count: NumberOfFiles) + +} FILE_LOCALE_BLOCK, *PFILE_LOCALE_BLOCK; + +typedef struct _FILE_ROOT_ENTRY +{ + BYTE EncodingKey[MD5_HASH_SIZE]; // MD5 of the file + ULONGLONG FileNameHash; // Jenkins hash of the file name + +} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY; + +typedef struct _ROOT_BLOCK_INFO +{ + PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block + PDWORD pInt32Array; // Pointer to the array of 32-bit integers + PFILE_ROOT_ENTRY pRootEntries; + +} ROOT_BLOCK_INFO, *PROOT_BLOCK_INFO; + +//----------------------------------------------------------------------------- +// Local variables + +static const TCHAR * szAllowedHexChars = _T("0123456789aAbBcCdDeEfF"); +static const TCHAR * szIndexFormat_V1 = _T("data.i%x%x"); +static const TCHAR * szIndexFormat_V2 = _T("%02x%08x.idx"); + +//----------------------------------------------------------------------------- +// Local functions + +inline void CopyFileKey(LPBYTE Trg, LPBYTE Src) +{ + Trg[0x00] = Src[0x00]; + Trg[0x01] = Src[0x01]; + Trg[0x02] = Src[0x02]; + Trg[0x03] = Src[0x03]; + Trg[0x04] = Src[0x04]; + Trg[0x05] = Src[0x05]; + Trg[0x06] = Src[0x06]; + Trg[0x07] = Src[0x07]; + Trg[0x08] = Src[0x08]; + Trg[0x09] = Src[0x09]; + Trg[0x0A] = Src[0x0A]; + Trg[0x0B] = Src[0x0B]; + Trg[0x0C] = Src[0x0C]; + Trg[0x0D] = Src[0x0D]; + Trg[0x0E] = Src[0x0E]; + Trg[0x0F] = Src[0x0F]; +} + +TCascStorage * IsValidStorageHandle(HANDLE hStorage) +{ + TCascStorage * hs = (TCascStorage *)hStorage; + + return (hs != NULL && hs->szClassName != NULL && !strcmp(hs->szClassName, "TCascStorage")) ? hs : NULL; +} + +// "data.iXY" +static bool IsIndexFileName_V1(const TCHAR * szFileName) +{ + // Check if the name looks like a valid index file + return (_tcslen(szFileName) == 8 && + _tcsnicmp(szFileName, _T("data.i"), 6) == 0 && + _tcsspn(szFileName + 6, szAllowedHexChars) == 2); +} + +static bool IsIndexFileName_V2(const TCHAR * szFileName) +{ + // Check if the name looks like a valid index file + return (_tcslen(szFileName) == 14 && + _tcsspn(szFileName, _T("0123456789aAbBcCdDeEfF")) == 0x0A && + _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0); +} + +static void QUERY_KEY_Free(PQUERY_KEY pBlob) +{ + if(pBlob != NULL) + { + if(pBlob->pbData != NULL) + CASC_FREE(pBlob->pbData); + + pBlob->pbData = NULL; + pBlob->cbData = 0; + } +} + +static void QUERY_KEY_FreeArray(PQUERY_KEY pBlobArray) +{ + // Free the buffer in the first blob + // (will also free all buffers in the array) + QUERY_KEY_Free(pBlobArray); + + // Free the array itself + CASC_FREE(pBlobArray); +} + +static int CompareRootEntries(const void *, const void * pvKeyEntry1, const void * pvKeyEntry2) +{ + PCASC_ROOT_ENTRY pRootEntry1 = (PCASC_ROOT_ENTRY)pvKeyEntry1; + PCASC_ROOT_ENTRY pRootEntry2 = (PCASC_ROOT_ENTRY)pvKeyEntry2; + + // Compare name hash first + if(pRootEntry1->FileNameHash < pRootEntry2->FileNameHash) + return -1; + if(pRootEntry1->FileNameHash > pRootEntry2->FileNameHash) + return +1; + return 0; +} + +static bool IsCascIndexHeader_V1(LPBYTE pbFileData, DWORD cbFileData) +{ + PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData; + DWORD dwHeaderHash; + bool bResult = false; + + // Check the size + if(cbFileData >= sizeof(FILE_INDEX_HEADER_V1)) + { + // Save the header hash + dwHeaderHash = pIndexHeader->dwHeaderHash; + pIndexHeader->dwHeaderHash = 0; + + // Calculate the hash + if(hashlittle(pIndexHeader, sizeof(FILE_INDEX_HEADER_V1), 0) == dwHeaderHash) + bResult = true; + + // Put the hash back + pIndexHeader->dwHeaderHash = dwHeaderHash; + } + + return bResult; +} + +static bool IsCascIndexHeader_V2(LPBYTE pbFileData, DWORD cbFileData) +{ + PBLOCK_SIZE_AND_HASH pSizeAndHash = (PBLOCK_SIZE_AND_HASH)pbFileData; + unsigned int HashHigh = 0; + unsigned int HashLow = 0; + + // Check for the header + if(cbFileData < sizeof(BLOCK_SIZE_AND_HASH) || pSizeAndHash->cbBlockSize < 0x10) + return false; + if(cbFileData < pSizeAndHash->cbBlockSize + sizeof(BLOCK_SIZE_AND_HASH)) + return false; + + // The index header for CASC v 2.0 begins with length and checksum + hashlittle2(pSizeAndHash + 1, pSizeAndHash->cbBlockSize, &HashHigh, &HashLow); + return (HashHigh == pSizeAndHash->dwBlockHash); +} + +static LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd) +{ + // Validate the locale header + pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer; + pbFilePointer += sizeof(FILE_LOCALE_BLOCK); + if(pbFilePointer >= pbFileEnd) + return NULL; + + // Validate the array of 32-bit integers + pBlockInfo->pInt32Array = (PDWORD)pbFilePointer; + pbFilePointer = (LPBYTE)(pBlockInfo->pInt32Array + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); + if(pbFilePointer >= pbFileEnd) + return NULL; + + // Validate the array of root entries + pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer; + pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles); + if(pbFilePointer > pbFileEnd) + return NULL; + + // Return the position of the next block + return pbFilePointer; +} + +static int InitializeCascDirectories(TCascStorage * hs, const TCHAR * szDataPath) +{ + TCHAR * szLastPathPart; + + // Save the game data directory + hs->szDataPath = NewStr(szDataPath, 0); + + // Save the root game directory + hs->szRootPath = NewStr(szDataPath, 0); + + // Find the last part + szLastPathPart = hs->szRootPath; + for(size_t i = 0; hs->szRootPath[i] != 0; i++) + { + if(hs->szRootPath[i] == '\\' || hs->szRootPath[i] == '/') + szLastPathPart = hs->szRootPath + i; + } + + // Cut the last part + if(szLastPathPart != NULL) + szLastPathPart[0] = 0; + return (hs->szRootPath && hs->szDataPath) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; +} + +static bool IndexDirectory_OnFileFound( + const TCHAR * szFileName, + PDWORD IndexArray, + PDWORD OldIndexArray, + void * pvContext) +{ + TCascStorage * hs = (TCascStorage *)pvContext; + DWORD IndexValue = 0; + DWORD IndexVersion = 0; + + // Auto-detect the format of the index file name + if(hs->szIndexFormat == NULL) + { + if(IsIndexFileName_V1(szFileName)) + hs->szIndexFormat = szIndexFormat_V1; + else if(IsIndexFileName_V2(szFileName)) + hs->szIndexFormat = szIndexFormat_V2; + else + return false; + } + + if(hs->szIndexFormat == szIndexFormat_V1) + { + // Check the index file name format + if(!IsIndexFileName_V1(szFileName)) + return false; + + // Get the main index from the first two digits + if(ConvertDigitToInt32(szFileName + 6, &IndexValue) != ERROR_SUCCESS) + return false; + if(ConvertDigitToInt32(szFileName + 7, &IndexVersion) != ERROR_SUCCESS) + return false; + } + + else if(hs->szIndexFormat == szIndexFormat_V2) + { + // Check the index file name format + if(!IsIndexFileName_V2(szFileName)) + return false; + + // Get the main index from the first two digits + if(ConvertStringToInt32(szFileName, 2, &IndexValue) != ERROR_SUCCESS) + return false; + if(ConvertStringToInt32(szFileName + 2, 8, &IndexVersion) != ERROR_SUCCESS) + return false; + } + else + { + // Should never happen + assert(false); + return false; + } + + // The index value must not be greater than 0x0F + if(IndexValue >= CASC_INDEX_COUNT) + return false; + + // If the new subindex is greater than the previous one, + // use this one instead + if(IndexVersion > IndexArray[IndexValue]) + { + OldIndexArray[IndexValue] = IndexArray[IndexValue]; + IndexArray[IndexValue] = IndexVersion; + } + else if(IndexVersion > OldIndexArray[IndexValue]) + { + OldIndexArray[IndexValue] = IndexVersion; + } + + // Note: WoW6 only keeps last two index files + // Any additional index files are deleted at this point + return true; +} + +static TCHAR * CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion) +{ + TCHAR szPlainName[0x40]; + + // Sanity checks + assert(hs->szIndexFormat != NULL); + assert(hs->szIndexPath != NULL); + assert(IndexValue <= 0x0F); + + // Create the full path + _stprintf(szPlainName, hs->szIndexFormat, IndexValue, IndexVersion); + return CombinePath(hs->szIndexPath, szPlainName); +} + +static int VerifyAndParseKeyMapping_V1(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex) +{ + PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pKeyMapping->pbFileData; + DWORD dwDataHash1; + DWORD dwDataHash2; + + // Verify the format + if(pIndexHeader->field_0 != 0x0005) + return ERROR_NOT_SUPPORTED; + if(pIndexHeader->KeyIndex != KeyIndex) + return ERROR_NOT_SUPPORTED; + if(pIndexHeader->field_8 == 0) + return ERROR_NOT_SUPPORTED; + + // Verofy the bit counts + if(pIndexHeader->SpanSizeBytes != 0x04 || + pIndexHeader->SpanOffsBytes != 0x05 || + pIndexHeader->KeyBytes != 0x09) + return ERROR_NOT_SUPPORTED; + + pKeyMapping->ExtraBytes = 0; + pKeyMapping->SpanSizeBytes = pIndexHeader->SpanSizeBytes; + pKeyMapping->SpanOffsBytes = pIndexHeader->SpanOffsBytes; + pKeyMapping->KeyBytes = pIndexHeader->KeyBytes; + pKeyMapping->SegmentBits = pIndexHeader->SegmentBits; + pKeyMapping->MaxFileOffset = pIndexHeader->MaxFileOffset; + + // Get the pointer to the key entry array + pKeyMapping->nIndexEntries = pIndexHeader->KeyCount1 + pIndexHeader->KeyCount2; + if(pKeyMapping->nIndexEntries != 0) + pKeyMapping->pIndexEntries = (PCASC_INDEX_ENTRY)(pKeyMapping->pbFileData + sizeof(FILE_INDEX_HEADER_V1)); + + // Verify hashes + dwDataHash1 = hashlittle(pKeyMapping->pIndexEntries, pIndexHeader->KeyCount1 * sizeof(CASC_INDEX_ENTRY), 0); + dwDataHash2 = hashlittle(pKeyMapping->pIndexEntries + pIndexHeader->KeyCount1, pIndexHeader->KeyCount2 * sizeof(CASC_INDEX_ENTRY), 0); + if(dwDataHash1 != pIndexHeader->KeysHash1 || dwDataHash2 != pIndexHeader->KeysHash2) + return ERROR_FILE_CORRUPT; + + return ERROR_SUCCESS; +} + +static int VerifyAndParseKeyMapping_V2(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex) +{ + PFILE_INDEX_HEADER_V2 pIndexHeader = (PFILE_INDEX_HEADER_V2)pKeyMapping->pbFileData; + PBLOCK_SIZE_AND_HASH pSizeAndHash; + LPBYTE pbLastPartEnd; + LPBYTE pbLastPart; + DWORD FilePosition; + DWORD LastPartLength; + unsigned int HashHigh = 0; + unsigned int HashLow = 0; + + // The index header v2 begins after the SizeAndHash + pSizeAndHash = (PBLOCK_SIZE_AND_HASH)pKeyMapping->pbFileData; + pIndexHeader = (PFILE_INDEX_HEADER_V2)(pSizeAndHash + 1); + if(pIndexHeader->IndexVersion != 0x07 || pIndexHeader->KeyIndex != KeyIndex) + return ERROR_BAD_FORMAT; + + if(pIndexHeader->ExtraBytes != 0x00 || + pIndexHeader->SpanSizeBytes != 0x04 || + pIndexHeader->SpanOffsBytes != 0x05 || + pIndexHeader->KeyBytes != CASC_FILE_KEY_SIZE) + return ERROR_BAD_FORMAT; + + // Remember the sizes + pKeyMapping->ExtraBytes = pIndexHeader->ExtraBytes; + pKeyMapping->SpanSizeBytes = pIndexHeader->SpanSizeBytes; + pKeyMapping->SpanOffsBytes = pIndexHeader->SpanOffsBytes; + pKeyMapping->KeyBytes = pIndexHeader->KeyBytes; + pKeyMapping->SegmentBits = pIndexHeader->SegmentBits; + pKeyMapping->MaxFileOffset = pIndexHeader->MaxFileOffset; + + // Get the data position + FilePosition = (sizeof(BLOCK_SIZE_AND_HASH) + pSizeAndHash->cbBlockSize + 0x0F) & 0xFFFFFFF0; + if((FilePosition + 0x08) > pKeyMapping->cbFileData) + return ERROR_BAD_FORMAT; + + // Get the pointer to "size+hash" block + pSizeAndHash = (PBLOCK_SIZE_AND_HASH)(pKeyMapping->pbFileData + FilePosition); + FilePosition += 0x08; + + if((FilePosition + pSizeAndHash->cbBlockSize) > pKeyMapping->cbFileData) + return ERROR_BAD_FORMAT; + if(pSizeAndHash->cbBlockSize < sizeof(CASC_INDEX_ENTRY)) + return ERROR_BAD_FORMAT; + + // Remember the array of file keys + pKeyMapping->pIndexEntries = (PCASC_INDEX_ENTRY)(pKeyMapping->pbFileData + FilePosition); + pKeyMapping->nIndexEntries = pSizeAndHash->cbBlockSize / sizeof(CASC_INDEX_ENTRY); + FilePosition += pSizeAndHash->cbBlockSize; + + // Verify the integrity of the key array + for(DWORD i = 0; i < pKeyMapping->nIndexEntries; i++) + hashlittle2(pKeyMapping->pIndexEntries + i, sizeof(CASC_INDEX_ENTRY), &HashHigh, &HashLow); + if(HashHigh != pSizeAndHash->dwBlockHash) + return ERROR_BAD_FORMAT; + + // Align the data position up to next 0x1000 + FilePosition = ALIGN_TO_SIZE(FilePosition, 0x1000); + if(FilePosition > pKeyMapping->cbFileData) + return ERROR_BAD_FORMAT; + + LastPartLength = pKeyMapping->cbFileData - FilePosition; + if(LastPartLength < 0x7800) + return ERROR_BAD_FORMAT; + + pbLastPart = pKeyMapping->pbFileData + FilePosition; + pbLastPartEnd = pbLastPart + ((LastPartLength >> 0x09) << 0x09); + + while(pbLastPart < pbLastPartEnd) + { + for(int i = 0; i < 0x1F8; i += 0x18) + { + PDWORD PtrLastPart = (PDWORD)pbLastPart; + if(PtrLastPart[0] == 0) + return ERROR_SUCCESS; + + HashLow = hashlittle(PtrLastPart + 1, 0x13, 0) | 0x80000000; + if(HashLow != PtrLastPart[0]) + return ERROR_BAD_FORMAT; + } + + pbLastPart += 0x200; + } + + return ERROR_SUCCESS; +} + +static int VerifyAndParseKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex) +{ + // Sanity checks + assert(pKeyMapping->pbFileData != NULL); + assert(pKeyMapping->cbFileData != 0); + + // Check for CASC version 2 + if(IsCascIndexHeader_V2(pKeyMapping->pbFileData, pKeyMapping->cbFileData)) + return VerifyAndParseKeyMapping_V2(pKeyMapping, KeyIndex); + + // Check for CASC version 1 + if(IsCascIndexHeader_V1(pKeyMapping->pbFileData, pKeyMapping->cbFileData)) + return VerifyAndParseKeyMapping_V1(pKeyMapping, KeyIndex); + + // Unknown CASC version + assert(false); + return ERROR_BAD_FORMAT; +} + +static int LoadKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex) +{ + TFileStream * pStream; + ULONGLONG FileSize = 0; + int nError = ERROR_SUCCESS; + + // Sanity checks + assert(pKeyMapping->szFileName != NULL && pKeyMapping->szFileName[0] != 0); + + // Open the stream for read-only access and read the file + pStream = FileStream_OpenFile(pKeyMapping->szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + if(pStream != NULL) + { + // Retrieve the file size + FileStream_GetSize(pStream, &FileSize); + if((0 < FileSize && FileSize <= 0x90000) || 1) + { + // WoW6 actually reads THE ENTIRE file to memory + // Verified on Mac build (x64) + pKeyMapping->pbFileData = CASC_ALLOC(BYTE, (DWORD)FileSize); + pKeyMapping->cbFileData = (DWORD)FileSize; + + // Load the data to memory and parse it + if(pKeyMapping->pbFileData != NULL) + { + if(FileStream_Read(pStream, NULL, pKeyMapping->pbFileData, pKeyMapping->cbFileData)) + { + nError = VerifyAndParseKeyMapping(pKeyMapping, KeyIndex); + } + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + else + { + assert(false); + nError = ERROR_BAD_FORMAT; + } + + // Close the file stream + FileStream_Close(pStream); + } + else + nError = GetLastError(); + + return ERROR_SUCCESS; +} + +static int CreateArrayOfIndexEntries(TCascStorage * hs) +{ + PCASC_MAP pMap; + DWORD TotalCount = 0; + int nError = ERROR_NOT_ENOUGH_MEMORY; + + // Count the total number of files in the storage + for(size_t i = 0; i < CASC_INDEX_COUNT; i++) + TotalCount += hs->KeyMapping[i].nIndexEntries; + + // Create the map of all index entries + pMap = Map_Create(TotalCount, CASC_FILE_KEY_SIZE, FIELD_OFFSET(CASC_INDEX_ENTRY, IndexKey)); + if(pMap != NULL) + { + // Put all index entries in the map + for(size_t i = 0; i < CASC_INDEX_COUNT; i++) + { + PCASC_INDEX_ENTRY pIndexEntry = hs->KeyMapping[i].pIndexEntries; + DWORD nIndexEntries = hs->KeyMapping[i].nIndexEntries; + + for(DWORD j = 0; j < nIndexEntries; j++) + { + // Insert the index entry to the map + // Note that duplicate entries will not be inserted to the map + // + // Duplicate entries in WoW-WOD build 18179: + // 9e dc a7 8f e2 09 ad d8 b7 (encoding file) + // f3 5e bb fb d1 2b 3f ef 8b + // c8 69 9f 18 a2 5e df 7e 52 + Map_InsertObject(pMap, pIndexEntry->IndexKey); + + // Move to the next entry + pIndexEntry++; + } + } + + // Store the map to the storage handle + hs->pIndexEntryMap = pMap; + nError = ERROR_SUCCESS; + } + + return nError; +} + +static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumberOfSegments) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + size_t nMaxEntries; + size_t nEntries = 0; + int nError = ERROR_SUCCESS; + + // Sanity check + assert(hs->ppEncodingEntries == NULL); + assert(hs->pIndexEntryMap != NULL); + + // Calculate the largest eventual number of encodign entries + nMaxEntries = (dwNumberOfSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE); + + // Allocate the array of pointers to encoding entries + hs->ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, nMaxEntries); + if(hs->ppEncodingEntries != NULL) + { + LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments); + + // Parse all segments + for(DWORD i = 0; i < dwNumberOfSegments; i++) + { + LPBYTE pbEncodingEntry = pbStartOfSegment; + LPBYTE pbEndOfSegment = pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE - sizeof(CASC_ENCODING_ENTRY) - MD5_HASH_SIZE; + + // Parse all encoding entries + while(pbEncodingEntry <= pbEndOfSegment) + { + // Get pointer to the encoding entry + pEncodingEntry = (PCASC_ENCODING_ENTRY)pbEncodingEntry; + if(pEncodingEntry->KeyCount == 0) + break; + + // Insert the pointer the array + hs->ppEncodingEntries[nEntries++] = pEncodingEntry; + + // Move to the next encoding entry + pbEncodingEntry += sizeof(CASC_ENCODING_ENTRY) + (pEncodingEntry->KeyCount * MD5_HASH_SIZE); + } + + // Move to the next segment + pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE; + } + + // Remember the total number of encoding entries + hs->nEncodingEntries = nEntries; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + + return nError; +} + +static DWORD GetSizeOfEncodingFile(HANDLE hFile) +{ + CASC_ENCODING_HEADER EncodingHeader; + DWORD cbEncodingFile = 0; + DWORD dwSegmentPos; + DWORD dwNumSegments; + DWORD dwBytesRead; + + // Read the endoding header + CascReadFile(hFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER), &dwBytesRead); + if(dwBytesRead == sizeof(CASC_ENCODING_HEADER)) + { + dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.NumSegments); + dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.SegmentsPos); + + cbEncodingFile = sizeof(CASC_ENCODING_HEADER) + + dwSegmentPos + + dwNumSegments * (sizeof(FILE_ENCODING_SEGMENT) + CASC_ENCODING_SEGMENT_SIZE); + } + + // Reset the position back + CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN); + return cbEncodingFile; +} + +static LPBYTE LoadCascFile(HANDLE hFile, DWORD cbMaxSize, PDWORD pcbFileData) +{ + LPBYTE pbFileData = NULL; + DWORD cbFileData; + DWORD dwBytesRead = 0; + int nError = ERROR_SUCCESS; + + // Retrieve the size of the file + cbFileData = CascGetFileSize(hFile, NULL); + if(cbFileData != 0 && cbFileData != CASC_INVALID_SIZE) + { + // Trim the size to the maximum + cbFileData = CASCLIB_MIN(cbMaxSize, cbFileData); + + // Allocate the buffer that will hold the entire file + pbFileData = CASC_ALLOC(BYTE, cbFileData); + if(pbFileData != NULL) + { + // Read the entire file to memory + CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead); + if(dwBytesRead != cbFileData) + nError = ERROR_FILE_CORRUPT; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + else + nError = ERROR_FILE_CORRUPT; + + // If something failed, clean-up the buffers + if(nError != ERROR_SUCCESS) + { + // Clear the file data + if(pbFileData != NULL) + CASC_FREE(pbFileData); + pbFileData = NULL; + cbFileData = 0; + + // Set the last error value + SetLastError(nError); + } + + // Return what we got + if(pcbFileData != NULL) + *pcbFileData = cbFileData; + return pbFileData; +} + +static int LoadIndexFiles(TCascStorage * hs) +{ + DWORD IndexArray[CASC_INDEX_COUNT]; + DWORD OldIndexArray[CASC_INDEX_COUNT]; + int nError; + int i; + + // Scan all index files + memset(IndexArray, 0, sizeof(IndexArray)); + memset(OldIndexArray, 0, sizeof(OldIndexArray)); + nError = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs); + if(nError == ERROR_SUCCESS) + { + // Load each index file + for(i = 0; i < CASC_INDEX_COUNT; i++) + { + hs->KeyMapping[i].szFileName = CreateIndexFileName(hs, i, IndexArray[i]); + if(hs->KeyMapping[i].szFileName != NULL) + { + nError = LoadKeyMapping(&hs->KeyMapping[i], i); + if(nError != ERROR_SUCCESS) + break; + } + } + } + + // Now we need to build the map of the index entries + if(nError == ERROR_SUCCESS) + { + nError = CreateArrayOfIndexEntries(hs); + } + + return nError; +} + +static int LoadEncodingFile(TCascStorage * hs) +{ + PFILE_ENCODING_SEGMENT pEncodingSegment; + PCASC_ENCODING_ENTRY pEncodingEntry; + LPBYTE pbStartOfSegment; + LPBYTE pbEncodingFile = NULL; + HANDLE hFile = NULL; + DWORD cbEncodingFile = 0; + DWORD dwNumberOfSegments = 0; + DWORD dwSegmentsPos = 0; + int nError = ERROR_SUCCESS; + + // Open the encoding file + if(!CascOpenFileByIndexKey((HANDLE)hs, &hs->EncodingEKey, 0, &hFile)) + nError = GetLastError(); + + // Load the encoding file to memory + if(nError == ERROR_SUCCESS) + { + // Retrieve the CASC header. We do not usually need to load + // the entire file, but we need to know how big part of it we need + cbEncodingFile = GetSizeOfEncodingFile(hFile); + + // Load the entire file to memory + pbEncodingFile = LoadCascFile(hFile, cbEncodingFile, &cbEncodingFile); + if(pbEncodingFile == NULL || cbEncodingFile <= sizeof(CASC_ENCODING_HEADER)) + nError = ERROR_FILE_CORRUPT; + + CascCloseFile(hFile); + } + + // Verify all encoding segments + if(nError == ERROR_SUCCESS) + { + // Save the encoding header + hs->pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile; + + // Convert size and offset + dwNumberOfSegments = ConvertBytesToInteger_4(hs->pEncodingHeader->NumSegments); + dwSegmentsPos = ConvertBytesToInteger_4(hs->pEncodingHeader->SegmentsPos); + + // Allocate the array of encoding segments + pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos); + pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments); + + // Go through all encoding segments and verify them + for(DWORD i = 0; i < dwNumberOfSegments; i++) + { + // Check if there is enough space in the buffer + if((pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE) > (pbEncodingFile + cbEncodingFile)) + { + nError = ERROR_FILE_CORRUPT; + break; + } + + // Check the hash of the entire segment + // Note that verifying takes considerable time of the storage loading +// if(!VerifyDataBlockHash(pbStartOfSegment, CASC_ENCODING_SEGMENT_SIZE, pEncodingSegment->SegmentHash)) +// { +// nError = ERROR_FILE_CORRUPT; +// break; +// } + + // Check if the encoding key matches + pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment; + if(memcmp(pEncodingEntry->EncodingKey, pEncodingSegment->FirstEncodingKey, MD5_HASH_SIZE)) + { + nError = ERROR_FILE_CORRUPT; + break; + } + + // Move to the next segment + pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE; + pEncodingSegment++; + } + } + + // Create the map of the encoding keys + // Note that the array of encoding keys is already sorted - no need to sort it + if(nError == ERROR_SUCCESS) + { + pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos); + nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumberOfSegments); + } + return nError; +} + +static int LoadRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +{ + PFILE_ROOT_ENTRY pSrcEntry; + PCASC_ROOT_ENTRY pTrgEntry; + ROOT_BLOCK_INFO BlockInfo; + LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; + LPBYTE pbFilePointer; + size_t nRootEntries = 0; + size_t nRootIndex = 0; + int nError = ERROR_NOT_ENOUGH_MEMORY; + + // Calculate the root entries + for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; ) + { + // Validate the root block + pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd); + if(pbFilePointer == NULL) + break; + + // Add the number of entries + nRootEntries = nRootEntries + BlockInfo.pLocaleBlockHdr->NumberOfFiles; + } + + // Create a linear array of the root entries and sort it + hs->pRootEntries = pTrgEntry = CASC_ALLOC(CASC_ROOT_ENTRY, nRootEntries); + hs->ppRootEntries = CASC_ALLOC(PCASC_ROOT_ENTRY, nRootEntries); + if(hs->ppRootEntries && hs->pRootEntries) + { + // Convert each entry from FILE_ROOT_ENTRY to CASC_ROOT_ENTRY + for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; ) + { + // Validate the root block + pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd); + if(pbFilePointer == NULL) + break; + + // Get the pointer to the first root entry + pSrcEntry = (PFILE_ROOT_ENTRY)BlockInfo.pRootEntries; + + // Convert all entries + for(DWORD i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++) + { + // Copy the root entry + CopyFileKey(pTrgEntry->EncodingKey, pSrcEntry->EncodingKey); + pTrgEntry->FileNameHash = pSrcEntry->FileNameHash; + pTrgEntry->Locales = BlockInfo.pLocaleBlockHdr->Locales; + pTrgEntry->Flags = BlockInfo.pLocaleBlockHdr->Flags; + +// if(pTrgEntry->FileNameHash == 0x5ddb88608673f698ULL) +// DebugBreak(); + + // Insert the CASC root entry to the linear array of pointers + hs->ppRootEntries[nRootIndex++] = pTrgEntry; + + // Move to the next root entry + pSrcEntry++; + pTrgEntry++; + } + } + + // Save the number of entries + assert(nRootIndex == nRootEntries); + hs->nRootEntries = nRootIndex; + + // Now sort the array + qsort_pointer_array((void **)hs->ppRootEntries, hs->nRootEntries, CompareRootEntries, NULL); + nError = ERROR_SUCCESS; + } + + return nError; + +/* + FILE * fp = fopen("E:\\root_entries.txt", "wt"); + if(fp != NULL) + { + for(size_t i = 0; i < nRootEntries; i++) + { + fprintf(fp, "%08X: %016I64lX\n", i, hs->ppRootEntries[i]->FileNameHash); + } + fclose(fp); + } +*/ +} + +static int LoadRootFile(TCascStorage * hs) +{ + PDWORD FileSignature; + HANDLE hFile = NULL; + LPBYTE pbRootFile = NULL; + DWORD cbRootFile = 0; + int nError = ERROR_SUCCESS; + + // Sanity checks + assert(hs->ppEncodingEntries != NULL); + assert(hs->pRootEntries == NULL); + assert(hs->nRootEntries == 0); + + // The root file is either MNDX file (Heroes of the Storm) + // or a file containing an array of root entries (World of Warcraft 6.0+) + // Note: The "root" key file's MD5 hash is equal to its name + // in the configuration + if(!CascOpenFileByEncodingKey((HANDLE)hs, &hs->RootKey, 0, &hFile)) + nError = GetLastError(); + + // Load ther entire root file to memory + if(nError == ERROR_SUCCESS) + { + // Load the entire root file to memory + pbRootFile = LoadCascFile(hFile, 0xFFFFFFFF, &cbRootFile); + if(pbRootFile == NULL || cbRootFile == 0) + nError = ERROR_FILE_CORRUPT; + + // Close the root file + CascCloseFile(hFile); + } + + // Check if the file is a MNDX file + if(nError == ERROR_SUCCESS) + { + FileSignature = (PDWORD)pbRootFile; + if(FileSignature[0] == CASC_MNDX_SIGNATURE) + { + nError = LoadMndxRootFile(hs, pbRootFile, cbRootFile); + } + else + { + nError = LoadRootFile(hs, pbRootFile, cbRootFile); + } + } + + // Free the root file + CASC_FREE(pbRootFile); + return nError; +} + +static TCascStorage * FreeCascStorage(TCascStorage * hs) +{ + size_t i; + + if(hs != NULL) + { + // Free the MNDX info + if(hs->pPackages != NULL) + CASC_FREE(hs->pPackages); + if(hs->pMndxInfo != NULL) + FreeMndxInfo(hs->pMndxInfo); + + // Free the pointers to file entries + if(hs->ppRootEntries != NULL) + CASC_FREE(hs->ppRootEntries); + if(hs->pRootEntries != NULL) + CASC_FREE(hs->pRootEntries); + if(hs->ppEncodingEntries != NULL) + CASC_FREE(hs->ppEncodingEntries); + if(hs->pEncodingHeader != NULL) + CASC_FREE(hs->pEncodingHeader); + if(hs->pIndexEntryMap != NULL) + Map_Free(hs->pIndexEntryMap); + + // Close all data files + for(i = 0; i < CASC_MAX_DATA_FILES; i++) + { + if(hs->DataFileArray[i] != NULL) + { + FileStream_Close(hs->DataFileArray[i]); + hs->DataFileArray[i] = NULL; + } + } + + // Close all key mappings + for(i = 0; i < CASC_INDEX_COUNT; i++) + { + if(hs->KeyMapping[i].szFileName != NULL) + CASC_FREE(hs->KeyMapping[i].szFileName); + if(hs->KeyMapping[i].pbFileData != NULL) + CASC_FREE(hs->KeyMapping[i].pbFileData); + hs->KeyMapping[i].pIndexEntries = NULL; + } + + // Free the file paths + if(hs->szRootPath != NULL) + CASC_FREE(hs->szRootPath); + if(hs->szDataPath != NULL) + CASC_FREE(hs->szDataPath); + if(hs->szIndexPath != NULL) + CASC_FREE(hs->szIndexPath); + if(hs->szUrlPath != NULL) + CASC_FREE(hs->szUrlPath); + + // Fre the blob arrays + QUERY_KEY_FreeArray(hs->pArchiveArray); + QUERY_KEY_FreeArray(hs->pPatchArchiveArray); + QUERY_KEY_FreeArray(hs->pEncodingKeys); + + // Free the blobs + QUERY_KEY_Free(&hs->CdnConfigKey); + QUERY_KEY_Free(&hs->CdnBuildKey); + QUERY_KEY_Free(&hs->ArchiveGroup); + QUERY_KEY_Free(&hs->PatchArchiveGroup); + QUERY_KEY_Free(&hs->RootKey); + QUERY_KEY_Free(&hs->PatchKey); + QUERY_KEY_Free(&hs->DownloadKey); + QUERY_KEY_Free(&hs->InstallKey); + + // Free the storage structure + hs->szClassName = NULL; + CASC_FREE(hs); + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Public functions + +bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * phStorage) +{ + TCascStorage * hs; + int nError = ERROR_SUCCESS; + + CASCLIB_UNUSED(dwFlags); + + // Allocate the storage structure + hs = (TCascStorage *)CASC_ALLOC(TCascStorage, 1); + if(hs == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + + // Load the storage configuration + if(nError == ERROR_SUCCESS) + { + // Prepare the base storage parameters + memset(hs, 0, sizeof(TCascStorage)); + hs->szClassName = "TCascStorage"; + hs->dwRefCount = 1; + nError = InitializeCascDirectories(hs, szDataPath); + } + + // Now we need to load the root file so we know the config files + if(nError == ERROR_SUCCESS) + { + nError = LoadBuildConfiguration(hs); + } + + // Load the index files + if(nError == ERROR_SUCCESS) + { + nError = LoadIndexFiles(hs); + } + + // Load the index files + if(nError == ERROR_SUCCESS) + { + nError = LoadEncodingFile(hs); + } + + // Load the index files + if(nError == ERROR_SUCCESS) + { + nError = LoadRootFile(hs); + } + +#ifdef _DEBUG +// if(nError == ERROR_SUCCESS) +// { +// CascDumpStorage("E:\\casc_dump.txt", hs, _T("e:\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt")); +// CascDumpIndexEntries("E:\\casc_index.txt", hs); +// } +#endif + + // If something failed, free the storage and return + if(nError != ERROR_SUCCESS) + { + hs = FreeCascStorage(hs); + SetLastError(nError); + } + + *phStorage = (HANDLE)hs; + return (nError == ERROR_SUCCESS); +} + +bool WINAPI CascGetStorageInfo( + HANDLE hStorage, + CASC_STORAGE_INFO_CLASS InfoClass, + void * pvStorageInfo, + size_t cbStorageInfo, + size_t * pcbLengthNeeded) +{ + TCascStorage * hs; + DWORD dwCascFeatures = 0; + + // Verify the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Differentiate between info classes + switch(InfoClass) + { + case CascStorageFileCount: + + // Check the buffer size + if(cbStorageInfo < sizeof(DWORD)) + { + *pcbLengthNeeded = sizeof(DWORD); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return false; + } + + // Give the number of files + *(PDWORD)pvStorageInfo = (DWORD)hs->pIndexEntryMap->ItemCount; + return true; + + case CascStorageFeatures: + + // Check the buffer size + if(cbStorageInfo < sizeof(DWORD)) + { + *pcbLengthNeeded = sizeof(DWORD); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return false; + } + + // Construct the features + if(hs->pMndxInfo != NULL) + dwCascFeatures |= CASC_FEATURE_LISTFILE; + + // Give the number of files + *(PDWORD)pvStorageInfo = dwCascFeatures; + return true; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } +} + + + +bool WINAPI CascCloseStorage(HANDLE hStorage) +{ + TCascStorage * hs; + + // Verify the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Only free the storage if the reference count reaches 0 + if(hs->dwRefCount == 1) + { + FreeCascStorage(hs); + return true; + } + + // Just decrement number of references + hs->dwRefCount--; + return true; +} + diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h new file mode 100644 index 00000000000..6d0595ce522 --- /dev/null +++ b/dep/CascLib/src/CascPort.h @@ -0,0 +1,260 @@ +/*****************************************************************************/ +/* CascPort.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Portability module for the CascLib library. Contains a wrapper symbols */ +/* to make the compilation under Linux work */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __CASCPORT_H__ +#define __CASCPORT_H__ + +#ifndef __cplusplus + #define bool char + #define true 1 + #define false 0 +#endif + +//----------------------------------------------------------------------------- +// Defines for Windows + +#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64)) + + // In MSVC 8.0, there are some functions declared as deprecated. + #if _MSC_VER >= 1400 + #define _CRT_SECURE_NO_DEPRECATE + #define _CRT_NON_CONFORMING_SWPRINTFS + #endif + + #include <tchar.h> + #include <assert.h> + #include <ctype.h> + #include <stdio.h> + #include <windows.h> + #include <wininet.h> + #include <sys/types.h> + #define PLATFORM_LITTLE_ENDIAN + + #ifdef WIN64 + #define PLATFORM_64BIT + #else + #define PLATFORM_32BIT + #endif + + #define PATH_SEPARATOR '\\' + #define CREATE_DIRECTORY(name) CreateDirectory(name, NULL); + + #define PLATFORM_WINDOWS + #define PLATFORM_DEFINED // The platform is known now + +#endif + +//----------------------------------------------------------------------------- +// Defines for Mac + +#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API + + // Macintosh + #include <sys/types.h> + #include <sys/stat.h> + #include <sys/mman.h> + #include <unistd.h> + #include <fcntl.h> + #include <stdlib.h> + #include <dirent.h> + #include <errno.h> + + // Support for PowerPC on Max OS X + #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) + #include <stdint.h> + #include <CoreFoundation/CFByteOrder.h> + #endif + + #define PKEXPORT + #define __SYS_ZLIB + #define __SYS_BZLIB + + #ifndef __BIG_ENDIAN__ + #define PLATFORM_LITTLE_ENDIAN + #endif + + #define PATH_SEPARATOR '/' + #define CREATE_DIRECTORY(name) mkdir(name, 0755) + + #define PLATFORM_MAC + #define PLATFORM_DEFINED // The platform is known now + + #define FIELD_OFFSET(t,f) offsetof(t,f) +#endif + +//----------------------------------------------------------------------------- +// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* + +#if !defined(PLATFORM_DEFINED) + + #include <sys/types.h> + #include <sys/stat.h> + #include <sys/mman.h> + #include <fcntl.h> + #include <dirent.h> + #include <unistd.h> + #include <stddef.h> + #include <stdint.h> + #include <stdlib.h> + #include <stdio.h> + #include <stdarg.h> + #include <string.h> + #include <ctype.h> + #include <assert.h> + #include <errno.h> + + #define PATH_SEPARATOR '/' + #define CREATE_DIRECTORY(name) mkdir(name, 0755) + + #define PLATFORM_LITTLE_ENDIAN + #define PLATFORM_LINUX + #define PLATFORM_DEFINED + + #define FIELD_OFFSET(t,f) offsetof(t,f) +#endif + +//----------------------------------------------------------------------------- +// Definition of Windows-specific types for non-Windows platforms + +#ifndef PLATFORM_WINDOWS + #if __LP64__ + #define PLATFORM_64BIT + #else + #define PLATFORM_32BIT + #endif + + // Typedefs for ANSI C + typedef unsigned char BYTE; + typedef unsigned short USHORT; + typedef int LONG; + typedef unsigned int DWORD; + typedef unsigned long DWORD_PTR; + typedef long LONG_PTR; + typedef long INT_PTR; + typedef long long LONGLONG; + typedef unsigned long long ULONGLONG; + typedef unsigned long long *PULONGLONG; + typedef void * HANDLE; + typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac + typedef char TCHAR; + typedef unsigned int LCID; + typedef LONG * PLONG; + typedef DWORD * PDWORD; + typedef BYTE * LPBYTE; + + #ifdef PLATFORM_32BIT + #define _LZMA_UINT32_IS_ULONG + #endif + + // Some Windows-specific defines + #ifndef MAX_PATH + #define MAX_PATH 1024 + #endif + + #define WINAPI + + #define FILE_BEGIN SEEK_SET + #define FILE_CURRENT SEEK_CUR + #define FILE_END SEEK_END + + #define INVALID_HANDLE_VALUE ((HANDLE)-1) + + #define _T(x) x + #define _tcslen strlen + #define _tcscpy strcpy + #define _tcscat strcat + #define _tcschr strchr + #define _tcsrchr strrchr + #define _tcsstr strstr + #define _tcsspn strspn + #define _tprintf printf + #define _stprintf sprintf + #define _tremove remove + #define _tmkdir mkdir + + #define _stricmp strcasecmp + #define _strnicmp strncasecmp + #define _tcsicmp strcasecmp + #define _tcsnicmp strncasecmp + +#endif // !PLATFORM_WINDOWS + +// 64-bit calls are supplied by "normal" calls on Mac +#if defined(PLATFORM_MAC) + #define stat64 stat + #define fstat64 fstat + #define lseek64 lseek + #define ftruncate64 ftruncate + #define off64_t off_t + #define O_LARGEFILE 0 +#endif + +// Platform-specific error codes for UNIX-based platforms +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + #define ERROR_SUCCESS 0 + #define ERROR_FILE_NOT_FOUND ENOENT + #define ERROR_ACCESS_DENIED EPERM + #define ERROR_INVALID_HANDLE EBADF + #define ERROR_NOT_ENOUGH_MEMORY ENOMEM + #define ERROR_NOT_SUPPORTED ENOTSUP + #define ERROR_INVALID_PARAMETER EINVAL + #define ERROR_DISK_FULL ENOSPC + #define ERROR_ALREADY_EXISTS EEXIST + #define ERROR_INSUFFICIENT_BUFFER ENOBUFS + #define ERROR_BAD_FORMAT 1000 // No such error code under Linux + #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux + #define ERROR_HANDLE_EOF 1002 // No such error code under Linux + #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux + #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux +#endif + +//----------------------------------------------------------------------------- +// Swapping functions + +#ifdef PLATFORM_LITTLE_ENDIAN + #define BSWAP_INT16_UNSIGNED(a) (a) + #define BSWAP_INT16_SIGNED(a) (a) + #define BSWAP_INT32_UNSIGNED(a) (a) + #define BSWAP_INT32_SIGNED(a) (a) + #define BSWAP_INT64_SIGNED(a) (a) + #define BSWAP_INT64_UNSIGNED(a) (a) + #define BSWAP_ARRAY16_UNSIGNED(a,b) {} + #define BSWAP_ARRAY32_UNSIGNED(a,b) {} + #define BSWAP_ARRAY64_UNSIGNED(a,b) {} +#else + +#ifdef __cplusplus + extern "C" { +#endif + int16_t SwapInt16(uint16_t); + uint16_t SwapUInt16(uint16_t); + int32_t SwapInt32(uint32_t); + uint32_t SwapUInt32(uint32_t); + int64_t SwapInt64(uint64_t); + uint64_t SwapUInt64(uint64_t); + void ConvertUInt16Buffer(void * ptr, size_t length); + void ConvertUInt32Buffer(void * ptr, size_t length); + void ConvertUInt64Buffer(void * ptr, size_t length); +#ifdef __cplusplus + } +#endif + #define BSWAP_INT16_SIGNED(a) SwapInt16((a)) + #define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) + #define BSWAP_INT32_SIGNED(a) SwapInt32((a)) + #define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) + #define BSWAP_INT64_SIGNED(a) SwapInt64((a)) + #define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) + #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) + #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) + #define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) +#endif + +#endif // __CASCPORT_H__ diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp new file mode 100644 index 00000000000..64dd66ef88a --- /dev/null +++ b/dep/CascLib/src/CascReadFile.cpp @@ -0,0 +1,475 @@ +/*****************************************************************************/ +/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +#define BLTE_HEADER_SIGNATURE 0x45544C42 + +// Data file begin: +// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array +// DWORD dwFileSize; // Size of the file +// BYTE SomeSize[4]; // Some size (big endian) +// BYTE Padding[6]; // Padding (?) + +typedef struct _BLTE_HEADER +{ + DWORD dwSignature; // Must be "BLTE" + BYTE HeaderSizeAsBytes[4]; // Header size in bytes (big endian) + BYTE MustBe0F; // Must be 0x0F + BYTE FrameCount[3]; // Number of frames (big endian) + +} BLTE_HEADER, *PBLTE_HEADER; + +typedef struct _BLTE_FRAME +{ + BYTE CompressedSize[4]; // Compressed file size as big endian + BYTE FrameSize[4]; // File size as big endian + BYTE md5[MD5_HASH_SIZE]; // Hash of the frame + +} BLTE_FRAME, *PBLTE_FRAME; + +//----------------------------------------------------------------------------- +// Local functions + +TCascFile * IsValidFileHandle(HANDLE hFile); // In CascOpenFile.cpp + +static int EnsureDataStreamIsOpen(TCascFile * hf) +{ + TCascStorage * hs = hf->hs; + TFileStream * pStream = NULL; + TCHAR * szDataFile; + TCHAR szPlainName[0x40]; + + // If the file is not open yet, do it + if(hs->DataFileArray[hf->ArchiveIndex] == NULL) + { + // Prepare the name of the data file + _stprintf(szPlainName, _T("data.%03u"), hf->ArchiveIndex); + szDataFile = CombinePath(hs->szIndexPath, szPlainName); + + // Open the data file + if(szDataFile != NULL) + { + // Open the stream + pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + hs->DataFileArray[hf->ArchiveIndex] = pStream; + + // TODO: There is 0x1E bytes at the beginning of the file stream + // Ignore them for now, but we will want to know what they mean + // Offs0000: MD5 of something + // Offs0010: 2 bytes + CASC_FREE(szDataFile); + } + } + + // Return error or success + hf->pStream = hs->DataFileArray[hf->ArchiveIndex]; + return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; +} + +static int LoadFileFrames(TCascFile * hf, DWORD FrameCount) +{ + PBLTE_FRAME pFileFrames; + PBLTE_FRAME pFileFrame; + ULONGLONG ArchiveFileOffset; + DWORD FrameOffset = 0; + DWORD FileSize = 0; + int nError = ERROR_SUCCESS; + + assert(hf != NULL); + assert(hf->pStream != NULL); + assert(hf->pFrames != NULL); + + // Allocate frame array + pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, FrameCount); + if(pFileFrames != NULL) + { + // Load the frame array + ArchiveFileOffset = hf->FramesOffset; + if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, FrameCount * sizeof(BLTE_FRAME))) + { + // Move the raw archive offset + ArchiveFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME)); + + // Copy the frames to the file structure + for(DWORD i = 0; i < FrameCount; i++, pFileFrame++) + { + hf->pFrames[i].FrameArchiveOffset = (DWORD)ArchiveFileOffset; + hf->pFrames[i].FrameFileOffset = FrameOffset; + hf->pFrames[i].CompressedSize = ConvertBytesToInteger_4(pFileFrame->CompressedSize); + hf->pFrames[i].FrameSize = ConvertBytesToInteger_4(pFileFrame->FrameSize); + memcpy(hf->pFrames[i].md5, pFileFrame->md5, MD5_HASH_SIZE); + + ArchiveFileOffset += hf->pFrames[i].CompressedSize; + FrameOffset += hf->pFrames[i].FrameSize; + FileSize += hf->pFrames[i].FrameSize; + } + + // Fill-in the frame count + hf->FrameCount = FrameCount; + } + else + nError = GetLastError(); + + // Verify the file size +// assert(FileSize == hf->FileSize); +// hf->FileSize = FileSize; + + // Free the array + CASC_FREE(pFileFrames); + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + + return nError; +} + +static int EnsureFrameHeadersLoaded(TCascFile * hf) +{ + PBLTE_HEADER pBlteHeader; + ULONGLONG FileOffset = hf->HeaderOffset; + DWORD dwHeaderOffsetFixup = 0; + DWORD dwFrameHeaderSize; + DWORD dwFrameCount; + BYTE HeaderBuffer[sizeof(BLTE_HEADER) + 0x20]; + int nError = ERROR_SUCCESS; + + // Sanity check + assert(hf->pStream != NULL); + + // If the frame headers are not loaded yet, do it + if(hf->pFrames == NULL) + { + // Note that older builds of Heroes of the Storm have entries pointing + // to the begin of the BLTE header, which is MD5 + some junk. + // Newer versions of HOTS have encoding entries pointing directly to + // the BLTE header + FileStream_Read(hf->pStream, &FileOffset, HeaderBuffer, sizeof(HeaderBuffer)); + pBlteHeader = (PBLTE_HEADER)HeaderBuffer; + + // If we don't have the BLTE header right there, + // just get the block that is 0x1E bytes later + if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE) + { + memcpy(&HeaderBuffer[0x00], &HeaderBuffer[0x1E], sizeof(BLTE_HEADER)); + dwHeaderOffsetFixup = 0x1E; + } + + // Check for the BLTE header signature + if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE) + return ERROR_BAD_FORMAT; + hf->HeaderOffset += dwHeaderOffsetFixup; + + // Check for a single unit file + dwFrameHeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSizeAsBytes); + dwFrameCount = (dwFrameHeaderSize != 0) ? ConvertBytesToInteger_3(pBlteHeader->FrameCount) : 1; + + // Allocate the frame array + hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, dwFrameCount); + if(hf->pFrames != NULL) + { + // Save the number of frames + hf->FrameCount = dwFrameCount; + + // Either load the frames from the file or supply them on our own + if(dwFrameHeaderSize != 0) + { + if(pBlteHeader->MustBe0F != 0x0F) + return ERROR_FILE_CORRUPT; + + hf->FramesOffset = hf->HeaderOffset + sizeof(BLTE_HEADER); + nError = LoadFileFrames(hf, dwFrameCount); + } + else + { + // Offset of the first frame is right after the file frames + hf->FramesOffset = hf->HeaderOffset + sizeof(pBlteHeader->dwSignature) + sizeof(pBlteHeader->HeaderSizeAsBytes); + + hf->pFrames[0].FrameArchiveOffset = hf->FramesOffset; + hf->pFrames[0].FrameFileOffset = 0; + hf->pFrames[0].CompressedSize = hf->CompressedSize; + hf->pFrames[0].FrameSize = hf->FileSize; + memset(hf->pFrames[0].md5, 0, MD5_HASH_SIZE); + } + } + + // Return result + return (hf->pFrames != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + } + + return ERROR_SUCCESS; +} + +static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer) +{ + PCASC_FILE_FRAME pFrame = hf->pFrames; + DWORD FrameBegin; + DWORD FrameEnd; + + // Sanity checks + assert(hf->pFrames != NULL); + assert(hf->FrameCount != 0); + + // Find the frame where to read from + for(DWORD i = 0; i < hf->FrameCount; i++, pFrame++) + { + // Does the read request fit into the current frame? + FrameBegin = pFrame->FrameFileOffset; + FrameEnd = FrameBegin + pFrame->FrameSize; + if(FrameBegin <= FilePointer && FilePointer < FrameEnd) + return pFrame; + } + + // Not found, sorry + return NULL; +} + +//----------------------------------------------------------------------------- +// Public functions + +DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh) +{ + TCascFile * hf; + + CASCLIB_UNUSED(pdwFileSizeHigh); + + // Validate the file handle + if((hf = IsValidFileHandle(hFile)) == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return CASC_INVALID_SIZE; + } + + // Give the file size to the caller + if(pdwFileSizeHigh != NULL) + *pdwFileSizeHigh = 0; + return hf->FileSize; +} + +DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) +{ + TCascFile * hf; + ULONGLONG FilePosition; + ULONGLONG MoveOffset; + DWORD dwFilePosHi; + + // If the hFile is not a valid file handle, return an error. + hf = IsValidFileHandle(hFile); + if(hf == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return CASC_INVALID_POS; + } + + // Get the relative point where to move from + switch(dwMoveMethod) + { + case FILE_BEGIN: + FilePosition = 0; + break; + + case FILE_CURRENT: + FilePosition = hf->FilePointer; + break; + + case FILE_END: + FilePosition = hf->FileSize; + break; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + return CASC_INVALID_POS; + } + + // Now get the move offset. Note that both values form + // a signed 64-bit value (a file pointer can be moved backwards) + if(plFilePosHigh != NULL) + dwFilePosHi = *plFilePosHigh; + else + dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0; + MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos); + + // Now calculate the new file pointer + // Do not allow the file pointer to overflow + FilePosition = ((FilePosition + MoveOffset) >= FilePosition) ? (FilePosition + MoveOffset) : 0; + + // CASC files can't be bigger than 4 GB. + // We don't allow to go past 4 GB + if(FilePosition >> 32) + { + SetLastError(ERROR_INVALID_PARAMETER); + return CASC_INVALID_POS; + } + + // Change the file position + hf->FilePointer = (DWORD)FilePosition; + + // Return the new file position + if(plFilePosHigh != NULL) + *plFilePosHigh = 0; + return hf->FilePointer; +} + +bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead) +{ + PCASC_FILE_FRAME pFrame = NULL; + ULONGLONG FileOffset; + TCascFile * hf; + LPBYTE pbBuffer = (LPBYTE)pvBuffer; + DWORD dwStartPointer = 0; + DWORD dwFilePointer = 0; + DWORD dwEndPointer = 0; + DWORD cbOutBuffer; + int nError = ERROR_SUCCESS; + + // The buffer must be valid + if(pvBuffer == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Validate the file handle + if((hf = IsValidFileHandle(hFile)) == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // If the file position is at or beyond end of file, do nothing + if(hf->FilePointer >= hf->FileSize) + { + *pdwBytesRead = 0; + return ERROR_SUCCESS; + } + + // Make sure we have that data file open + if(nError == ERROR_SUCCESS) + { + nError = EnsureDataStreamIsOpen(hf); + } + + // If the file frames are not loaded yet, do it now + if(nError == ERROR_SUCCESS) + { + nError = EnsureFrameHeadersLoaded(hf); + } + + // Find the file frame where to read from + if(nError == ERROR_SUCCESS) + { + // Get the frame + pFrame = FindFileFrame(hf, hf->FilePointer); + if(pFrame == NULL) + nError = ERROR_FILE_CORRUPT; + } + + // Perform the read + if(nError == ERROR_SUCCESS) + { + // If not enough bytes in the file remaining, cut them + dwStartPointer = dwFilePointer = hf->FilePointer; + dwEndPointer = dwStartPointer + dwBytesToRead; + if(dwEndPointer > hf->FileSize) + dwEndPointer = hf->FileSize; + + // Perform block read from each file frame + while(dwFilePointer < dwEndPointer) + { + LPBYTE pbRawData = NULL; + DWORD dwFrameStart = pFrame->FrameFileOffset; + DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize; + + // Shall we populate the cache with a new data? + if(dwFrameStart != hf->CacheStart || hf->CacheEnd != dwFrameEnd) + { + // Shall we reallocate the cache buffer? + if(pFrame->FrameSize > hf->cbFileCache) + { + if(hf->pbFileCache != NULL) + CASC_FREE(hf->pbFileCache); + + hf->pbFileCache = CASC_ALLOC(BYTE, pFrame->FrameSize); + hf->cbFileCache = pFrame->FrameSize; + } + + // We also need to allocate buffer for the raw data + pbRawData = CASC_ALLOC(BYTE, pFrame->CompressedSize); + if(pbRawData == NULL) + { + nError = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + // Load the raw file data to memory + FileOffset = pFrame->FrameArchiveOffset; + if(!FileStream_Read(hf->pStream, &FileOffset, pbRawData, pFrame->CompressedSize)) + { + CASC_FREE(pbRawData); + nError = GetLastError(); + break; + } + + // Verify the block MD5 + if(IsValidMD5(pFrame->md5) && !VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5)) + { + CASC_FREE(pbRawData); + nError = ERROR_FILE_CORRUPT; + break; + } + + // Decompress the file frame + cbOutBuffer = pFrame->FrameSize; + nError = CascDecompress(hf->pbFileCache, &cbOutBuffer, pbRawData, pFrame->CompressedSize); + if(nError != ERROR_SUCCESS || cbOutBuffer != pFrame->FrameSize) + { + CASC_FREE(pbRawData); + nError = ERROR_FILE_CORRUPT; + break; + } + + // Set the start and end of the cache + hf->CacheStart = dwFrameStart; + hf->CacheEnd = dwFrameEnd; + + // Free the decompress buffer, if needed + CASC_FREE(pbRawData); + } + + // Copy the decompressed data + if(dwFrameEnd > dwEndPointer) + dwFrameEnd = dwEndPointer; + memcpy(pbBuffer, hf->pbFileCache + (dwFilePointer - dwFrameStart), (dwFrameEnd - dwFilePointer)); + pbBuffer += (dwFrameEnd - dwFilePointer); + + // Move pointers + dwFilePointer = dwFrameEnd; + pFrame++; + } + } + + // Update the file position + if(nError == ERROR_SUCCESS) + { + if(pdwBytesRead != NULL) + *pdwBytesRead = (dwFilePointer - dwStartPointer); + hf->FilePointer = dwFilePointer; + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp new file mode 100644 index 00000000000..d81c1b6fd89 --- /dev/null +++ b/dep/CascLib/src/common/Common.cpp @@ -0,0 +1,672 @@ +/*****************************************************************************/ +/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Conversion to uppercase/lowercase + +// Converts ASCII characters to lowercase +// Converts slash (0x2F) to backslash (0x5C) +unsigned char AsciiToLowerTable[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +// Converts ASCII characters to uppercase +// Converts slash (0x2F) to backslash (0x5C) +unsigned char AsciiToUpperTable[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +unsigned char IntToHexChar[] = "0123456789abcdef"; + +//----------------------------------------------------------------------------- +// Support for memory reallocation + +#if defined(_MSC_VER) && defined(_DEBUG) +void * DbgRealloc(void * ptr, size_t nSize) +{ + // HeapReAlloc does not support NULL as previous block + if(ptr == NULL) + return HeapAlloc(GetProcessHeap, 0, nSize); + + return HeapReAlloc(GetProcessHeap(), 0, ptr, nSize); +} +#endif + +//----------------------------------------------------------------------------- +// GetLastError/SetLastError support for non-Windows platform + +#ifndef PLATFORM_WINDOWS +static int nLastError = ERROR_SUCCESS; + +int GetLastError() +{ + return nLastError; +} + +void SetLastError(int nError) +{ + nLastError = nError; +} +#endif + +//----------------------------------------------------------------------------- +// String manipulation + +void CopyString(char * szTarget, const char * szSource, size_t cchLength) +{ + memcpy(szTarget, szSource, cchLength); + szTarget[cchLength] = 0; +} + +void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength) +{ + mbstowcs(szTarget, szSource, cchLength); + szTarget[cchLength] = 0; +} + +void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength) +{ + wcstombs(szTarget, szSource, cchLength); + szTarget[cchLength] = 0; +} + +char * NewStr(const char * szString, size_t nCharsToReserve) +{ + char * szNewString = NULL; + size_t nLength; + + if(szString != NULL) + { + nLength = strlen(szString); + szNewString = CASC_ALLOC(char, nLength + nCharsToReserve + 1); + if(szNewString != NULL) + { + memcpy(szNewString, szString, nLength); + szNewString[nLength] = 0; + } + } + + return szNewString; +} + +wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve) +{ + wchar_t * szNewString = NULL; + size_t nLength; + + if(szString != NULL) + { + nLength = wcslen(szString); + szNewString = CASC_ALLOC(wchar_t, nLength + nCharsToReserve + 1); + if(szNewString != NULL) + { + memcpy(szNewString, szString, nLength * sizeof(wchar_t)); + szNewString[nLength] = 0; + } + } + + return szNewString; +} + +TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd) +{ + TCHAR * szNewString = NULL; + TCHAR * szStringPtr = NULL; + size_t nLength = (size_t)(pbStringEnd - pbStringBegin); + + if(pbStringEnd > pbStringBegin) + { + szNewString = szStringPtr = CASC_ALLOC(TCHAR, nLength + 1); + if(szNewString != NULL) + { + CopyString(szStringPtr, (const char *)pbStringBegin, nLength); + szStringPtr[nLength] = 0; + } + } + + return szNewString; +} + +TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir) +{ + TCHAR * szFullPath = NULL; + TCHAR * szPathPtr; + size_t nLength1 = 0; + size_t nLength2 = 0; + + // Calculate lengths of each part + if(szDirectory != NULL) + { + // Get the length of the directory + nLength1 = _tcslen(szDirectory); + + // Cut all ending backslashes + while(nLength1 > 0 && szDirectory[nLength1 - 1] == _T(PATH_SEPARATOR)) + nLength1--; + } + + if(szSubDir != NULL) + { + // Cut all leading backslashes + while(szSubDir[0] == _T(PATH_SEPARATOR)) + szSubDir++; + + // Get the length of the subdir + nLength2 = _tcslen(szSubDir); + + // Cut all ending backslashes + while(nLength2 > 0 && szSubDir[nLength2 - 1] == _T(PATH_SEPARATOR)) + nLength2--; + } + + // Allocate space for the full path + szFullPath = szPathPtr = CASC_ALLOC(TCHAR, nLength1 + nLength2 + 2); + if(szFullPath != NULL) + { + // Copy the directory + if(szDirectory != NULL && nLength1 != 0) + { + memcpy(szPathPtr, szDirectory, (nLength1 * sizeof(TCHAR))); + szPathPtr += nLength1; + } + + // Copy the sub-directory + if(szSubDir != NULL && nLength2 != 0) + { + // Append backslash to the previous one + if(szPathPtr > szFullPath) + *szPathPtr++ = _T(PATH_SEPARATOR); + + // Copy the string + memcpy(szPathPtr, szSubDir, (nLength2 * sizeof(TCHAR))); + szPathPtr += nLength2; + } + + // Terminate the string + szPathPtr[0] = 0; + } + + return szFullPath; +} + +void NormalizeFileName_UpperBkSlash(char * szFileName) +{ + // Normalize the file name: ToLower + BackSlashToSlash + for(size_t i = 0; szFileName[i] != 0; i++) + szFileName[i] = AsciiToUpperTable[szFileName[i]]; +} + +void NormalizeFileName_LowerSlash(char * szFileName) +{ + // Normalize the file name: ToLower + BackSlashToSlash + for(size_t i = 0; szFileName[i] != 0; i++) + { + szFileName[i] = AsciiToLowerTable[szFileName[i]]; + szFileName[i] = (szFileName[i] != '\\') ? szFileName[i] : '/'; + } +} + +int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue) +{ + BYTE Digit; + + Digit = (BYTE)(AsciiToUpperTable[szString[0]] - _T('0')); + if(Digit > 9) + Digit -= 'A' - '9' - 1; + + PtrValue[0] = Digit; + return (Digit > 0x0F) ? ERROR_BAD_FORMAT : ERROR_SUCCESS; +} + +int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue) +{ + // The number of digits must be even + assert((nMaxDigits & 0x01) == 0); + assert(nMaxDigits <= 8); + + // Prepare the variables + PtrValue[0] = 0; + nMaxDigits >>= 1; + + // Convert the string up to the number of digits + for(size_t i = 0; i < nMaxDigits; i++) + { + BYTE DigitOne; + BYTE DigitTwo; + + DigitOne = (BYTE)(AsciiToUpperTable[szString[0]] - _T('0')); + if(DigitOne > 9) + DigitOne -= 'A' - '9' - 1; + + DigitTwo = (BYTE)(AsciiToUpperTable[szString[1]] - _T('0')); + if(DigitTwo > 9) + DigitTwo -= 'A' - '9' - 1; + + if(DigitOne > 0x0F || DigitTwo > 0x0F) + return ERROR_BAD_FORMAT; + + PtrValue[0] = (PtrValue[0] << 0x08) | (DigitOne << 0x04) | DigitTwo; + szString += 2; + } + + return ERROR_SUCCESS; +} + +char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer) +{ + char * szSaveBuffer = szBuffer; + + // Convert the string to the array of MD5 + // Copy the blob data as text + for(size_t i = 0; i < cbBinary; i++) + { + *szBuffer++ = IntToHexChar[pbBinary[0] >> 0x04]; + *szBuffer++ = IntToHexChar[pbBinary[0] & 0x0F]; + pbBinary++; + } + + // Terminate the string + *szBuffer = 0; + return szSaveBuffer; +} + +//----------------------------------------------------------------------------- +// File name utilities + +const wchar_t * GetPlainFileName(const wchar_t * szFileName) +{ + const wchar_t * szPlainName = szFileName; + + while(*szFileName != 0) + { + if(*szFileName == '\\' || *szFileName == '/') + szPlainName = szFileName + 1; + szFileName++; + } + + return szPlainName; +} + +const char * GetPlainFileName(const char * szFileName) +{ + const char * szPlainName = szFileName; + + while(*szFileName != 0) + { + if(*szFileName == '\\' || *szFileName == '/') + szPlainName = szFileName + 1; + szFileName++; + } + + return szPlainName; +} + +bool CheckWildCard(const char * szString, const char * szWildCard) +{ + const char * szSubString; + int nSubStringLength; + int nMatchCount = 0; + + // When the mask is empty, it never matches + if(szWildCard == NULL || *szWildCard == 0) + return false; + + // If the wildcard contains just "*", then it always matches + if(szWildCard[0] == '*' && szWildCard[1] == 0) + return true; + + // Do normal test + for(;;) + { + // If there is '?' in the wildcard, we skip one char + while(*szWildCard == '?') + { + szWildCard++; + szString++; + } + + // If there is '*', means zero or more chars. We have to + // find the sequence after '*' + if(*szWildCard == '*') + { + // More stars is equal to one star + while(*szWildCard == '*' || *szWildCard == '?') + szWildCard++; + + // If we found end of the wildcard, it's a match + if(*szWildCard == 0) + return true; + + // Determine the length of the substring in szWildCard + szSubString = szWildCard; + while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*') + szSubString++; + nSubStringLength = (int)(szSubString - szWildCard); + nMatchCount = 0; + + // Now we have to find a substring in szString, + // that matches the substring in szWildCard + while(*szString != 0) + { + // Calculate match count + while(nMatchCount < nSubStringLength) + { + if(AsciiToUpperTable[(BYTE)szString[nMatchCount]] != AsciiToUpperTable[(BYTE)szWildCard[nMatchCount]]) + break; + if(szString[nMatchCount] == 0) + break; + nMatchCount++; + } + + // If the match count has reached substring length, we found a match + if(nMatchCount == nSubStringLength) + { + szWildCard += nMatchCount; + szString += nMatchCount; + break; + } + + // No match, move to the next char in szString + nMatchCount = 0; + szString++; + } + } + else + { + // If we came to the end of the string, compare it to the wildcard + if(AsciiToUpperTable[(BYTE)*szString] != AsciiToUpperTable[(BYTE)*szWildCard]) + return false; + + // If we arrived to the end of the string, it's a match + if(*szString == 0) + return true; + + // Otherwise, continue in comparing + szWildCard++; + szString++; + } + } +} + +//----------------------------------------------------------------------------- +// Hashing functions + +bool IsValidMD5(LPBYTE pbMd5) +{ + BYTE BitSummary = 0; + + // The MD5 is considered invalid of it is zeroed + BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07]; + BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F]; + return (BitSummary != 0); +} + +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) +{ + hash_state md5_state; + BYTE md5_digest[MD5_HASH_SIZE]; + + // Don't verify the block if the MD5 is not valid. + if(!IsValidMD5(expected_md5)) + return true; + + // Calculate the MD5 of the data block + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_digest); + + // Does the MD5's match? + return (memcmp(md5_digest, expected_md5, MD5_HASH_SIZE) == 0); +} + +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) +{ + hash_state md5_state; + + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_hash); +} + +//----------------------------------------------------------------------------- +// We have our own qsort implementation, optimized for using array of pointers + +#define STKSIZ (8*sizeof(void*) - 2) + +#define SWAP_ENTRIES(index1, index2) \ +{ \ + temp = base[index1]; \ + base[index1] = base[index2]; \ + base[index2] = temp; \ +} + +void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context) +{ + size_t lo, hi; /* ends of sub-array currently sorting */ + size_t mid; /* points to middle of subarray */ + size_t loguy, higuy; /* traveling pointers for partition step */ + size_t size; /* size of the sub-array */ + size_t lostk[STKSIZ], histk[STKSIZ]; + void * temp; + int stkptr; /* stack for saving sub-array to be processed */ + + /* validation section */ + assert(base != NULL); + assert(compare != NULL); + + if (num < 2) + return; /* nothing to do */ + + stkptr = 0; /* initialize stack */ + + lo = 0; + hi = (num-1); /* initialize limits */ + + /* this entry point is for pseudo-recursion calling: setting + lo and hi and jumping to here is like recursion, but stkptr is + preserved, locals aren't, so we preserve stuff on the stack */ +recurse: + + size = (hi - lo) + 1; /* number of el's to sort */ + + /* First we pick a partitioning element. The efficiency of the + algorithm demands that we find one that is approximately the median + of the values, but also that we select one fast. We choose the + median of the first, middle, and last elements, to avoid bad + performance in the face of already sorted data, or data that is made + up of multiple sorted runs appended together. Testing shows that a + median-of-three algorithm provides better performance than simply + picking the middle element for the latter case. */ + + mid = lo + (size / 2); /* find middle element */ + + /* Sort the first, middle, last elements into order */ + if (compare(context, base[lo], base[mid]) > 0) { + SWAP_ENTRIES(lo, mid); + } + if (compare(context, base[lo], base[hi]) > 0) { + SWAP_ENTRIES(lo, hi); + } + if (compare(context, base[mid], base[hi]) > 0) { + SWAP_ENTRIES(mid, hi); + } + + /* We now wish to partition the array into three pieces, one consisting + of elements <= partition element, one of elements equal to the + partition element, and one of elements > than it. This is done + below; comments indicate conditions established at every step. */ + + loguy = lo; + higuy = hi; + + /* Note that higuy decreases and loguy increases on every iteration, + so loop must terminate. */ + for (;;) { + /* lo <= loguy < hi, lo < higuy <= hi, + A[i] <= A[mid] for lo <= i <= loguy, + A[i] > A[mid] for higuy <= i < hi, + A[hi] >= A[mid] */ + + /* The doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same + value for both pointers. */ + + if (mid > loguy) { + do { + loguy ++; + } while (loguy < mid && compare(context, base[loguy], base[mid]) <= 0); + } + if (mid <= loguy) { + do { + loguy ++; + } while (loguy <= hi && compare(context, base[loguy], base[mid]) <= 0); + } + + /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy, + either loguy > hi or A[loguy] > A[mid] */ + + do { + higuy --; + } while (higuy > mid && compare(context, base[higuy], base[mid]) > 0); + + /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi, + either higuy == lo or A[higuy] <= A[mid] */ + + if (higuy < loguy) + break; + + /* if loguy > hi or higuy == lo, then we would have exited, so + A[loguy] > A[mid], A[higuy] <= A[mid], + loguy <= hi, higuy > lo */ + + SWAP_ENTRIES(loguy, higuy); + + /* If the partition element was moved, follow it. Only need + to check for mid == higuy, since before the swap, + A[loguy] > A[mid] implies loguy != mid. */ + + if (mid == higuy) + mid = loguy; + + /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top + of loop is re-established */ + } + + /* A[i] <= A[mid] for lo <= i < loguy, + A[i] > A[mid] for higuy < i < hi, + A[hi] >= A[mid] + higuy < loguy + implying: + higuy == loguy-1 + or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */ + + /* Find adjacent elements equal to the partition element. The + doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same value + for both pointers. */ + + higuy ++; + if (mid < higuy) { + do { + higuy --; + } while (higuy > mid && compare(context, base[higuy], base[mid]) == 0); + } + if (mid >= higuy) { + do { + higuy --; + } while (higuy > lo && compare(context, base[higuy], base[mid]) == 0); + } + + /* OK, now we have the following: + higuy < loguy + lo <= higuy <= hi + A[i] <= A[mid] for lo <= i <= higuy + A[i] == A[mid] for higuy < i < loguy + A[i] > A[mid] for loguy <= i < hi + A[hi] >= A[mid] */ + + /* We've finished the partition, now we want to sort the subarrays + [lo, higuy] and [loguy, hi]. + We do the smaller one first to minimize stack usage. + We only sort arrays of length 2 or more.*/ + + if ( higuy - lo >= hi - loguy ) { + if (lo < higuy) { + lostk[stkptr] = lo; + histk[stkptr] = higuy; + ++stkptr; + } /* save big recursion for later */ + + if (loguy < hi) { + lo = loguy; + goto recurse; /* do small recursion */ + } + } + else { + if (loguy < hi) { + lostk[stkptr] = loguy; + histk[stkptr] = hi; + ++stkptr; /* save big recursion for later */ + } + + if (lo < higuy) { + hi = higuy; + goto recurse; /* do small recursion */ + } + } + + /* We have sorted the array, except for any pending sorts on the stack. + Check if there are any, and do them. */ + + --stkptr; + if (stkptr >= 0) { + lo = lostk[stkptr]; + hi = histk[stkptr]; + goto recurse; /* pop subarray from stack */ + } + else + return; /* all subarrays done */ +} diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h new file mode 100644 index 00000000000..eb192fd50ca --- /dev/null +++ b/dep/CascLib/src/common/Common.h @@ -0,0 +1,98 @@ +/*****************************************************************************/ +/* CascCommon.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of CascCommon.h */ +/*****************************************************************************/ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +//----------------------------------------------------------------------------- +// Common macros + +// Macro for building 64-bit file offset from two 32-bit +#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) + +#ifndef ALIGN_TO_SIZE +#define ALIGN_TO_SIZE(x, a) (((x) + (a)-1) & ~((a)-1)) +#endif + +//----------------------------------------------------------------------------- +// Conversion tables + +extern unsigned char AsciiToLowerTable[256]; +extern unsigned char AsciiToUpperTable[256]; +extern unsigned char IntToHexChar[]; + +//----------------------------------------------------------------------------- +// Memory management helper + +#if defined(_MSC_VER) && defined(_DEBUG) +void * DbgRealloc(void * ptr, size_t nSize); +#endif + +//----------------------------------------------------------------------------- +// GetLastError/SetLastError support for non-Windows platform + +#ifndef PLATFORM_WINDOWS +int GetLastError(); +void SetLastError(int nError); +#endif // PLATFORM_WINDOWS + +//----------------------------------------------------------------------------- +// String manipulation + +void CopyString(char * szTarget, const char * szSource, size_t cchLength); +void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength); +void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength); + +char * NewStr(const char * szString, size_t nCharsToReserve); +wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve); + +TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd); + +TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir); + +void NormalizeFileName_UpperBkSlash(char * szFileName); +void NormalizeFileName_LowerSlash(char * szFileName); + +int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue); +int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue); +char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer); + +//----------------------------------------------------------------------------- +// File name utilities + +bool CheckWildCard(const char * szString, const char * szWildCard); +const wchar_t * GetPlainFileName(const wchar_t * szFileName); +const char * GetPlainFileName(const char * szFileName); + +//----------------------------------------------------------------------------- +// Hashing functions + +ULONGLONG HashStringJenkins(const char * szFileName); + +bool IsValidMD5(LPBYTE pbMd5); +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); + +//----------------------------------------------------------------------------- +// Scanning a directory + +typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext); + +bool DirectoryExists(const TCHAR * szDirectory); + +int ScanIndexDirectory( + const TCHAR * szIndexPath, + INDEX_FILE_FOUND pfnOnFileFound, + PDWORD IndexArray, + PDWORD OldIndexArray, + void * pvContext + ); + +#endif // __COMMON_H__ diff --git a/dep/CascLib/src/common/Directory.cpp b/dep/CascLib/src/common/Directory.cpp new file mode 100644 index 00000000000..cd02db19be3 --- /dev/null +++ b/dep/CascLib/src/common/Directory.cpp @@ -0,0 +1,102 @@ +/*****************************************************************************/ +/* Directory.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.04.14 1.00 Lad The first version of Directory.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Public functions + +bool DirectoryExists(const TCHAR * szDirectory) +{ +#ifdef PLATFORM_WINDOWS + + DWORD dwAttributes = GetFileAttributes(szDirectory); + if((dwAttributes != INVALID_FILE_ATTRIBUTES) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return true; + +#else // PLATFORM_WINDOWS + + DIR * dir = opendir(szDirectory); + + if(dir != NULL) + { + closedir(dir); + return true; + } + +#endif + + return false; +} + +int ScanIndexDirectory( + const TCHAR * szIndexPath, + INDEX_FILE_FOUND pfnOnFileFound, + PDWORD MainIndexes, + PDWORD OldIndexArray, + void * pvContext) +{ +#ifdef PLATFORM_WINDOWS + + WIN32_FIND_DATA wf; + TCHAR * szSearchMask; + HANDLE hFind; + + // Prepare the search mask + szSearchMask = CombinePath(szIndexPath, _T("*")); + if(szSearchMask == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Prepare directory search + hFind = FindFirstFile(szSearchMask, &wf); + if(hFind != INVALID_HANDLE_VALUE) + { + // Skip the first file as it's always just "." or ".." + while(FindNextFile(hFind, &wf)) + { + // If the found object is a file, pass it to the handler + if(!(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + // Let the callback scan the file name + pfnOnFileFound(wf.cFileName, MainIndexes, OldIndexArray, pvContext); + } + } + + // Close the search handle + FindClose(hFind); + } + + CASC_FREE(szSearchMask); + +#else // PLATFORM_WINDOWS + + struct dirent * dir_entry; + DIR * dir; + + dir = opendir(szIndexPath); + if(dir != NULL) + { + while((dir_entry = readdir(dir)) != NULL) + { + if(dir_entry->d_type != DT_DIR) + { + pfnOnFileFound(dir_entry->d_name, MainIndexes, OldIndexArray, pvContext); + } + } + + closedir(dir); + } + +#endif + + return ERROR_SUCCESS; +} diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp new file mode 100644 index 00000000000..09ac47bd095 --- /dev/null +++ b/dep/CascLib/src/common/FileStream.cpp @@ -0,0 +1,2721 @@ +/*****************************************************************************/ +/* FileStream.cpp Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* File stream support for CascLib */ +/* */ +/* Windows support: Written by Ladislav Zezula */ +/* Mac support: Written by Sam Wilkins */ +/* Linux support: Written by Sam Wilkins and Ivan Komissarov */ +/* Big-endian: Written & debugged by Sam Wilkins */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 28.04.14 1.00 Lad Copied from StormLib */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" +#include "FileStream.h" + +#ifdef _MSC_VER +#pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream +#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) +#endif + +//----------------------------------------------------------------------------- +// Local functions - platform-specific functions + +static DWORD StringToInt(const char * szString) +{ + DWORD dwValue = 0; + + while('0' <= szString[0] && szString[0] <= '9') + { + dwValue = (dwValue * 10) + (szString[0] - '9'); + szString++; + } + + return dwValue; +} + +//----------------------------------------------------------------------------- +// Dummy init function + +static void BaseNone_Init(TFileStream *) +{ + // Nothing here +} + +//----------------------------------------------------------------------------- +// Local functions - base file support + +static bool BaseFile_Create(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS + { + DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + pStream->Base.File.hFile = CreateFile(pStream->szFileName, + GENERIC_READ | GENERIC_WRITE, + dwWriteShare | FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + 0, + NULL); + if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) + return false; + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + intptr_t handle; + + handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(handle == -1) + { + SetLastError(errno); + return false; + } + + pStream->Base.File.hFile = (HANDLE)handle; + } +#endif + + // Reset the file size and position + pStream->Base.File.FileSize = 0; + pStream->Base.File.FilePos = 0; + return true; +} + +static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + { + ULARGE_INTEGER FileSize; + DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; + DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + // Open the file + pStream->Base.File.hFile = CreateFile(szFileName, + FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess, + FILE_SHARE_READ | dwWriteShare, + NULL, + OPEN_EXISTING, + 0, + NULL); + if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) + return false; + + // Query the file size + FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); + pStream->Base.File.FileSize = FileSize.QuadPart; + + // Query last write time + GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime); + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + struct stat64 fileinfo; + int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; + intptr_t handle; + + // Open the file + handle = open(szFileName, oflag | O_LARGEFILE); + if(handle == -1) + { + SetLastError(errno); + return false; + } + + // Get the file size + if(fstat64(handle, &fileinfo) == -1) + { + SetLastError(errno); + return false; + } + + // time_t is number of seconds since 1.1.1970, UTC. + // 1 second = 10000000 (decimal) in FILETIME + // Set the start to 1.1.1970 00:00:00 + pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); + pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size; + pStream->Base.File.hFile = (HANDLE)handle; + } +#endif + + // Reset the file position + pStream->Base.File.FilePos = 0; + return true; +} + +static bool BaseFile_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; + DWORD dwBytesRead = 0; // Must be set by platform-specific code + +#ifdef PLATFORM_WINDOWS + { + // Note: CascLib no longer supports Windows 9x. + // Thus, we can use the OVERLAPPED structure to specify + // file offset to read from file. This allows us to skip + // one system call to SetFilePointer + + // Update the byte offset + pStream->Base.File.FilePos = ByteOffset; + + // Read the data + if(dwBytesToRead != 0) + { + OVERLAPPED Overlapped; + + Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); + Overlapped.Offset = (DWORD)ByteOffset; + Overlapped.hEvent = NULL; + if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped)) + return false; + } + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + ssize_t bytes_read; + + // If the byte offset is different from the current file position, + // 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); + pStream->Base.File.FilePos = ByteOffset; + } + + // Perform the read operation + if(dwBytesToRead != 0) + { + bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead); + if(bytes_read == -1) + { + SetLastError(errno); + return false; + } + + dwBytesRead = (DWORD)(size_t)bytes_read; + } + } +#endif + + // Increment the current file position by number of bytes read + // If the number of bytes read doesn't match to required amount, return false + pStream->Base.File.FilePos = ByteOffset + dwBytesRead; + if(dwBytesRead != dwBytesToRead) + SetLastError(ERROR_HANDLE_EOF); + return (dwBytesRead == dwBytesToRead); +} + +/** + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ + +static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) +{ + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; + DWORD dwBytesWritten = 0; // Must be set by platform-specific code + +#ifdef PLATFORM_WINDOWS + { + // Note: CascLib no longer supports Windows 9x. + // Thus, we can use the OVERLAPPED structure to specify + // file offset to read from file. This allows us to skip + // one system call to SetFilePointer + + // Update the byte offset + pStream->Base.File.FilePos = ByteOffset; + + // Read the data + if(dwBytesToWrite != 0) + { + OVERLAPPED Overlapped; + + Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); + Overlapped.Offset = (DWORD)ByteOffset; + Overlapped.hEvent = NULL; + if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped)) + return false; + } + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + ssize_t bytes_written; + + // If the byte offset is different from the current file position, + // 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); + pStream->Base.File.FilePos = ByteOffset; + } + + // Perform the read operation + bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite); + if(bytes_written == -1) + { + SetLastError(errno); + return false; + } + + dwBytesWritten = (DWORD)(size_t)bytes_written; + } +#endif + + // Increment the current file position by number of bytes read + pStream->Base.File.FilePos = ByteOffset + dwBytesWritten; + + // Also modify the file size, if needed + if(pStream->Base.File.FilePos > pStream->Base.File.FileSize) + pStream->Base.File.FileSize = pStream->Base.File.FilePos; + + if(dwBytesWritten != dwBytesToWrite) + SetLastError(ERROR_DISK_FULL); + return (dwBytesWritten == dwBytesToWrite); +} + +/** + * \a pStream Pointer to an open stream + * \a NewFileSize New size of the file + */ +static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize) +{ +#ifdef PLATFORM_WINDOWS + { + LONG FileSizeHi = (LONG)(NewFileSize >> 32); + LONG FileSizeLo; + DWORD dwNewPos; + bool bResult; + + // Set the position at the new file size + dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN); + if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS) + return false; + + // Set the current file pointer as the end of the file + bResult = (bool)SetEndOfFile(pStream->Base.File.hFile); + if(bResult) + pStream->Base.File.FileSize = NewFileSize; + + // Restore the file position + FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); + FileSizeLo = (LONG)(pStream->Base.File.FilePos); + SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); + return bResult; + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1) + { + SetLastError(errno); + return false; + } + + pStream->Base.File.FileSize = NewFileSize; + return true; + } +#endif +} + +// Gives the current file size +static bool BaseFile_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ + // Note: Used by all thre base providers. + // Requires the TBaseData union to have the same layout for all three base providers + *pFileSize = pStream->Base.File.FileSize; + return true; +} + +// Gives the current file position +static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + // Note: Used by all thre base providers. + // Requires the TBaseData union to have the same layout for all three base providers + *pByteOffset = pStream->Base.File.FilePos; + return true; +} + +// Renames the file pointed by pStream so that it contains data from pNewStream +static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream) +{ +#ifdef PLATFORM_WINDOWS + // Delete the original stream file. Don't check the result value, + // because if the file doesn't exist, it would fail + DeleteFile(pStream->szFileName); + + // Rename the new file to the old stream's file + return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + // "rename" on Linux also works if the target file exists + if(rename(pNewStream->szFileName, pStream->szFileName) == -1) + { + SetLastError(errno); + return false; + } + + return true; +#endif +} + +static void BaseFile_Close(TFileStream * pStream) +{ + if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) + { +#ifdef PLATFORM_WINDOWS + CloseHandle(pStream->Base.File.hFile); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + close((intptr_t)pStream->Base.File.hFile); +#endif + } + + // Also invalidate the handle + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; +} + +// Initializes base functions for the disk file +static void BaseFile_Init(TFileStream * pStream) +{ + pStream->BaseCreate = BaseFile_Create; + pStream->BaseOpen = BaseFile_Open; + pStream->BaseRead = BaseFile_Read; + pStream->BaseWrite = BaseFile_Write; + pStream->BaseResize = BaseFile_Resize; + pStream->BaseGetSize = BaseFile_GetSize; + pStream->BaseGetPos = BaseFile_GetPos; + pStream->BaseClose = BaseFile_Close; +} + +//----------------------------------------------------------------------------- +// Local functions - base memory-mapped file support + +static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + + ULARGE_INTEGER FileSize; + HANDLE hFile; + HANDLE hMap; + bool bResult = false; + + // Keep compiler happy + dwStreamFlags = dwStreamFlags; + + // Open the file for read access + hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile != INVALID_HANDLE_VALUE) + { + // Retrieve file size. Don't allow mapping file of a zero size. + FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); + if(FileSize.QuadPart != 0) + { + // Now create mapping object + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if(hMap != NULL) + { + // Map the entire view into memory + // Note that this operation will fail if the file can't fit + // into usermode address space + pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if(pStream->Base.Map.pbFile != NULL) + { + // Retrieve file time + GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + + // Retrieve file size and position + pStream->Base.Map.FileSize = FileSize.QuadPart; + pStream->Base.Map.FilePos = 0; + bResult = true; + } + + // Close the map handle + CloseHandle(hMap); + } + } + + // Close the file handle + CloseHandle(hFile); + } + + // If the file is not there and is not available for random access, + // report error + if(bResult == false) + return false; +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + struct stat64 fileinfo; + intptr_t handle; + bool bResult = false; + + // Open the file + handle = open(szFileName, O_RDONLY); + if(handle != -1) + { + // Get the file size + if(fstat64(handle, &fileinfo) != -1) + { + pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0); + if(pStream->Base.Map.pbFile != NULL) + { + // time_t is number of seconds since 1.1.1970, UTC. + // 1 second = 10000000 (decimal) in FILETIME + // Set the start to 1.1.1970 00:00:00 + pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); + pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size; + pStream->Base.Map.FilePos = 0; + bResult = true; + } + } + close(handle); + } + + // Did the mapping fail? + if(bResult == false) + { + SetLastError(errno); + return false; + } +#endif + + return true; +} + +static bool BaseMap_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos; + + // Do we have to read anything at all? + if(dwBytesToRead != 0) + { + // Don't allow reading past file size + if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize) + return false; + + // Copy the required data + memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead); + } + + // Move the current file position + pStream->Base.Map.FilePos += dwBytesToRead; + return true; +} + +static void BaseMap_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS + if(pStream->Base.Map.pbFile != NULL) + UnmapViewOfFile(pStream->Base.Map.pbFile); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + if(pStream->Base.Map.pbFile != NULL) + munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize); +#endif + + pStream->Base.Map.pbFile = NULL; +} + +// Initializes base functions for the mapped file +static void BaseMap_Init(TFileStream * pStream) +{ + // Supply the file stream functions + pStream->BaseOpen = BaseMap_Open; + pStream->BaseRead = BaseMap_Read; + pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function + pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function + pStream->BaseClose = BaseMap_Close; + + // Mapped files are read-only + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; +} + +//----------------------------------------------------------------------------- +// Local functions - base HTTP file support + +static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR * szServerName) +{ + // Check for HTTP + if(!_tcsnicmp(szFileName, _T("http://"), 7)) + szFileName += 7; + + // Cut off the server name + if(szServerName != NULL) + { + while(szFileName[0] != 0 && szFileName[0] != _T('/')) + *szServerName++ = *szFileName++; + *szServerName = 0; + } + else + { + while(szFileName[0] != 0 && szFileName[0] != _T('/')) + szFileName++; + } + + // Return the remainder + return szFileName; +} + +static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + + HINTERNET hRequest; + DWORD dwTemp = 0; + bool bFileAvailable = false; + int nError = ERROR_SUCCESS; + + // Keep compiler happy + dwStreamFlags = dwStreamFlags; + + // Don't connect to the internet + if(!InternetGetConnectedState(&dwTemp, 0)) + nError = GetLastError(); + + // Initiate the connection to the internet + if(nError == ERROR_SUCCESS) + { + pStream->Base.Http.hInternet = InternetOpen(_T("CascLib HTTP archive reader"), + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, + NULL, + 0); + if(pStream->Base.Http.hInternet == NULL) + nError = GetLastError(); + } + + // Connect to the server + if(nError == ERROR_SUCCESS) + { + TCHAR szServerName[MAX_PATH]; + DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE; + + // Initiate connection with the server + szFileName = BaseHttp_ExtractServerName(szFileName, szServerName); + pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet, + szServerName, + INTERNET_DEFAULT_HTTP_PORT, + NULL, + NULL, + INTERNET_SERVICE_HTTP, + dwFlags, + 0); + if(pStream->Base.Http.hConnect == NULL) + nError = GetLastError(); + } + + // Now try to query the file size + if(nError == ERROR_SUCCESS) + { + // Open HTTP request to the file + hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if(hRequest != NULL) + { + if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) + { + ULONGLONG FileTime = 0; + DWORD dwFileSize = 0; + DWORD dwDataSize; + DWORD dwIndex = 0; + + // Check if the archive has Last Modified field + dwDataSize = sizeof(ULONGLONG); + if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex)) + pStream->Base.Http.FileTime = FileTime; + + // Verify if the server supports random access + dwDataSize = sizeof(DWORD); + if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex)) + { + if(dwFileSize != 0) + { + pStream->Base.Http.FileSize = dwFileSize; + pStream->Base.Http.FilePos = 0; + bFileAvailable = true; + } + } + } + InternetCloseHandle(hRequest); + } + } + + // If the file is not there and is not available for random access, + // report error + if(bFileAvailable == false) + { + pStream->BaseClose(pStream); + return false; + } + + return true; + +#else + + // Not supported + SetLastError(ERROR_NOT_SUPPORTED); + pStream = pStream; + return false; + +#endif +} + +static bool BaseHttp_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ +#ifdef PLATFORM_WINDOWS + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos; + DWORD dwTotalBytesRead = 0; + + // Do we have to read anything at all? + if(dwBytesToRead != 0) + { + HINTERNET hRequest; + LPCTSTR szFileName; + LPBYTE pbBuffer = (LPBYTE)pvBuffer; + TCHAR szRangeRequest[0x80]; + DWORD dwStartOffset = (DWORD)ByteOffset; + DWORD dwEndOffset = dwStartOffset + dwBytesToRead; + + // Open HTTP request to the file + szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL); + hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if(hRequest != NULL) + { + // Add range request to the HTTP headers + // http://www.clevercomponents.com/articles/article015/resuming.asp + _stprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset); + HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW); + + // Send the request to the server + if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) + { + while(dwTotalBytesRead < dwBytesToRead) + { + DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead; + DWORD dwBlockBytesRead = 0; + + // Read the block from the file + if(dwBlockBytesToRead > 0x200) + dwBlockBytesToRead = 0x200; + InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead); + + // Check for end + if(dwBlockBytesRead == 0) + break; + + // Move buffers + dwTotalBytesRead += dwBlockBytesRead; + pbBuffer += dwBlockBytesRead; + } + } + InternetCloseHandle(hRequest); + } + } + + // Increment the current file position by number of bytes read + pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead; + + // If the number of bytes read doesn't match the required amount, return false + if(dwTotalBytesRead != dwBytesToRead) + SetLastError(ERROR_HANDLE_EOF); + return (dwTotalBytesRead == dwBytesToRead); + +#else + + // Not supported + pStream = pStream; + pByteOffset = pByteOffset; + pvBuffer = pvBuffer; + dwBytesToRead = dwBytesToRead; + SetLastError(ERROR_NOT_SUPPORTED); + return false; + +#endif +} + +static void BaseHttp_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS + if(pStream->Base.Http.hConnect != NULL) + InternetCloseHandle(pStream->Base.Http.hConnect); + pStream->Base.Http.hConnect = NULL; + + if(pStream->Base.Http.hInternet != NULL) + InternetCloseHandle(pStream->Base.Http.hInternet); + pStream->Base.Http.hInternet = NULL; +#else + pStream = pStream; +#endif +} + +// Initializes base functions for the mapped file +static void BaseHttp_Init(TFileStream * pStream) +{ + // Supply the stream functions + pStream->BaseOpen = BaseHttp_Open; + pStream->BaseRead = BaseHttp_Read; + pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function + pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function + pStream->BaseClose = BaseHttp_Close; + + // HTTP files are read-only + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; +} + +//----------------------------------------------------------------------------- +// Local functions - base block-based support + +// Generic function that loads blocks from the file +// The function groups the block with the same availability, +// so the called BlockRead can finish the request in a single system call +static bool BlockStream_Read( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ + ULONGLONG BlockOffset0; + ULONGLONG BlockOffset; + ULONGLONG ByteOffset; + ULONGLONG EndOffset; + LPBYTE TransferBuffer; + LPBYTE BlockBuffer; + DWORD BlockBufferOffset; // Offset of the desired data in the block buffer + DWORD BytesNeeded; // Number of bytes that really need to be read + DWORD BlockSize = pStream->BlockSize; + DWORD BlockCount; + bool bPrevBlockAvailable; + bool bCallbackCalled = false; + bool bBlockAvailable; + bool bResult = true; + + // The base block read function must be present + assert(pStream->BlockRead != NULL); + + // NOP reading of zero bytes + if(dwBytesToRead == 0) + return true; + + // Get the current position in the stream + ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : pStream->StreamPos; + EndOffset = ByteOffset + dwBytesToRead; + if(EndOffset > pStream->StreamSize) + { + SetLastError(ERROR_HANDLE_EOF); + return false; + } + + // Calculate the block parameters + BlockOffset0 = BlockOffset = ByteOffset & ~((ULONGLONG)BlockSize - 1); + BlockCount = (DWORD)(((EndOffset - BlockOffset) + (BlockSize - 1)) / BlockSize); + BytesNeeded = (DWORD)(EndOffset - BlockOffset); + + // Remember where we have our data + assert((BlockSize & (BlockSize - 1)) == 0); + BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1)); + + // Allocate buffer for reading blocks + TransferBuffer = BlockBuffer = CASC_ALLOC(BYTE, (BlockCount * BlockSize)); + if(TransferBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return false; + } + + // If all blocks are available, just read all blocks at once + if(pStream->IsComplete == 0) + { + // Now parse the blocks and send the block read request + // to all blocks with the same availability + assert(pStream->BlockCheck != NULL); + bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); + + // Loop as long as we have something to read + while(BlockOffset < EndOffset) + { + // Determine availability of the next block + bBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); + + // If the availability has changed, read all blocks up to this one + if(bBlockAvailable != bPrevBlockAvailable) + { + // Call the file stream callback, if the block is not available + if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) + { + pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); + bCallbackCalled = true; + } + + // Load the continuous blocks with the same availability + assert(BlockOffset > BlockOffset0); + bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); + if(!bResult) + break; + + // Move the block offset + BlockBuffer += (DWORD)(BlockOffset - BlockOffset0); + BytesNeeded -= (DWORD)(BlockOffset - BlockOffset0); + bPrevBlockAvailable = bBlockAvailable; + BlockOffset0 = BlockOffset; + } + + // Move to the block offset in the stream + BlockOffset += BlockSize; + } + + // If there is a block(s) remaining to be read, do it + if(BlockOffset > BlockOffset0) + { + // Call the file stream callback, if the block is not available + if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) + { + pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); + bCallbackCalled = true; + } + + // Read the complete blocks from the file + if(BlockOffset > pStream->StreamSize) + BlockOffset = pStream->StreamSize; + bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); + } + } + else + { + // Read the complete blocks from the file + if(EndOffset > pStream->StreamSize) + EndOffset = pStream->StreamSize; + bResult = pStream->BlockRead(pStream, BlockOffset, EndOffset, BlockBuffer, BytesNeeded, true); + } + + // Now copy the data to the user buffer + if(bResult) + { + memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead); + pStream->StreamPos = ByteOffset + dwBytesToRead; + } + else + { + // If the block read failed, set the last error + SetLastError(ERROR_FILE_INCOMPLETE); + } + + // Call the callback to indicate we are done + if(bCallbackCalled) + pStream->pfnCallback(pStream->UserData, 0, 0); + + // Free the block buffer and return + CASC_FREE(TransferBuffer); + return bResult; +} + +static bool BlockStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ + *pFileSize = pStream->StreamSize; + return true; +} + +static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + *pByteOffset = pStream->StreamPos; + return true; +} + +static void BlockStream_Close(TBlockStream * pStream) +{ + // Free the data map, if any + if(pStream->FileBitmap != NULL) + CASC_FREE(pStream->FileBitmap); + pStream->FileBitmap = NULL; + + // Call the base class for closing the stream + pStream->BaseClose(pStream); +} + +//----------------------------------------------------------------------------- +// File stream allocation function + +static STREAM_INIT StreamBaseInit[4] = +{ + BaseFile_Init, + BaseMap_Init, + BaseHttp_Init, + BaseNone_Init +}; + +// This function allocates an empty structure for the file stream +// The stream structure is created as flat block, variable length +// The file name is placed after the end of the stream structure data +static TFileStream * AllocateFileStream( + const TCHAR * szFileName, + size_t StreamSize, + DWORD dwStreamFlags) +{ + TFileStream * pMaster = NULL; + TFileStream * pStream; + const TCHAR * szNextFile = szFileName; + size_t FileNameSize; + + // Sanity check + assert(StreamSize != 0); + + // The caller can specify chain of files in the following form: + // C:\archive.MPQ*http://www.server.com/MPQs/archive-server.MPQ + // In that case, we use the part after "*" as master file name + while(szNextFile[0] != 0 && szNextFile[0] != _T('*')) + szNextFile++; + FileNameSize = (size_t)((szNextFile - szFileName) * sizeof(TCHAR)); + + // If we have a next file, we need to open it as master stream + // Note that we don't care if the master stream exists or not, + // If it doesn't, later attempts to read missing file block will fail + if(szNextFile[0] == _T('*')) + { + // Don't allow another master file in the string + if(_tcschr(szNextFile + 1, _T('*')) != NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + // Open the master file + pMaster = FileStream_OpenFile(szNextFile + 1, STREAM_FLAG_READ_ONLY); + } + + // Allocate the stream structure for the given stream type + pStream = (TFileStream *)CASC_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR)); + if(pStream != NULL) + { + // Zero the entire structure + memset(pStream, 0, StreamSize); + pStream->pMaster = pMaster; + pStream->dwFlags = dwStreamFlags; + + // Initialize the file name + pStream->szFileName = (TCHAR *)((BYTE *)pStream + StreamSize); + memcpy(pStream->szFileName, szFileName, FileNameSize); + pStream->szFileName[FileNameSize / sizeof(TCHAR)] = 0; + + // Initialize the stream functions + StreamBaseInit[dwStreamFlags & 0x03](pStream); + } + + return pStream; +} + +//----------------------------------------------------------------------------- +// Local functions - flat stream support + +static DWORD FlatStream_CheckFile(TBlockStream * pStream) +{ + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD WholeByteCount = (pStream->BlockCount / 8); + DWORD ExtraBitsCount = (pStream->BlockCount & 7); + BYTE ExpectedValue; + + // Verify the whole bytes - their value must be 0xFF + for(DWORD i = 0; i < WholeByteCount; i++) + { + if(FileBitmap[i] != 0xFF) + return 0; + } + + // If there are extra bits, calculate the mask + if(ExtraBitsCount != 0) + { + ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); + if(FileBitmap[WholeByteCount] != ExpectedValue) + return 0; + } + + // Yes, the file is complete + return 1; +} + +static bool FlatStream_LoadBitmap(TBlockStream * pStream) +{ + FILE_BITMAP_FOOTER Footer; + ULONGLONG ByteOffset; + LPBYTE FileBitmap; + DWORD BlockCount; + DWORD BitmapSize; + + // Do not load the bitmap if we should not have to + if(!(pStream->dwFlags & STREAM_FLAG_USE_BITMAP)) + return false; + + // Only if the size is greater than size of bitmap footer + if(pStream->Base.File.FileSize > sizeof(FILE_BITMAP_FOOTER)) + { + // Load the bitmap footer + ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER); + if(pStream->BaseRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER))) + { + // Make sure that the array is properly BSWAP-ed + BSWAP_ARRAY32_UNSIGNED((PDWORD)(&Footer), sizeof(FILE_BITMAP_FOOTER)); + + // Verify if there is actually a footer + if(Footer.Signature == ID_FILE_BITMAP_FOOTER && Footer.Version == 0x03) + { + // Get the offset of the bitmap, number of blocks and size of the bitmap + ByteOffset = MAKE_OFFSET64(Footer.MapOffsetHi, Footer.MapOffsetLo); + BlockCount = (DWORD)(((ByteOffset - 1) / Footer.BlockSize) + 1); + BitmapSize = ((BlockCount + 7) / 8); + + // Check if the sizes match + if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize) + { + // Allocate space for the bitmap + FileBitmap = CASC_ALLOC(BYTE, BitmapSize); + if(FileBitmap != NULL) + { + // Load the bitmap bits + if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) + { + CASC_FREE(FileBitmap); + return false; + } + + // Update the stream size + pStream->BuildNumber = Footer.BuildNumber; + pStream->StreamSize = ByteOffset; + + // Fill the bitmap information + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = BitmapSize; + pStream->BlockSize = Footer.BlockSize; + pStream->BlockCount = BlockCount; + pStream->IsComplete = FlatStream_CheckFile(pStream); + return true; + } + } + } + } + } + + return false; +} + +static void FlatStream_UpdateBitmap( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset) +{ + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD BlockIndex; + DWORD BlockSize = pStream->BlockSize; + DWORD ByteIndex; + BYTE BitMask; + + // Sanity checks + assert((StartOffset & (BlockSize - 1)) == 0); + assert(FileBitmap != NULL); + + // Calculate the index of the block + BlockIndex = (DWORD)(StartOffset / BlockSize); + ByteIndex = (BlockIndex / 0x08); + BitMask = (BYTE)(1 << (BlockIndex & 0x07)); + + // Set all bits for the specified range + while(StartOffset < EndOffset) + { + // Set the bit + FileBitmap[ByteIndex] |= BitMask; + + // Move all + StartOffset += BlockSize; + ByteIndex += (BitMask >> 0x07); + BitMask = (BitMask >> 0x07) | (BitMask << 0x01); + } + + // Increment the bitmap update count + pStream->IsModified = 1; +} + +static bool FlatStream_BlockCheck( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG BlockOffset) +{ + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD BlockIndex; + BYTE BitMask; + + // Sanity checks + assert((BlockOffset & (pStream->BlockSize - 1)) == 0); + assert(FileBitmap != NULL); + + // Calculate the index of the block + BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize); + BitMask = (BYTE)(1 << (BlockIndex & 0x07)); + + // Check if the bit is present + return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false; +} + +static bool FlatStream_BlockRead( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + DWORD BytesToRead = (DWORD)(EndOffset - StartOffset); + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + + // If the blocks are not available, we need to load them from the master + // and then save to the mirror + if(bAvailable == false) + { + // If we have no master, we cannot satisfy read request + if(pStream->pMaster == NULL) + return false; + + // Load the blocks from the master stream + // Note that we always have to read complete blocks + // so they get properly stored to the mirror stream + if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) + return false; + + // Store the loaded blocks to the mirror file. + // Note that this operation is not required to succeed + if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead)) + FlatStream_UpdateBitmap(pStream, StartOffset, EndOffset); + + return true; + } + else + { + if(BytesToRead > BytesNeeded) + BytesToRead = BytesNeeded; + return pStream->BaseRead(pStream, &StartOffset, BlockBuffer, BytesToRead); + } +} + +static void FlatStream_Close(TBlockStream * pStream) +{ + FILE_BITMAP_FOOTER Footer; + + if(pStream->FileBitmap && pStream->IsModified) + { + // Write the file bitmap + pStream->BaseWrite(pStream, &pStream->StreamSize, pStream->FileBitmap, pStream->BitmapSize); + + // Prepare and write the file footer + Footer.Signature = ID_FILE_BITMAP_FOOTER; + Footer.Version = 3; + Footer.BuildNumber = pStream->BuildNumber; + Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); + Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20); + Footer.BlockSize = pStream->BlockSize; + BSWAP_ARRAY32_UNSIGNED(&Footer, sizeof(FILE_BITMAP_FOOTER)); + pStream->BaseWrite(pStream, NULL, &Footer, sizeof(FILE_BITMAP_FOOTER)); + } + + // Close the base class + BlockStream_Close(pStream); +} + +static bool FlatStream_CreateMirror(TBlockStream * pStream) +{ + ULONGLONG MasterSize = 0; + ULONGLONG MirrorSize = 0; + LPBYTE FileBitmap = NULL; + DWORD dwBitmapSize; + DWORD dwBlockCount; + bool bNeedCreateMirrorStream = true; + bool bNeedResizeMirrorStream = true; + + // Do we have master function and base creation function? + if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) + return false; + + // Retrieve the master file size, block count and bitmap size + FileStream_GetSize(pStream->pMaster, &MasterSize); + dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); + dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8); + + // Setup stream size and position + pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? + pStream->StreamSize = MasterSize; + pStream->StreamPos = 0; + + // Open the base stream for write access + if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) + { + // If the file open succeeded, check if the file size matches required size + pStream->BaseGetSize(pStream, &MirrorSize); + if(MirrorSize == MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER)) + { + // Attempt to load an existing file bitmap + if(FlatStream_LoadBitmap(pStream)) + return true; + + // We need to create new file bitmap + bNeedResizeMirrorStream = false; + } + + // We need to create mirror stream + bNeedCreateMirrorStream = false; + } + + // Create a new stream, if needed + if(bNeedCreateMirrorStream) + { + if(!pStream->BaseCreate(pStream)) + return false; + } + + // If we need to, then resize the mirror stream + if(bNeedResizeMirrorStream) + { + if(!pStream->BaseResize(pStream, MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER))) + return false; + } + + // Allocate the bitmap array + FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize); + if(FileBitmap == NULL) + return false; + + // Initialize the bitmap + memset(FileBitmap, 0, dwBitmapSize); + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = dwBitmapSize; + pStream->BlockSize = DEFAULT_BLOCK_SIZE; + pStream->BlockCount = dwBlockCount; + pStream->IsComplete = 0; + pStream->IsModified = 1; + + // Note: Don't write the stream bitmap right away. + // Doing so would cause sparse file resize on NTFS, + // which would take long time on larger files. + return true; +} + +static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBlockStream * pStream; + ULONGLONG ByteOffset = 0; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Do we have a master stream? + if(pStream->pMaster != NULL) + { + if(!FlatStream_CreateMirror(pStream)) + { + FileStream_Close(pStream); + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + } + else + { + // Attempt to open the base stream + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + { + FileStream_Close(pStream); + return NULL; + } + + // Load the bitmap, if required to + if(dwStreamFlags & STREAM_FLAG_USE_BITMAP) + FlatStream_LoadBitmap(pStream); + } + + // If we have a stream bitmap, set the reading functions + // which check presence of each file block + if(pStream->FileBitmap != NULL) + { + // Set the stream position to zero. Stream size is already set + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Supply the stream functions + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamClose = (STREAM_CLOSE)FlatStream_Close; + + // Supply the block functions + pStream->BlockCheck = (BLOCK_CHECK)FlatStream_BlockCheck; + pStream->BlockRead = (BLOCK_READ)FlatStream_BlockRead; + } + else + { + // Reset the base position to zero + pStream->BaseRead(pStream, &ByteOffset, NULL, 0); + + // Setup stream size and position + pStream->StreamSize = pStream->Base.File.FileSize; + pStream->StreamPos = 0; + + // Set the base functions + pStream->StreamRead = pStream->BaseRead; + pStream->StreamWrite = pStream->BaseWrite; + pStream->StreamResize = pStream->BaseResize; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamClose = pStream->BaseClose; + } + + return pStream; +} + +//----------------------------------------------------------------------------- +// Local functions - partial stream support + +static bool IsPartHeader(PPART_FILE_HEADER pPartHdr) +{ + // Version number must be 2 + if(pPartHdr->PartialVersion == 2) + { + // GameBuildNumber must be an ASCII number + if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2])) + { + // Block size must be power of 2 + if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0) + return true; + } + } + + return false; +} + +static DWORD PartStream_CheckFile(TBlockStream * pStream) +{ + PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap; + DWORD dwBlockCount; + + // Get the number of blocks + dwBlockCount = (DWORD)((pStream->StreamSize + pStream->BlockSize - 1) / pStream->BlockSize); + + // Check all blocks + for(DWORD i = 0; i < dwBlockCount; i++, FileBitmap++) + { + // Few sanity checks + assert(FileBitmap->LargeValueHi == 0); + assert(FileBitmap->LargeValueLo == 0); + assert(FileBitmap->Flags == 0 || FileBitmap->Flags == 3); + + // Check if this block is present + if(FileBitmap->Flags != 3) + return 0; + } + + // Yes, the file is complete + return 1; +} + +static bool PartStream_LoadBitmap(TBlockStream * pStream) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + PART_FILE_HEADER PartHdr; + ULONGLONG ByteOffset = 0; + ULONGLONG StreamSize = 0; + DWORD BlockCount; + DWORD BitmapSize; + + // Only if the size is greater than size of the bitmap header + if(pStream->Base.File.FileSize > sizeof(PART_FILE_HEADER)) + { + // Attempt to read PART file header + if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) + { + // We need to swap PART file header on big-endian platforms + BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER)); + + // Verify the PART file header + if(IsPartHeader(&PartHdr)) + { + // Get the number of blocks and size of one block + StreamSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); + ByteOffset = sizeof(PART_FILE_HEADER); + BlockCount = (DWORD)((StreamSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); + BitmapSize = BlockCount * sizeof(PART_FILE_MAP_ENTRY); + + // Check if sizes match + if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize) + { + // Allocate space for the array of PART_FILE_MAP_ENTRY + FileBitmap = CASC_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); + if(FileBitmap != NULL) + { + // Load the block map + if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) + { + CASC_FREE(FileBitmap); + return false; + } + + // Make sure that the byte order is correct + BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize); + + // Update the stream size + pStream->BuildNumber = StringToInt(PartHdr.GameBuildNumber); + pStream->StreamSize = StreamSize; + + // Fill the bitmap information + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = BitmapSize; + pStream->BlockSize = PartHdr.BlockSize; + pStream->BlockCount = BlockCount; + pStream->IsComplete = PartStream_CheckFile(pStream); + return true; + } + } + } + } + } + + return false; +} + +static void PartStream_UpdateBitmap( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + ULONGLONG RealOffset) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + DWORD BlockSize = pStream->BlockSize; + + // Sanity checks + assert((StartOffset & (BlockSize - 1)) == 0); + assert(pStream->FileBitmap != NULL); + + // Calculate the first entry in the block map + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (StartOffset / BlockSize); + + // Set all bits for the specified range + while(StartOffset < EndOffset) + { + // Set the bit + FileBitmap->BlockOffsHi = (DWORD)(RealOffset >> 0x20); + FileBitmap->BlockOffsLo = (DWORD)(RealOffset & 0xFFFFFFFF); + FileBitmap->Flags = 3; + + // Move all + StartOffset += BlockSize; + RealOffset += BlockSize; + FileBitmap++; + } + + // Increment the bitmap update count + pStream->IsModified = 1; +} + +static bool PartStream_BlockCheck( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG BlockOffset) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + + // Sanity checks + assert((BlockOffset & (pStream->BlockSize - 1)) == 0); + assert(pStream->FileBitmap != NULL); + + // Calculate the block map entry + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (BlockOffset / pStream->BlockSize); + + // Check if the flags are present + return (FileBitmap->Flags & 0x03) ? true : false; +} + +static bool PartStream_BlockRead( + TBlockStream * pStream, + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + ULONGLONG ByteOffset; + DWORD BytesToRead; + DWORD BlockIndex = (DWORD)(StartOffset / pStream->BlockSize); + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + + // If the blocks are not available, we need to load them from the master + // and then save to the mirror + if(bAvailable == false) + { + // If we have no master, we cannot satisfy read request + if(pStream->pMaster == NULL) + return false; + + // Load the blocks from the master stream + // Note that we always have to read complete blocks + // so they get properly stored to the mirror stream + BytesToRead = (DWORD)(EndOffset - StartOffset); + if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) + return false; + + // The loaded blocks are going to be stored to the end of the file + // Note that this operation is not required to succeed + if(pStream->BaseGetSize(pStream, &ByteOffset)) + { + // Store the loaded blocks to the mirror file. + if(pStream->BaseWrite(pStream, &ByteOffset, BlockBuffer, BytesToRead)) + { + PartStream_UpdateBitmap(pStream, StartOffset, EndOffset, ByteOffset); + } + } + } + else + { + // Get the file map entry + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex; + + // Read all blocks + while(StartOffset < EndOffset) + { + // Get the number of bytes to be read + BytesToRead = (DWORD)(EndOffset - StartOffset); + if(BytesToRead > pStream->BlockSize) + BytesToRead = pStream->BlockSize; + if(BytesToRead > BytesNeeded) + BytesToRead = BytesNeeded; + + // Read the block + ByteOffset = MAKE_OFFSET64(FileBitmap->BlockOffsHi, FileBitmap->BlockOffsLo); + if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead)) + return false; + + // Move the pointers + StartOffset += pStream->BlockSize; + BlockBuffer += pStream->BlockSize; + BytesNeeded -= pStream->BlockSize; + FileBitmap++; + } + } + + return true; +} + +static void PartStream_Close(TBlockStream * pStream) +{ + PART_FILE_HEADER PartHeader; + ULONGLONG ByteOffset = 0; + + if(pStream->FileBitmap && pStream->IsModified) + { + // Prepare the part file header + memset(&PartHeader, 0, sizeof(PART_FILE_HEADER)); + PartHeader.PartialVersion = 2; + PartHeader.FileSizeHi = (DWORD)(pStream->StreamSize >> 0x20); + PartHeader.FileSizeLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); + PartHeader.BlockSize = pStream->BlockSize; + + // Make sure that the header is properly BSWAPed + BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER)); + sprintf(PartHeader.GameBuildNumber, "%u", (unsigned int)pStream->BuildNumber); + + // Write the part header + pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER)); + + // Write the block bitmap + BSWAP_ARRAY32_UNSIGNED(pStream->FileBitmap, pStream->BitmapSize); + pStream->BaseWrite(pStream, NULL, pStream->FileBitmap, pStream->BitmapSize); + } + + // Close the base class + BlockStream_Close(pStream); +} + +static bool PartStream_CreateMirror(TBlockStream * pStream) +{ + ULONGLONG RemainingSize; + ULONGLONG MasterSize = 0; + ULONGLONG MirrorSize = 0; + LPBYTE FileBitmap = NULL; + DWORD dwBitmapSize; + DWORD dwBlockCount; + bool bNeedCreateMirrorStream = true; + bool bNeedResizeMirrorStream = true; + + // Do we have master function and base creation function? + if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) + return false; + + // Retrieve the master file size, block count and bitmap size + FileStream_GetSize(pStream->pMaster, &MasterSize); + dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); + dwBitmapSize = (DWORD)(dwBlockCount * sizeof(PART_FILE_MAP_ENTRY)); + + // Setup stream size and position + pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? + pStream->StreamSize = MasterSize; + pStream->StreamPos = 0; + + // Open the base stream for write access + if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) + { + // If the file open succeeded, check if the file size matches required size + pStream->BaseGetSize(pStream, &MirrorSize); + if(MirrorSize >= sizeof(PART_FILE_HEADER) + dwBitmapSize) + { + // Check if the remaining size is aligned to block + RemainingSize = MirrorSize - sizeof(PART_FILE_HEADER) - dwBitmapSize; + if((RemainingSize & (DEFAULT_BLOCK_SIZE - 1)) == 0 || RemainingSize == MasterSize) + { + // Attempt to load an existing file bitmap + if(PartStream_LoadBitmap(pStream)) + return true; + } + } + + // We need to create mirror stream + bNeedCreateMirrorStream = false; + } + + // Create a new stream, if needed + if(bNeedCreateMirrorStream) + { + if(!pStream->BaseCreate(pStream)) + return false; + } + + // If we need to, then resize the mirror stream + if(bNeedResizeMirrorStream) + { + if(!pStream->BaseResize(pStream, sizeof(PART_FILE_HEADER) + dwBitmapSize)) + return false; + } + + // Allocate the bitmap array + FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize); + if(FileBitmap == NULL) + return false; + + // Initialize the bitmap + memset(FileBitmap, 0, dwBitmapSize); + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = dwBitmapSize; + pStream->BlockSize = DEFAULT_BLOCK_SIZE; + pStream->BlockCount = dwBlockCount; + pStream->IsComplete = 0; + pStream->IsModified = 1; + + // Note: Don't write the stream bitmap right away. + // Doing so would cause sparse file resize on NTFS, + // which would take long time on larger files. + return true; +} + + +static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBlockStream * pStream; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Do we have a master stream? + if(pStream->pMaster != NULL) + { + if(!PartStream_CreateMirror(pStream)) + { + FileStream_Close(pStream); + SetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + } + else + { + // Attempt to open the base stream + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + { + FileStream_Close(pStream); + return NULL; + } + + // Load the part stream block map + if(!PartStream_LoadBitmap(pStream)) + { + FileStream_Close(pStream); + SetLastError(ERROR_BAD_FORMAT); + return NULL; + } + } + + // Set the stream position to zero. Stream size is already set + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Set new function pointers + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamClose = (STREAM_CLOSE)PartStream_Close; + + // Supply the block functions + pStream->BlockCheck = (BLOCK_CHECK)PartStream_BlockCheck; + pStream->BlockRead = (BLOCK_READ)PartStream_BlockRead; + return pStream; +} + +//----------------------------------------------------------------------------- +// Local functions - encrypted stream support + +static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; + +static const char * AuthCodeArray[] = +{ + // Starcraft II (Heart of the Swarm) + // Authentication code URL: http://dist.blizzard.com/mediakey/hots-authenticationcode-bgdl.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U", // SC2 Heart of the Swarm-all : "expand 32-byte kQAKQ0000FM3UN5XE000073FD6CDT0000LJBJS48B0000DJND" + + // Diablo III: Agent.exe (1.0.0.954) + // Address of decryption routine: 00502b00 + // Pointer to decryptor object: ECX + // Pointer to key: ECX+0x5C + // Authentication code URL: http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK", // Diablo III Installer (deDE): "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX" + "MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP", // Diablo III Installer (enGB): "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ" + "8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP", // Diablo III Installer (enSG): "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS" + "EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ", // Diablo III Installer (enUS): "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG" + "PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT", // Diablo III Installer (esES): "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3" + "X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2", // Diablo III Installer (esMX): "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS" + "5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2", // Diablo III Installer (frFR): "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5" + "478JD2K56EVNVVY4XX8TDWYT5B8KB254", // Diablo III Installer (itIT): "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T" + "8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A", // Diablo III Installer (koKR): "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH" + "LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB", // Diablo III Installer (plPL): "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK" + "K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG", // Diablo III Installer (ptBR): "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB" + "NDVW8GWLAYCRPGRNY8RT7ZZUQU63VLPR", // Diablo III Installer (ruRU): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H", // Diablo III Installer (zhTW): "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8" +// "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // Diablo III Installer (zhCN): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + + // Starcraft II (Wings of Liberty): Installer.exe (4.1.1.4219) + // Address of decryption routine: 0053A3D0 + // Pointer to decryptor object: ECX + // Pointer to key: ECX+0x5C + // Authentication code URL: http://dist.blizzard.com/mediakey/sc2-authenticationcode-enUS.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX", // SC2 Wings of Liberty (deDE): "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V" + "G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH", // SC2 Wings of Liberty (enGB): "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D" + "W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW", // SC2 Wings of Liberty (enSG): "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE" + "3DH5RE5NVM5GTFD85LXGWT6FK859ETR5", // SC2 Wings of Liberty (enUS): "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG" + "8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ", // SC2 Wings of Liberty (esES): "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P" + "A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54", // SC2 Wings of Liberty (esMX): "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U" + "ZG7J9K938HJEFWPQUA768MA2PFER6EAJ", // SC2 Wings of Liberty (frFR): "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76" + "NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2", // SC2 Wings of Liberty (itIT): "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B" + "3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F", // SC2 Wings of Liberty (koKR): "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA" + "2NSFB8MELULJ83U6YHA3UP6K4MQD48L6", // SC2 Wings of Liberty (plPL): "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3" + "QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E", // SC2 Wings of Liberty (ptBR): "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX" + "VHB378W64BAT9SH7D68VV9NLQDK9YEGT", // SC2 Wings of Liberty (ruRU): "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V" + "U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE", // SC2 Wings of Liberty (zhTW): "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ" + + NULL +}; + +static DWORD Rol32(DWORD dwValue, DWORD dwRolCount) +{ + DWORD dwShiftRight = 32 - dwRolCount; + + return (dwValue << dwRolCount) | (dwValue >> dwShiftRight); +} + +static void CreateKeyFromAuthCode( + LPBYTE pbKeyBuffer, + const char * szAuthCode) +{ + PDWORD KeyPosition = (PDWORD)(pbKeyBuffer + 0x10); + PDWORD AuthCode32 = (PDWORD)szAuthCode; + + memcpy(pbKeyBuffer, szKeyTemplate, ENCRYPTED_CHUNK_SIZE); + KeyPosition[0x00] = AuthCode32[0x03]; + KeyPosition[0x02] = AuthCode32[0x07]; + KeyPosition[0x03] = AuthCode32[0x02]; + KeyPosition[0x05] = AuthCode32[0x06]; + KeyPosition[0x06] = AuthCode32[0x01]; + KeyPosition[0x08] = AuthCode32[0x05]; + KeyPosition[0x09] = AuthCode32[0x00]; + KeyPosition[0x0B] = AuthCode32[0x04]; + BSWAP_ARRAY32_UNSIGNED(pbKeyBuffer, ENCRYPTED_CHUNK_SIZE); +} + +static void DecryptFileChunk( + DWORD * ChunkData, + LPBYTE pbKey, + ULONGLONG ByteOffset, + DWORD dwLength) +{ + ULONGLONG ChunkOffset; + DWORD KeyShuffled[0x10]; + DWORD KeyMirror[0x10]; + DWORD RoundCount = 0x14; + + // Prepare the key + ChunkOffset = ByteOffset / ENCRYPTED_CHUNK_SIZE; + memcpy(KeyMirror, pbKey, ENCRYPTED_CHUNK_SIZE); + BSWAP_ARRAY32_UNSIGNED(KeyMirror, ENCRYPTED_CHUNK_SIZE); + KeyMirror[0x05] = (DWORD)(ChunkOffset >> 32); + KeyMirror[0x08] = (DWORD)(ChunkOffset); + + while(dwLength >= ENCRYPTED_CHUNK_SIZE) + { + // Shuffle the key - part 1 + KeyShuffled[0x0E] = KeyMirror[0x00]; + KeyShuffled[0x0C] = KeyMirror[0x01]; + KeyShuffled[0x05] = KeyMirror[0x02]; + KeyShuffled[0x0F] = KeyMirror[0x03]; + KeyShuffled[0x0A] = KeyMirror[0x04]; + KeyShuffled[0x07] = KeyMirror[0x05]; + KeyShuffled[0x0B] = KeyMirror[0x06]; + KeyShuffled[0x09] = KeyMirror[0x07]; + KeyShuffled[0x03] = KeyMirror[0x08]; + KeyShuffled[0x06] = KeyMirror[0x09]; + KeyShuffled[0x08] = KeyMirror[0x0A]; + KeyShuffled[0x0D] = KeyMirror[0x0B]; + KeyShuffled[0x02] = KeyMirror[0x0C]; + KeyShuffled[0x04] = KeyMirror[0x0D]; + KeyShuffled[0x01] = KeyMirror[0x0E]; + KeyShuffled[0x00] = KeyMirror[0x0F]; + + // Shuffle the key - part 2 + for(DWORD i = 0; i < RoundCount; i += 2) + { + KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x02]), 0x07); + KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0E]), 0x09); + KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x0A]), 0x0D); + KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x03]), 0x12); + + KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x04]), 0x07); + KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x0C]), 0x09); + KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x07]), 0x0D); + KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x06]), 0x12); + + KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x01]), 0x07); + KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x05]), 0x09); + KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x0B]), 0x0D); + KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x08]), 0x12); + + KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x00]), 0x07); + KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x0F]), 0x09); + KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x09]), 0x0D); + KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x0D]), 0x12); + + KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x09]), 0x07); + KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x0E]), 0x09); + KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x04]), 0x0D); + KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x08]), 0x12); + + KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x0A]), 0x07); + KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x0C]), 0x09); + KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x01]), 0x0D); + KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0D]), 0x12); + + KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x07]), 0x07); + KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x05]), 0x09); + KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x00]), 0x0D); + KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x03]), 0x12); + + KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x0B]), 0x07); + KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x0F]), 0x09); + KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x02]), 0x0D); + KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x06]), 0x12); + } + + // Decrypt one data chunk + BSWAP_ARRAY32_UNSIGNED(ChunkData, ENCRYPTED_CHUNK_SIZE); + ChunkData[0x00] = ChunkData[0x00] ^ (KeyShuffled[0x0E] + KeyMirror[0x00]); + ChunkData[0x01] = ChunkData[0x01] ^ (KeyShuffled[0x04] + KeyMirror[0x0D]); + ChunkData[0x02] = ChunkData[0x02] ^ (KeyShuffled[0x08] + KeyMirror[0x0A]); + ChunkData[0x03] = ChunkData[0x03] ^ (KeyShuffled[0x09] + KeyMirror[0x07]); + ChunkData[0x04] = ChunkData[0x04] ^ (KeyShuffled[0x0A] + KeyMirror[0x04]); + ChunkData[0x05] = ChunkData[0x05] ^ (KeyShuffled[0x0C] + KeyMirror[0x01]); + ChunkData[0x06] = ChunkData[0x06] ^ (KeyShuffled[0x01] + KeyMirror[0x0E]); + ChunkData[0x07] = ChunkData[0x07] ^ (KeyShuffled[0x0D] + KeyMirror[0x0B]); + ChunkData[0x08] = ChunkData[0x08] ^ (KeyShuffled[0x03] + KeyMirror[0x08]); + ChunkData[0x09] = ChunkData[0x09] ^ (KeyShuffled[0x07] + KeyMirror[0x05]); + ChunkData[0x0A] = ChunkData[0x0A] ^ (KeyShuffled[0x05] + KeyMirror[0x02]); + ChunkData[0x0B] = ChunkData[0x0B] ^ (KeyShuffled[0x00] + KeyMirror[0x0F]); + ChunkData[0x0C] = ChunkData[0x0C] ^ (KeyShuffled[0x02] + KeyMirror[0x0C]); + ChunkData[0x0D] = ChunkData[0x0D] ^ (KeyShuffled[0x06] + KeyMirror[0x09]); + ChunkData[0x0E] = ChunkData[0x0E] ^ (KeyShuffled[0x0B] + KeyMirror[0x06]); + ChunkData[0x0F] = ChunkData[0x0F] ^ (KeyShuffled[0x0F] + KeyMirror[0x03]); + BSWAP_ARRAY32_UNSIGNED(ChunkData, ENCRYPTED_CHUNK_SIZE); + + // Update byte offset in the key + KeyMirror[0x08]++; + if(KeyMirror[0x08] == 0) + KeyMirror[0x05]++; + + // Move pointers and decrease number of bytes to decrypt + ChunkData += (ENCRYPTED_CHUNK_SIZE / sizeof(DWORD)); + dwLength -= ENCRYPTED_CHUNK_SIZE; + } +} + +static bool EncrStream_DetectFileKey(TEncryptedStream * pStream) +{ + ULONGLONG ByteOffset = 0; + BYTE EncryptedHeader[ENCRYPTED_CHUNK_SIZE]; + BYTE FileHeader[ENCRYPTED_CHUNK_SIZE]; + + // Read the first file chunk + if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) + { + // We just try all known keys one by one + for(int i = 0; AuthCodeArray[i] != NULL; i++) + { + // Prepare they decryption key from game serial number + CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]); + + // Try to decrypt with the given key + memcpy(FileHeader, EncryptedHeader, ENCRYPTED_CHUNK_SIZE); + DecryptFileChunk((PDWORD)FileHeader, pStream->Key, ByteOffset, ENCRYPTED_CHUNK_SIZE); + + // We check the decrypted data + // All known encrypted archives have header at the begin of the file, + // so we check for archive signature there. + if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q') + { + // Update the stream size + pStream->StreamSize = pStream->Base.File.FileSize; + + // Fill the block information + pStream->BlockSize = ENCRYPTED_CHUNK_SIZE; + pStream->BlockCount = (DWORD)(pStream->Base.File.FileSize + ENCRYPTED_CHUNK_SIZE - 1) / ENCRYPTED_CHUNK_SIZE; + pStream->IsComplete = 1; + return true; + } + } + } + + // Key not found, sorry + return false; +} + +static bool EncrStream_BlockRead( + TEncryptedStream * pStream, + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + DWORD dwBytesToRead; + + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + assert(bAvailable != false); + BytesNeeded = BytesNeeded; + bAvailable = bAvailable; + + // Read the file from the stream as-is + // Limit the reading to number of blocks really needed + dwBytesToRead = (DWORD)(EndOffset - StartOffset); + if(!pStream->BaseRead(pStream, &StartOffset, BlockBuffer, dwBytesToRead)) + return false; + + // Decrypt the data + dwBytesToRead = (dwBytesToRead + ENCRYPTED_CHUNK_SIZE - 1) & ~(ENCRYPTED_CHUNK_SIZE - 1); + DecryptFileChunk((PDWORD)BlockBuffer, pStream->Key, StartOffset, dwBytesToRead); + return true; +} + +static TFileStream * EncrStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TEncryptedStream * pStream; + + // Create new empty stream + pStream = (TEncryptedStream *)AllocateFileStream(szFileName, sizeof(TEncryptedStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Attempt to open the base stream + assert(pStream->BaseOpen != NULL); + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + return NULL; + + // Determine the encryption key for the archive + if(EncrStream_DetectFileKey(pStream)) + { + // Set the stream position and size + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Set new function pointers + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamClose = pStream->BaseClose; + + // Supply the block functions + pStream->BlockRead = (BLOCK_READ)EncrStream_BlockRead; + return pStream; + } + + // Cleanup the stream and return + FileStream_Close(pStream); + SetLastError(ERROR_UNKNOWN_FILE_KEY); + return NULL; +} + +//----------------------------------------------------------------------------- +// Local functions - Block4 stream support + +#define BLOCK4_BLOCK_SIZE 0x4000 // Size of one block +#define BLOCK4_HASH_SIZE 0x20 // Size of MD5 hash that is after each block +#define BLOCK4_MAX_BLOCKS 0x00002000 // Maximum amount of blocks per file +#define BLOCK4_MAX_FSIZE 0x08040000 // Max size of one file + +static bool Block4Stream_BlockRead( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; + ULONGLONG ByteOffset; + DWORD BytesToRead; + DWORD StreamIndex; + DWORD BlockIndex; + bool bResult; + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + assert(bAvailable == true); + + // Keep compiler happy + bAvailable = bAvailable; + EndOffset = EndOffset; + + while(BytesNeeded != 0) + { + // Calculate the block index and the file index + StreamIndex = (DWORD)((StartOffset / pStream->BlockSize) / BLOCK4_MAX_BLOCKS); + BlockIndex = (DWORD)((StartOffset / pStream->BlockSize) % BLOCK4_MAX_BLOCKS); + if(StreamIndex > pStream->BitmapSize) + return false; + + // Calculate the block offset + ByteOffset = ((ULONGLONG)BlockIndex * (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE)); + BytesToRead = CASCLIB_MIN(BytesNeeded, BLOCK4_BLOCK_SIZE); + + // Read from the base stream + pStream->Base = BaseArray[StreamIndex]; + bResult = pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead); + BaseArray[StreamIndex] = pStream->Base; + + // Did the result succeed? + if(bResult == false) + return false; + + // Move pointers + StartOffset += BytesToRead; + BlockBuffer += BytesToRead; + BytesNeeded -= BytesToRead; + } + + return true; +} + + +static void Block4Stream_Close(TBlockStream * pStream) +{ + TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; + + // If we have a non-zero count of base streams, + // we have to close them all + if(BaseArray != NULL) + { + // Close all base streams + for(DWORD i = 0; i < pStream->BitmapSize; i++) + { + memcpy(&pStream->Base, BaseArray + i, sizeof(TBaseProviderData)); + pStream->BaseClose(pStream); + } + } + + // Free the data map, if any + if(pStream->FileBitmap != NULL) + CASC_FREE(pStream->FileBitmap); + pStream->FileBitmap = NULL; + + // Do not call the BaseClose function, + // we closed all handles already + return; +} + +static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBaseProviderData * NewBaseArray = NULL; + ULONGLONG RemainderBlock; + ULONGLONG BlockCount; + ULONGLONG FileSize; + TBlockStream * pStream; + TCHAR * szNameBuff; + size_t nNameLength; + DWORD dwBaseFiles = 0; + DWORD dwBaseFlags; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Sanity check + assert(pStream->BaseOpen != NULL); + + // Get the length of the file name without numeric suffix + nNameLength = _tcslen(pStream->szFileName); + if(pStream->szFileName[nNameLength - 2] == '.' && pStream->szFileName[nNameLength - 1] == '0') + nNameLength -= 2; + pStream->szFileName[nNameLength] = 0; + + // Supply the stream functions + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamClose = (STREAM_CLOSE)Block4Stream_Close; + pStream->BlockRead = (BLOCK_READ)Block4Stream_BlockRead; + + // Allocate work space for numeric names + szNameBuff = CASC_ALLOC(TCHAR, nNameLength + 4); + if(szNameBuff != NULL) + { + // Set the base flags + dwBaseFlags = (dwStreamFlags & STREAM_PROVIDERS_MASK) | STREAM_FLAG_READ_ONLY; + + // Go all suffixes from 0 to 30 + for(int nSuffix = 0; nSuffix < 30; nSuffix++) + { + // Open the n-th file + _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix); + if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags)) + break; + + // If the open succeeded, we re-allocate the base provider array + NewBaseArray = CASC_ALLOC(TBaseProviderData, dwBaseFiles + 1); + if(NewBaseArray == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + // Copy the old base data array to the new base data array + if(pStream->FileBitmap != NULL) + { + memcpy(NewBaseArray, pStream->FileBitmap, sizeof(TBaseProviderData) * dwBaseFiles); + CASC_FREE(pStream->FileBitmap); + } + + // Also copy the opened base array + memcpy(NewBaseArray + dwBaseFiles, &pStream->Base, sizeof(TBaseProviderData)); + pStream->FileBitmap = NewBaseArray; + dwBaseFiles++; + + // Get the size of the base stream + pStream->BaseGetSize(pStream, &FileSize); + assert(FileSize <= BLOCK4_MAX_FSIZE); + RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); + BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); + + // Increment the stream size and number of blocks + pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE); + pStream->BlockCount += (DWORD)BlockCount; + + // Is this the last file? + if(FileSize < BLOCK4_MAX_FSIZE) + { + if(RemainderBlock) + { + pStream->StreamSize += (RemainderBlock - BLOCK4_HASH_SIZE); + pStream->BlockCount++; + } + break; + } + } + + // Fill the remainining block stream variables + pStream->BitmapSize = dwBaseFiles; + pStream->BlockSize = BLOCK4_BLOCK_SIZE; + pStream->IsComplete = 1; + pStream->IsModified = 0; + + // Fill the remaining stream variables + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + CASC_FREE(szNameBuff); + } + + // If we opened something, return success + if(dwBaseFiles == 0) + { + FileStream_Close(pStream); + SetLastError(ERROR_FILE_NOT_FOUND); + pStream = NULL; + } + + return pStream; +} + +//----------------------------------------------------------------------------- +// Public functions + +/** + * This function creates a new file for read-write access + * + * - If the current platform supports file sharing, + * the file must be created for read sharing (i.e. another application + * can open the file for read, but not for write) + * - If the file does not exist, the function must create new one + * - If the file exists, the function must rewrite it and set to zero size + * - The parameters of the function must be validate by the caller + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + * and free all memory that has been allocated in the process of stream creation, + * including the TFileStream structure itself + * + * \a szFileName Name of the file to create + */ + +TFileStream * FileStream_CreateFile( + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ + TFileStream * pStream; + + // We only support creation of flat, local file + if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) + { + SetLastError(ERROR_NOT_SUPPORTED); + return NULL; + } + + // Allocate file stream structure for flat stream + pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream != NULL) + { + // Attempt to create the disk file + if(BaseFile_Create(pStream)) + { + // Fill the stream provider functions + pStream->StreamRead = pStream->BaseRead; + pStream->StreamWrite = pStream->BaseWrite; + pStream->StreamResize = pStream->BaseResize; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamClose = pStream->BaseClose; + return pStream; + } + + // File create failed, delete the stream + CASC_FREE(pStream); + pStream = NULL; + } + + // Return the stream + return pStream; +} + +/** + * This function opens an existing file for read or read-write access + * - If the current platform supports file sharing, + * the file must be open for read sharing (i.e. another application + * can open the file for read, but not for write) + * - If the file does not exist, the function must return NULL + * - If the file exists but cannot be open, then function must return NULL + * - The parameters of the function must be validate by the caller + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + * and free all memory that has been allocated in the process of stream creation, + * including the TFileStream structure itself + * + * \a szFileName Name of the file to open + * \a dwStreamFlags specifies the provider and base storage type + */ + +TFileStream * FileStream_OpenFile( + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ + DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK; + size_t nPrefixLength = FileStream_Prefix(szFileName, &dwProvider); + + // Re-assemble the stream flags + dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | dwProvider; + szFileName += nPrefixLength; + + // Perform provider-specific open + switch(dwStreamFlags & STREAM_PROVIDER_MASK) + { + case STREAM_PROVIDER_FLAT: + return FlatStream_Open(szFileName, dwStreamFlags); + + case STREAM_PROVIDER_PARTIAL: + return PartStream_Open(szFileName, dwStreamFlags); + + case STREAM_PROVIDER_ENCRYPTED: + return EncrStream_Open(szFileName, dwStreamFlags); + + case STREAM_PROVIDER_BLOCK4: + return Block4Stream_Open(szFileName, dwStreamFlags); + + default: + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } +} + +/** + * Returns the file name of the stream + * + * \a pStream Pointer to an open stream + */ +const TCHAR * FileStream_GetFileName(TFileStream * pStream) +{ + assert(pStream != NULL); + return pStream->szFileName; +} + +/** + * Returns the length of the provider prefix. Returns zero if no prefix + * + * \a szFileName Pointer to a stream name (file, mapped file, URL) + * \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX) + */ + +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider) +{ + size_t nPrefixLength1 = 0; + size_t nPrefixLength2 = 0; + DWORD dwProvider = 0; + + if(szFileName != NULL) + { + // + // Determine the stream provider + // + + if(!_tcsnicmp(szFileName, _T("flat-"), 5)) + { + dwProvider |= STREAM_PROVIDER_FLAT; + nPrefixLength1 = 5; + } + + else if(!_tcsnicmp(szFileName, _T("part-"), 5)) + { + dwProvider |= STREAM_PROVIDER_PARTIAL; + nPrefixLength1 = 5; + } + + else if(!_tcsnicmp(szFileName, _T("mpqe-"), 5)) + { + dwProvider |= STREAM_PROVIDER_ENCRYPTED; + nPrefixLength1 = 5; + } + + else if(!_tcsnicmp(szFileName, _T("blk4-"), 5)) + { + dwProvider |= STREAM_PROVIDER_BLOCK4; + nPrefixLength1 = 5; + } + + // + // Determine the base provider + // + + if(!_tcsnicmp(szFileName+nPrefixLength1, _T("file:"), 5)) + { + dwProvider |= BASE_PROVIDER_FILE; + nPrefixLength2 = 5; + } + + else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4)) + { + dwProvider |= BASE_PROVIDER_MAP; + nPrefixLength2 = 4; + } + + else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5)) + { + dwProvider |= BASE_PROVIDER_HTTP; + nPrefixLength2 = 5; + } + + // Only accept stream provider if we recognized the base provider + if(nPrefixLength2 != 0) + { + // It is also allowed to put "//" after the base provider, e.g. "file://", "http://" + if(szFileName[nPrefixLength1+nPrefixLength2] == '/' && szFileName[nPrefixLength1+nPrefixLength2+1] == '/') + nPrefixLength2 += 2; + + if(pdwProvider != NULL) + *pdwProvider = dwProvider; + return nPrefixLength1 + nPrefixLength2; + } + } + + return 0; +} + +/** + * Sets a download callback. Whenever the stream needs to download one or more blocks + * from the server, the callback is called + * + * \a pStream Pointer to an open stream + * \a pfnCallback Pointer to callback function + * \a pvUserData Arbitrary user pointer passed to the download callback + */ + +bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData) +{ + TBlockStream * pBlockStream = (TBlockStream *)pStream; + + if(pStream->BlockRead == NULL) + { + SetLastError(ERROR_NOT_SUPPORTED); + return false; + } + + pBlockStream->pfnCallback = pfnCallback; + pBlockStream->UserData = pvUserData; + return true; +} + +/** + * Reads data from the stream + * + * - Returns true if the read operation succeeded and all bytes have been read + * - Returns false if either read failed or not all bytes have been read + * - If the pByteOffset is NULL, the function must read the data from the current file position + * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored + * and the function just adjusts file pointer. + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be read + * \a dwBytesToRead Number of bytes to read from the file + * + * \returns + * - If the function reads the required amount of bytes, it returns true. + * - If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF + * - If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF + */ +bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) +{ + assert(pStream->StreamRead != NULL); + return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); +} + +/** + * This function writes data to the stream + * + * - Returns true if the write operation succeeded and all bytes have been written + * - Returns false if either write failed or not all bytes have been written + * - If the pByteOffset is NULL, the function must write the data to the current file position + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ +bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) +{ + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SetLastError(ERROR_ACCESS_DENIED); + return false; + } + + assert(pStream->StreamWrite != NULL); + return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); +} + +/** + * Returns the size of a file + * + * \a pStream Pointer to an open stream + * \a FileSize Pointer where to store the file size + */ +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ + assert(pStream->StreamGetSize != NULL); + return pStream->StreamGetSize(pStream, pFileSize); +} + +/** + * Sets the size of a file + * + * \a pStream Pointer to an open stream + * \a NewFileSize File size to set + */ +bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +{ + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SetLastError(ERROR_ACCESS_DENIED); + return false; + } + + assert(pStream->StreamResize != NULL); + return pStream->StreamResize(pStream, NewFileSize); +} + +/** + * This function returns the current file position + * \a pStream + * \a pByteOffset + */ +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + assert(pStream->StreamGetPos != NULL); + return pStream->StreamGetPos(pStream, pByteOffset); +} + +/** + * Returns the last write time of a file + * + * \a pStream Pointer to an open stream + * \a pFileType Pointer where to store the file last write time + */ +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ + // Just use the saved filetime value + *pFileTime = pStream->Base.File.FileTime; + return true; +} + +/** + * Returns the stream flags + * + * \a pStream Pointer to an open stream + * \a pdwStreamFlags Pointer where to store the stream flags + */ +bool FileStream_GetFlags(TFileStream * pStream, PDWORD pdwStreamFlags) +{ + *pdwStreamFlags = pStream->dwFlags; + return true; +} + +/** + * Switches a stream with another. Used for final phase of archive compacting. + * Performs these steps: + * + * 1) Closes the handle to the existing file + * 2) Renames the temporary file to the original file, overwrites existing one + * 3) Opens the file stores the handle and stream position to the new stream structure + * + * \a pStream Pointer to an open stream + * \a pNewStream Temporary ("working") stream (created during archive compacting) + */ +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream) +{ + // Only supported on flat files + if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) + { + SetLastError(ERROR_NOT_SUPPORTED); + return false; + } + + // Not supported on read-only streams + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SetLastError(ERROR_ACCESS_DENIED); + return false; + } + + // Close both stream's base providers + pNewStream->BaseClose(pNewStream); + pStream->BaseClose(pStream); + + // Now we have to delete the (now closed) old file and rename the new file + if(!BaseFile_Replace(pStream, pNewStream)) + return false; + + // Now open the base file again + if(!BaseFile_Open(pStream, pStream->szFileName, pStream->dwFlags)) + return false; + + // Cleanup the new stream + FileStream_Close(pNewStream); + return true; +} + +/** + * This function closes an archive file and frees any data buffers + * that have been allocated for stream management. The function must also + * support partially allocated structure, i.e. one or more buffers + * can be NULL, if there was an allocation failure during the process + * + * \a pStream Pointer to an open stream + */ +void FileStream_Close(TFileStream * pStream) +{ + // Check if the stream structure is allocated at all + if(pStream != NULL) + { + // Free the master stream, if any + if(pStream->pMaster != NULL) + FileStream_Close(pStream->pMaster); + pStream->pMaster = NULL; + + // Close the stream provider. + if(pStream->StreamClose != NULL) + pStream->StreamClose(pStream); + + // Also close base stream, if any + else if(pStream->BaseClose != NULL) + pStream->BaseClose(pStream); + + // Free the stream itself + CASC_FREE(pStream); + } +} diff --git a/dep/CascLib/src/common/FileStream.h b/dep/CascLib/src/common/FileStream.h new file mode 100644 index 00000000000..1e51acfc845 --- /dev/null +++ b/dep/CascLib/src/common/FileStream.h @@ -0,0 +1,238 @@ +/*****************************************************************************/ +/* FileStream.h Copyright (c) Ladislav Zezula 2012 */ +/*---------------------------------------------------------------------------*/ +/* Description: Definitions for FileStream object */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 14.04.12 1.00 Lad The first version of FileStream.h */ +/*****************************************************************************/ + +#ifndef __FILESTREAM_H__ +#define __FILESTREAM_H__ + +//----------------------------------------------------------------------------- +// Function prototypes + +typedef void (*STREAM_INIT)( + struct TFileStream * pStream // Pointer to an unopened stream +); + +typedef bool (*STREAM_CREATE)( + struct TFileStream * pStream // Pointer to an unopened stream + ); + +typedef bool (*STREAM_OPEN)( + struct TFileStream * pStream, // Pointer to an unopened stream + const TCHAR * szFileName, // Pointer to file name to be open + DWORD dwStreamFlags // Stream flags + ); + +typedef bool (*STREAM_READ)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead // Number of bytes to read from the file + ); + +typedef bool (*STREAM_WRITE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position + const void * pvBuffer, // Pointer to data to be written + DWORD dwBytesToWrite // Number of bytes to read from the file + ); + +typedef bool (*STREAM_RESIZE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG FileSize // New size for the file, in bytes + ); + +typedef bool (*STREAM_GETSIZE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize // Receives the file size, in bytes + ); + +typedef bool (*STREAM_GETPOS)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset // Pointer to store current file position + ); + +typedef void (*STREAM_CLOSE)( + struct TFileStream * pStream // Pointer to an open stream + ); + +typedef bool (*BLOCK_READ)( + struct TFileStream * pStream, // Pointer to a block-oriented stream + ULONGLONG StartOffset, // Byte offset of start of the block array + ULONGLONG EndOffset, // End offset (either end of the block or end of the file) + LPBYTE BlockBuffer, // Pointer to block-aligned buffer + DWORD BytesNeeded, // Number of bytes that are really needed + bool bAvailable // true if the block is available + ); + +typedef bool (*BLOCK_CHECK)( + struct TFileStream * pStream, // Pointer to a block-oriented stream + ULONGLONG BlockOffset // Offset of the file to check + ); + +typedef void (*BLOCK_SAVEMAP)( + struct TFileStream * pStream // Pointer to a block-oriented stream + ); + +//----------------------------------------------------------------------------- +// Local structures - partial file structure and bitmap footer + +#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3') +#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block +#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs + +typedef struct _PART_FILE_HEADER +{ + DWORD PartialVersion; // Always set to 2 + char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ + DWORD Flags; // Flags (details unknown) + DWORD FileSizeLo; // Low 32 bits of the contained file size + DWORD FileSizeHi; // High 32 bits of the contained file size + DWORD BlockSize; // Size of one file block, in bytes + +} PART_FILE_HEADER, *PPART_FILE_HEADER; + +// Structure describing the block-to-file map entry +typedef struct _PART_FILE_MAP_ENTRY +{ + DWORD Flags; // 3 = the block is present in the file + DWORD BlockOffsLo; // Low 32 bits of the block position in the file + DWORD BlockOffsHi; // High 32 bits of the block position in the file + DWORD LargeValueLo; // 64-bit value, meaning is unknown + DWORD LargeValueHi; + +} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; + +typedef struct _FILE_BITMAP_FOOTER +{ + DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER) + DWORD Version; // Unknown, seems to always have value of 3 (version?) + DWORD BuildNumber; // Game build number for that MPQ + DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map + DWORD MapOffsetHi; // High 32-bits of the offset of the bit map + DWORD BlockSize; // Size of one block (usually 0x4000 bytes) + +} FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER; + +//----------------------------------------------------------------------------- +// Structure for file stream + +union TBaseProviderData +{ + struct + { + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Last write time + HANDLE hFile; // File handle + } File; + + struct + { + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Last write time + LPBYTE pbFile; // Pointer to mapped view + } Map; + + struct + { + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Last write time + HANDLE hInternet; // Internet handle + HANDLE hConnect; // Connection to the internet server + } Http; +}; + +struct TFileStream +{ + // Stream provider functions + STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly. + STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly. + STREAM_RESIZE StreamResize; // Pointer to function changing file size + STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size + STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position + STREAM_CLOSE StreamClose; // Pointer to function closing the stream + + // Block-oriented functions + BLOCK_READ BlockRead; // Pointer to function reading one or more blocks + BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present + + // Base provider functions + STREAM_CREATE BaseCreate; // Pointer to base create function + STREAM_OPEN BaseOpen; // Pointer to base open function + STREAM_READ BaseRead; // Read from the stream + STREAM_WRITE BaseWrite; // Write to the stream + STREAM_RESIZE BaseResize; // Pointer to function changing file size + STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size + STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position + STREAM_CLOSE BaseClose; // Pointer to function closing the stream + + // Base provider data (file size, file position) + TBaseProviderData Base; + + // Stream provider data + TFileStream * pMaster; // Master stream (e.g. MPQ on a web server) + TCHAR * szFileName; // File name (self-relative pointer) + + ULONGLONG StreamSize; // Stream size (can be less than file size) + ULONGLONG StreamPos; // Stream position + DWORD BuildNumber; // Game build number + DWORD dwFlags; // Stream flags + + // Followed by stream provider data, with variable length +}; + +//----------------------------------------------------------------------------- +// Structures for block-oriented stream + +struct TBlockStream : public TFileStream +{ + STREAM_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading + void * FileBitmap; // Array of bits for file blocks + void * UserData; // User data to be passed to the download callback + DWORD BitmapSize; // Size of the file bitmap (in bytes) + DWORD BlockSize; // Size of one block, in bytes + DWORD BlockCount; // Number of data blocks in the file + DWORD IsComplete; // If nonzero, no blocks are missing + DWORD IsModified; // nonzero if the bitmap has been modified +}; + +//----------------------------------------------------------------------------- +// Structure for encrypted stream + +#define ENCRYPTED_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted + +struct TEncryptedStream : public TBlockStream +{ + BYTE Key[ENCRYPTED_CHUNK_SIZE]; // File key +}; + +//----------------------------------------------------------------------------- +// Public functions for file stream + +TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags); +TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags); +const TCHAR * FileStream_GetFileName(TFileStream * pStream); +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider); + +bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); + +bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); +bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); +bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); +bool FileStream_GetFlags(TFileStream * pStream, PDWORD pdwStreamFlags); +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream); +void FileStream_Close(TFileStream * pStream); + + +#endif // __FILESTREAM_H__ diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp new file mode 100644 index 00000000000..4b742b1d07c --- /dev/null +++ b/dep/CascLib/src/common/ListFile.cpp @@ -0,0 +1,266 @@ +/*****************************************************************************/ +/* ListFile.cpp Copyright (c) Ladislav Zezula 2004 */ +/*---------------------------------------------------------------------------*/ +/* Description: */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 12.06.04 1.00 Lad The first version of ListFile.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Listfile entry structure + +#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer + +typedef bool (*RELOAD_CACHE)(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead); +typedef void (*CLOSE_STREAM)(void * pvCacheContext); + +struct TListFileCache +{ + RELOAD_CACHE pfnReloadCache; // Function for reloading the cache + CLOSE_STREAM pfnCloseStream; // Function for closing the stream + void * pvCacheContext; // Reload context passed to reload function + char * szMask; // Self-relative pointer to file mask + DWORD dwFileSize; // Total size of the cached file + DWORD dwFilePos; // Position of the cache in the file + BYTE * pBegin; // The begin of the listfile cache + BYTE * pPos; + BYTE * pEnd; // The last character in the file cache + + BYTE Buffer[CACHE_BUFFER_SIZE]; +// char MaskBuff[1] // Followed by the name mask (if any) +}; + +//----------------------------------------------------------------------------- +// Local functions + +static bool ReloadCache_ExternalFile(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead) +{ + TFileStream * pStream = (TFileStream *)pvCacheContext; + + return FileStream_Read(pStream, NULL, pbBuffer, dwBytesToRead); +} + +static void CloseStream_ExternalFile(void * pvCacheContext) +{ + TFileStream * pStream = (TFileStream *)pvCacheContext; + + return FileStream_Close(pStream); +} + + +// Reloads the cache. Returns number of characters +// that has been loaded into the cache. +static DWORD ReloadListFileCache(TListFileCache * pCache) +{ + DWORD dwBytesToRead = 0; + + // Only do something if the cache is empty + if(pCache->pPos >= pCache->pEnd) + { + // Move the file position forward + pCache->dwFilePos += CACHE_BUFFER_SIZE; + if(pCache->dwFilePos >= pCache->dwFileSize) + return 0; + + // Get the number of bytes remaining + dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos; + if(dwBytesToRead > CACHE_BUFFER_SIZE) + dwBytesToRead = CACHE_BUFFER_SIZE; + + // Load the next data chunk to the cache + // If we didn't read anything, it might mean that the block + // of the file is not available + // We stop reading the file at this point, because the rest + // of the listfile is unreliable + if(!pCache->pfnReloadCache(pCache->pvCacheContext, pCache->Buffer, dwBytesToRead)) + return 0; + + // Set the buffer pointers + pCache->pBegin = + pCache->pPos = &pCache->Buffer[0]; + pCache->pEnd = pCache->pBegin + dwBytesToRead; + } + + return dwBytesToRead; +} + +static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nMaxChars) +{ + char * szLineBegin = szLine; + char * szLineEnd = szLine + nMaxChars - 1; + char * szExtraString = NULL; + + // Skip newlines, spaces, tabs and another non-printable stuff + for(;;) + { + // If we need to reload the cache, do it + if(pCache->pPos == pCache->pEnd) + { + if(ReloadListFileCache(pCache) == 0) + break; + } + + // If we found a non-whitespace character, stop + if(pCache->pPos[0] > 0x20) + break; + + // Skip the character + pCache->pPos++; + } + + // Copy the remaining characters + while(szLine < szLineEnd) + { + // If we need to reload the cache, do it now and resume copying + if(pCache->pPos == pCache->pEnd) + { + if(ReloadListFileCache(pCache) == 0) + break; + } + + // If we have found a newline, stop loading + if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A) + break; + + // Blizzard listfiles can also contain information about patch: + // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326) + if(*pCache->pPos == '~') + szExtraString = szLine; + + // Copy the character + *szLine++ = *pCache->pPos++; + } + + // Terminate line with zero + *szLine = 0; + + // If there was extra string after the file name, clear it + if(szExtraString != NULL) + { + if(szExtraString[0] == '~' && szExtraString[1] == 'P') + { + szLine = szExtraString; + *szExtraString = 0; + } + } + + // Return the length of the line + return (szLine - szLineBegin); +} + +static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_STREAM pfnCloseStream, void * pvCacheContext, DWORD dwFileSize) +{ + TListFileCache * pCache = NULL; + DWORD dwBytesToRead; + + // Allocate cache for one file block + pCache = (TListFileCache *)CASC_ALLOC(BYTE, sizeof(TListFileCache)); + if(pCache != NULL) + { + // Clear the entire structure + memset(pCache, 0, sizeof(TListFileCache)); + pCache->pfnReloadCache = pfnReloadCache; + pCache->pfnCloseStream = pfnCloseStream; + pCache->pvCacheContext = pvCacheContext; + pCache->dwFileSize = dwFileSize; + + // Load the file cache from the file + dwBytesToRead = CASCLIB_MIN(CACHE_BUFFER_SIZE, dwFileSize); + if(pfnReloadCache(pvCacheContext, pCache->Buffer, dwBytesToRead)) + { + // Allocate pointers + pCache->pBegin = pCache->pPos = &pCache->Buffer[0]; + pCache->pEnd = pCache->pBegin + dwBytesToRead; + } + else + { + ListFile_Free(pCache); + pCache = NULL; + } + } + + // Return the cache + return pCache; +} + +//----------------------------------------------------------------------------- +// Listfile functions + +void * ListFile_OpenExternal(const TCHAR * szListFile) +{ + TListFileCache * pCache; + TFileStream * pStream; + ULONGLONG FileSize = 0; + + // Open the external listfile + pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY); + if(pStream != NULL) + { + // Retrieve the size of the external listfile + FileStream_GetSize(pStream, &FileSize); + if(0 < FileSize && FileSize <= 0xFFFFFFFF) + { + // Create the cache for the listfile + pCache = CreateListFileCache(ReloadCache_ExternalFile, CloseStream_ExternalFile, pStream, (DWORD)FileSize); + if(pCache != NULL) + return pCache; + } + + // Close the file stream + FileStream_Close(pStream); + } + + return NULL; +} + +size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars) +{ + TListFileCache * pCache = (TListFileCache *)pvListFile; + size_t nLength = 0; + int nError = ERROR_INVALID_PARAMETER; + + // Check for parameters + if(pCache != NULL) + { + for(;;) + { + // Read the (next) line + nLength = ReadListFileLine(pCache, szBuffer, nMaxChars); + if(nLength == 0) + { + nError = ERROR_NO_MORE_FILES; + break; + } + + // If some mask entered, check it + if(CheckWildCard(szBuffer, szMask)) + { + nError = ERROR_SUCCESS; + break; + } + } + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return nLength; +} + +void ListFile_Free(void * pvListFile) +{ + TListFileCache * pCache = (TListFileCache *)pvListFile; + + // Valid parameter check + if(pCache != NULL) + { + if(pCache->pfnCloseStream != NULL) + pCache->pfnCloseStream(pCache->pvCacheContext); + CASC_FREE(pCache); + } +} diff --git a/dep/CascLib/src/common/ListFile.h b/dep/CascLib/src/common/ListFile.h new file mode 100644 index 00000000000..1c603af3766 --- /dev/null +++ b/dep/CascLib/src/common/ListFile.h @@ -0,0 +1,18 @@ +/*****************************************************************************/ +/* ListFile.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 10.05.14 1.00 Lad The first version of ListFile.h */ +/*****************************************************************************/ + +#ifndef __LISTFILE_H__ +#define __LISTFILE_H__ + +void * ListFile_OpenExternal(const TCHAR * szListFile); +size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars); +void ListFile_Free(void * pvListFile); + +#endif // __LISTFILE_H__ diff --git a/dep/CascLib/src/common/Map.cpp b/dep/CascLib/src/common/Map.cpp new file mode 100644 index 00000000000..70697a158ab --- /dev/null +++ b/dep/CascLib/src/common/Map.cpp @@ -0,0 +1,178 @@ +/*****************************************************************************/ +/* Map.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Implementation of map for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 10.06.14 1.00 Lad The first version of Map.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "../CascLib.h" +#include "../CascCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static DWORD CalcHashIndex(PCASC_MAP pMap, void * pvIdentifier) +{ + DWORD dwHash = 0x7EEE7EEE; + + // Is it a string table? + if(pMap->KeyLength == KEY_LENGTH_STRING) + { + char * szString = (char *)pvIdentifier; + + for(size_t i = 0; szString[i] != 0; i++) + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[i]; + } + else + { + LPBYTE pbHash = (LPBYTE)pvIdentifier; + + // Construct the hash from the first 4 digits + for(size_t i = 0; i < pMap->KeyLength; i++) + dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbHash[i]; + } + + // Return the hash limited by the table size + return (dwHash % pMap->TableSize); +} + +static bool CompareIdentifier(PCASC_MAP pMap, void * pvTableEntry, void * pvIdentifier) +{ + // Is it a string table? + if(pMap->KeyLength == KEY_LENGTH_STRING) + { + char * szTableEntry = (char *)pvTableEntry; + char * szIdentifier = (char *)pvIdentifier; + + return (strcmp(szTableEntry, szIdentifier) == 0); + } + else + { + return (memcmp(pvTableEntry, pvIdentifier, pMap->KeyLength) == 0); + } +} + +//----------------------------------------------------------------------------- +// Public functions + +PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset) +{ + PCASC_MAP pMap; + size_t cbToAllocate; + size_t dwTableSize; + + // Calculate the size of the table + dwTableSize = (dwMaxItems * 3 / 2) | 0x01; + + // Allocate new map for the objects + cbToAllocate = sizeof(CASC_MAP) + (dwTableSize * sizeof(void *)); + pMap = (PCASC_MAP)CASC_ALLOC(LPBYTE, cbToAllocate); + if(pMap != NULL) + { + memset(pMap, 0, cbToAllocate); + pMap->KeyLength = dwKeyLength; + pMap->TableSize = dwTableSize; + pMap->MemberOffset = dwMemberOffset; + } + + // Return the allocated map + return pMap; +} + +size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray) +{ + size_t nIndex = 0; + + // Verify pointer to the map + if(pMap != NULL && ppvArray != NULL) + { + // Enumerate all items in main table + for(size_t i = 0; i < pMap->TableSize; i++) + { + // Is that cell valid? + if(pMap->HashTable[i] != NULL) + { + ppvArray[nIndex++] = pMap->HashTable[i]; + } + } + } + + return pMap->ItemCount; +} + +void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier) +{ + void * pvTableEntry; + DWORD dwHashIndex; + + // Verify pointer to the map + if(pMap != NULL) + { + // Construct the main index + dwHashIndex = CalcHashIndex(pMap, pvIdentifier); + while(pMap->HashTable[dwHashIndex] != NULL) + { + // Get the pointer at that position + pvTableEntry = pMap->HashTable[dwHashIndex]; + + // Compare the hash + if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier)) + return ((LPBYTE)pvTableEntry - pMap->MemberOffset); + + // Move to the next entry + dwHashIndex = (dwHashIndex + 1) % pMap->TableSize; + } + } + + // Not found, sorry + return NULL; +} + +bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier) +{ + void * pvTableEntry; + DWORD dwHashIndex; + + // Verify pointer to the map + if(pMap != NULL) + { + // Limit check + if((pMap->ItemCount + 1) >= pMap->TableSize) + return false; + + // Construct the hash index + dwHashIndex = CalcHashIndex(pMap, pvIdentifier); + while(pMap->HashTable[dwHashIndex] != NULL) + { + // Get the pointer at that position + pvTableEntry = pMap->HashTable[dwHashIndex]; + + // Check if hash being inserted conflicts with an existing hash + if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier)) + return false; + + // Move to the next entry + dwHashIndex = (dwHashIndex + 1) % pMap->TableSize; + } + + // Insert at that position + pMap->HashTable[dwHashIndex] = pvIdentifier; + pMap->ItemCount++; + return true; + } + + // Failed + return false; +} + +void Map_Free(PCASC_MAP pMap) +{ + if(pMap != NULL) + { + CASC_FREE(pMap); + } +} diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h new file mode 100644 index 00000000000..b4b9c918df3 --- /dev/null +++ b/dep/CascLib/src/common/Map.h @@ -0,0 +1,39 @@ +/*****************************************************************************/ +/* Map.h Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Interface of hash-to-ptr map for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 10.06.14 1.00 Lad The first version of Map.h */ +/*****************************************************************************/ + +#ifndef __HASHTOPTR_H__ +#define __HASHTOPTR_H__ + +//----------------------------------------------------------------------------- +// Structures + +#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object + +typedef struct _CASC_MAP +{ + size_t TableSize; + size_t ItemCount; // Number of items in the map + size_t MemberOffset; // How far is the hash from the begin of the structure (in bytes) + size_t KeyLength; // Length of the hash key + void * HashTable[1]; // Pointer table + +} CASC_MAP, *PCASC_MAP; + +//----------------------------------------------------------------------------- +// Functions + +PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset); +size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray); +void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier); +bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier); +void Map_Sort(PCASC_MAP pMap); +void Map_Free(PCASC_MAP pMap); + +#endif // __HASHTOPTR_H__ diff --git a/dep/CascLib/src/jenkins/lookup.h b/dep/CascLib/src/jenkins/lookup.h new file mode 100644 index 00000000000..54ccc979ca6 --- /dev/null +++ b/dep/CascLib/src/jenkins/lookup.h @@ -0,0 +1,24 @@ +#ifndef __LOOKUP3_H__ +#define __LOOKUP3_H__ + +#ifdef WIN32 +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#else +#include <stdint.h> /* defines uint32_t etc */ +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +uint32_t hashlittle(const void *key, size_t length, uint32_t initval); +void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +#ifdef __cplusplus +} +#endif + +#endif // __LOOKUP3_H__ diff --git a/dep/CascLib/src/jenkins/lookup3.c b/dep/CascLib/src/jenkins/lookup3.c new file mode 100644 index 00000000000..6af56b481ad --- /dev/null +++ b/dep/CascLib/src/jenkins/lookup3.c @@ -0,0 +1,1003 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +//#define SELF_TEST 1 + +#include <stdio.h> /* defines printf for tests */ +#include <time.h> /* defines time_t for timings in the test */ + +#ifdef linux +#include <sys/param.h> /* attempt to define endianness */ +#include <endian.h> /* attempt to define endianness */ +#endif + +#include "lookup.h" + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable. Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +uint32_t hashlittle( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + uint8_t buf[256]; + uint32_t i; + uint32_t h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + uint32_t x[HASHSTATE],y[HASHSTATE]; + uint32_t hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i<hlen; ++i) /*----------------------- for each input byte, */ + { + for (j=0; j<8; ++j) /*------------------------ for each input bit, */ + { + for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */ + { + for (l=0; l<HASHSTATE; ++l) + e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0); + + /*---- check that every output bit is affected by that input bit */ + for (k=0; k<MAXPAIR; k+=2) + { + uint32_t finished=1; + /* keys have one bit different */ + for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;} + /* have a and b be two keys differing in only one bit */ + a[i] ^= (k<<j); + a[i] ^= (k>>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<<j); + b[i] ^= ((k+1)>>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; l<HASHSTATE; ++l) + { + e[l] &= (c[l]^d[l]); + f[l] &= ~(c[l]^d[l]); + g[l] &= c[l]; + h[l] &= ~c[l]; + x[l] &= d[l]; + y[l] &= ~d[l]; + if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0; + } + if (finished) break; + } + if (k>z) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + uint8_t buf[MAXLEN+20], *b; + uint32_t len; + uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; + uint32_t h; + uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; + uint32_t i; + uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + uint32_t j; + uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + uint32_t ref,x,y; + uint8_t *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + + /* check that hashlittle2 and hashlittle produce the same results */ + i=47; j=0; + hashlittle2(q, sizeof(q), &i, &j); + if (hashlittle(q, sizeof(q), 47) != i) + printf("hashlittle2 and hashlittle mismatch\n"); + + /* check that hashword2 and hashword produce the same results */ + len = 0xdeadbeef; + i=47, j=0; + hashword2(&len, 1, &i, &j); + if (hashword(&len, 1, 47) != i) + printf("hashword2 and hashword mismatch %x %x\n", + i, hashword(&len, 1, 47)); + + /* check hashlittle doesn't read before or after the ends of the string */ + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i<MAXLEN; ++i) + { + len = i; + for (j=0; j<i; ++j) *(b+j)=0; + + /* these should all be equal */ + ref = hashlittle(b, len, (uint32_t)1); + *(b+i)=(uint8_t)~0; + *(b-1)=(uint8_t)~0; + x = hashlittle(b, len, (uint32_t)1); + y = hashlittle(b, len, (uint32_t)1); + if ((ref != x) || (ref != y)) + { + printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y, + h, i); + } + } + } +} + +/* check for problems with nulls */ + void driver4() +{ + uint8_t buf[1]; + uint32_t h,i,state[HASHSTATE]; + + + buf[0] = ~0; + for (i=0; i<HASHSTATE; ++i) state[i] = 1; + printf("These should all be different\n"); + for (i=0, h=0; i<8; ++i) + { + h = hashlittle(buf, 0, h); + printf("%2ld 0-byte strings, hash is %.8x\n", i, h); + } +} + +void driver5() +{ + uint32_t b,c; + b=0, c=0, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* deadbeef deadbeef */ + b=0xdeadbeef, c=0, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* bd5b7dde deadbeef */ + b=0xdeadbeef, c=0xdeadbeef, hashlittle2("", 0, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* 9c093ccd bd5b7dde */ + b=0, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* 17770551 ce7226e6 */ + b=1, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* e3607cae bd371de4 */ + b=0, c=1, hashlittle2("Four score and seven years ago", 30, &c, &b); + printf("hash is %.8lx %.8lx\n", c, b); /* cd628161 6cbea4b3 */ + c = hashlittle("Four score and seven years ago", 30, 0); + printf("hash is %.8lx\n", c); /* 17770551 */ + c = hashlittle("Four score and seven years ago", 30, 1); + printf("hash is %.8lx\n", c); /* cd628161 */ +} + + +int main() +{ + driver1(); /* test that the key is hashed: used for timings */ + driver2(); /* test that whole key is hashed thoroughly */ + driver3(); /* test that nothing but the key is hashed */ + driver4(); /* test hashing multiple buffers (all buffers are null) */ + driver5(); /* test the hash against known vectors */ + return 1; +} + +#endif /* SELF_TEST */ diff --git a/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c b/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c new file mode 100644 index 00000000000..1daf0bffa1b --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c @@ -0,0 +1,69 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** + @file hash_memory.c + Hash memory helper, Tom St Denis +*/ + +/** + Hash a block of memory and store the digest. + @param hash The index of the hash you wish to use + @param in The data you wish to hash + @param inlen The length of the data to hash (octets) + @param out [out] Where to store the digest + @param outlen [in/out] Max size and resulting size of the digest + @return CRYPT_OK if successful +*/ +int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) +{ + hash_state *md; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) { + goto LBL_ERR; + } + err = hash_descriptor[hash].done(md, out); + *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + XFREE(md); + + return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/hashes/md5.c b/dep/CascLib/src/libtomcrypt/src/hashes/md5.c new file mode 100644 index 00000000000..4cbd000c0d4 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/hashes/md5.c @@ -0,0 +1,368 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + + +/** + @file md5.c + LTC_MD5 hash function by Tom St Denis +*/ + +#ifdef LTC_MD5 + +const struct ltc_hash_descriptor md5_desc = +{ + "md5", + 3, + 16, + 64, + + /* OID */ + { 1, 2, 840, 113549, 2, 5, }, + 6, + + &md5_init, + &md5_process, + &md5_done, + &md5_test, + NULL +}; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define G(x,y,z) (y ^ (z & (y ^ x))) +#define H(x,y,z) (x^y^z) +#define I(x,y,z) (y^(x|(~z))) + +#ifdef LTC_SMALL_CODE + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + +static const unsigned char Worder[64] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, + 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, + 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 +}; + +static const unsigned char Rorder[64] = { + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const ulong32 Korder[64] = { +0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, +0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, +0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, +0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, +0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, +0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, +0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, +0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL +}; + +#else + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; + + +#endif + +#ifdef LTC_CLEAN_STACK +static int _md5_compress(hash_state *md, unsigned char *buf) +#else +static int md5_compress(hash_state *md, unsigned char *buf) +#endif +{ + ulong32 i, W[16], a, b, c, d; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32L(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->md5.state[0]; + b = md->md5.state[1]; + c = md->md5.state[2]; + d = md->md5.state[3]; + +#ifdef LTC_SMALL_CODE + for (i = 0; i < 16; ++i) { + FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 32; ++i) { + GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 48; ++i) { + HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 64; ++i) { + II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + +#else + FF(a,b,c,d,W[0],7,0xd76aa478UL) + FF(d,a,b,c,W[1],12,0xe8c7b756UL) + FF(c,d,a,b,W[2],17,0x242070dbUL) + FF(b,c,d,a,W[3],22,0xc1bdceeeUL) + FF(a,b,c,d,W[4],7,0xf57c0fafUL) + FF(d,a,b,c,W[5],12,0x4787c62aUL) + FF(c,d,a,b,W[6],17,0xa8304613UL) + FF(b,c,d,a,W[7],22,0xfd469501UL) + FF(a,b,c,d,W[8],7,0x698098d8UL) + FF(d,a,b,c,W[9],12,0x8b44f7afUL) + FF(c,d,a,b,W[10],17,0xffff5bb1UL) + FF(b,c,d,a,W[11],22,0x895cd7beUL) + FF(a,b,c,d,W[12],7,0x6b901122UL) + FF(d,a,b,c,W[13],12,0xfd987193UL) + FF(c,d,a,b,W[14],17,0xa679438eUL) + FF(b,c,d,a,W[15],22,0x49b40821UL) + GG(a,b,c,d,W[1],5,0xf61e2562UL) + GG(d,a,b,c,W[6],9,0xc040b340UL) + GG(c,d,a,b,W[11],14,0x265e5a51UL) + GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) + GG(a,b,c,d,W[5],5,0xd62f105dUL) + GG(d,a,b,c,W[10],9,0x02441453UL) + GG(c,d,a,b,W[15],14,0xd8a1e681UL) + GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) + GG(a,b,c,d,W[9],5,0x21e1cde6UL) + GG(d,a,b,c,W[14],9,0xc33707d6UL) + GG(c,d,a,b,W[3],14,0xf4d50d87UL) + GG(b,c,d,a,W[8],20,0x455a14edUL) + GG(a,b,c,d,W[13],5,0xa9e3e905UL) + GG(d,a,b,c,W[2],9,0xfcefa3f8UL) + GG(c,d,a,b,W[7],14,0x676f02d9UL) + GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) + HH(a,b,c,d,W[5],4,0xfffa3942UL) + HH(d,a,b,c,W[8],11,0x8771f681UL) + HH(c,d,a,b,W[11],16,0x6d9d6122UL) + HH(b,c,d,a,W[14],23,0xfde5380cUL) + HH(a,b,c,d,W[1],4,0xa4beea44UL) + HH(d,a,b,c,W[4],11,0x4bdecfa9UL) + HH(c,d,a,b,W[7],16,0xf6bb4b60UL) + HH(b,c,d,a,W[10],23,0xbebfbc70UL) + HH(a,b,c,d,W[13],4,0x289b7ec6UL) + HH(d,a,b,c,W[0],11,0xeaa127faUL) + HH(c,d,a,b,W[3],16,0xd4ef3085UL) + HH(b,c,d,a,W[6],23,0x04881d05UL) + HH(a,b,c,d,W[9],4,0xd9d4d039UL) + HH(d,a,b,c,W[12],11,0xe6db99e5UL) + HH(c,d,a,b,W[15],16,0x1fa27cf8UL) + HH(b,c,d,a,W[2],23,0xc4ac5665UL) + II(a,b,c,d,W[0],6,0xf4292244UL) + II(d,a,b,c,W[7],10,0x432aff97UL) + II(c,d,a,b,W[14],15,0xab9423a7UL) + II(b,c,d,a,W[5],21,0xfc93a039UL) + II(a,b,c,d,W[12],6,0x655b59c3UL) + II(d,a,b,c,W[3],10,0x8f0ccc92UL) + II(c,d,a,b,W[10],15,0xffeff47dUL) + II(b,c,d,a,W[1],21,0x85845dd1UL) + II(a,b,c,d,W[8],6,0x6fa87e4fUL) + II(d,a,b,c,W[15],10,0xfe2ce6e0UL) + II(c,d,a,b,W[6],15,0xa3014314UL) + II(b,c,d,a,W[13],21,0x4e0811a1UL) + II(a,b,c,d,W[4],6,0xf7537e82UL) + II(d,a,b,c,W[11],10,0xbd3af235UL) + II(c,d,a,b,W[2],15,0x2ad7d2bbUL) + II(b,c,d,a,W[9],21,0xeb86d391UL) +#endif + + md->md5.state[0] = md->md5.state[0] + a; + md->md5.state[1] = md->md5.state[1] + b; + md->md5.state[2] = md->md5.state[2] + c; + md->md5.state[3] = md->md5.state[3] + d; + + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int md5_compress(hash_state *md, unsigned char *buf) +{ + int err; + err = _md5_compress(md, buf); + burn_stack(sizeof(ulong32) * 21); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int md5_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->md5.state[0] = 0x67452301UL; + md->md5.state[1] = 0xefcdab89UL; + md->md5.state[2] = 0x98badcfeUL; + md->md5.state[3] = 0x10325476UL; + md->md5.curlen = 0; + md->md5.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(md5_process, md5_compress, md5, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (16 bytes) + @return CRYPT_OK if successful +*/ +int md5_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->md5.curlen >= sizeof(md->md5.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->md5.length += md->md5.curlen * 8; + + /* append the '1' bit */ + md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->md5.curlen > 56) { + while (md->md5.curlen < 64) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + md5_compress(md, md->md5.buf); + md->md5.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->md5.curlen < 56) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64L(md->md5.length, md->md5.buf+56); + md5_compress(md, md->md5.buf); + + /* copy output */ + for (i = 0; i < 4; i++) { + STORE32L(md->md5.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int md5_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[16]; + } tests[] = { + { "", + { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, + { "a", + {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, + { "abc", + { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, + { "message digest", + { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }, + { "abcdefghijklmnopqrstuvwxyz", + { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, + 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, + 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }, + { NULL, { 0 } } + }; + + int i; + unsigned char tmp[16]; + hash_state md; + + for (i = 0; tests[i].msg != NULL; i++) { + md5_init(&md); + md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + md5_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/md5.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h new file mode 100644 index 00000000000..74cdff47549 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h @@ -0,0 +1,87 @@ +#ifndef TOMCRYPT_H_ +#define TOMCRYPT_H_ +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#include <limits.h> + +/* use configuration data */ +#include "tomcrypt_custom.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ +#define CRYPT 0x0117 +#define SCRYPT "1.17" + +/* max size of either a cipher/hash block or symmetric key [largest of the two] */ +#define MAXBLOCKSIZE 128 + +/* descriptor table size */ +#define TAB_SIZE 32 + +/* error codes [will be expanded in future releases] */ +enum { + CRYPT_OK=0, /* Result OK */ + CRYPT_ERROR, /* Generic Error */ + CRYPT_NOP, /* Not a failure but no operation was performed */ + + CRYPT_INVALID_KEYSIZE, /* Invalid key size given */ + CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */ + CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */ + + CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */ + CRYPT_INVALID_PACKET, /* Invalid input packet given */ + + CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ + CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */ + + CRYPT_INVALID_CIPHER, /* Invalid cipher specified */ + CRYPT_INVALID_HASH, /* Invalid hash specified */ + CRYPT_INVALID_PRNG, /* Invalid PRNG specified */ + + CRYPT_MEM, /* Out of memory */ + + CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ + CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */ + + CRYPT_INVALID_ARG, /* Generic invalid argument */ + CRYPT_FILE_NOTFOUND, /* File Not Found */ + + CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ + CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */ + CRYPT_PK_DUP, /* Duplicate key already in key ring */ + CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ + CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ + + CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */ + CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ +}; + +#include "tomcrypt_cfg.h" +#include "tomcrypt_macros.h" +#include "tomcrypt_cipher.h" +#include "tomcrypt_hash.h" +#include "tomcrypt_mac.h" +#include "tomcrypt_prng.h" +#include "tomcrypt_pk.h" +#include "tomcrypt_math.h" +#include "tomcrypt_misc.h" +#include "tomcrypt_argchk.h" +#include "tomcrypt_pkcs.h" + +#ifdef __cplusplus + } +#endif + +#endif /* TOMCRYPT_H_ */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ +/* $Revision: 1.21 $ */ +/* $Date: 2006/12/16 19:34:05 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h new file mode 100644 index 00000000000..cfc93ad7ea6 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h @@ -0,0 +1,38 @@ +/* Defines the LTC_ARGCHK macro used within the library */ +/* ARGTYPE is defined in mycrypt_cfg.h */ +#if ARGTYPE == 0 + +#include <signal.h> + +/* this is the default LibTomCrypt macro */ +void crypt_argchk(char *v, char *s, int d); +#define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 1 + +/* fatal type of error */ +#define LTC_ARGCHK(x) assert((x)) +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 2 + +#define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); } +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 3 + +#define LTC_ARGCHK(x) +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 4 + +#define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; +#define LTC_ARGCHKVD(x) if (!(x)) return; + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/08/27 20:50:21 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h new file mode 100644 index 00000000000..7feae6e8bdc --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h @@ -0,0 +1,136 @@ +/* This is the build config file. + * + * With this you can setup what to inlcude/exclude automatically during any build. Just comment + * out the line that #define's the word for the thing you want to remove. phew! + */ + +#ifndef TOMCRYPT_CFG_H +#define TOMCRYPT_CFG_H + +#if defined(_WIN32) || defined(_MSC_VER) +#define LTC_CALL __cdecl +#else +#ifndef LTC_CALL + #define LTC_CALL +#endif +#endif + +#ifndef LTC_EXPORT +#define LTC_EXPORT +#endif + +/* certain platforms use macros for these, making the prototypes broken */ +#ifndef LTC_NO_PROTOTYPES + +/* you can change how memory allocation works ... */ +LTC_EXPORT void * LTC_CALL XMALLOC(size_t n); +LTC_EXPORT void * LTC_CALL XREALLOC(void *p, size_t n); +LTC_EXPORT void * LTC_CALL XCALLOC(size_t n, size_t s); +LTC_EXPORT void LTC_CALL XFREE(void *p); + +LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); + + +/* change the clock function too */ +LTC_EXPORT clock_t LTC_CALL XCLOCK(void); + +/* various other functions */ +LTC_EXPORT void * LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); +LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); +LTC_EXPORT void * LTC_CALL XMEMSET(void *s, int c, size_t n); + +LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); + +#endif + +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +#ifndef ARGTYPE + #define ARGTYPE 0 +#endif + +/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code + * + * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. + * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST** + * use the portable [slower] macros. + */ + +/* detect x86-32 machines somewhat */ +#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))) + #define ENDIAN_LITTLE + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detects MIPS R5900 processors (PS2) */ +#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips)) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD +#endif + +/* detect amd64 */ +#if !defined(__STRICT_ANSI__) && defined(__x86_64__) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect PPC32 */ +#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + #define ENDIAN_BIG + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect sparc and sparc64 */ +#if defined(__sparc__) + #define ENDIAN_BIG + #if defined(__arch64__) + #define ENDIAN_64BITWORD + #else + #define ENDIAN_32BITWORD + #endif +#endif + + +#ifdef LTC_NO_FAST + #ifdef LTC_FAST + #undef LTC_FAST + #endif +#endif + +/* No asm is a quick way to disable anything "not portable" */ +#ifdef LTC_NO_ASM + #undef ENDIAN_LITTLE + #undef ENDIAN_BIG + #undef ENDIAN_32BITWORD + #undef ENDIAN_64BITWORD + #undef LTC_FAST + #undef LTC_FAST_TYPE + #define LTC_NO_ROLC + #define LTC_NO_BSWAP +#endif + +/* #define ENDIAN_LITTLE */ +/* #define ENDIAN_BIG */ + +/* #define ENDIAN_32BITWORD */ +/* #define ENDIAN_64BITWORD */ + +#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD)) + #error You must specify a word size as well as endianess in tomcrypt_cfg.h +#endif + +#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) + #define ENDIAN_NEUTRAL +#endif + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */ +/* $Revision: 1.19 $ */ +/* $Date: 2006/12/04 02:19:48 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h new file mode 100644 index 00000000000..bd740bf4a0c --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h @@ -0,0 +1,891 @@ +/* ---- SYMMETRIC KEY STUFF ----- + * + * We put each of the ciphers scheduled keys in their own structs then we put all of + * the key formats in one union. This makes the function prototypes easier to use. + */ +#ifdef LTC_BLOWFISH +struct blowfish_key { + ulong32 S[4][256]; + ulong32 K[18]; +}; +#endif + +#ifdef LTC_RC5 +struct rc5_key { + int rounds; + ulong32 K[50]; +}; +#endif + +#ifdef LTC_RC6 +struct rc6_key { + ulong32 K[44]; +}; +#endif + +#ifdef LTC_SAFERP +struct saferp_key { + unsigned char K[33][16]; + long rounds; +}; +#endif + +#ifdef LTC_RIJNDAEL +struct rijndael_key { + ulong32 eK[60], dK[60]; + int Nr; +}; +#endif + +#ifdef LTC_KSEED +struct kseed_key { + ulong32 K[32], dK[32]; +}; +#endif + +#ifdef LTC_KASUMI +struct kasumi_key { + ulong32 KLi1[8], KLi2[8], + KOi1[8], KOi2[8], KOi3[8], + KIi1[8], KIi2[8], KIi3[8]; +}; +#endif + +#ifdef LTC_XTEA +struct xtea_key { + unsigned long A[32], B[32]; +}; +#endif + +#ifdef LTC_TWOFISH +#ifndef LTC_TWOFISH_SMALL + struct twofish_key { + ulong32 S[4][256], K[40]; + }; +#else + struct twofish_key { + ulong32 K[40]; + unsigned char S[32], start; + }; +#endif +#endif + +#ifdef LTC_SAFER +#define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6 +#define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10 +#define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8 +#define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10 +#define LTC_SAFER_MAX_NOF_ROUNDS 13 +#define LTC_SAFER_BLOCK_LEN 8 +#define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS)) +typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN]; +typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN]; +struct safer_key { safer_key_t key; }; +#endif + +#ifdef LTC_RC2 +struct rc2_key { unsigned xkey[64]; }; +#endif + +#ifdef LTC_DES +struct des_key { + ulong32 ek[32], dk[32]; +}; + +struct des3_key { + ulong32 ek[3][32], dk[3][32]; +}; +#endif + +#ifdef LTC_CAST5 +struct cast5_key { + ulong32 K[32], keylen; +}; +#endif + +#ifdef LTC_NOEKEON +struct noekeon_key { + ulong32 K[4], dK[4]; +}; +#endif + +#ifdef LTC_SKIPJACK +struct skipjack_key { + unsigned char key[10]; +}; +#endif + +#ifdef LTC_KHAZAD +struct khazad_key { + ulong64 roundKeyEnc[8 + 1]; + ulong64 roundKeyDec[8 + 1]; +}; +#endif + +#ifdef LTC_ANUBIS +struct anubis_key { + int keyBits; + int R; + ulong32 roundKeyEnc[18 + 1][4]; + ulong32 roundKeyDec[18 + 1][4]; +}; +#endif + +#ifdef LTC_MULTI2 +struct multi2_key { + int N; + ulong32 uk[8]; +}; +#endif + +typedef union Symmetric_key { +#ifdef LTC_DES + struct des_key des; + struct des3_key des3; +#endif +#ifdef LTC_RC2 + struct rc2_key rc2; +#endif +#ifdef LTC_SAFER + struct safer_key safer; +#endif +#ifdef LTC_TWOFISH + struct twofish_key twofish; +#endif +#ifdef LTC_BLOWFISH + struct blowfish_key blowfish; +#endif +#ifdef LTC_RC5 + struct rc5_key rc5; +#endif +#ifdef LTC_RC6 + struct rc6_key rc6; +#endif +#ifdef LTC_SAFERP + struct saferp_key saferp; +#endif +#ifdef LTC_RIJNDAEL + struct rijndael_key rijndael; +#endif +#ifdef LTC_XTEA + struct xtea_key xtea; +#endif +#ifdef LTC_CAST5 + struct cast5_key cast5; +#endif +#ifdef LTC_NOEKEON + struct noekeon_key noekeon; +#endif +#ifdef LTC_SKIPJACK + struct skipjack_key skipjack; +#endif +#ifdef LTC_KHAZAD + struct khazad_key khazad; +#endif +#ifdef LTC_ANUBIS + struct anubis_key anubis; +#endif +#ifdef LTC_KSEED + struct kseed_key kseed; +#endif +#ifdef LTC_KASUMI + struct kasumi_key kasumi; +#endif +#ifdef LTC_MULTI2 + struct multi2_key multi2; +#endif + void *data; +} symmetric_key; + +#ifdef LTC_ECB_MODE +/** A block cipher ECB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The scheduled key */ + symmetric_key key; +} symmetric_ECB; +#endif + +#ifdef LTC_CFB_MODE +/** A block cipher CFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CFB; +#endif + +#ifdef LTC_OFB_MODE +/** A block cipher OFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_OFB; +#endif + +#ifdef LTC_CBC_MODE +/** A block cipher CBC structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CBC; +#endif + + +#ifdef LTC_CTR_MODE +/** A block cipher CTR structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen, + /** The mode (endianess) of the CTR, 0==little, 1==big */ + mode, + /** counter width */ + ctrlen; + + /** The counter */ + unsigned char ctr[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CTR; +#endif + + +#ifdef LTC_LRW_MODE +/** A LRW structure */ +typedef struct { + /** The index of the cipher chosen (must be a 128-bit block cipher) */ + int cipher; + + /** The current IV */ + unsigned char IV[16], + + /** the tweak key */ + tweak[16], + + /** The current pad, it's the product of the first 15 bytes against the tweak key */ + pad[16]; + + /** The scheduled symmetric key */ + symmetric_key key; + +#ifdef LRW_TABLES + /** The pre-computed multiplication table */ + unsigned char PC[16][256][16]; +#endif +} symmetric_LRW; +#endif + +#ifdef LTC_F8_MODE +/** A block cipher F8 structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + MIV[MAXBLOCKSIZE]; + /** Current block count */ + ulong32 blockcnt; + /** The scheduled key */ + symmetric_key key; +} symmetric_F8; +#endif + + +/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */ +extern struct ltc_cipher_descriptor { + /** name of cipher */ + char *name; + /** internal ID */ + unsigned char ID; + /** min keysize (octets) */ + int min_key_length, + /** max keysize (octets) */ + max_key_length, + /** block size (octets) */ + block_length, + /** default number of rounds */ + default_rounds; + /** Setup the cipher + @param key The input symmetric key + @param keylen The length of the input key (octets) + @param num_rounds The requested number of rounds (0==default) + @param skey [out] The destination of the scheduled key + @return CRYPT_OK if successful + */ + int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); + /** Encrypt a block + @param pt The plaintext + @param ct [out] The ciphertext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); + /** Decrypt a block + @param ct The ciphertext + @param pt [out] The plaintext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); + /** Test the block cipher + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); + + /** Terminate the context + @param skey The scheduled key + */ + void (*done)(symmetric_key *skey); + + /** Determine a key size + @param keysize [in/out] The size of the key desired and the suggested size + @return CRYPT_OK if successful + */ + int (*keysize)(int *keysize); + +/** Accelerators **/ + /** Accelerated ECB encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey); + + /** Accelerated ECB decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey); + + /** Accelerated CBC encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CBC decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CTR encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param mode little or big endian counter (mode=0 or mode=1) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey); + + /** Accelerated LRW + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated LRW + @param ct Ciphertext + @param pt Plaintext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated CCM packet (one-shot) + @param key The secret key to use + @param keylen The length of the secret key (octets) + @param uskey A previously scheduled key [optional can be NULL] + @param nonce The session nonce [use once] + @param noncelen The length of the nonce + @param header The header for the session + @param headerlen The length of the header (octets) + @param pt [out] The plaintext + @param ptlen The length of the plaintext (octets) + @param ct [out] The ciphertext + @param tag [out] The destination tag + @param taglen [in/out] The max size and resulting size of the authentication tag + @param direction Encrypt or Decrypt direction (0 or 1) + @return CRYPT_OK if successful + */ + int (*accel_ccm_memory)( + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated GCM packet (one shot) + @param key The secret key + @param keylen The length of the secret key + @param IV The initial vector + @param IVlen The length of the initial vector + @param adata The additional authentication data (header) + @param adatalen The length of the adata + @param pt The plaintext + @param ptlen The length of the plaintext (ciphertext length is the same) + @param ct The ciphertext + @param tag [out] The MAC tag + @param taglen [in/out] The MAC tag length + @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) + @return CRYPT_OK on success + */ + int (*accel_gcm_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated one shot LTC_OMAC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*omac_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot XCBC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*xcbc_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot F9 + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + @remark Requires manual padding + */ + int (*f9_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +} cipher_descriptor[]; + +#ifdef LTC_BLOWFISH +int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int blowfish_test(void); +void blowfish_done(symmetric_key *skey); +int blowfish_keysize(int *keysize); +extern const struct ltc_cipher_descriptor blowfish_desc; +#endif + +#ifdef LTC_RC5 +int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc5_test(void); +void rc5_done(symmetric_key *skey); +int rc5_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc5_desc; +#endif + +#ifdef LTC_RC6 +int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc6_test(void); +void rc6_done(symmetric_key *skey); +int rc6_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc6_desc; +#endif + +#ifdef LTC_RC2 +int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc2_test(void); +void rc2_done(symmetric_key *skey); +int rc2_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc2_desc; +#endif + +#ifdef LTC_SAFERP +int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int saferp_test(void); +void saferp_done(symmetric_key *skey); +int saferp_keysize(int *keysize); +extern const struct ltc_cipher_descriptor saferp_desc; +#endif + +#ifdef LTC_SAFER +int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key); +int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key); +int safer_k64_test(void); +int safer_sk64_test(void); +int safer_sk128_test(void); +void safer_done(symmetric_key *skey); +int safer_64_keysize(int *keysize); +int safer_128_keysize(int *keysize); +extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc; +#endif + +#ifdef LTC_RIJNDAEL + +/* make aes an alias */ +#define aes_setup rijndael_setup +#define aes_ecb_encrypt rijndael_ecb_encrypt +#define aes_ecb_decrypt rijndael_ecb_decrypt +#define aes_test rijndael_test +#define aes_done rijndael_done +#define aes_keysize rijndael_keysize + +#define aes_enc_setup rijndael_enc_setup +#define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt +#define aes_enc_keysize rijndael_enc_keysize + +int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rijndael_test(void); +void rijndael_done(symmetric_key *skey); +int rijndael_keysize(int *keysize); +int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +void rijndael_enc_done(symmetric_key *skey); +int rijndael_enc_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc; +extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc; +#endif + +#ifdef LTC_XTEA +int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int xtea_test(void); +void xtea_done(symmetric_key *skey); +int xtea_keysize(int *keysize); +extern const struct ltc_cipher_descriptor xtea_desc; +#endif + +#ifdef LTC_TWOFISH +int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int twofish_test(void); +void twofish_done(symmetric_key *skey); +int twofish_keysize(int *keysize); +extern const struct ltc_cipher_descriptor twofish_desc; +#endif + +#ifdef LTC_DES +int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des_test(void); +void des_done(symmetric_key *skey); +int des_keysize(int *keysize); +int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des3_test(void); +void des3_done(symmetric_key *skey); +int des3_keysize(int *keysize); +extern const struct ltc_cipher_descriptor des_desc, des3_desc; +#endif + +#ifdef LTC_CAST5 +int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int cast5_test(void); +void cast5_done(symmetric_key *skey); +int cast5_keysize(int *keysize); +extern const struct ltc_cipher_descriptor cast5_desc; +#endif + +#ifdef LTC_NOEKEON +int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int noekeon_test(void); +void noekeon_done(symmetric_key *skey); +int noekeon_keysize(int *keysize); +extern const struct ltc_cipher_descriptor noekeon_desc; +#endif + +#ifdef LTC_SKIPJACK +int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int skipjack_test(void); +void skipjack_done(symmetric_key *skey); +int skipjack_keysize(int *keysize); +extern const struct ltc_cipher_descriptor skipjack_desc; +#endif + +#ifdef LTC_KHAZAD +int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int khazad_test(void); +void khazad_done(symmetric_key *skey); +int khazad_keysize(int *keysize); +extern const struct ltc_cipher_descriptor khazad_desc; +#endif + +#ifdef LTC_ANUBIS +int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int anubis_test(void); +void anubis_done(symmetric_key *skey); +int anubis_keysize(int *keysize); +extern const struct ltc_cipher_descriptor anubis_desc; +#endif + +#ifdef LTC_KSEED +int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kseed_test(void); +void kseed_done(symmetric_key *skey); +int kseed_keysize(int *keysize); +extern const struct ltc_cipher_descriptor kseed_desc; +#endif + +#ifdef LTC_KASUMI +int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kasumi_test(void); +void kasumi_done(symmetric_key *skey); +int kasumi_keysize(int *keysize); +extern const struct ltc_cipher_descriptor kasumi_desc; +#endif + + +#ifdef LTC_MULTI2 +int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int multi2_test(void); +void multi2_done(symmetric_key *skey); +int multi2_keysize(int *keysize); +extern const struct ltc_cipher_descriptor multi2_desc; +#endif + +#ifdef LTC_ECB_MODE +int ecb_start(int cipher, const unsigned char *key, + int keylen, int num_rounds, symmetric_ECB *ecb); +int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb); +int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb); +int ecb_done(symmetric_ECB *ecb); +#endif + +#ifdef LTC_CFB_MODE +int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CFB *cfb); +int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb); +int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb); +int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb); +int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb); +int cfb_done(symmetric_CFB *cfb); +#endif + +#ifdef LTC_OFB_MODE +int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_OFB *ofb); +int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb); +int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb); +int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb); +int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb); +int ofb_done(symmetric_OFB *ofb); +#endif + +#ifdef LTC_CBC_MODE +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CBC *cbc); +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc); +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc); +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc); +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc); +int cbc_done(symmetric_CBC *cbc); +#endif + +#ifdef LTC_CTR_MODE + +#define CTR_COUNTER_LITTLE_ENDIAN 0x0000 +#define CTR_COUNTER_BIG_ENDIAN 0x1000 +#define LTC_CTR_RFC3686 0x2000 + +int ctr_start( int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + int num_rounds, int ctr_mode, + symmetric_CTR *ctr); +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr); +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr); +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr); +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr); +int ctr_done(symmetric_CTR *ctr); +int ctr_test(void); +#endif + +#ifdef LTC_LRW_MODE + +#define LRW_ENCRYPT 0 +#define LRW_DECRYPT 1 + +int lrw_start( int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *tweak, + int num_rounds, + symmetric_LRW *lrw); +int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw); +int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw); +int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw); +int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw); +int lrw_done(symmetric_LRW *lrw); +int lrw_test(void); + +/* don't call */ +int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw); +#endif + +#ifdef LTC_F8_MODE +int f8_start( int cipher, const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *salt_key, int skeylen, + int num_rounds, symmetric_F8 *f8); +int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8); +int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8); +int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8); +int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8); +int f8_done(symmetric_F8 *f8); +int f8_test_mode(void); +#endif + +#ifdef LTC_XTS_MODE +typedef struct { + symmetric_key key1, key2; + int cipher; +} symmetric_xts; + +int xts_start( int cipher, + const unsigned char *key1, + const unsigned char *key2, + unsigned long keylen, + int num_rounds, + symmetric_xts *xts); + +int xts_encrypt( + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + const unsigned char *tweak, + symmetric_xts *xts); +int xts_decrypt( + const unsigned char *ct, unsigned long ptlen, + unsigned char *pt, + const unsigned char *tweak, + symmetric_xts *xts); + +void xts_done(symmetric_xts *xts); +int xts_test(void); +void xts_mult_x(unsigned char *I); +#endif + +int find_cipher(const char *name); +int find_cipher_any(const char *name, int blocklen, int keylen); +int find_cipher_id(unsigned char ID); +int register_cipher(const struct ltc_cipher_descriptor *cipher); +int unregister_cipher(const struct ltc_cipher_descriptor *cipher); +int cipher_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_cipher_mutex) + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */ +/* $Revision: 1.54 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h new file mode 100644 index 00000000000..88ec8f984ab --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h @@ -0,0 +1,424 @@ +#ifndef TOMCRYPT_CUSTOM_H_ +#define TOMCRYPT_CUSTOM_H_ + +#define LTC_NO_CIPHERS +#define LTC_NO_HASHES +#define LTC_NO_MACS +#define LTC_NO_PRNGS +#define LTC_NO_CURVES +#define LTC_NO_MODES +#define LTC_NO_PKCS +#define LTC_NO_ROLC + +#define LTC_SOURCE +#define LTC_SHA1 +#define LTC_MD5 +#define LTC_DER +#define LTC_RC4 + +#define USE_LTM +#define LTM_DESC + +/* macros for various libc functions you can change for embedded targets */ +#ifndef XMALLOC + #ifdef malloc + #define LTC_NO_PROTOTYPES + #endif +#define XMALLOC LibTomMalloc +#endif +#ifndef XREALLOC + #ifdef realloc + #define LTC_NO_PROTOTYPES + #endif +#define XREALLOC LibTomRealloc +#endif +#ifndef XCALLOC + #ifdef calloc + #define LTC_NO_PROTOTYPES + #endif +#define XCALLOC LibTomCalloc +#endif +#ifndef XFREE + #ifdef free + #define LTC_NO_PROTOTYPES + #endif +#define XFREE LibTomFree +#endif + +#ifndef XMEMSET + #ifdef memset + #define LTC_NO_PROTOTYPES + #endif +#define XMEMSET memset +#endif +#ifndef XMEMCPY + #ifdef memcpy + #define LTC_NO_PROTOTYPES + #endif +#define XMEMCPY memcpy +#endif +#ifndef XMEMCMP + #ifdef memcmp + #define LTC_NO_PROTOTYPES + #endif +#define XMEMCMP memcmp +#endif +#ifndef XSTRCMP + #ifdef strcmp + #define LTC_NO_PROTOTYPES + #endif +#define XSTRCMP strcmp +#endif + +#ifndef XCLOCK +#define XCLOCK LibTomClock +#endif +#ifndef XCLOCKS_PER_SEC +#define XCLOCKS_PER_SEC CLOCKS_PER_SEC +#endif + +#ifndef XQSORT + #ifdef qsort + #define LTC_NO_PROTOTYPES + #endif +#define XQSORT LibTomQsort +#endif + +/* Easy button? */ +#ifdef LTC_EASY + #define LTC_NO_CIPHERS + #define LTC_RIJNDAEL + #define LTC_BLOWFISH + #define LTC_DES + #define LTC_CAST5 + + #define LTC_NO_MODES + #define LTC_ECB_MODE + #define LTC_CBC_MODE + #define LTC_CTR_MODE + + #define LTC_NO_HASHES + #define LTC_SHA1 + #define LTC_SHA512 + #define LTC_SHA384 + #define LTC_SHA256 + #define LTC_SHA224 + + #define LTC_NO_MACS + #define LTC_HMAC + #define LTC_OMAC + #define LTC_CCM_MODE + + #define LTC_NO_PRNGS + #define LTC_SPRNG + #define LTC_YARROW + #define LTC_DEVRANDOM + #define TRY_URANDOM_FIRST + + #define LTC_NO_PK + #define LTC_MRSA + #define LTC_MECC +#endif + +/* Use small code where possible */ +/* #define LTC_SMALL_CODE */ + +/* Enable self-test test vector checking */ +#ifndef LTC_NO_TEST + #define LTC_TEST +#endif + +/* clean the stack of functions which put private information on stack */ +/* #define LTC_CLEAN_STACK */ + +/* disable all file related functions */ +/* #define LTC_NO_FILE */ + +/* disable all forms of ASM */ +/* #define LTC_NO_ASM */ + +/* disable FAST mode */ +/* #define LTC_NO_FAST */ + +/* disable BSWAP on x86 */ +/* #define LTC_NO_BSWAP */ + +/* ---> Symmetric Block Ciphers <--- */ +#ifndef LTC_NO_CIPHERS + +#define LTC_BLOWFISH +#define LTC_RC2 +#define LTC_RC5 +#define LTC_RC6 +#define LTC_SAFERP +#define LTC_RIJNDAEL +#define LTC_XTEA +/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format + * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */ +#define LTC_TWOFISH +#ifndef LTC_NO_TABLES + #define LTC_TWOFISH_TABLES + /* #define LTC_TWOFISH_ALL_TABLES */ +#else + #define LTC_TWOFISH_SMALL +#endif +/* #define LTC_TWOFISH_SMALL */ +/* LTC_DES includes EDE triple-LTC_DES */ +#define LTC_DES +#define LTC_CAST5 +#define LTC_NOEKEON +#define LTC_SKIPJACK +#define LTC_SAFER +#define LTC_KHAZAD +#define LTC_ANUBIS +#define LTC_ANUBIS_TWEAK +#define LTC_KSEED +#define LTC_KASUMI + +#endif /* LTC_NO_CIPHERS */ + + +/* ---> Block Cipher Modes of Operation <--- */ +#ifndef LTC_NO_MODES + +#define LTC_CFB_MODE +#define LTC_OFB_MODE +#define LTC_ECB_MODE +#define LTC_CBC_MODE +#define LTC_CTR_MODE + +/* F8 chaining mode */ +#define LTC_F8_MODE + +/* LRW mode */ +#define LTC_LRW_MODE +#ifndef LTC_NO_TABLES + /* like GCM mode this will enable 16 8x128 tables [64KB] that make + * seeking very fast. + */ + #define LRW_TABLES +#endif + +/* XTS mode */ +#define LTC_XTS_MODE + +#endif /* LTC_NO_MODES */ + +/* ---> One-Way Hash Functions <--- */ +#ifndef LTC_NO_HASHES + +#define LTC_CHC_HASH +#define LTC_WHIRLPOOL +#define LTC_SHA512 +#define LTC_SHA384 +#define LTC_SHA256 +#define LTC_SHA224 +#define LTC_TIGER +#define LTC_SHA1 +#define LTC_MD5 +#define LTC_MD4 +#define LTC_MD2 +#define LTC_RIPEMD128 +#define LTC_RIPEMD160 +#define LTC_RIPEMD256 +#define LTC_RIPEMD320 + +#endif /* LTC_NO_HASHES */ + +/* ---> MAC functions <--- */ +#ifndef LTC_NO_MACS + +#define LTC_HMAC +#define LTC_OMAC +#define LTC_PMAC +#define LTC_XCBC +#define LTC_F9_MODE +#define LTC_PELICAN + +#if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL) + #error Pelican-MAC requires LTC_RIJNDAEL +#endif + +/* ---> Encrypt + Authenticate Modes <--- */ + +#define LTC_EAX_MODE +#if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC)) + #error LTC_EAX_MODE requires CTR and LTC_OMAC mode +#endif + +#define LTC_OCB_MODE +#define LTC_CCM_MODE +#define LTC_GCM_MODE + +/* Use 64KiB tables */ +#ifndef LTC_NO_TABLES + #define LTC_GCM_TABLES +#endif + +/* USE SSE2? requires GCC works on x86_32 and x86_64*/ +#ifdef LTC_GCM_TABLES +/* #define LTC_GCM_TABLES_SSE2 */ +#endif + +#endif /* LTC_NO_MACS */ + +/* Various tidbits of modern neatoness */ +#define LTC_BASE64 + +/* --> Pseudo Random Number Generators <--- */ +#ifndef LTC_NO_PRNGS + +/* Yarrow */ +#define LTC_YARROW +/* which descriptor of AES to use? */ +/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */ +#define LTC_YARROW_AES 0 + +#if defined(LTC_YARROW) && !defined(LTC_CTR_MODE) + #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined! +#endif + +/* a PRNG that simply reads from an available system source */ +#define LTC_SPRNG + +/* The LTC_RC4 stream cipher */ +#define LTC_RC4 + +/* Fortuna PRNG */ +#define LTC_FORTUNA +/* reseed every N calls to the read function */ +#define LTC_FORTUNA_WD 10 +/* number of pools (4..32) can save a bit of ram by lowering the count */ +#define LTC_FORTUNA_POOLS 32 + +/* Greg's LTC_SOBER128 PRNG ;-0 */ +#define LTC_SOBER128 + +/* the *nix style /dev/random device */ +#define LTC_DEVRANDOM +/* try /dev/urandom before trying /dev/random */ +#define TRY_URANDOM_FIRST + +#endif /* LTC_NO_PRNGS */ + +/* ---> math provider? <--- */ +#ifndef LTC_NO_MATH + +/* LibTomMath */ +#define LTM_LTC_DESC + +/* TomsFastMath */ +//#define TFM_LTC_DESC + +#endif /* LTC_NO_MATH */ + +/* ---> Public Key Crypto <--- */ +#ifndef LTC_NO_PK + +/* Include RSA support */ +#define LTC_MRSA + +/* Include Katja (a Rabin variant like RSA) */ +/* #define MKAT */ + +/* Digital Signature Algorithm */ +#define LTC_MDSA + +/* ECC */ +#define LTC_MECC + +/* use Shamir's trick for point mul (speeds up signature verification) */ +#define LTC_ECC_SHAMIR + +#if defined(TFM_LTC_DESC) && defined(LTC_MECC) + #define LTC_MECC_ACCEL +#endif + +/* do we want fixed point ECC */ +/* #define LTC_MECC_FP */ + +/* Timing Resistant? */ +/* #define LTC_ECC_TIMING_RESISTANT */ + +#endif /* LTC_NO_PK */ + +/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */ +#ifndef LTC_NO_PKCS + +#define LTC_PKCS_1 +#define LTC_PKCS_5 + +/* Include ASN.1 DER (required by DSA/RSA) */ +#define LTC_DER + +#endif /* LTC_NO_PKCS */ + +/* cleanup */ + +#ifdef LTC_MECC +/* Supported ECC Key Sizes */ +#ifndef LTC_NO_CURVES + #define ECC112 + #define ECC128 + #define ECC160 + #define ECC192 + #define ECC224 + #define ECC256 + #define ECC384 + #define ECC521 +#endif +#endif + +#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA) + /* Include the MPI functionality? (required by the PK algorithms) */ + #define MPI +#endif + +#ifdef LTC_MRSA + #define LTC_PKCS_1 +#endif + +#if defined(LTC_DER) && !defined(MPI) + #error ASN.1 DER requires MPI functionality +#endif + +#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER) + #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled +#endif + +/* THREAD management */ +#ifdef LTC_PTHREAD + +#include <pthread.h> + +#define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; +#define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x; +#define LTC_MUTEX_TYPE(x) pthread_mutex_t x; +#define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL); +#define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x); +#define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x); + +#else + +/* default no functions */ +#define LTC_MUTEX_GLOBAL(x) +#define LTC_MUTEX_PROTO(x) +#define LTC_MUTEX_TYPE(x) +#define LTC_MUTEX_INIT(x) +#define LTC_MUTEX_LOCK(x) +#define LTC_MUTEX_UNLOCK(x) + +#endif + +/* Debuggers */ + +/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */ +/* #define LTC_VALGRIND */ + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */ +/* $Revision: 1.73 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h new file mode 100644 index 00000000000..18553ebf9da --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h @@ -0,0 +1,378 @@ +/* ---- HASH FUNCTIONS ---- */ +#ifdef LTC_SHA512 +struct sha512_state { + ulong64 length, state[8]; + unsigned long curlen; + unsigned char buf[128]; +}; +#endif + +#ifdef LTC_SHA256 +struct sha256_state { + ulong64 length; + ulong32 state[8], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_SHA1 +struct sha1_state { + ulong64 length; + ulong32 state[5], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD5 +struct md5_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD4 +struct md4_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_TIGER +struct tiger_state { + ulong64 state[3], length; + unsigned long curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD2 +struct md2_state { + unsigned char chksum[16], X[48], buf[16]; + unsigned long curlen; +}; +#endif + +#ifdef LTC_RIPEMD128 +struct rmd128_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[4]; +}; +#endif + +#ifdef LTC_RIPEMD160 +struct rmd160_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[5]; +}; +#endif + +#ifdef LTC_RIPEMD256 +struct rmd256_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[8]; +}; +#endif + +#ifdef LTC_RIPEMD320 +struct rmd320_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[10]; +}; +#endif + +#ifdef LTC_WHIRLPOOL +struct whirlpool_state { + ulong64 length, state[8]; + unsigned char buf[64]; + ulong32 curlen; +}; +#endif + +#ifdef LTC_CHC_HASH +struct chc_state { + ulong64 length; + unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE]; + ulong32 curlen; +}; +#endif + +typedef union Hash_state { +#ifdef LTC_CHC_HASH + struct chc_state chc; +#endif +#ifdef LTC_WHIRLPOOL + struct whirlpool_state whirlpool; +#endif +#ifdef LTC_SHA512 + struct sha512_state sha512; +#endif +#ifdef LTC_SHA256 + struct sha256_state sha256; +#endif +#ifdef LTC_SHA1 + struct sha1_state sha1; +#endif +#ifdef LTC_MD5 + struct md5_state md5; +#endif +#ifdef LTC_MD4 + struct md4_state md4; +#endif +#ifdef LTC_MD2 + struct md2_state md2; +#endif +#ifdef LTC_TIGER + struct tiger_state tiger; +#endif +#ifdef LTC_RIPEMD128 + struct rmd128_state rmd128; +#endif +#ifdef LTC_RIPEMD160 + struct rmd160_state rmd160; +#endif +#ifdef LTC_RIPEMD256 + struct rmd256_state rmd256; +#endif +#ifdef LTC_RIPEMD320 + struct rmd320_state rmd320; +#endif + void *data; +} hash_state; + +/** hash descriptor */ +extern struct ltc_hash_descriptor { + /** name of hash */ + char *name; + /** internal ID */ + unsigned char ID; + /** Size of digest in octets */ + unsigned long hashsize; + /** Input block size in octets */ + unsigned long blocksize; + /** ASN.1 OID */ + unsigned long OID[16]; + /** Length of DER encoding */ + unsigned long OIDlen; + + /** Init a hash state + @param hash The hash to initialize + @return CRYPT_OK if successful + */ + int (*init)(hash_state *hash); + /** Process a block of data + @param hash The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ + int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen); + /** Produce the digest and store it + @param hash The hash state + @param out [out] The destination of the digest + @return CRYPT_OK if successful + */ + int (*done)(hash_state *hash, unsigned char *out); + /** Self-test + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled + */ + int (*test)(void); + + /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */ + int (*hmac_block)(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +} hash_descriptor[]; + +#ifdef LTC_CHC_HASH +int chc_register(int cipher); +int chc_init(hash_state * md); +int chc_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int chc_done(hash_state * md, unsigned char *hash); +int chc_test(void); +extern const struct ltc_hash_descriptor chc_desc; +#endif + +#ifdef LTC_WHIRLPOOL +int whirlpool_init(hash_state * md); +int whirlpool_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int whirlpool_done(hash_state * md, unsigned char *hash); +int whirlpool_test(void); +extern const struct ltc_hash_descriptor whirlpool_desc; +#endif + +#ifdef LTC_SHA512 +int sha512_init(hash_state * md); +int sha512_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha512_done(hash_state * md, unsigned char *hash); +int sha512_test(void); +extern const struct ltc_hash_descriptor sha512_desc; +#endif + +#ifdef LTC_SHA384 +#ifndef LTC_SHA512 + #error LTC_SHA512 is required for LTC_SHA384 +#endif +int sha384_init(hash_state * md); +#define sha384_process sha512_process +int sha384_done(hash_state * md, unsigned char *hash); +int sha384_test(void); +extern const struct ltc_hash_descriptor sha384_desc; +#endif + +#ifdef LTC_SHA256 +int sha256_init(hash_state * md); +int sha256_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha256_done(hash_state * md, unsigned char *hash); +int sha256_test(void); +extern const struct ltc_hash_descriptor sha256_desc; + +#ifdef LTC_SHA224 +#ifndef LTC_SHA256 + #error LTC_SHA256 is required for LTC_SHA224 +#endif +int sha224_init(hash_state * md); +#define sha224_process sha256_process +int sha224_done(hash_state * md, unsigned char *hash); +int sha224_test(void); +extern const struct ltc_hash_descriptor sha224_desc; +#endif +#endif + +#ifdef LTC_SHA1 +int sha1_init(hash_state * md); +int sha1_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha1_done(hash_state * md, unsigned char *hash); +int sha1_test(void); +extern const struct ltc_hash_descriptor sha1_desc; +#endif + +#ifdef LTC_MD5 +int md5_init(hash_state * md); +int md5_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md5_done(hash_state * md, unsigned char *hash); +int md5_test(void); +extern const struct ltc_hash_descriptor md5_desc; +#endif + +#ifdef LTC_MD4 +int md4_init(hash_state * md); +int md4_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md4_done(hash_state * md, unsigned char *hash); +int md4_test(void); +extern const struct ltc_hash_descriptor md4_desc; +#endif + +#ifdef LTC_MD2 +int md2_init(hash_state * md); +int md2_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md2_done(hash_state * md, unsigned char *hash); +int md2_test(void); +extern const struct ltc_hash_descriptor md2_desc; +#endif + +#ifdef LTC_TIGER +int tiger_init(hash_state * md); +int tiger_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int tiger_done(hash_state * md, unsigned char *hash); +int tiger_test(void); +extern const struct ltc_hash_descriptor tiger_desc; +#endif + +#ifdef LTC_RIPEMD128 +int rmd128_init(hash_state * md); +int rmd128_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd128_done(hash_state * md, unsigned char *hash); +int rmd128_test(void); +extern const struct ltc_hash_descriptor rmd128_desc; +#endif + +#ifdef LTC_RIPEMD160 +int rmd160_init(hash_state * md); +int rmd160_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd160_done(hash_state * md, unsigned char *hash); +int rmd160_test(void); +extern const struct ltc_hash_descriptor rmd160_desc; +#endif + +#ifdef LTC_RIPEMD256 +int rmd256_init(hash_state * md); +int rmd256_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd256_done(hash_state * md, unsigned char *hash); +int rmd256_test(void); +extern const struct ltc_hash_descriptor rmd256_desc; +#endif + +#ifdef LTC_RIPEMD320 +int rmd320_init(hash_state * md); +int rmd320_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd320_done(hash_state * md, unsigned char *hash); +int rmd320_test(void); +extern const struct ltc_hash_descriptor rmd320_desc; +#endif + + +int find_hash(const char *name); +int find_hash_id(unsigned char ID); +int find_hash_oid(const unsigned long *ID, unsigned long IDlen); +int find_hash_any(const char *name, int digestlen); +int register_hash(const struct ltc_hash_descriptor *hash); +int unregister_hash(const struct ltc_hash_descriptor *hash); +int hash_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_hash_mutex) + +int hash_memory(int hash, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen); +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen); + +/* a simple macro for making hash "process" functions */ +#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ +int func_name (hash_state * md, const unsigned char *in, unsigned long inlen) \ +{ \ + unsigned long n; \ + int err; \ + LTC_ARGCHK(md != NULL); \ + LTC_ARGCHK(in != NULL); \ + if (md-> state_var .curlen > sizeof(md-> state_var .buf)) { \ + return CRYPT_INVALID_ARG; \ + } \ + while (inlen > 0) { \ + if (md-> state_var .curlen == 0 && inlen >= block_size) { \ + if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) { \ + return err; \ + } \ + md-> state_var .length += block_size * 8; \ + in += block_size; \ + inlen -= block_size; \ + } else { \ + n = MIN(inlen, (block_size - md-> state_var .curlen)); \ + memcpy(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n); \ + md-> state_var .curlen += n; \ + in += n; \ + inlen -= n; \ + if (md-> state_var .curlen == block_size) { \ + if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) { \ + return err; \ + } \ + md-> state_var .length += 8*block_size; \ + md-> state_var .curlen = 0; \ + } \ + } \ + } \ + return CRYPT_OK; \ +} + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h new file mode 100644 index 00000000000..7ad9516bd29 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h @@ -0,0 +1,384 @@ +#ifdef LTC_HMAC +typedef struct Hmac_state { + hash_state md; + int hash; + hash_state hashstate; + unsigned char *key; +} hmac_state; + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); +int hmac_test(void); +int hmac_memory(int hash, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hmac_memory_multi(int hash, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hmac_file(int hash, const char *fname, const unsigned char *key, + unsigned long keylen, + unsigned char *dst, unsigned long *dstlen); +#endif + +#ifdef LTC_OMAC + +typedef struct { + int cipher_idx, + buflen, + blklen; + unsigned char block[MAXBLOCKSIZE], + prev[MAXBLOCKSIZE], + Lu[2][MAXBLOCKSIZE]; + symmetric_key key; +} omac_state; + +int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen); +int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen); +int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen); +int omac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int omac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int omac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int omac_test(void); +#endif /* LTC_OMAC */ + +#ifdef LTC_PMAC + +typedef struct { + unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + block[MAXBLOCKSIZE], /* currently accumulated block */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher_idx, /* cipher idx */ + block_len, /* length of block */ + buflen; /* number of bytes in the buffer */ +} pmac_state; + +int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen); +int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen); +int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen); + +int pmac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *msg, unsigned long msglen, + unsigned char *out, unsigned long *outlen); + +int pmac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); + +int pmac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); + +int pmac_test(void); + +/* internal functions */ +int pmac_ntz(unsigned long x); +void pmac_shift_xor(pmac_state *pmac); + +#endif /* PMAC */ + +#ifdef LTC_EAX_MODE + +#if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE)) + #error LTC_EAX_MODE requires LTC_OMAC and CTR +#endif + +typedef struct { + unsigned char N[MAXBLOCKSIZE]; + symmetric_CTR ctr; + omac_state headeromac, ctomac; +} eax_state; + +int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen); + +int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length); +int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length); +int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length); +int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen); + +int eax_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int eax_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + unsigned char *tag, unsigned long taglen, + int *stat); + + int eax_test(void); +#endif /* EAX MODE */ + +#ifdef LTC_OCB_MODE +typedef struct { + unsigned char L[MAXBLOCKSIZE], /* L value */ + Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + R[MAXBLOCKSIZE], /* R value */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher, /* cipher idx */ + block_len; /* length of block */ +} ocb_state; + +int ocb_init(ocb_state *ocb, int cipher, + const unsigned char *key, unsigned long keylen, const unsigned char *nonce); + +int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct); +int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt); + +int ocb_done_encrypt(ocb_state *ocb, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_done_decrypt(ocb_state *ocb, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, int *stat); + +int ocb_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, + int *stat); + +int ocb_test(void); + +/* internal functions */ +void ocb_shift_xor(ocb_state *ocb, unsigned char *Z); +int ocb_ntz(unsigned long x); +int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode); + +#endif /* LTC_OCB_MODE */ + +#ifdef LTC_CCM_MODE + +#define CCM_ENCRYPT 0 +#define CCM_DECRYPT 1 + +int ccm_memory(int cipher, + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + +int ccm_test(void); + +#endif /* LTC_CCM_MODE */ + +#if defined(LRW_MODE) || defined(LTC_GCM_MODE) +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); +#endif + + +/* table shared between GCM and LRW */ +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) +extern const unsigned char gcm_shift_table[]; +#endif + +#ifdef LTC_GCM_MODE + +#define GCM_ENCRYPT 0 +#define GCM_DECRYPT 1 + +#define LTC_GCM_MODE_IV 0 +#define LTC_GCM_MODE_AAD 1 +#define LTC_GCM_MODE_TEXT 2 + +typedef struct { + symmetric_key K; + unsigned char H[16], /* multiplier */ + X[16], /* accumulator */ + Y[16], /* counter */ + Y_0[16], /* initial counter */ + buf[16]; /* buffer for stuff */ + + int cipher, /* which cipher */ + ivmode, /* Which mode is the IV in? */ + mode, /* mode the GCM code is in */ + buflen; /* length of data in buf */ + + ulong64 totlen, /* 64-bit counter used for IV and AAD */ + pttotlen; /* 64-bit counter for the PT */ + +#ifdef LTC_GCM_TABLES + unsigned char PC[16][256][16] /* 16 tables of 8x128 */ +#ifdef LTC_GCM_TABLES_SSE2 +__attribute__ ((aligned (16))) +#endif +; +#endif +} gcm_state; + +void gcm_mult_h(gcm_state *gcm, unsigned char *I); + +int gcm_init(gcm_state *gcm, int cipher, + const unsigned char *key, int keylen); + +int gcm_reset(gcm_state *gcm); + +int gcm_add_iv(gcm_state *gcm, + const unsigned char *IV, unsigned long IVlen); + +int gcm_add_aad(gcm_state *gcm, + const unsigned char *adata, unsigned long adatalen); + +int gcm_process(gcm_state *gcm, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + int direction); + +int gcm_done(gcm_state *gcm, + unsigned char *tag, unsigned long *taglen); + +int gcm_memory( int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); +int gcm_test(void); + +#endif /* LTC_GCM_MODE */ + +#ifdef LTC_PELICAN + +typedef struct pelican_state +{ + symmetric_key K; + unsigned char state[16]; + int buflen; +} pelican_state; + +int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen); +int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen); +int pelican_done(pelican_state *pelmac, unsigned char *out); +int pelican_test(void); + +int pelican_memory(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out); + +#endif + +#ifdef LTC_XCBC + +/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */ +#define LTC_XCBC_PURE 0x8000UL + +typedef struct { + unsigned char K[3][MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + blocksize; +} xcbc_state; + +int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen); +int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen); +int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen); +int xcbc_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int xcbc_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int xcbc_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int xcbc_test(void); + +#endif + +#ifdef LTC_F9_MODE + +typedef struct { + unsigned char akey[MAXBLOCKSIZE], + ACC[MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + keylen, + blocksize; +} f9_state; + +int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen); +int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen); +int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen); +int f9_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int f9_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int f9_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int f9_test(void); + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h new file mode 100644 index 00000000000..53bda9bb4ba --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h @@ -0,0 +1,424 @@ +/* fix for MSVC ...evil! */ +#ifdef _MSC_VER + #define CONST64(n) n ## ui64 + typedef unsigned __int64 ulong64; +#else + #define CONST64(n) n ## ULL + typedef unsigned long long ulong64; +#endif + +/* this is the "32-bit at least" data type + * Re-define it to suit your platform but it must be at least 32-bits + */ +#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) + typedef unsigned ulong32; +#else + typedef unsigned long ulong32; +#endif + +/* ---- HELPER MACROS ---- */ +#ifdef ENDIAN_NEUTRAL + +#define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255)<<24) | \ + ((unsigned long)((y)[2] & 255)<<16) | \ + ((unsigned long)((y)[1] & 255)<<8) | \ + ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ + (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif /* ENDIAN_NEUTRAL */ + +#ifdef ENDIAN_LITTLE + +#if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) + +#define STORE32H(x, y) \ +asm __volatile__ ( \ + "bswapl %0 \n\t" \ + "movl %0,(%1)\n\t" \ + "bswapl %0 \n\t" \ + ::"r"(x), "r"(y)); + +#define LOAD32H(x, y) \ +asm __volatile__ ( \ + "movl (%1),%0\n\t" \ + "bswapl %0\n\t" \ + :"=r"(x): "r"(y)); + +#else + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#endif + + +/* x86_64 processor */ +#if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) + +#define STORE64H(x, y) \ +asm __volatile__ ( \ + "bswapq %0 \n\t" \ + "movq %0,(%1)\n\t" \ + "bswapq %0 \n\t" \ + ::"r"(x), "r"(y)); + +#define LOAD64H(x, y) \ +asm __volatile__ ( \ + "movq (%1),%0\n\t" \ + "bswapq %0\n\t" \ + :"=r"(x): "r"(y)); + +#else + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ + (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif + +#ifdef ENDIAN_32BITWORD + +#define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y) \ + XMEMCPY(&(x), y, 4); + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#else /* 64-bit words then */ + +#define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64L(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64L(x, y) \ + { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ + +#endif /* ENDIAN_LITTLE */ + +#ifdef ENDIAN_BIG +#define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255)<<24) | \ + ((unsigned long)((y)[2] & 255)<<16) | \ + ((unsigned long)((y)[1] & 255)<<8) | \ + ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#ifdef ENDIAN_32BITWORD + +#define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y) \ + XMEMCPY(&(x), y, 4); + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \ + (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); } + +#else /* 64-bit words then */ + +#define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64H(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64H(x, y) \ + { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_BIG */ + +#define BSWAP(x) ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL) | \ + ((x>>8)&0x0000FF00UL) | ((x<<8)&0x00FF0000UL) ) + + +/* 32-bit Rotates */ +#if defined(_MSC_VER) + +/* instrinsic rotate */ +#include <stdlib.h> +#pragma intrinsic(_lrotr,_lrotl) +#define ROR(x,n) _lrotr(x,n) +#define ROL(x,n) _lrotl(x,n) +#define RORc(x,n) _lrotr(x,n) +#define ROLc(x,n) _lrotl(x,n) + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) + +static inline unsigned ROL(unsigned word, int i) +{ + asm ("roll %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ + asm ("rorl %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ + asm ("roll %2,%0" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ + asm ("rorl %2,%0" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + +#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + +static inline unsigned ROL(unsigned word, int i) +{ + asm ("rotlw %0,%0,%2" + :"=r" (word) + :"0" (word),"r" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ + asm ("rotlw %0,%0,%2" + :"=r" (word) + :"0" (word),"r" (32-i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ + asm ("rotlwi %0,%0,%2" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ + asm ("rotrwi %0,%0,%2" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + + +#else + +/* rotates the hard way */ +#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +#endif + + +/* 64-bit Rotates */ +#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) + +static inline unsigned long ROL64(unsigned long word, int i) +{ + asm("rolq %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +static inline unsigned long ROR64(unsigned long word, int i) +{ + asm("rorq %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned long ROL64c(unsigned long word, const int i) +{ + asm("rolq %2,%0" + :"=r" (word) + :"0" (word),"J" (i)); + return word; +} + +static inline unsigned long ROR64c(unsigned long word, const int i) +{ + asm("rorq %2,%0" + :"=r" (word) + :"0" (word),"J" (i)); + return word; +} + +#else /* LTC_NO_ROLC */ + +#define ROL64c ROL64 +#define ROR64c ROR64 + +#endif + +#else /* Not x86_64 */ + +#define ROL64(x, y) \ + ( (((x)<<((ulong64)(y)&63)) | \ + (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64(x, y) \ + ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ + ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROL64c(x, y) \ + ( (((x)<<((ulong64)(y)&63)) | \ + (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64c(x, y) \ + ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ + ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#endif + +#ifndef MAX + #define MAX(x, y) ( ((x)>(y))?(x):(y) ) +#endif + +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* extract a byte portably */ +#ifdef _MSC_VER + #define byte(x, n) ((unsigned char)((x) >> (8 * (n)))) +#else + #define byte(x, n) (((x) >> (8 * (n))) & 255) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2006/11/29 23:43:57 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h new file mode 100644 index 00000000000..a05d7fff942 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h @@ -0,0 +1,500 @@ +/** math functions **/ + +#define LTC_MP_LT -1 +#define LTC_MP_EQ 0 +#define LTC_MP_GT 1 + +#define LTC_MP_NO 0 +#define LTC_MP_YES 1 + +#ifndef LTC_MECC + typedef void ecc_point; +#endif + +#ifndef LTC_MRSA + typedef void rsa_key; +#endif + +/** math descriptor */ +typedef struct { + /** Name of the math provider */ + char *name; + + /** Bits per digit, amount of bits must fit in an unsigned long */ + int bits_per_digit; + +/* ---- init/deinit functions ---- */ + + /** initialize a bignum + @param a The number to initialize + @return CRYPT_OK on success + */ + int (*init)(void **a); + + /** init copy + @param dst The number to initialize and write to + @param src The number to copy from + @return CRYPT_OK on success + */ + int (*init_copy)(void **dst, void *src); + + /** deinit + @param a The number to free + @return CRYPT_OK on success + */ + void (*deinit)(void *a); + +/* ---- data movement ---- */ + + /** negate + @param src The number to negate + @param dst The destination + @return CRYPT_OK on success + */ + int (*neg)(void *src, void *dst); + + /** copy + @param src The number to copy from + @param dst The number to write to + @return CRYPT_OK on success + */ + int (*copy)(void *src, void *dst); + +/* ---- trivial low level functions ---- */ + + /** set small constant + @param a Number to write to + @param n Source upto bits_per_digit (actually meant for very small constants) + @return CRYPT_OK on succcess + */ + int (*set_int)(void *a, unsigned long n); + + /** get small constant + @param a Number to read, only fetches upto bits_per_digit from the number + @return The lower bits_per_digit of the integer (unsigned) + */ + unsigned long (*get_int)(void *a); + + /** get digit n + @param a The number to read from + @param n The number of the digit to fetch + @return The bits_per_digit sized n'th digit of a + */ + unsigned long (*get_digit)(void *a, int n); + + /** Get the number of digits that represent the number + @param a The number to count + @return The number of digits used to represent the number + */ + int (*get_digit_count)(void *a); + + /** compare two integers + @param a The left side integer + @param b The right side integer + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare)(void *a, void *b); + + /** compare against int + @param a The left side integer + @param b The right side integer (upto bits_per_digit) + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare_d)(void *a, unsigned long n); + + /** Count the number of bits used to represent the integer + @param a The integer to count + @return The number of bits required to represent the integer + */ + int (*count_bits)(void * a); + + /** Count the number of LSB bits which are zero + @param a The integer to count + @return The number of contiguous zero LSB bits + */ + int (*count_lsb_bits)(void *a); + + /** Compute a power of two + @param a The integer to store the power in + @param n The power of two you want to store (a = 2^n) + @return CRYPT_OK on success + */ + int (*twoexpt)(void *a , int n); + +/* ---- radix conversions ---- */ + + /** read ascii string + @param a The integer to store into + @param str The string to read + @param radix The radix the integer has been represented in (2-64) + @return CRYPT_OK on success + */ + int (*read_radix)(void *a, const char *str, int radix); + + /** write number to string + @param a The integer to store + @param str The destination for the string + @param radix The radix the integer is to be represented in (2-64) + @return CRYPT_OK on success + */ + int (*write_radix)(void *a, char *str, int radix); + + /** get size as unsigned char string + @param a The integer to get the size (when stored in array of octets) + @return The length of the integer + */ + unsigned long (*unsigned_size)(void *a); + + /** store an integer as an array of octets + @param src The integer to store + @param dst The buffer to store the integer in + @return CRYPT_OK on success + */ + int (*unsigned_write)(void *src, unsigned char *dst); + + /** read an array of octets and store as integer + @param dst The integer to load + @param src The array of octets + @param len The number of octets + @return CRYPT_OK on success + */ + int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len); + +/* ---- basic math ---- */ + + /** add two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*add)(void *a, void *b, void *c); + + + /** add two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*addi)(void *a, unsigned long b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*sub)(void *a, void *b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*subi)(void *a, unsigned long b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*mul)(void *a, void *b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*muli)(void *a, unsigned long b, void *c); + + /** Square an integer + @param a The integer to square + @param b The destination + @return CRYPT_OK on success + */ + int (*sqr)(void *a, void *b); + + /** Divide an integer + @param a The dividend + @param b The divisor + @param c The quotient (can be NULL to signify don't care) + @param d The remainder (can be NULL to signify don't care) + @return CRYPT_OK on success + */ + int (*mpdiv)(void *a, void *b, void *c, void *d); + + /** divide by two + @param a The integer to divide (shift right) + @param b The destination + @return CRYPT_OK on success + */ + int (*div_2)(void *a, void *b); + + /** Get remainder (small value) + @param a The integer to reduce + @param b The modulus (upto bits_per_digit in length) + @param c The destination for the residue + @return CRYPT_OK on success + */ + int (*modi)(void *a, unsigned long b, unsigned long *c); + + /** gcd + @param a The first integer + @param b The second integer + @param c The destination for (a, b) + @return CRYPT_OK on success + */ + int (*gcd)(void *a, void *b, void *c); + + /** lcm + @param a The first integer + @param b The second integer + @param c The destination for [a, b] + @return CRYPT_OK on success + */ + int (*lcm)(void *a, void *b, void *c); + + /** Modular multiplication + @param a The first source + @param b The second source + @param c The modulus + @param d The destination (a*b mod c) + @return CRYPT_OK on success + */ + int (*mulmod)(void *a, void *b, void *c, void *d); + + /** Modular squaring + @param a The first source + @param b The modulus + @param c The destination (a*a mod b) + @return CRYPT_OK on success + */ + int (*sqrmod)(void *a, void *b, void *c); + + /** Modular inversion + @param a The value to invert + @param b The modulus + @param c The destination (1/a mod b) + @return CRYPT_OK on success + */ + int (*invmod)(void *, void *, void *); + +/* ---- reduction ---- */ + + /** setup montgomery + @param a The modulus + @param b The destination for the reduction digit + @return CRYPT_OK on success + */ + int (*montgomery_setup)(void *a, void **b); + + /** get normalization value + @param a The destination for the normalization value + @param b The modulus + @return CRYPT_OK on success + */ + int (*montgomery_normalization)(void *a, void *b); + + /** reduce a number + @param a The number [and dest] to reduce + @param b The modulus + @param c The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + int (*montgomery_reduce)(void *a, void *b, void *c); + + /** clean up (frees memory) + @param a The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + void (*montgomery_deinit)(void *a); + +/* ---- exponentiation ---- */ + + /** Modular exponentiation + @param a The base integer + @param b The power (can be negative) integer + @param c The modulus integer + @param d The destination + @return CRYPT_OK on success + */ + int (*exptmod)(void *a, void *b, void *c, void *d); + + /** Primality testing + @param a The integer to test + @param b The destination of the result (FP_YES if prime) + @return CRYPT_OK on success + */ + int (*isprime)(void *a, int *b); + +/* ---- (optional) ecc point math ---- */ + + /** ECC GF(p) point multiplication (from the NIST curves) + @param k The integer to multiply the point by + @param G The point to multiply + @param R The destination for kG + @param modulus The modulus for the field + @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only) + @return CRYPT_OK on success + */ + int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + + /** ECC GF(p) point addition + @param P The first point + @param Q The second point + @param R The destination of P + Q + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + + /** ECC GF(p) point double + @param P The first point + @param R The destination of 2P + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp); + + /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1) + @param P The point to map + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + @remark The mapping can be different but keep in mind a ecc_point only has three + integers (x,y,z) so if you use a different mapping you have to make it fit. + */ + int (*ecc_map)(ecc_point *P, void *modulus, void *mp); + + /** Computes kA*A + kB*B = C using Shamir's Trick + @param A First point to multiply + @param kA What to multiple A by + @param B Second point to multiply + @param kB What to multiple B by + @param C [out] Destination point (can overlap with A or B + @param modulus Modulus for curve + @return CRYPT_OK on success + */ + int (*ecc_mul2add)(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + +/* ---- (optional) rsa optimized math (for internal CRT) ---- */ + + /** RSA Key Generation + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param size The size of the modulus (key size) desired (octets) + @param e The "e" value (public key). e==65537 is a good choice + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful, upon error all allocated ram is freed + */ + int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key); + + + /** RSA exponentiation + @param in The octet array representing the base + @param inlen The length of the input + @param out The destination (to be stored in an octet array format) + @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus) + @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA + @param key The RSA key to use + @return CRYPT_OK on success + */ + int (*rsa_me)(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); +} ltc_math_descriptor; + +extern ltc_math_descriptor ltc_mp; + +int ltc_init_multi(void **a, ...); +void ltc_deinit_multi(void *a, ...); + +#ifdef LTM_DESC +extern const ltc_math_descriptor ltm_desc; +#endif + +#ifdef TFM_DESC +extern const ltc_math_descriptor tfm_desc; +#endif + +#ifdef GMP_DESC +extern const ltc_math_descriptor gmp_desc; +#endif + +#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE) + +#define MP_DIGIT_BIT ltc_mp.bits_per_digit + +/* some handy macros */ +#define mp_init(a) ltc_mp.init(a) +#define mp_init_multi ltc_init_multi +#define mp_clear(a) ltc_mp.deinit(a) +#define mp_clear_multi ltc_deinit_multi +#define mp_init_copy(a, b) ltc_mp.init_copy(a, b) + +#define mp_neg(a, b) ltc_mp.neg(a, b) +#define mp_copy(a, b) ltc_mp.copy(a, b) + +#define mp_set(a, b) ltc_mp.set_int(a, b) +#define mp_set_int(a, b) ltc_mp.set_int(a, b) +#define mp_get_int(a) ltc_mp.get_int(a) +#define mp_get_digit(a, n) ltc_mp.get_digit(a, n) +#define mp_get_digit_count(a) ltc_mp.get_digit_count(a) +#define mp_cmp(a, b) ltc_mp.compare(a, b) +#define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) +#define mp_count_bits(a) ltc_mp.count_bits(a) +#define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) +#define mp_2expt(a, b) ltc_mp.twoexpt(a, b) + +#define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) +#define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) +#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + +#define mp_add(a, b, c) ltc_mp.add(a, b, c) +#define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) +#define mp_sub(a, b, c) ltc_mp.sub(a, b, c) +#define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) +#define mp_mul(a, b, c) ltc_mp.mul(a, b, c) +#define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) +#define mp_sqr(a, b) ltc_mp.sqr(a, b) +#define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) +#define mp_div_2(a, b) ltc_mp.div_2(a, b) +#define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) +#define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) +#define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) +#define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) + +#define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) +#define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) +#define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) + +#define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) +#define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) +#define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) +#define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) + +#define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d) +#define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) + +#define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) +#define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) +#define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while(0); + +#define mp_tohex(a, b) mp_toradix(a, b, 16) + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */ +/* $Revision: 1.44 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h new file mode 100644 index 00000000000..f5384cacc51 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h @@ -0,0 +1,23 @@ +/* ---- LTC_BASE64 Routines ---- */ +#ifdef LTC_BASE64 +int base64_encode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); + +int base64_decode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); +#endif + +/* ---- MEM routines ---- */ +void zeromem(void *dst, size_t len); +void burn_stack(unsigned long len); + +const char *error_to_string(int err); + +extern const char *crypt_build_settings; + +/* ---- HMM ---- */ +int crypt_fsa(void *mp, ...); + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h new file mode 100644 index 00000000000..b5f277a8848 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h @@ -0,0 +1,558 @@ +/* ---- NUMBER THEORY ---- */ + +enum { + PK_PUBLIC=0, + PK_PRIVATE=1 +}; + +int rand_prime(void *N, long len, prng_state *prng, int wprng); + +/* ---- RSA ---- */ +#ifdef LTC_MRSA + +/* Min and Max RSA key sizes (in bits) */ +#define MIN_RSA_SIZE 1024 +#define MAX_RSA_SIZE 4096 + +/** RSA LTC_PKCS style key */ +typedef struct Rsa_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The public exponent */ + void *e; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; +} rsa_key; + +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key); + +int rsa_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); + +void rsa_free(rsa_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +#define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \ + rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key) + +#define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \ + rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key) + +#define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \ + rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key) + +#define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \ + rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key) + +/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */ +int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key); + +int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int padding, + int *stat, rsa_key *key); + +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key); + +int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int padding, + int hash_idx, unsigned long saltlen, + int *stat, rsa_key *key); + +/* LTC_PKCS #1 import/export */ +int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key); +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key); + +/* Ladik: Added for verifying Blizzard strong signature verification */ +int rsa_verify_simple(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, + rsa_key *key); + +#endif + +/* ---- Katja ---- */ +#ifdef MKAT + +/* Min and Max KAT key sizes (in bits) */ +#define MIN_KAT_SIZE 1024 +#define MAX_KAT_SIZE 4096 + +/** Katja LTC_PKCS style key */ +typedef struct KAT_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; + /** The pq param */ + void *pq; +} katja_key; + +int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key); + +int katja_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + katja_key *key); + +void katja_free(katja_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +int katja_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, katja_key *key); + +int katja_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int *stat, + katja_key *key); + +/* LTC_PKCS #1 import/export */ +int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key); +int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key); + +#endif + +/* ---- ECC Routines ---- */ +#ifdef LTC_MECC + +/* size of our temp buffers for exported keys */ +#define ECC_BUF_SIZE 256 + +/* max private key size */ +#define ECC_MAXSIZE 66 + +/** Structure defines a NIST GF(p) curve */ +typedef struct { + /** The size of the curve in octets */ + int size; + + /** name of curve */ + char *name; + + /** The prime that defines the field the curve is in (encoded in hex) */ + char *prime; + + /** The fields B param (hex) */ + char *B; + + /** The order of the curve (hex) */ + char *order; + + /** The x co-ordinate of the base point on the curve (hex) */ + char *Gx; + + /** The y co-ordinate of the base point on the curve (hex) */ + char *Gy; +} ltc_ecc_set_type; + +/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */ +typedef struct { + /** The x co-ordinate */ + void *x; + + /** The y co-ordinate */ + void *y; + + /** The z co-ordinate */ + void *z; +} ecc_point; + +/** An ECC key */ +typedef struct { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */ + int idx; + + /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */ + const ltc_ecc_set_type *dp; + + /** The public key */ + ecc_point pubkey; + + /** The private key */ + void *k; +} ecc_key; + +/** the ECC params provided */ +extern const ltc_ecc_set_type ltc_ecc_sets[]; + +int ecc_test(void); +void ecc_sizes(int *low, int *high); +int ecc_get_size(ecc_key *key); + +int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); +int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp); +void ecc_free(ecc_key *key); + +int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key); +int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp); + +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen); +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp); + +int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, + unsigned char *out, unsigned long *outlen); + +int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + ecc_key *key); + +int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + ecc_key *key); + +int ecc_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, ecc_key *key); + +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, ecc_key *key); + +/* low level functions */ +ecc_point *ltc_ecc_new_point(void); +void ltc_ecc_del_point(ecc_point *p); +int ltc_ecc_is_valid_idx(int n); + +/* point ops (mp == montgomery digit) */ +#if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC) +/* R = 2P */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp); + +/* R = P + Q */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); +#endif + +#if defined(LTC_MECC_FP) +/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */ +int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +/* functions for saving/loading/freeing/adding to fixed point cache */ +int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen); +int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen); +void ltc_ecc_fp_free(void); +int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock); + +/* lock/unlock all points currently in fixed point cache */ +void ltc_ecc_fp_tablelock(int lock); +#endif + +/* R = kG */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +#ifdef LTC_ECC_SHAMIR +/* kA*A + kB*B = C */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + +#ifdef LTC_MECC_FP +/* Shamir's trick with optimized point multiplication using fixed point cache */ +int ltc_ecc_fp_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, void *modulus); +#endif + +#endif + + +/* map P to affine from projective */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp); + +#endif + +#ifdef LTC_MDSA + +/* Max diff between group and modulus size in bytes */ +#define LTC_MDSA_DELTA 512 + +/* Max DSA group size in bytes (default allows 4k-bit groups) */ +#define LTC_MDSA_MAX_GROUP 512 + +/** DSA key structure */ +typedef struct { + /** The key type, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** The order of the sub-group used in octets */ + int qord; + + /** The generator */ + void *g; + + /** The prime used to generate the sub-group */ + void *q; + + /** The large prime that generats the field the contains the sub-group */ + void *p; + + /** The private key */ + void *x; + + /** The public key */ + void *y; +} dsa_key; + +int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key); +void dsa_free(dsa_key *key); + +int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen, + void *r, void *s, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_verify_hash_raw( void *r, void *s, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + dsa_key *key); + +int dsa_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + dsa_key *key); + +int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key); +int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key); +int dsa_verify_key(dsa_key *key, int *stat); + +int dsa_shared_secret(void *private_key, void *base, + dsa_key *public_key, + unsigned char *out, unsigned long *outlen); +#endif + +#ifdef LTC_DER +/* DER handling */ + +enum { + LTC_ASN1_EOL, + LTC_ASN1_BOOLEAN, + LTC_ASN1_INTEGER, + LTC_ASN1_SHORT_INTEGER, + LTC_ASN1_BIT_STRING, + LTC_ASN1_OCTET_STRING, + LTC_ASN1_NULL, + LTC_ASN1_OBJECT_IDENTIFIER, + LTC_ASN1_IA5_STRING, + LTC_ASN1_PRINTABLE_STRING, + LTC_ASN1_UTF8_STRING, + LTC_ASN1_UTCTIME, + LTC_ASN1_CHOICE, + LTC_ASN1_SEQUENCE, + LTC_ASN1_SET, + LTC_ASN1_SETOF +}; + +/** A LTC ASN.1 list type */ +typedef struct ltc_asn1_list_ { + /** The LTC ASN.1 enumerated type identifier */ + int type; + /** The data to encode or place for decoding */ + void *data; + /** The size of the input or resulting output */ + unsigned long size; + /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */ + int used; + /** prev/next entry in the list */ + struct ltc_asn1_list_ *prev, *next, *child, *parent; +} ltc_asn1_list; + +#define LTC_SET_ASN1(list, index, Type, Data, Size) \ + do { \ + int LTC_MACRO_temp = (index); \ + ltc_asn1_list *LTC_MACRO_list = (list); \ + LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \ + LTC_MACRO_list[LTC_MACRO_temp].data = (void*)(Data); \ + LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \ + LTC_MACRO_list[LTC_MACRO_temp].used = 0; \ + } while (0); + +/* SEQUENCE */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of); + +#define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE) + +int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, + ltc_asn1_list *list, unsigned long outlen, int ordered); + +#define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1) + +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, + unsigned long *outlen); + +/* SET */ +#define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0) +#define der_length_set der_length_sequence +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +/* VA list handy helpers with triplets of <type, size, data> */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...); +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...); + +/* FLEXI DECODER handle unknown list decoder */ +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out); +void der_free_sequence_flexi(ltc_asn1_list *list); +void der_sequence_free(ltc_asn1_list *in); + +/* BOOLEAN */ +int der_length_boolean(unsigned long *outlen); +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen); +int der_decode_boolean(const unsigned char *in, unsigned long inlen, + int *out); +/* INTEGER */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen); +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num); +int der_length_integer(void *num, unsigned long *len); + +/* INTEGER -- handy for 0..2^32-1 values */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num); +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen); +int der_length_short_integer(unsigned long num, unsigned long *outlen); + +/* BIT STRING */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_bit_string(unsigned long nbits, unsigned long *outlen); + +/* OCTET STRING */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_octet_string(unsigned long noctets, unsigned long *outlen); + +/* OBJECT IDENTIFIER */ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen); +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen); +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen); +unsigned long der_object_identifier_bits(unsigned long x); + +/* IA5 STRING */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_ia5_char_encode(int c); +int der_ia5_value_decode(int v); + +/* Printable STRING */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_printable_char_encode(int c); +int der_printable_value_decode(int v); + +/* UTF-8 */ +#if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR) +#include <wchar.h> +#else +typedef ulong32 wchar_t; +#endif + +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, + wchar_t *out, unsigned long *outlen); +unsigned long der_utf8_charsize(const wchar_t c); +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen); + + +/* CHOICE */ +int der_decode_choice(const unsigned char *in, unsigned long *inlen, + ltc_asn1_list *list, unsigned long outlen); + +/* UTCTime */ +typedef struct { + unsigned YY, /* year */ + MM, /* month */ + DD, /* day */ + hh, /* hour */ + mm, /* minute */ + ss, /* second */ + off_dir, /* timezone offset direction 0 == +, 1 == - */ + off_hh, /* timezone offset hours */ + off_mm; /* timezone offset minutes */ +} ltc_utctime; + +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen); + +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, + ltc_utctime *out); + +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */ +/* $Revision: 1.81 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h new file mode 100644 index 00000000000..84fb82a6229 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h @@ -0,0 +1,89 @@ +/* LTC_PKCS Header Info */ + +/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */ +#ifdef LTC_PKCS_1 + +enum ltc_pkcs_1_v1_5_blocks +{ + LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */ + LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */ +}; + +enum ltc_pkcs_1_paddings +{ + LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */ + LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */ + LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */ +}; + +int pkcs_1_mgf1( int hash_idx, + const unsigned char *seed, unsigned long seedlen, + unsigned char *mask, unsigned long masklen); + +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out); +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen); + +/* *** v1.5 padding */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen); + +int pkcs_1_v1_5_decode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + unsigned char *out, + unsigned long *outlen, + int *is_valid); + +/* *** v2.1 padding */ +int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, int hash_idx, + unsigned char *out, unsigned long *outlen, + int *res); + +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, + const unsigned char *sig, unsigned long siglen, + unsigned long saltlen, int hash_idx, + unsigned long modulus_bitlen, int *res); + +#endif /* LTC_PKCS_1 */ + +/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */ +#ifdef LTC_PKCS_5 + +/* Algorithm #1 (old) */ +int pkcs_5_alg1(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); + +/* Algorithm #2 (new) */ +int pkcs_5_alg2(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, unsigned long salt_len, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); + +#endif /* LTC_PKCS_5 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h new file mode 100644 index 00000000000..f3e3e550e88 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h @@ -0,0 +1,199 @@ +/* ---- PRNG Stuff ---- */ +#ifdef LTC_YARROW +struct yarrow_prng { + int cipher, hash; + unsigned char pool[MAXBLOCKSIZE]; + symmetric_CTR ctr; + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_RC4 +struct rc4_prng { + int x, y; + unsigned char buf[256]; +}; +#endif + +#ifdef LTC_FORTUNA +struct fortuna_prng { + hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */ + + symmetric_key skey; + + unsigned char K[32], /* the current key */ + IV[16]; /* IV for CTR mode */ + + unsigned long pool_idx, /* current pool we will add to */ + pool0_len, /* length of 0'th pool */ + wd; + + ulong64 reset_cnt; /* number of times we have reset */ + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_SOBER128 +struct sober128_prng { + ulong32 R[17], /* Working storage for the shift register */ + initR[17], /* saved register contents */ + konst, /* key dependent constant */ + sbuf; /* partial word encryption buffer */ + + int nbuf, /* number of part-word stream bits buffered */ + flag, /* first add_entropy call or not? */ + set; /* did we call add_entropy to set key? */ + +}; +#endif + +typedef union Prng_state { + char dummy[1]; +#ifdef LTC_YARROW + struct yarrow_prng yarrow; +#endif +#ifdef LTC_RC4 + struct rc4_prng rc4; +#endif +#ifdef LTC_FORTUNA + struct fortuna_prng fortuna; +#endif +#ifdef LTC_SOBER128 + struct sober128_prng sober128; +#endif +} prng_state; + +/** PRNG descriptor */ +extern struct ltc_prng_descriptor { + /** Name of the PRNG */ + char *name; + /** size in bytes of exported state */ + int export_size; + /** Start a PRNG state + @param prng [out] The state to initialize + @return CRYPT_OK if successful + */ + int (*start)(prng_state *prng); + /** Add entropy to the PRNG + @param in The entropy + @param inlen Length of the entropy (octets)\ + @param prng The PRNG state + @return CRYPT_OK if successful + */ + int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng); + /** Ready a PRNG state to read from + @param prng The PRNG state to ready + @return CRYPT_OK if successful + */ + int (*ready)(prng_state *prng); + /** Read from the PRNG + @param out [out] Where to store the data + @param outlen Length of data desired (octets) + @param prng The PRNG state to read from + @return Number of octets read + */ + unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng); + /** Terminate a PRNG state + @param prng The PRNG state to terminate + @return CRYPT_OK if successful + */ + int (*done)(prng_state *prng); + /** Export a PRNG state + @param out [out] The destination for the state + @param outlen [in/out] The max size and resulting size of the PRNG state + @param prng The PRNG to export + @return CRYPT_OK if successful + */ + int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng); + /** Import a PRNG state + @param in The data to import + @param inlen The length of the data to import (octets) + @param prng The PRNG to initialize/import + @return CRYPT_OK if successful + */ + int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng); + /** Self-test the PRNG + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); +} prng_descriptor[]; + +#ifdef LTC_YARROW +int yarrow_start(prng_state *prng); +int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_ready(prng_state *prng); +unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int yarrow_done(prng_state *prng); +int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_test(void); +extern const struct ltc_prng_descriptor yarrow_desc; +#endif + +#ifdef LTC_FORTUNA +int fortuna_start(prng_state *prng); +int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_ready(prng_state *prng); +unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int fortuna_done(prng_state *prng); +int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_test(void); +extern const struct ltc_prng_descriptor fortuna_desc; +#endif + +#ifdef LTC_RC4 +int rc4_start(prng_state *prng); +int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_ready(prng_state *prng); +unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int rc4_done(prng_state *prng); +int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_test(void); +extern const struct ltc_prng_descriptor rc4_desc; +#endif + +#ifdef LTC_SPRNG +int sprng_start(prng_state *prng); +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_ready(prng_state *prng); +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sprng_done(prng_state *prng); +int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_test(void); +extern const struct ltc_prng_descriptor sprng_desc; +#endif + +#ifdef LTC_SOBER128 +int sober128_start(prng_state *prng); +int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_ready(prng_state *prng); +unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sober128_done(prng_state *prng); +int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_test(void); +extern const struct ltc_prng_descriptor sober128_desc; +#endif + +int find_prng(const char *name); +int register_prng(const struct ltc_prng_descriptor *prng); +int unregister_prng(const struct ltc_prng_descriptor *prng); +int prng_is_valid(int idx); +LTC_MUTEX_PROTO(ltc_prng_mutex) + +/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this + * might not work on all platforms as planned + */ +unsigned long rng_get_bytes(unsigned char *out, + unsigned long outlen, + void (*callback)(void)); + +int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c new file mode 100644 index 00000000000..537516d80d9 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c @@ -0,0 +1,30 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" +#include <signal.h> + +/** + @file crypt_argchk.c + Perform argument checking, Tom St Denis +*/ + +#if (ARGTYPE == 0) +void crypt_argchk(char *v, char *s, int d) +{ + fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", + v, d, s); + (void)raise(SIGABRT); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c new file mode 100644 index 00000000000..5925fd27302 --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c @@ -0,0 +1,27 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** + @file crypt_hash_descriptor.c + Stores the hash descriptor table, Tom St Denis +*/ + +struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = { +{ NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_hash_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c new file mode 100644 index 00000000000..8ed5105b56c --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c @@ -0,0 +1,36 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** + @file crypt_hash_is_valid.c + Determine if hash is valid, Tom St Denis +*/ + +/* + Test if a hash index is valid + @param idx The index of the hash to search for + @return CRYPT_OK if valid +*/ +int hash_is_valid(int idx) +{ + LTC_MUTEX_LOCK(<c_hash_mutex); + if (idx < 0 || idx >= TAB_SIZE || hash_descriptor[idx].name == NULL) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_INVALID_HASH; + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c new file mode 100644 index 00000000000..bcc89f4f94d --- /dev/null +++ b/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c @@ -0,0 +1,43 @@ +/*****************************************************************************/ +/* crypt_libc.c Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* Description: */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 05.05.10 1.00 Lad The first version of crypt_libc.c */ +/*****************************************************************************/ + +// LibTomCrypt header +#include <stdlib.h> +#include "../headers/tomcrypt.h" + +void * LibTomMalloc(size_t n) +{ + return malloc(n); +} + +void * LibTomCalloc(size_t n, size_t s) +{ + return calloc(n, s); +} + +void * LibTomRealloc(void *p, size_t n) +{ + return realloc(p, n); +} + +void LibTomFree(void * p) +{ + free(p); +} + +clock_t LibTomClock(void) +{ + return clock(); +} + +void LibTomQsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)) +{ + qsort(base, nmemb, size, compar); +} |