Example #1
0
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 = (DWORD)(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;
}
Example #2
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;
}
Example #3
0
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;
}
static LPBYTE LoadFileToMemory(TCascStorage * hs, LPBYTE pbEncodingKey, DWORD * pcbFileData)
{
    QUERY_KEY EncodingKey;
    LPBYTE pbFileData = NULL;
    HANDLE hFile;
    DWORD cbBytesRead = 0;
    DWORD cbFileData = 0;

    // Open the file by encoding key
    EncodingKey.pbData = pbEncodingKey;
    EncodingKey.cbData = MD5_HASH_SIZE;
    if(CascOpenFileByEncodingKey((HANDLE)hs, &EncodingKey, 0, &hFile))
    {
        // Retrieve the file size
        cbFileData = CascGetFileSize(hFile, NULL);
        if(cbFileData > 0)
        {
            pbFileData = CASC_ALLOC(BYTE, cbFileData);
            if(pbFileData != NULL)
            {
                CascReadFile(hFile, pbFileData, cbFileData, &cbBytesRead);
            }
        }

        // Close the file
        CascCloseFile(hFile);
    }

    // Give the file to the caller
    if(pcbFileData != NULL)
        pcbFileData[0] = cbBytesRead;
    return pbFileData;
}
Example #5
0
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
{
    ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1;
    ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
    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 file size. Note that for all files except ENCODING,
        // this is the compressed file size
        hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);

        // 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 table
        hf->FileSize = hf->CompressedSize;

        // Increment the number of references to the archive
        hs->dwRefCount++;
        hf->hs = hs;
    }
Example #6
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;
}
Example #7
0
static TCHAR * MakeCdnList(CASC_CSV & Csv, size_t * Indices)
{
    TCHAR * szCdnList = NULL;
    TCHAR * szCdnPtr;
    TCHAR * szCdnEnd;
    const char * szHostPtr;
    char szHostsList[0x100] = { 0 };
    char szHostPath[0x80] = { 0 };

    // Retrieve both strings
    if (Csv.GetString(szHostsList, sizeof(szHostsList) - 1, Indices[0]) != ERROR_SUCCESS)
        return NULL;
    if (Csv.GetString(szHostPath, sizeof(szHostPath), Indices[1]) != ERROR_SUCCESS)
        return NULL;
    
    // Did we retrieve something?
    if (szHostsList[0] != 0)
    {
        size_t nCombinedLength;
        size_t nHostsLength = strlen(szHostsList) + 1;
        size_t nPathLength = strlen(szHostPath);
        size_t cbToAllocate;
        size_t nHostsCount = 1;

        // Cut the hosts list to pieces
        for (size_t i = 0; i < nHostsLength; i++)
        {
            if (szHostsList[i] == ' ')
            {
                szHostsList[i] = 0;
                nHostsCount++;
            }
        }

        // Merge the hosts list
        cbToAllocate = nHostsLength + (nPathLength + 1) * nHostsCount + 1;
        szCdnList = szCdnPtr = CASC_ALLOC(TCHAR, cbToAllocate);
        szCdnEnd = szCdnList + cbToAllocate - 1;
        if (szCdnList != NULL)
        {
            for (szHostPtr = szHostsList; szHostPtr[0] != 0; szHostPtr += strlen(szHostPtr) + 1)
            {
                nCombinedLength = CombineUrlPath(szCdnPtr, (szCdnEnd - szCdnPtr), szHostPtr, szHostPath);
                if (nCombinedLength == 0)
                    break;

                szCdnPtr += nCombinedLength + 1;
                szCdnPtr[0] = 0;
            }
        }
    }

    return szCdnList;
}
Example #8
0
int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
{
    TRootHandler_WoW6 * pRootHandler;
    LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
    int nError;

    // Verify the size
    if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK))
        nError = ERROR_FILE_CORRUPT;

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

    // Fill-in the handler functions
    memset(pRootHandler, 0, sizeof(TRootHandler_WoW6));
    pRootHandler->Insert      = (ROOT_INSERT)WowHandler_Insert;
    pRootHandler->Search      = (ROOT_SEARCH)WowHandler_Search;
    pRootHandler->EndSearch   = (ROOT_ENDSEARCH)WowHandler_EndSearch;
    pRootHandler->GetKey      = (ROOT_GETKEY)WowHandler_GetKey;
    pRootHandler->Close       = (ROOT_CLOSE)WowHandler_Close;

#ifdef _DEBUG
    pRootHandler->Dump = TRootHandlerWoW6_Dump;    // Support for ROOT file dump
#endif  // _DEBUG

    // Count the files that are going to be loaded
    ParseWowRootFile(pRootHandler, ParseRoot_CountFiles, pbRootFile, pbRootFileEnd, dwLocaleMask);
    pRootHandler->dwTotalFileCount += CASC_EXTRA_FILES;

    // Create linear table that will contain all root items
    nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, pRootHandler->dwTotalFileCount);
    if(nError != ERROR_SUCCESS)
        return nError;

    // Create sorted table that will contain all root items to lookup by FileDataId
    nError = Array_Create(&pRootHandler->FileDataIdLookupTable, PCASC_FILE_ENTRY, pRootHandler->dwTotalFileCount);
    if (nError != ERROR_SUCCESS)
        return nError;

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

    // Parse the root file again and insert all files to the map
    ParseWowRootFile(pRootHandler, ParseRoot_AddRootEntries, pbRootFile, pbRootFileEnd, dwLocaleMask);

    // Sort entries by FileDataId for searches
    qsort_pointer_array((void**)pRootHandler->FileDataIdLookupTable.ItemArray, pRootHandler->FileDataIdLookupTable.ItemCount, &FileDataIdCompare, NULL);
    return ERROR_SUCCESS;
}
Example #9
0
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);
}
Example #10
0
static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd, DWORD dwBlobCount)
{
    size_t nLength = (szLineEnd - szLineBegin);

    // We expect each blob to have length of the encoding key and one space between
    if(nLength > (dwBlobCount * MD5_STRING_SIZE) + ((dwBlobCount - 1) * sizeof(char))) 
        return ERROR_INVALID_PARAMETER;

    // Allocate the blob buffer
    pBlob->pbData = CASC_ALLOC(BYTE, dwBlobCount * MD5_HASH_SIZE);
    if(pBlob->pbData == NULL)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Set the buffer size and load the blob array
    pBlob->cbData = dwBlobCount * MD5_HASH_SIZE;
    return LoadBlobArray(pBlob, szLineBegin, szLineEnd, dwBlobCount);
}
Example #11
0
static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
{
    PLISTFILE_CACHE pCache;

    // Allocate cache for one file block
    pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize);
    if(pCache != NULL)
    {
        // Set the initial pointers
        pCache->pBegin = 
        pCache->pPos   = (char *)(pCache + 1);
        pCache->pEnd   = pCache->pBegin + dwFileSize;
    }

    // Return the cache
    return pCache;
}
Example #12
0
static TCascFile * CreateFileHandle(TCascStorage * hs, PQUERY_KEY pCKey, PQUERY_KEY pEKey, PCASC_EKEY_ENTRY pEKeyEntry, DWORD dwOpenFlags, DWORD dwContentSize)
{
    ULONGLONG StorageOffset = ConvertBytesToInteger_5(pEKeyEntry->StorageOffset);
    ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->IndexFile[0].FileOffsetBits) - 1;
    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)(StorageOffset >> hs->IndexFile[0].FileOffsetBits);
        hf->ArchiveOffset = (DWORD)(StorageOffset & FileOffsMask);
        hf->szClassName = "TCascFile";
        hf->OpenFlags = dwOpenFlags;

        // Supply the content key, if available
        if(pCKey != NULL)
        {
            assert(pCKey->pbData != NULL && pCKey->cbData == MD5_HASH_SIZE);
            memcpy(hf->CKey.Value, pCKey->pbData, pCKey->cbData);
        }

        // Supply the encoded key, if available
        if(pEKey != NULL)
        {
            assert(pEKey->pbData != NULL && CASC_EKEY_SIZE <= pEKey->cbData && pEKey->cbData <= MD5_HASH_SIZE);
            memcpy(hf->EKey.Value, pEKey->pbData, pEKey->cbData);
        }

        // Copy the encoded file size
        if(pEKeyEntry != NULL)
        {
            hf->EncodedSize = ConvertBytesToInteger_4_LE(pEKeyEntry->EncodedSize);
        }

        // Set the content size
        hf->ContentSize = dwContentSize;

        // Increment the number of references to the archive
        hs->dwRefCount++;
        hf->hs = hs;
    }
Example #13
0
static PLISTFILE_MAP ListMap_Create()
{
    PLISTFILE_MAP pListMap;
    size_t cbToAllocate;
    
    // Create buffer for the listfile
    // Note that because the listfile is quite big and CASC_REALLOC
    // is a costly operation, we want to have as few reallocs as possible.
    cbToAllocate = sizeof(LISTFILE_MAP) + LISTMAP_INITIAL;
    pListMap = (PLISTFILE_MAP)CASC_ALLOC(BYTE, cbToAllocate);
    if(pListMap != NULL)
    {
        // Fill the listfile buffer
        memset(pListMap, 0, sizeof(LISTFILE_MAP));
        pListMap->cbBufferMax = LISTMAP_INITIAL;
    }

    return pListMap;
}
Example #14
0
static int EnsureFrameHeadersLoaded(TCascFile * hf)
{
    int nError;

    // Make sure we have header area loaded
    nError = EnsureHeaderAreaIsLoaded(hf);
    if(nError != ERROR_SUCCESS)
        return nError;

    // If the frame headers are not loaded yet, do it
    if(hf->pFrames == NULL)
    {
        // Allocate the frame array
        hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount);
        if(hf->pFrames != NULL)
        {
            // Either load the frames from the file or supply them on our own
            if(hf->HeaderSize != 0)
            {
                hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD) + sizeof(DWORD);
                nError = LoadFileFrames(hf);
            }
            else
            {
                // Offset of the first frame is right after the file frames
                hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD);
                
                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;
}
Example #15
0
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;
}
Example #16
0
static int LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
{
    size_t HashCount = 0;
    int nError = ERROR_SUCCESS;

    // Retrieve the hash count
    if (CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL)
        return ERROR_BAD_FORMAT;

    // Do nothing if there is no data
    if(HashCount != 0)
    {
        // Allocate the blob buffer
        pBlob->pbData = CASC_ALLOC(BYTE, HashCount * MD5_HASH_SIZE);
        if(pBlob->pbData == NULL)
            return ERROR_NOT_ENOUGH_MEMORY;

        // Set the buffer size and load the blob array
        pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE);
        nError = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount);
    }

    return nError;
}
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;
}
Example #18
0
static int ParseInfoFile(TCascStorage * hs, void * pvListFile)
{
    QUERY_KEY Active = {NULL, 0};
    QUERY_KEY TagString = {NULL, 0};
    QUERY_KEY CdnHost = {NULL, 0};
    QUERY_KEY CdnPath = {NULL, 0};
    char szOneLine1[0x200];
    char szOneLine2[0x200];
    size_t nLength1;
    size_t nLength2;
    int nError = ERROR_BAD_FORMAT;

    // Extract the first line, cotaining the headers
    nLength1 = ListFile_GetNextLine(pvListFile, szOneLine1, _maxchars(szOneLine1));
    if(nLength1 == 0)
        return ERROR_BAD_FORMAT;

    // Now parse the second and the next lines. We are looking for line
    // with "Active" set to 1
    for(;;)
    {
        const char * szLinePtr1 = szOneLine1;
        const char * szLineEnd1 = szOneLine1 + nLength1;
        const char * szLinePtr2 = szOneLine2;
        const char * szLineEnd2;

        // Read the next line
        nLength2 = ListFile_GetNextLine(pvListFile, szOneLine2, _maxchars(szOneLine2));
        if(nLength2 == 0)
            break;
        szLineEnd2 = szLinePtr2 + nLength2;

        // Parse all variables
        while(szLinePtr1 < szLineEnd1)
        {
            // Check for variables we need
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC"))
                LoadInfoVariable(&Active, szLinePtr2, szLineEnd2, false);
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX"))
                LoadInfoVariable(&hs->CdnBuildKey, szLinePtr2, szLineEnd2, true);
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX"))
                LoadInfoVariable(&hs->CdnConfigKey, szLinePtr2, szLineEnd2, true);
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING"))
                LoadInfoVariable(&CdnHost, szLinePtr2, szLineEnd2, false);
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING"))
                LoadInfoVariable(&CdnPath, szLinePtr2, szLineEnd2, false);
            if(IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING"))
                LoadInfoVariable(&TagString, szLinePtr2, szLineEnd2, false);

            // Move both line pointers
            szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1);
            if(szLinePtr1 == NULL)
                break;

            szLinePtr2 = SkipInfoVariable(szLinePtr2, szLineEnd2);
            if(szLinePtr2 == NULL)
                break;
        }

        // Stop parsing if found active config
        if(Active.pbData != NULL && *Active.pbData == '1')
            break;

        // Free the blobs
        FreeCascBlob(&CdnHost);
        FreeCascBlob(&CdnPath);
        FreeCascBlob(&TagString);
    }

    // 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;
        }
    }

    // If we found tags, we can extract language build from it
    if(TagString.pbData != NULL)
        GetDefaultLocaleMask(hs, &TagString);

    FreeCascBlob(&CdnHost);
    FreeCascBlob(&CdnPath);
    FreeCascBlob(&TagString);
    FreeCascBlob(&Active);
    return nError;
}
Example #19
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);
}
Example #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;
}
Example #21
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);
}
Example #22
0
static int ParseInfoFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
{
    QUERY_KEY Active = {NULL, 0};
    QUERY_KEY TagString = {NULL, 0};
    QUERY_KEY CdnHost = {NULL, 0};
    QUERY_KEY CdnPath = {NULL, 0};
    const char * szLineBegin1 = NULL;
    const char * szLinePtr1 = 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++;
    }

    while (szFilePtr < szFileEnd)
    {
        // Rewind header line pointers
        szLinePtr1 = szLineBegin1;

        // Skip the newline character(s)
        while (szFilePtr < szFileEnd && (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A))
            szFilePtr++;

        // Find the next 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 (szLinePtr1 < szLineEnd1)
        {
            // Check for variables we need
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC"))
                LoadInfoVariable(&Active, szLineBegin2, szLineEnd2, false);
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX"))
                LoadInfoVariable(&hs->CdnBuildKey, szLineBegin2, szLineEnd2, true);
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX"))
                LoadInfoVariable(&hs->CdnConfigKey, szLineBegin2, szLineEnd2, true);
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING"))
                LoadInfoVariable(&CdnHost, szLineBegin2, szLineEnd2, false);
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING"))
                LoadInfoVariable(&CdnPath, szLineBegin2, szLineEnd2, false);
            if (IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING"))
                LoadInfoVariable(&TagString, szLineBegin2, szLineEnd2, false);

            // Move both line pointers
            szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1);
            if (szLineBegin1 == NULL)
                break;

            szLineBegin2 = SkipInfoVariable(szLineBegin2, szLineEnd2);
            if (szLineBegin2 == NULL)
                break;
        }

        // Stop parsing if found active config
        if(Active.pbData != NULL && *Active.pbData == '1')
            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;
        }
    }

    // If we found tags, we can extract language build from it
    if(TagString.pbData != NULL)
        GetDefaultLocaleMask(hs, &TagString);

    FreeCascBlob(&CdnHost);
    FreeCascBlob(&CdnPath);
    FreeCascBlob(&TagString);
    return nError;
}
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;
}