Пример #1
0
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;
}
Пример #2
0
void ListFile_Free(void * pvListFile)
{
    if(pvListFile != NULL)
    {
        CASC_FREE(pvListFile);
    }
}
Пример #3
0
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;
            CASC_FREE(szDataFile);
        }
    }

    // Return error or success
    hf->pStream = hs->DataFileArray[hf->ArchiveIndex];
    return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
}
Пример #4
0
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;
}
Пример #5
0
// Checks whether there is a ".agent.db". If yes, the function
// sets "szRootPath" and "szDataPath" in the storage structure
// and returns ERROR_SUCCESS
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
    QUERY_KEY AgentFile;
    TCHAR * szFilePath;
    size_t nLength = 0;
    char * szValue;
    int nError = ERROR_FILE_NOT_FOUND;

    // Create the full name of the .agent.db file
    szFilePath = CombinePath(szDirectory, _T(".agent.db"));
    if(szFilePath != NULL)
    {
        // Load the file to memory
        nError = LoadTextFile(szFilePath, &AgentFile);
        if(nError == ERROR_SUCCESS)
        {
            // Extract the data directory from the ".agent.db" file
            szValue = ExtractStringVariable(&AgentFile, "data_dir", &nLength);
            if(szValue != NULL)
            {
                hs->szRootPath = CascNewStr(szDirectory, 0);
                hs->szDataPath = CombinePathAndString(szDirectory, szValue, nLength);
                nError = (hs->szDataPath != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
            }

            // Free the loaded blob
            FreeCascBlob(&AgentFile);
        }

        // Freee the file path
        CASC_FREE(szFilePath);
    }

    return nError;
}
Пример #6
0
static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
    TCHAR * szDataPath;
    int nError = ERROR_FILE_NOT_FOUND;

    // Try all known subdirectories
    for(size_t i = 0; DataDirs[i] != NULL; i++)
    {
        // Create the eventual data path
        szDataPath = CombinePath(szDirectory, DataDirs[i]);
        if(szDataPath != NULL)
        {
            // Does that directory exist?
            if(DirectoryExists(szDataPath))
            {
                hs->szDataPath = szDataPath;
                return ERROR_SUCCESS;
            }

            // Free the data path
            CASC_FREE(szDataPath);
        }
    }

    return nError;
}
Пример #7
0
static void * FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey)
{
    TCHAR * szFileName;
    void * pvListFile = NULL;

    // Construct the local file name
    szFileName = CascNewStr(hs->szDataPath, 8 + 3 + 3 + 32);
    if(szFileName != NULL)
    {
        // Add the part where the config file path is
        AppendConfigFilePath(szFileName, pFileKey);

        // Load and verify the external listfile
        pvListFile = ListFile_OpenExternal(szFileName);
        if(pvListFile != NULL)
        {
            if(!ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
            {
                ListFile_Free(pvListFile);
                pvListFile = NULL;
            }
        }

        // Free the file name
        CASC_FREE(szFileName);
    }

    return pvListFile;
}
Пример #8
0
static int LoadFileFrames(TCascFile * hf)
{
    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, hf->FrameCount);
    if(pFileFrames != NULL)
    {
        // Load the frame array
        ArchiveFileOffset = hf->FramesOffset;
        if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, hf->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 < hf->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;
            }
        }
        else
            nError = GetLastError();

        // Note: on ENCODING file, this value is almost always bigger
        // then the real size of ENCODING. We handle this problem
        // by calculating size of the ENCODIG file from its header.
        hf->FileSize = FileSize;

#ifdef CASCLIB_TEST
        hf->FileSize_FrameSum = FileSize;
#endif

        // Free the array
        CASC_FREE(pFileFrames);
    }
    else
        nError = ERROR_NOT_ENOUGH_MEMORY;

    return nError;
}
Пример #9
0
static void WowHandler_Close(TRootHandler_WoW6 * pRootHandler)
{
    if(pRootHandler != NULL)
    {
        Array_Free(&pRootHandler->FileTable);
        Map_Free(pRootHandler->pRootMap);
        CASC_FREE(pRootHandler);
    }
}
Пример #10
0
void ListFile_FreeMap(PLISTFILE_MAP pListMap)
{
    if(pListMap != NULL)
    {
        if(pListMap->pNameMap != NULL)
            Map_Free(pListMap->pNameMap);
        CASC_FREE(pListMap);
    }
}
Пример #11
0
static void FreeCascBlob(PQUERY_KEY pBlob)
{
    if(pBlob != NULL)
    {
        if(pBlob->pbData != NULL)
            CASC_FREE(pBlob->pbData);

        pBlob->pbData = NULL;
        pBlob->cbData = 0;
    }
}
Пример #12
0
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);
    }
}
Пример #13
0
static DWORD ScanDirectoryFile(
    TCascStorage * hs,
    LPBYTE pbRootFile,
    LPBYTE pbFileEnd)
{
    PDIABLO3_NAMED_ENTRY pNamedEntry;
    DIABLO3_DIR_HEADER RootHeader;
    DIABLO3_DIR_HEADER DirHeader;
    LPBYTE pbSubDir;
    DWORD dwTotalFileCount;
    DWORD cbNamedEntry;
    DWORD cbSubDir;
    int nError;

    // Parse the directory header in order to retrieve the items
    nError = ParseDirectoryHeader(&RootHeader, pbRootFile, pbFileEnd);
    if(nError != ERROR_SUCCESS)
        return 0;

    // Add the root directory's entries
    dwTotalFileCount = RootHeader.dwEntries1 + RootHeader.dwEntries2 + RootHeader.dwEntries3;

    // Parse the named entries
    for(DWORD i = 0; i < RootHeader.dwEntries3; i++)
    {
        // Get the this named entry
        if((cbNamedEntry = VerifyNamedFileEntry(RootHeader.pbEntries3, pbFileEnd)) == 0)
            return 0;
        pNamedEntry = (PDIABLO3_NAMED_ENTRY)RootHeader.pbEntries3;
        RootHeader.pbEntries3 += cbNamedEntry;

        // Load the subdirectory to memory
        pbSubDir = LoadFileToMemory(hs, pNamedEntry->EncodingKey.Value, &cbSubDir);
        if(pbSubDir != NULL)
        {
            // Count the files in the subdirectory
            if(ParseDirectoryHeader(&DirHeader, pbSubDir, pbSubDir + cbSubDir) == ERROR_SUCCESS)
            {
                dwTotalFileCount += DirHeader.dwEntries1 + DirHeader.dwEntries2 + DirHeader.dwEntries3;
            }

            // Free the subdirectory
            CASC_FREE(pbSubDir);
        }
    }

    // Return the total number of entries
    return dwTotalFileCount;
}
Пример #14
0
static void D3Handler_Close(TRootHandler_Diablo3 * pRootHandler)
{
    if(pRootHandler != NULL)
    {
        // Free the file map
        Map_Free(pRootHandler->pRootMap);

        // Free the array of the file entries and file names
        Array_Free(&pRootHandler->FileTable);
        Array_Free(&pRootHandler->FileNames);

        // Free the root file itself
        CASC_FREE(pRootHandler);
    }
}
Пример #15
0
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;
    }

    CASC_FREE(szIndexPath);
    return NULL;
}
Пример #16
0
FILE * CreateDumpFile(const char * szFormat, TCascStorage * hs)
{
    FILE * fp = NULL;
    char * szFileName;

    // Validate the storage handle
    if(hs != NULL)
    {
        // Format the real file name
        szFileName = FormatFileName(szFormat, hs);
        if(szFileName != NULL)
        {
            // Create the dump file
            fp = fopen(szFileName, "wt");
            CASC_FREE(szFileName);
        }
    }

    return fp;
}
Пример #17
0
static int FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSEINFOFILE PfnParseProc)
{
    TCHAR * szFileName;
    void * pvListFile = NULL;
    int nError = ERROR_CAN_NOT_COMPLETE;

    // Construct the local file name
    szFileName = CascNewStr(hs->szDataPath, 8 + 3 + 3 + 32);
    if (szFileName != NULL)
    {
        // Add the part where the config file path is
        AppendConfigFilePath(szFileName, pFileKey);

        // Load and verify the external listfile
        pvListFile = ListFile_OpenExternal(szFileName);
        if (pvListFile != NULL)
        {
            if (ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
            {
                nError = PfnParseProc(hs, pvListFile);
            }
            else
            {
                nError = ERROR_FILE_CORRUPT;
            }
            ListFile_Free(pvListFile);
        }
        else
        {
            nError = ERROR_FILE_NOT_FOUND;
        }
        CASC_FREE(szFileName);
    }
    else
    {
        nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    return nError;
}
Пример #18
0
// Checks whether there is a ".build.info" or ".build.db".
// If yes, the function sets "szDataPath" and "szIndexPath"
// in the storage structure and returns ERROR_SUCCESS
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
    TFileStream * pStream;
    TCHAR * szBuildFile;
    int nError = ERROR_FILE_NOT_FOUND;

    // Try to find any of the root files used in the history
    for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
    {
        // Create the full name of the .agent.db file
        szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName);
        if (szBuildFile != NULL)
        {
            // Attempt to open the file
            pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY);
            if (pStream != NULL)
            {
                // Free the stream
                FileStream_Close(pStream);

                // Check for the data directory
                nError = CheckDataDirectory(hs, szDirectory);
                if (nError == ERROR_SUCCESS)
                {
                    hs->szBuildFile = szBuildFile;
                    hs->BuildFileType = BuildTypes[i].BuildFileType;
                    return ERROR_SUCCESS;
                }
            }

            CASC_FREE(szBuildFile);
        }
    }

    return nError;
}
Пример #19
0
int LoadBuildInfo(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;
}
Пример #20
0
static int ProcessFileFrame(
    LPBYTE pbOutBuffer,
    DWORD  cbOutBuffer,
    LPBYTE pbInBuffer,
    DWORD cbInBuffer,
    DWORD dwFrameIndex)
{
    LPBYTE pbTempBuffer;
    LPBYTE pbWorkBuffer;
    DWORD cbTempBuffer = CASCLIB_MAX(cbInBuffer, cbOutBuffer);
    DWORD cbWorkBuffer = cbOutBuffer + 1;
    DWORD dwStepCount = 0;
    bool bWorkComplete = false;
    int nError = ERROR_SUCCESS;

    // Allocate the temporary buffer that will serve as output
    pbWorkBuffer = pbTempBuffer = CASC_ALLOC(BYTE, cbTempBuffer);
    if(pbWorkBuffer == NULL)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Perform the loop
    for(;;)
    {
        // Set the output buffer.
        // Even operations: extract to temporary buffer
        // Odd operations: extract to output buffer
        pbWorkBuffer = (dwStepCount & 0x01) ? pbOutBuffer : pbTempBuffer;
        cbWorkBuffer = (dwStepCount & 0x01) ? cbOutBuffer : cbTempBuffer;

        // Perform the operation specific to the operation ID
        switch(pbInBuffer[0])
        {
            case 'E':   // Encrypted files
                nError = CascDecrypt(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex);
                bWorkComplete = (nError != ERROR_SUCCESS);
                break;

            case 'Z':   // ZLIB compressed files
                nError = CascDecompress(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
                bWorkComplete = true;
                break;

            case 'N':   // Normal stored files
                nError = CascDirectCopy(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
                bWorkComplete = true;
                break;

            case 'F':   // Recursive frames - not supported
            default:    // Unrecognized - if we unpacked something, we consider it done
                nError = ERROR_NOT_SUPPORTED;
                bWorkComplete = true;
                assert(false);
                break;
        }

        // Are we done?
        if(bWorkComplete)
            break;

        // Set the input buffer to the work buffer
        pbInBuffer = pbWorkBuffer;
        cbInBuffer = cbWorkBuffer;
        dwStepCount++;
    }

    // If the data are currently in the temporary buffer,
    // we need to copy them to output buffer
    if(nError == ERROR_SUCCESS && pbWorkBuffer != pbOutBuffer)
    {
        if(cbWorkBuffer != cbOutBuffer)
            nError = ERROR_INSUFFICIENT_BUFFER;
        memcpy(pbOutBuffer, pbWorkBuffer, cbOutBuffer);
    }

    // Free the temporary buffer
    CASC_FREE(pbTempBuffer);
    return nError;
}
Пример #21
0
static int ParseCoreTOC(
    TRootHandler_Diablo3 * pRootHandler,
    PCASC_MAP pPackageMap,
    LPBYTE pbCoreTocFile,
    LPBYTE pbCoreTocEnd)
{
    PDIABLO3_CORE_TOC_HEADER pTocHeader;
    PDIABLO3_CORE_TOC_ENTRY pSortedEntries;
    PDIABLO3_CORE_TOC_ENTRY pTocEntry;
    LPBYTE pbCoreTocNames;
    DWORD dwFileIndexes = 0;
    DWORD i;

    // Check the space for header
    if((pbCoreTocFile + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbCoreTocEnd)
        return ERROR_FILE_CORRUPT;
    pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbCoreTocFile;
    pbCoreTocFile += sizeof(DIABLO3_CORE_TOC_HEADER);

    // Calculate space needed for allocation
    for(i = 0; i < DIABLO3_MAX_ASSETS; i++)
    {
        // Get the first entry
        pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]);

        // Find out the entry with the maximum index
        for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
        {
            if(pTocEntry->FileIndex >= dwFileIndexes)
                dwFileIndexes = pTocEntry->FileIndex + 1;
            pTocEntry++;
        }
    }

    // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
    pSortedEntries = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwFileIndexes);
    if(pSortedEntries != NULL)
    {
        // Initialize all entries to invalid
        memset(pSortedEntries, 0xFF, dwFileIndexes * sizeof(DIABLO3_CORE_TOC_ENTRY));

        // Populate the linear array with the entries
        for(i = 0; i < DIABLO3_MAX_ASSETS; i++)
        {
            // Set the pointers
            pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]);
            pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]);

            // Setup the entries
            for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
            {
                pSortedEntries[pTocEntry->FileIndex].AssetIndex = pTocEntry->AssetIndex;
                pSortedEntries[pTocEntry->FileIndex].FileIndex  = pTocEntry->FileIndex;
                pSortedEntries[pTocEntry->FileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocFile) + pTocEntry->NameOffset;
                pTocEntry++;
            }
        }

        // Now use the linear array to resolve the asset indexes and plain names
        ResolveFullFileNames(pRootHandler, pSortedEntries, pPackageMap, pbCoreTocFile, dwFileIndexes);
        CASC_FREE(pSortedEntries);
    }

    return ERROR_SUCCESS;
}
Пример #22
0
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead)
{
    PCASC_FILE_FRAME pFrame = NULL;
    ULONGLONG StreamSize;
    ULONGLONG FileOffset;
    TCascFile * hf;
    LPBYTE pbBuffer = (LPBYTE)pvBuffer;
    DWORD dwStartPointer = 0;
    DWORD dwFilePointer = 0;
    DWORD dwEndPointer = 0;
    DWORD dwFrameSize;
    bool bReadResult;
    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 frames are not loaded yet, do it now
    if(nError == ERROR_SUCCESS)
    {
        nError = EnsureFrameHeadersLoaded(hf);
    }

    // If the file position is at or beyond end of file, do nothing
    if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize)
    {
        *pdwBytesRead = 0;
        return ERROR_SUCCESS;
    }

    // Find the file frame where to read from
    if(nError == ERROR_SUCCESS)
    {
        // Get the frame
        pFrame = FindFileFrame(hf, hf->FilePointer);
        if(pFrame == NULL || pFrame->CompressedSize < 1)
            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 pbFrameData = 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
                pbFrameData = CASC_ALLOC(BYTE, pFrame->CompressedSize);
                if(pbFrameData == NULL)
                {
                    nError = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }

                // Load the raw file data to memory
                FileOffset = pFrame->FrameArchiveOffset;
                bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbFrameData, pFrame->CompressedSize);

                // Note: The raw file data size could be less than expected
                // Happened in WoW build 19342 with the ROOT file. MD5 in the frame header
                // is zeroed, which means it should not be checked
                // Frame File: data.029
                // Frame Offs: 0x013ED9F0 size 0x01325B32
                // Frame End:  0x02713522
                // File Size:  0x027134FC
                if(bReadResult == false && GetLastError() == ERROR_HANDLE_EOF && !IsValidMD5(pFrame->md5))
                {
                    // Get the size of the remaining file
                    FileStream_GetSize(hf->pStream, &StreamSize);
                    dwFrameSize = (DWORD)(StreamSize - FileOffset);

                    // If the frame offset is before EOF and frame end is beyond EOF, correct it
                    if(FileOffset < StreamSize && dwFrameSize < pFrame->CompressedSize)
                    {
                        memset(pbFrameData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize));
                        bReadResult = true;
                    }
                }

                // If the read result failed, we cannot finish reading it
                if(bReadResult && VerifyDataBlockHash(pbFrameData, pFrame->CompressedSize, pFrame->md5))
                {
                    // Convert the source frame to the file cache
                    nError = ProcessFileFrame(hf->pbFileCache,
                                              pFrame->FrameSize,
                                              pbFrameData,
                                              pFrame->CompressedSize,
                                      (DWORD)(pFrame - hf->pFrames));
                    if(nError == ERROR_SUCCESS)
                    {
                        // Set the start and end of the cache
                        hf->CacheStart = dwFrameStart;
                        hf->CacheEnd = dwFrameEnd;
                    }
                }
                else
                {
                    nError = ERROR_FILE_CORRUPT;
                }

                // Free the raw frame data
                CASC_FREE(pbFrameData);
            }

            // 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);
}
Пример #23
0
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
    TRootHandler_Diablo3 * pRootHandler;
    PCASC_MAP pPackageMap = NULL;
    LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
    LPBYTE pbPackagesDat = NULL;
    DWORD dwTotalFileCount;
    DWORD cbPackagesDat = 0;
    int nError;

    // Allocate the root handler object
    hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Diablo3, 1);
    if(pRootHandler == NULL)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Fill-in the handler functions
    memset(pRootHandler, 0, sizeof(TRootHandler_Diablo3));
    pRootHandler->Insert      = (ROOT_INSERT)D3Handler_Insert;
    pRootHandler->Search      = (ROOT_SEARCH)D3Handler_Search;
    pRootHandler->EndSearch   = (ROOT_ENDSEARCH)D3Handler_EndSearch;
    pRootHandler->GetKey      = (ROOT_GETKEY)D3Handler_GetKey;
    pRootHandler->Close       = (ROOT_CLOSE)D3Handler_Close;
    pRootHandler->GetFileId   = (ROOT_GETFILEID)D3Handler_GetFileId;

    // Fill-in the flags
    pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;

    // Scan the total number of files in the root directories
    // Reserve space for extra files
    dwTotalFileCount = ScanDirectoryFile(hs, pbRootFile, pbRootFileEnd);
    if(dwTotalFileCount == 0)
        return ERROR_FILE_CORRUPT;
    dwTotalFileCount += CASC_EXTRA_FILES;

    // Allocate the global linear file table
    // Note: This is about 18 MB of memory for Diablo III PTR build 30013
    nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, dwTotalFileCount);
    if(nError != ERROR_SUCCESS)
        return nError;

    // Allocate global buffer for file names.
    // The size of the buffer was taken from Diablo III build 30013
    nError = Array_Create(&pRootHandler->FileNames, char, 0x01000000);
    if(nError != ERROR_SUCCESS)
        return nError;

    // Create map of ROOT_ENTRY -> FileEntry
    pRootHandler->pRootMap = Map_Create(dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
    if(pRootHandler->pRootMap == NULL)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Parse the ROOT file and insert all entries in the file table
    nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFileEnd, DIABLO3_INVALID_INDEX);
    if(nError == ERROR_SUCCESS)
    {
        size_t dwRootEntries = pRootHandler->FileTable.ItemCount;

        // We expect the number of level-0 to be less than maximum
        assert(dwRootEntries < DIABLO3_MAX_SUBDIRS);

        // Now parse the all root items and load them
        for(DWORD i = 0; i < dwRootEntries; i++)
        {
            PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i);

            // Load the entire file to memory
            pbRootFile = LoadFileToMemory(hs, pRootEntry->EncodingKey.Value, &cbRootFile);
            if(pbRootFile != NULL)
            {
                nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFile + cbRootFile, i);
                CASC_FREE(pbRootFile);
            }
        }
    }

    // Note: The file "Base\Data_D3\PC\Misc\Packages.dat" contains the names
    // of the files (without level-0 and level-1 directory). We can use these
    // names for supplying the missing extensions
    if(nError == ERROR_SUCCESS)
    {
        // Load the entire file to memory
        pbPackagesDat = LoadFileToMemory(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat", &cbPackagesDat);
        if(pbPackagesDat != NULL)
        {
            pPackageMap = CreatePackageMap(pbPackagesDat, pbPackagesDat + cbPackagesDat);
        }
    }

    // Vast majorify of files at this moment don't have names.
    // We can load the Base\CoreTOC.dat file in order
    // to get directory asset indexes, file names and extensions
    if(nError == ERROR_SUCCESS)
    {
        LPBYTE pbCoreTOC;
        DWORD cbCoreTOC = 0;

        // Load the entire file to memory
        pbCoreTOC = LoadFileToMemory(hs, "Base\\CoreTOC.dat", &cbCoreTOC);
        if(pbCoreTOC != NULL)
        {
            ParseCoreTOC(pRootHandler, pPackageMap, pbCoreTOC, pbCoreTOC + cbCoreTOC);
            CASC_FREE(pbCoreTOC);
        }
    }

    // Free the packages map
    if(pPackageMap != NULL)
        Map_Free(pPackageMap);
    if(pbPackagesDat != NULL)
        CASC_FREE(pbPackagesDat);
    return nError;
}
Пример #24
0
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 frames are not loaded yet, do it now
    if(nError == ERROR_SUCCESS)
    {
        nError = EnsureFrameHeadersLoaded(hf);
    }

    // If the file position is at or beyond end of file, do nothing
    if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize)
    {
        *pdwBytesRead = 0;
        return ERROR_SUCCESS;
    }

    // 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(!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);
}
Пример #25
0
int LoadBuildInfo(TCascStorage * hs)
{
    TCHAR * szAgentFile;
    TCHAR * szInfoFile;
    void * pvListFile;
    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)
        {
            pvListFile = ListFile_OpenExternal(szInfoFile);
            if(pvListFile != NULL)
            {
                // Parse the info file
                nError = ParseInfoFile(hs, pvListFile);
                if(nError == ERROR_SUCCESS)
                    bBuildConfigComplete = true;
                
                ListFile_Free(pvListFile);
            }

            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)
        {
            pvListFile = ListFile_OpenExternal(szAgentFile);
            if(pvListFile != NULL)
            {
                nError = ParseAgentFile(hs, pvListFile);
                if(nError == ERROR_SUCCESS)
                    bBuildConfigComplete = true;

                ListFile_Free(pvListFile);
            }
            CASC_FREE(szAgentFile);
        }
    }

    // If the .build.info OR .build.db file has been loaded,
    // proceed with loading the CDN config file and CDN build file
    if(nError == ERROR_SUCCESS && bBuildConfigComplete)
    {
        // Load the configuration file
        pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey);
        if(pvListFile != NULL)
        {
            nError = LoadCdnConfigFile(hs, pvListFile);
            ListFile_Free(pvListFile);
        }
        else
            nError = ERROR_FILE_NOT_FOUND;
    }

    // Load the build file
    if(nError == ERROR_SUCCESS && bBuildConfigComplete)
    {
        pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey);
        if(pvListFile != NULL)
        {
            nError = LoadCdnBuildFile(hs, pvListFile);
            ListFile_Free(pvListFile);
        }
        else
            nError = ERROR_FILE_NOT_FOUND;
    }

    // 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;
}
Пример #26
0
static void WowHandler_EndSearch(TRootHandler_WoW6 * /* pRootHandler */, TCascSearch * pSearch)
{
    if(pSearch->pRootContext != NULL)
        CASC_FREE(pSearch->pRootContext);
    pSearch->pRootContext = NULL;
}