aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascRootFile_Diablo3.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-06-06 16:48:21 +0200
committerShauren <shauren.trinity@gmail.com>2019-06-08 17:09:24 +0200
commitfc330fd8ff0115804d9c4b53a1f810c00dd63de9 (patch)
treecfa10998fed66779834bf0b7a9b8b799d33d91d4 /dep/CascLib/src/CascRootFile_Diablo3.cpp
parent82c7b6c5688495d90c4ee5995a4ff74039348296 (diff)
Dep/CascLib: Update to ladislav-zezula/CascLib@a1197edf0b3bd4d52c3f39be7fa7b44bb0b98012
Diffstat (limited to 'dep/CascLib/src/CascRootFile_Diablo3.cpp')
-rw-r--r--dep/CascLib/src/CascRootFile_Diablo3.cpp1552
1 files changed, 621 insertions, 931 deletions
diff --git a/dep/CascLib/src/CascRootFile_Diablo3.cpp b/dep/CascLib/src/CascRootFile_Diablo3.cpp
index f695ad978b1..bbe369c7646 100644
--- a/dep/CascLib/src/CascRootFile_Diablo3.cpp
+++ b/dep/CascLib/src/CascRootFile_Diablo3.cpp
@@ -20,61 +20,36 @@
#define DIABLO3_SUBDIR_SIGNATURE 0xEAF1FE87
#define DIABLO3_PACKAGES_SIGNATURE 0xAABB0002
#define DIABLO3_MAX_SUBDIRS 0x20
-
-#define DIABLO3_INVALID_INDEX 0xFFFFFFFF
-#define DIABLO3_INVALID_FILE 0xFFFFFFFF
#define DIABLO3_MAX_ASSETS 70 // Maximum possible number of assets
-#define DIABLO3_MAX_LEVEL0_LENGTH 0x10 // Maximum length of the level-0 directory name
-
-#define INVALID_FILE_INDEX 0xFFFFFFFF
-#define INVALID_ASSET_INDEX 0xFF
-
-#define ENTRY_FLAG_DIRECTORY_ENTRY 0x80 // The file is actually a directory entry
-#define ENTRY_FLAG_PLAIN_NAME 0x01 // If set, the file entry contains offset of the plain file name
-#define ENTRY_FLAG_FULL_NAME 0x02 // If set, the file entry contains offset of the full name
-#define ENTRY_FLAG_FLAGS_MASK 0xF0 // Mask for the entry flags
-#define ENTRY_FLAG_NAME_MASK 0x0F // Mask for the entry file name type
-
-// Values for CASC_FILE_ENTRY::dwFlags
-#define CASC_ENTRY_SHORT_NAME 0x000000001 // If set, the name is in format XXYYplain-name[\sub-index].ext
-#define CASC_ENTRY_HAS_SUBINDEX 0x000000002 // If set, the subitem is present in the file name (i.e. XXYYplain-name\sub-index.ext)
-
-#define SEARCH_PHASE_NAMES 0 // Searching named entry
-#define SEARCH_PHASE_FILE_IDS 1 // Searching filed by ID
-
-// Macro for constructing 64-bit integer from root-index, file-index and sub-index
-// The result value is RRAAAAAAAASSSSSS
-#define MAKE_INDEX64(ri, fi, si) (((ULONGLONG)ri << 0x38) | ((ULONGLONG)fi << 0x18) | ((ULONGLONG)si))
-#define INDEX64_ROOT_INDEX(hash) (DWORD)((hash >> 0x38) & 0x000000FF)
-#define INDEX64_FILE_INDEX(hash) (DWORD)((hash >> 0x18) & 0xFFFFFFFF)
-#define INDEX64_SUB_INDEX(hash) (DWORD)((hash >> 0x00) & 0x00FFFFFF)
+#define DIABLO3_MAX_ROOT_FOLDERS 0x20 // Maximum count of root directory named entries
// On-disk structure for a file given by file number
-typedef struct _DIABLO3_FILEID1_ENTRY
+typedef struct _DIABLO3_ASSET_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- DWORD FileIndex; // File index
-} DIABLO3_FILEID1_ENTRY, *PDIABLO3_FILEID1_ENTRY;
+ CONTENT_KEY CKey; // Content key for the file
+ DWORD FileIndex; // File index
+} DIABLO3_ASSET_ENTRY, *PDIABLO3_ASSET_ENTRY;
// On-disk structure for a file given by file number and suffix
-typedef struct _DIABLO3_FILEID2_ENTRY
+typedef struct _DIABLO3_ASSETIDX_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- DWORD FileIndex; // File index
- DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp"
-} DIABLO3_FILEID2_ENTRY, *PDIABLO3_FILEID2_ENTRY;
+ CONTENT_KEY CKey; // Content key for the file
+ DWORD FileIndex; // File index
+ DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp"
+} DIABLO3_ASSETIDX_ENTRY, *PDIABLO3_ASSETIDX_ENTRY;
-// On-disk structure of the named entry
+// In-memory structure of the named entry
typedef struct _DIABLO3_NAMED_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- BYTE szFileName[1]; // ASCIIZ file name (variable length)
+ PCONTENT_KEY pCKey; // Pointer to the content key
+ const char * szFileName; // Pointer to the zero-terminated file name
+ const char * szFileEnd; // Position of the zero terminator (aka end of the file name)
} DIABLO3_NAMED_ENTRY, *PDIABLO3_NAMED_ENTRY;
// On-disk structure of CoreToc.dat header
typedef struct _DIABLO3_CORE_TOC_HEADER
{
- DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset (level-1 directory)
+ DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset
DWORD EntryOffsets[DIABLO3_MAX_ASSETS]; // Array of offsets of each DIABLO3_CORE_TOC_ENTRY, relative to data after header
DWORD Unknowns[DIABLO3_MAX_ASSETS]; // Unknown
DWORD Alignment;
@@ -89,33 +64,19 @@ typedef struct _DIABLO3_CORE_TOC_ENTRY
} DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY;
-// In-memory structure of parsed directory header
-typedef struct _DIABLO3_DIR_HEADER
-{
- LPBYTE pbEntries1;
- LPBYTE pbEntries2;
- LPBYTE pbEntries3;
- DWORD dwEntries1;
- DWORD dwEntries2;
- DWORD dwEntries3;
-} DIABLO3_DIR_HEADER, *PDIABLO3_DIR_HEADER;
-
-// In-memory structure of loaded CoreTOC.dat
-typedef struct _DIABLO3_CORE_TOC
+// In-memory structure of parsed directory data
+typedef struct _DIABLO3_DIRECTORY
{
- DIABLO3_CORE_TOC_HEADER Hdr; // Header of CoreTOC.dat
-
- LPBYTE pbCoreToc; // Content of the CoreTOC.dat file
- DIABLO3_CORE_TOC_ENTRY Entries[1]; // Buffer for storing the entries (variable length)
-
-} DIABLO3_CORE_TOC, *PDIABLO3_CORE_TOC;
-
-// On-disk structure of Packages.dat header
-typedef struct _DIABLO3_PACKAGES_DAT_HEADER
-{
- DWORD Signature;
- DWORD NumberOfNames;
-} DIABLO3_PACKAGES_DAT_HEADER, *PDIABLO3_PACKAGES_DAT_HEADER;
+ LPBYTE pbDirectoryData; // The begin of the directory data block
+ LPBYTE pbDirectoryEnd; // The end of the directory data block
+ LPBYTE pbAssetEntries; // Pointer to asset entries without subitem number. Example: "SoundBank\SoundFile.smp"
+ LPBYTE pbAssetIdxEntries; // Pointer to asset entries with subitem number
+ LPBYTE pbNamedEntries; // Pointer to named entries. These are for files with arbitrary names, and they do not belong to an asset
+ DWORD dwAssetEntries; // Number of asset entries without subitem number
+ DWORD dwAssetIdxEntries;
+ DWORD dwNamedEntries;
+ DWORD dwNodeIndex; // Index of file node for this folder
+} DIABLO3_DIRECTORY, *PDIABLO3_DIRECTORY;
// Structure for conversion DirectoryID -> Directory name
typedef struct _DIABLO3_ASSET_INFO
@@ -126,35 +87,6 @@ typedef struct _DIABLO3_ASSET_INFO
} DIABLO3_ASSET_INFO;
typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO;
-// In-memory structure of a file entry in the linear file list
-typedef struct _CASC_FILE_ENTRY
-{
- ENCODING_KEY EncodingKey; // Encoding key
- ULONGLONG FileNameHash; // Hash of the full file name
- DWORD dwFileName; // Offset of the name (in name's dynamic array)
- DWORD dwFlags; // Entry flags (see CASC_ENTRY_XXXX)
-
- DWORD NameOffset; // Offset of the name (in name's dynamic array)
- USHORT SubIndex; // File\SubFile index
- BYTE AssetIndex; // Asset index (aka directory index)
- BYTE EntryFlags; // Entry flags
-} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
-
-//-----------------------------------------------------------------------------
-// Structure definitions for Diablo3 root file
-
-struct TRootHandler_Diablo3 : public TRootHandler
-{
- // Linear global list of all files
- DYNAMIC_ARRAY FileTable;
-
- // Linear global list of names
- DYNAMIC_ARRAY FileNames;
-
- // Global map of FileName -> FileEntry
- PCASC_MAP pRootMap;
-};
-
//-----------------------------------------------------------------------------
// Local variables
@@ -231,969 +163,727 @@ static const DIABLO3_ASSET_INFO Assets[] =
{"Accolade", "aco"}, // 0x42
};
-static const DIABLO3_ASSET_INFO UnknownAsset = {"Unknown", "unk"};
-
#define DIABLO3_ASSET_COUNT (sizeof(Assets) / sizeof(Assets[0]))
//-----------------------------------------------------------------------------
-// Local functions
-
-static PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
-{
- if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
- return &Assets[dwAssetIndex];
- return &UnknownAsset;
-}
-
-static DWORD VerifyNamedFileEntry(LPBYTE pbNamedEntry, LPBYTE pbFileEnd)
-{
- LPBYTE pbFileName = ((PDIABLO3_NAMED_ENTRY)pbNamedEntry)->szFileName;
-
- // Find the end of the name
- while(pbFileName < pbFileEnd && pbFileName[0] != 0)
- pbFileName++;
+// Handler definitions for Diablo3 root file
- // Did we get past the end of the root file?
- if(pbFileName >= pbFileEnd)
- return 0;
- pbFileName++;
-
- // Return the length of the structure
- return (DWORD)(pbFileName - pbNamedEntry);
-}
-
-static char * FindPackageName(
- PCASC_MAP pPackageMap,
- const char * szAssetName,
- const char * szPlainName)
+struct TDiabloRoot : public TFileTreeRoot
{
- char szFileName[MAX_PATH+1];
- size_t nLength;
+ public:
- // Construct the name without extension and find it in the map
- nLength = sprintf(szFileName, "%s\\%s", szAssetName, szPlainName);
- return (char *)Map_FindString(pPackageMap, szFileName, szFileName + nLength);
-}
+ TDiabloRoot() : TFileTreeRoot(0)
+ {
+ memset(RootFolders, 0, sizeof(RootFolders));
+ pFileIndices = NULL;
+ pbCoreTocFile = NULL;
+ pbCoreTocData = NULL;
+ nFileIndices = 0;
+ cbCoreTocFile = 0;
+
+ // Map for searching a real file extension
+ memset(&PackagesMap, 0, sizeof(CASC_MAP));
+ pbPackagesDat = NULL;
+ cbPackagesDat = 0;
+
+ // We have file names and return CKey as result of search
+ dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
+ }
-static size_t CreateShortName(
- PCASC_MAP pPackageMap,
- DWORD dwRootIndex, // Level-0-dir: Index of the root subdirectory
- DWORD dwAssetIndex, // Level-1-dir: Index of the asset name
- const char * szPlainName, // Plain name of the file, without extension
- DWORD dwSubIndex,
- char * szBuffer)
-{
- PDIABLO3_ASSET_INFO pAssetInfo = GetAssetInfo(dwAssetIndex);
- const char * szPackageName = NULL;
- const char * szFormat;
- size_t nLength;
-
- // Write the level-0 directory index as 2-digit hexa number
- assert(dwRootIndex < 0x100);
- *szBuffer++ = IntToHexChar[dwRootIndex >> 0x04];
- *szBuffer++ = IntToHexChar[dwRootIndex & 0x0F];
-
- // Write the level-1 directory index as 2-digit hexa number
- assert(dwAssetIndex < 0x100);
- *szBuffer++ = IntToHexChar[dwAssetIndex >> 0x04];
- *szBuffer++ = IntToHexChar[dwAssetIndex & 0x0F];
-
- // Construct the file name with ending "." for extension
- szFormat = (dwSubIndex != DIABLO3_INVALID_INDEX) ? "%s\\%04u." : "%s.";
- nLength = sprintf(szBuffer, szFormat, szPlainName, dwSubIndex);
-
- // Try to fixup the file extension from the package name.
- // File extensions are not predictable because for subitems,
- // they are not always equal to the main items:
- //
- // SoundBank\3D Ambience.sbk
- // SoundBank\3D Ambience\0000.smp
- // SoundBank\3D Ambience\0002.smp
- // ...
- // SoundBank\Angel.sbk
- // SoundBank\Angel\0000.fsb
- // SoundBank\Angel\0002.fsb
- //
- // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
- //
- if(pPackageMap != NULL)
+ ~TDiabloRoot()
{
- // Retrieve the asset name
- szPackageName = FindPackageName(pPackageMap, pAssetInfo->szDirectoryName, szBuffer);
- if(szPackageName != NULL)
- {
- strcpy(szBuffer, szPackageName + strlen(pAssetInfo->szDirectoryName) + 1);
- nLength = strlen(szBuffer);
- }
+ FreeLoadingStuff();
}
- // If we havent't found the package, we either use the default asset extension or "unk"
- if(szPackageName == NULL)
+ void AppendBackslashToTotalPath(PATH_BUFFER & PathBuffer)
{
- if(dwSubIndex == DIABLO3_INVALID_INDEX)
+ if(PathBuffer.szPtr < PathBuffer.szEnd)
{
- strcpy(szBuffer + nLength, pAssetInfo->szExtension);
- nLength += strlen(pAssetInfo->szExtension);
- }
- else
- {
- strcpy(szBuffer + nLength, "unk");
- nLength += 3;
+ PathBuffer.szPtr[0] = '\\';
+ PathBuffer.szPtr++;
}
}
- // Return the length of the short file name
- return nLength + 4;
-}
-
-static size_t CreateFileName(
- TRootHandler_Diablo3 * pRootHandler,
- const char * szShortName, // Short file name of the file
- char * szBuffer)
-{
- PCASC_FILE_ENTRY pRootEntry;
- const char * szNameLevel0;
- const char * szNameLevel1 = NULL;
- DWORD dwRootIndex0 = 0;
- DWORD dwAssetIndex = 0;
-
- // Retrieve the level-0 and level-1 directory indexes
- ConvertStringToInt08(szShortName+0, &dwRootIndex0);
- ConvertStringToInt08(szShortName+2, &dwAssetIndex);
-
- // Retrieve the name of the level-0 directory (aka root subdirectory)
- pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootIndex0);
- szNameLevel0 = (char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName);
-
- // Retrieve the name of the level-1 directory (aka asset name)
- if(dwAssetIndex < DIABLO3_ASSET_COUNT)
- szNameLevel1 = Assets[dwAssetIndex].szDirectoryName;
- if(szNameLevel1 == NULL)
- szNameLevel1 = UnknownAsset.szDirectoryName;
-
- // Copy the rest of the name as-is
- return sprintf(szBuffer, "%s\\%s\\%s", szNameLevel0, szNameLevel1, szShortName + 4);
-}
-
-
-// Creates a map of String -> Pointer
-static PCASC_MAP CreatePackageMap(
- LPBYTE pbPackagesDat,
- LPBYTE pbPackagesEnd)
-{
- PDIABLO3_PACKAGES_DAT_HEADER pDatHeader = (PDIABLO3_PACKAGES_DAT_HEADER)pbPackagesDat;
- PCASC_MAP pPackageMap;
-
- // Get the header
- if((pbPackagesDat + sizeof(DIABLO3_PACKAGES_DAT_HEADER)) >= pbPackagesEnd)
- return NULL;
- pbPackagesDat += sizeof(DIABLO3_PACKAGES_DAT_HEADER);
-
- // Check the signature and name count
- if(pDatHeader->Signature != DIABLO3_PACKAGES_SIGNATURE)
- return NULL;
-
- // Create the map for fast search of the file name
- pPackageMap = Map_Create(pDatHeader->NumberOfNames, KEY_LENGTH_STRING, 0);
- if(pPackageMap != NULL)
+ void AppendPathToTotalPath(PATH_BUFFER & PathBuffer, const char * szFileName, const char * szFileEnd)
{
- char * szFileName = (char *)pbPackagesDat;
+ char * szPathPtr = PathBuffer.szPtr;
+ size_t nLength = (szFileEnd - szFileName);
- // Go as long as there is something
- for(DWORD i = 0; i < pDatHeader->NumberOfNames; i++)
+ // Append the name
+ if((szPathPtr + nLength) < PathBuffer.szEnd)
{
- // Get the file extension
- if((LPBYTE)szFileName >= pbPackagesEnd)
- break;
-
- // Insert the file name to the map. The file extension is not included
- Map_InsertString(pPackageMap, szFileName, true);
- szFileName = szFileName + strlen(szFileName) + 1;
+ memcpy(szPathPtr, szFileName, nLength);
+ szPathPtr += nLength;
}
- }
-
- return pPackageMap;
-}
-
-// Insert an entry with file name as-is
-static int InsertFileEntry(
- TRootHandler_Diablo3 * pRootHandler,
- ENCODING_KEY & EncodingKey,
- const char * szFileName,
- size_t cchFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
- // We must not allow the file name array to be reallocated.
- // Reallocating the array would cause pointers in TRootHandler_Diablo3::pRootMap
- // become invalid
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ // Append zero terminator
+ if(szPathPtr < PathBuffer.szEnd)
+ szPathPtr[0] = 0;
+ PathBuffer.szPtr = szPathPtr;
}
- // Insert the plain name to the root handler's global name list
- szFileName = (const char *)Array_Insert(&pRootHandler->FileNames, szFileName, cchFileName);
- if(szFileName == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Make sure that we don't exceed the file limit at this phase
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Store the info into the file entry
- pFileEntry->EncodingKey = EncodingKey;
- pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szFileName);
- pFileEntry->dwFlags = 0;
-
- // Verify collisions (debug version only)
- assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
-
- // Calculate the file name hash
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
-
- // Success
- return ERROR_SUCCESS;
-}
-
-static int ParseDirEntries_FileId1(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- PDIABLO3_FILEID1_ENTRY pEntry = (PDIABLO3_FILEID1_ENTRY)pbFileEntries;
- PCASC_FILE_ENTRY pFileEntry;
-
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
+ PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
{
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
+ return &Assets[dwAssetIndex];
+ return NULL;
}
- // Parse the all ID1 entries in the file
- for(DWORD i = 0; i < dwFileEntries; i++, pEntry++)
+ char * FindPackageName(const char * szAssetName, const char * szPlainName)
{
- // Insert the file entry to the global list
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Fill the index entry
- pFileEntry->EncodingKey = pEntry->EncodingKey;
- pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, 0);
- pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME;
- }
-
- return ERROR_SUCCESS;
-}
+ char szFileName[MAX_PATH+1];
+ size_t nLength;
-static int ParseDirEntries_FileId2(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- PDIABLO3_FILEID2_ENTRY pEntry = (PDIABLO3_FILEID2_ENTRY)pbFileEntries;
- PCASC_FILE_ENTRY pFileEntry;
-
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ // Construct the name without extension and find it in the map
+ nLength = CascStrPrintf(szFileName, _countof(szFileName), "%s\\%s", szAssetName, szPlainName);
+ return (char *)PackagesMap.FindString(szFileName, szFileName + nLength);
}
- // Parse the all ID1 entries in the file
- for(DWORD i = 0; i < dwFileEntries; i++, pEntry++)
+ LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData)
{
- // Insert the file entry to the global list
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Fill the index entry
- pFileEntry->EncodingKey = pEntry->EncodingKey;
- pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, pEntry->SubIndex);
- pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME | CASC_ENTRY_HAS_SUBINDEX;
- }
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ LPBYTE pbFileData = NULL;
- return ERROR_SUCCESS;
-}
-
-static int ParseDirEntries_Named(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- LPBYTE pbFileEnd,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- char szFileName[MAX_PATH+1];
- char * szNamePtr = szFileName;
- DWORD cbFileEntry;
- int nError = ERROR_SUCCESS;
+ // Try to find CKey for the file
+ pCKeyEntry = GetFile(hs, szFileName);
+ if(pCKeyEntry != NULL)
+ pbFileData = LoadInternalFileToMemory(hs, pCKeyEntry, pcbFileData);
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ return pbFileData;
}
- // If we the file is not in the root directory itself,
- // prepare the prefix for the root directory.
- if(dwRootDirIndex != DIABLO3_INVALID_INDEX)
+ static LPBYTE CaptureDirectoryData(
+ DIABLO3_DIRECTORY & DirHeader,
+ LPBYTE pbDirectory,
+ DWORD cbDirectory)
{
- PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootDirIndex);
- const char * szRootName = (const char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName);
+ LPBYTE pbDirectoryData = pbDirectory;
+ LPBYTE pbDataEnd = pbDirectory + cbDirectory;
+ DWORD Signature = 0;
+
+ //
+ // Structure of a Diablo3 directory header
+ // 1) Signature (4 bytes)
+ // 2) Number of DIABLO3_ASSET_ENTRY entries (4 bytes)
+ // 3) Array of DIABLO3_ASSET_ENTRY entries
+ // 4) Number of DIABLO3_ASSETIDX_ENTRY entries (4 bytes)
+ // 5) Array of DIABLO3_ASSETIDX_ENTRY entries
+ // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes)
+ // 7) Array of DIABLO3_NAMED_ENTRY entries
+ //
+
+ // Prepare the header signature
+ memset(&DirHeader, 0, sizeof(DIABLO3_DIRECTORY));
+
+ // Get the header signature
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &Signature);
+ if((pbDirectory == NULL) || (Signature != CASC_DIABLO3_ROOT_SIGNATURE && Signature != DIABLO3_SUBDIR_SIGNATURE))
+ return NULL;
+
+ // Subdirectories have extra two arrays
+ if(Signature == DIABLO3_SUBDIR_SIGNATURE)
+ {
+ // Capture the number of DIABLO3_ASSET_ENTRY items
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the array of DIABLO3_ASSET_ENTRY
+ pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetEntries, DIABLO3_ASSET_ENTRY, DirHeader.dwAssetEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the number of DIABLO3_ASSETIDX_ENTRY items
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetIdxEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the array of DIABLO3_ASSETIDX_ENTRY
+ pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetIdxEntries, DIABLO3_ASSETIDX_ENTRY, DirHeader.dwAssetIdxEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+ }
- // Copy the root directory name
- while(szRootName[0] != 0)
- *szNamePtr++ = *szRootName++;
+ // Capture the number of DIABLO3_NAMED_ENTRY array
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwNamedEntries);
+ if(pbDirectory == NULL)
+ return NULL;
- // Append the backslash
- *szNamePtr++ = '\\';
- }
+ // Note: Do not capture the array here. We will do that later,
+ // when we will be parsing the directory
+ DirHeader.pbNamedEntries = pbDirectory;
- // Parse the file entry
- while(pbFileEntries < pbFileEnd)
- {
- PDIABLO3_NAMED_ENTRY pNamedEntry = (PDIABLO3_NAMED_ENTRY)pbFileEntries;
- DWORD cchFileName;
-
- // Verify the named entry whether it does not go beyond the EOF
- cbFileEntry = VerifyNamedFileEntry(pbFileEntries, pbFileEnd);
- if(cbFileEntry == 0)
- return ERROR_FILE_CORRUPT;
-
- // Append the file name to the prepared file name
- // This way we obtain the full name and the name lookup
- // will be fully operational
- memcpy(szNamePtr, pNamedEntry->szFileName, (cbFileEntry - sizeof(ENCODING_KEY)));
- cchFileName = (DWORD)((szNamePtr - szFileName) + (cbFileEntry - sizeof(ENCODING_KEY)));
-
- // Insert the named entry to the global file table
- nError = InsertFileEntry(pRootHandler,
- pNamedEntry->EncodingKey,
- szFileName,
- cchFileName);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Move the pointer to the next entry
- pbFileEntries += cbFileEntry;
+ // Put the directory range
+ DirHeader.pbDirectoryData = pbDirectoryData;
+ DirHeader.pbDirectoryEnd = pbDirectoryData + cbDirectory;
+ return pbDirectory;
}
- return ERROR_SUCCESS;
-}
-
-static void ResolveFullFileNames(
- TRootHandler_Diablo3 * pRootHandler,
- PDIABLO3_CORE_TOC_ENTRY pCoreTocEntries,
- PCASC_MAP pPackageMap,
- LPBYTE pbCoreTocFile,
- DWORD dwFileIndexes)
-{
- PCASC_FILE_ENTRY pFileEntry;
- char * szPlainName;
- char * szNamePtr;
- size_t nLength;
- DWORD dwRootIndex;
- DWORD dwFileIndex;
- DWORD dwSubIndex;
- char szShortName[MAX_PATH+1];
- char szFullName[MAX_PATH+1];
-
- // Keep compiler happy
- CASCLIB_UNUSED(dwFileIndexes);
-
- // Parse the entire file table
- for(size_t i = 0; i < pRootHandler->FileTable.ItemCount; i++)
+ LPBYTE CaptureCoreTocHeader(
+ PDIABLO3_CORE_TOC_HEADER * PtrHeader,
+ PDWORD PtrMaxIndex,
+ LPBYTE pbDataPtr,
+ LPBYTE pbDataEnd)
{
- // Retrieve the file entry at n-th position
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i);
+ PDIABLO3_CORE_TOC_HEADER pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbDataPtr;
+ DWORD dwMaxFileIndex = 0;
- // Skip the items that already have full name
- if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME)
- {
- // Retrieve the file index of that file
- dwRootIndex = INDEX64_ROOT_INDEX(pFileEntry->FileNameHash);
- dwFileIndex = INDEX64_FILE_INDEX(pFileEntry->FileNameHash);
- dwSubIndex = (pFileEntry->dwFlags & CASC_ENTRY_HAS_SUBINDEX) ? INDEX64_SUB_INDEX(pFileEntry->FileNameHash) : DIABLO3_INVALID_INDEX;
- assert(dwFileIndex < dwFileIndexes);
-
- // Get the plain name of the file
- szPlainName = (char *)(pbCoreTocFile + pCoreTocEntries[dwFileIndex].NameOffset);
-
- // Create the short file name
- nLength = CreateShortName(pPackageMap,
- dwRootIndex,
- pCoreTocEntries[dwFileIndex].AssetIndex,
- szPlainName,
- dwSubIndex,
- szShortName);
-
- // Insert the short name to the list of the names
- szNamePtr = (char *)Array_Insert(&pRootHandler->FileNames, szShortName, nLength + 1);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szNamePtr);
-
- // Create the full file name
- nLength = CreateFileName(pRootHandler, szShortName, szFullName);
- pFileEntry->FileNameHash = CalcFileNameHash(szFullName);
-
- // Insert the entry to the name map. Use the mapping of FullName -> FileHash
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
- }
- }
-}
+ // Check the space for header
+ if((pbDataPtr + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbDataEnd)
+ return NULL;
+ pbDataPtr += sizeof(DIABLO3_CORE_TOC_HEADER);
-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)
+ // Verify all asset arrays
+ for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++)
{
- pbFileData = CASC_ALLOC(BYTE, cbFileData);
- if(pbFileData != NULL)
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbDataPtr + pTocHeader->EntryOffsets[i]);
+ DWORD EntryOffset = pTocHeader->EntryOffsets[i];
+ DWORD EntryCount = pTocHeader->EntryCounts[i];
+
+ // Verify file range
+ if((pbDataPtr + EntryOffset + EntryCount * sizeof(DIABLO3_CORE_TOC_ENTRY)) > pbDataEnd)
+ return NULL;
+
+ // Find out the entry with the maximum index
+ for(DWORD n = 0; n < EntryCount; n++)
{
- CascReadFile(hFile, pbFileData, cbFileData, &cbBytesRead);
+ if(pTocEntry->FileIndex >= dwMaxFileIndex)
+ dwMaxFileIndex = pTocEntry->FileIndex;
+ pTocEntry++;
}
}
- // Close the file
- CascCloseFile(hFile);
+ // Give data and return
+ PtrMaxIndex[0] = dwMaxFileIndex;
+ PtrHeader[0] = pTocHeader;
+ return pbDataPtr;
}
- // Give the file to the caller
- if(pcbFileData != NULL)
- pcbFileData[0] = cbBytesRead;
- return pbFileData;
-}
-
-static LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData)
-{
- LPBYTE pbEncodingKey = NULL;
- LPBYTE pbFileData = NULL;
-
- // Try to find encoding key for the file
- pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName);
- if(pbEncodingKey != NULL)
- pbFileData = LoadFileToMemory(hs, pbEncodingKey, pcbFileData);
-
- return pbFileData;
-}
-
-static int ParseDirectoryHeader(
- PDIABLO3_DIR_HEADER pDirHeader,
- LPBYTE pbDirFile,
- LPBYTE pbFileEnd)
-{
- DWORD dwSignature = 0;
-
- //
- // Structure of a Diablo3 directory file
- // 1) Signature (4 bytes)
- // 2) Number of DIABLO3_FILEID1_ENTRY entries (4 bytes)
- // 3) Array of DIABLO3_FILEID1_ENTRY entries
- // 4) Number of DIABLO3_FILEID2_ENTRY entries (4 bytes)
- // 5) Array of DIABLO3_FILEID2_ENTRY entries
- // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes)
- // 7) Array of DIABLO3_NAMED_ENTRY entries
- //
-
- // Prepare the header signature
- memset(pDirHeader, 0, sizeof(DIABLO3_DIR_HEADER));
-
- // Get the signature
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- dwSignature = *(PDWORD)pbDirFile;
-
- // Check the signature
- if(dwSignature != CASC_DIABLO3_ROOT_SIGNATURE && dwSignature != DIABLO3_SUBDIR_SIGNATURE)
- return ERROR_BAD_FORMAT;
- pbDirFile += sizeof(DWORD);
-
- // Subdirectories have extra two arrays
- if(dwSignature == DIABLO3_SUBDIR_SIGNATURE)
+ LPBYTE CaptureNamedEntry(
+ LPBYTE pbDataPtr,
+ LPBYTE pbDataEnd,
+ PDIABLO3_NAMED_ENTRY pEntry)
{
- // Get the number of DIABLO3_FILEID1_ENTRY items
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries1 = *(PDWORD)pbDirFile;
-
- // Get the array of DIABLO3_FILEID1_ENTRY
- pDirHeader->pbEntries1 = (pbDirFile + sizeof(DWORD));
- pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries1 * sizeof(DIABLO3_FILEID1_ENTRY);
-
- // Get the number of DIABLO3_FILEID2_ENTRY items
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries2 = *(PDWORD)pbDirFile;
-
- // Get the array of DIABLO3_FILEID2_ENTRY
- pDirHeader->pbEntries2 = (pbDirFile + sizeof(DWORD));
- pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries2 * sizeof(DIABLO3_FILEID2_ENTRY);
- }
+ // Capture the content key
+ pbDataPtr = CaptureContentKey(pbDataPtr, pbDataEnd, &pEntry->pCKey);
+ if(pbDataPtr == NULL)
+ return NULL;
+
+ // Capture file name. Must be ASCIIZ file name
+ pEntry->szFileName = (const char *)pbDataPtr;
+ while(pbDataPtr < pbDataEnd && pbDataPtr[0] != 0)
+ pbDataPtr++;
+
+ // Did we find a zero char?
+ if(pbDataPtr < pbDataEnd && pbDataPtr[0] == 0)
+ {
+ pEntry->szFileEnd = (const char *)pbDataPtr;
+ return pbDataPtr + 1;
+ }
- // Get the pointer and length DIABLO3_NAMED_ENTRY array
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries3 = *(PDWORD)pbDirFile;
- pDirHeader->pbEntries3 = (pbDirFile + sizeof(DWORD));
- return ERROR_SUCCESS;
-}
+ return NULL;
+ }
-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++)
+ int LoadDirectoryFile(TCascStorage * hs, DIABLO3_DIRECTORY & DirHeader, PCASC_CKEY_ENTRY pCKeyEntry)
{
- // 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)
+ LPBYTE pbData;
+ DWORD cbData = 0;
+
+ // Load the n-th folder
+ pbData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbData);
+ if(pbData && cbData)
{
- // Count the files in the subdirectory
- if(ParseDirectoryHeader(&DirHeader, pbSubDir, pbSubDir + cbSubDir) == ERROR_SUCCESS)
+ if(CaptureDirectoryData(DirHeader, pbData, cbData) == NULL)
{
- dwTotalFileCount += DirHeader.dwEntries1 + DirHeader.dwEntries2 + DirHeader.dwEntries3;
+ // Clear the directory
+ CASC_FREE(pbData);
+ return ERROR_BAD_FORMAT;
}
-
- // Free the subdirectory
- CASC_FREE(pbSubDir);
}
+ return ERROR_SUCCESS;
}
- // Return the total number of entries
- return dwTotalFileCount;
-}
+ bool CreateAssetFileName(
+ PATH_BUFFER & PathBuffer,
+ DWORD FileIndex,
+ DWORD SubIndex)
+ {
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry;
+ PDIABLO3_ASSET_INFO pAssetInfo;
+ const char * szPackageName = NULL;
+ const char * szPlainName;
+ const char * szFormat;
+ char * szPathEnd = PathBuffer.szEnd;
+ char * szPathPtr = PathBuffer.szPtr;
+ size_t nLength = 0;
+
+ // Find and check the entry
+ pTocEntry = pFileIndices + FileIndex;
+ if(pTocEntry->FileIndex == FileIndex)
+ {
+ // Retrieve the asset information
+ pAssetInfo = GetAssetInfo(pTocEntry->AssetIndex);
+
+ // Either use the asset info for getting the folder name or supply "Asset##"
+ if(pAssetInfo != NULL)
+ {
+ CascStrCopy(szPathPtr, (szPathEnd - szPathPtr), pAssetInfo->szDirectoryName);
+ szPathPtr += strlen(szPathPtr);
+ }
+ else
+ {
+ szPathPtr[0] = 'A';
+ szPathPtr[1] = 's';
+ szPathPtr[2] = 's';
+ szPathPtr[3] = 'e';
+ szPathPtr[4] = 't';
+ szPathPtr[5] = (char)('0' + (pTocEntry->AssetIndex / 10));
+ szPathPtr[6] = (char)('0' + (pTocEntry->AssetIndex % 10));
+ szPathPtr += 7;
+ }
-static int ParseDirectoryFile(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbDirFile,
- LPBYTE pbFileEnd,
- DWORD dwRootDirIndex)
-{
- DIABLO3_DIR_HEADER DirHeader;
- int nError;
+ // Put the backslash
+ if(szPathPtr < PathBuffer.szEnd)
+ *szPathPtr++ = '\\';
+
+ // Construct the file name, up to the extension. Don't include the '.'
+ szPlainName = (const char *)(pbCoreTocData + pTocEntry->NameOffset);
+ szFormat = (SubIndex != CASC_INVALID_INDEX) ? "%s\\%04u" : "%s";
+ nLength = CascStrPrintf(szPathPtr, (szPathEnd - szPathPtr), szFormat, szPlainName, SubIndex);
+
+ // Try to fixup the file extension from the package name.
+ // File extensions are not predictable because for subitems,
+ // they are not always equal to the main items:
+ //
+ // SoundBank\3D Ambience.sbk
+ // SoundBank\3D Ambience\0000.smp
+ // SoundBank\3D Ambience\0002.smp
+ // ...
+ // SoundBank\Angel.sbk
+ // SoundBank\Angel\0000.fsb
+ // SoundBank\Angel\0002.fsb
+ //
+ // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
+ //
+
+ if(PackagesMap.IsInitialized() && pAssetInfo != NULL)
+ {
+ // Retrieve the asset name
+ szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szPathPtr);
+ if(szPackageName != NULL)
+ {
+ CascStrCopy(PathBuffer.szPtr, (PathBuffer.szEnd - PathBuffer.szPtr), szPackageName);
+ return true;
+ }
+ }
- // Sanity checks
- assert(pRootHandler->FileTable.ItemArray != NULL);
- assert(pRootHandler->FileTable.ItemCount < pRootHandler->FileTable.ItemCountMax);
+ // Use the extension from the AssetInfo, if we have any
+ if(pAssetInfo != NULL && pAssetInfo->szExtension != NULL)
+ {
+ szPathPtr[nLength++] = '.';
+ CascStrCopy(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), pAssetInfo->szExtension);
+ return true;
+ }
- // Parse the directory header in order to retrieve the items
- nError = ParseDirectoryHeader(&DirHeader, pbDirFile, pbFileEnd);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Otherwise, supply "a##"
+ CascStrPrintf(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), ".a%02u", pTocEntry->AssetIndex);
+ return true;
+ }
- // Process all DIABLO3_FILEID1_ENTRY entries. These are for files
- // belonging to an asset group, without subitem number.
- // Example: "SoundBank\SoundFile.smp"
- // We skip inserting them to the name map, because the names are not known yet
- if(DirHeader.pbEntries1 && DirHeader.dwEntries1)
- {
- assert(dwRootDirIndex != DIABLO3_INVALID_INDEX);
- nError = ParseDirEntries_FileId1(pRootHandler, DirHeader.pbEntries1, DirHeader.dwEntries1, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
+ return false;
}
- // Parse all DIABLO3_FILEID2_ENTRY entries. These are for files
- // belonging to an asset group, with a subitem number.
- // Example: "SoundBank\SoundFile\0001.smp"
- // We skip inserting them to the name map, because the names are not known yet
- if(DirHeader.pbEntries2 && DirHeader.dwEntries2)
+ // Parse the asset entries
+ int ParseAssetEntries(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer)
{
- assert(dwRootDirIndex != DIABLO3_INVALID_INDEX);
- nError = ParseDirEntries_FileId2(pRootHandler, DirHeader.pbEntries2, DirHeader.dwEntries2, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
- }
+ PDIABLO3_ASSET_ENTRY pEntry = (PDIABLO3_ASSET_ENTRY)Directory.pbAssetEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ DWORD dwEntries = Directory.dwAssetEntries;
+ // Do nothing if there is no entries
+ if(pEntry != NULL && dwEntries != 0)
+ {
+ // Insert all asset entries to the file tree
+ for(DWORD i = 0; i < dwEntries; i++, pEntry++)
+ {
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Construct the full path name of the entry
+ if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, CASC_INVALID_INDEX))
+ {
+ // Insert the entry to the file tree
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+ }
+ }
+ }
- // Parse all named entries. These are for files with arbitrary names,
- // and they do not belong to an asset.
- if(DirHeader.pbEntries3 && DirHeader.dwEntries3)
- {
- nError = ParseDirEntries_Named(pRootHandler, DirHeader.pbEntries3, pbFileEnd, DirHeader.dwEntries3, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
+ return ERROR_SUCCESS;
}
- // Give the directory to the caller
- 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++)
+ int ParseAssetAndIdxEntries(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer)
{
- // Get the first entry
- pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]);
+ PDIABLO3_ASSETIDX_ENTRY pEntry = (PDIABLO3_ASSETIDX_ENTRY)Directory.pbAssetIdxEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ DWORD dwEntries = Directory.dwAssetIdxEntries;
- // Find out the entry with the maximum index
- for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
+ // Do nothing if there is no entries
+ if(pEntry != NULL && dwEntries != 0)
{
- if(pTocEntry->FileIndex >= dwFileIndexes)
- dwFileIndexes = pTocEntry->FileIndex + 1;
- pTocEntry++;
+ // Insert all asset entries to the file tree
+ for(DWORD i = 0; i < dwEntries; i++, pEntry++)
+ {
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Construct the full path name of the entry
+ if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, pEntry->SubIndex))
+ {
+ // Insert the entry to the file tree
+// fprintf(fp, "%08u %04u %s\n", pEntry->FileIndex, pEntry->SubIndex, PathBuffer.szBegin);
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+ }
+ }
}
+
+ return ERROR_SUCCESS;
}
- // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
- pSortedEntries = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwFileIndexes);
- if(pSortedEntries != NULL)
+ // Parse the named entries of all folders
+ int ParseDirectory_Phase1(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer,
+ bool bIsRootDirectory)
{
- // Initialize all entries to invalid
- memset(pSortedEntries, 0xFF, dwFileIndexes * sizeof(DIABLO3_CORE_TOC_ENTRY));
+ DIABLO3_NAMED_ENTRY NamedEntry;
+ char * szSavePtr = PathBuffer.szPtr;
+ size_t nFolderIndex = 0;
+ int nError = ERROR_SUCCESS;
- // Populate the linear array with the entries
- for(i = 0; i < DIABLO3_MAX_ASSETS; i++)
+ // Do nothing if there is no named headers
+ if(Directory.pbNamedEntries && Directory.dwNamedEntries)
{
- // 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++)
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PCASC_FILE_NODE pFileNode;
+ LPBYTE pbDataPtr = Directory.pbNamedEntries;
+ LPBYTE pbDataEnd = Directory.pbDirectoryEnd;
+ DWORD dwNodeIndex;
+
+ // Parse all entries
+ while(pbDataPtr < pbDataEnd)
{
- pSortedEntries[pTocEntry->FileIndex].AssetIndex = pTocEntry->AssetIndex;
- pSortedEntries[pTocEntry->FileIndex].FileIndex = pTocEntry->FileIndex;
- pSortedEntries[pTocEntry->FileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocFile) + pTocEntry->NameOffset;
- pTocEntry++;
+ // Capture the named entry
+ pbDataPtr = CaptureNamedEntry(pbDataPtr, pbDataEnd, &NamedEntry);
+ if(pbDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Append the path fragment to the total path
+ AppendPathToTotalPath(PathBuffer, NamedEntry.szFileName, NamedEntry.szFileEnd);
+
+ // Check whether the file exists in the storage
+ pCKeyEntry = FindCKeyEntry_CKey(hs, NamedEntry.pCKey->Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Create file node belonging to this folder
+ pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ dwNodeIndex = (DWORD)FileTree.IndexOf(pFileNode);
+
+ // If we are parsing root folder, we also need to load the data of the sub-folder file
+ if(bIsRootDirectory)
+ {
+ // Mark the node as directory
+ AppendBackslashToTotalPath(PathBuffer);
+ pCKeyEntry->Flags |= CASC_CE_FOLDER_ENTRY;
+ pFileNode->Flags |= CFN_FLAG_FOLDER;
+
+ // Load the sub-directory file
+ nError = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Parse the sub-directory file
+ nError = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Also save the item pointer and increment the folder index
+ RootFolders[nFolderIndex].dwNodeIndex = dwNodeIndex;
+ nFolderIndex++;
+ }
+
+ // Restore the path pointer
+ PathBuffer.szPtr = szSavePtr;
+ szSavePtr[0] = 0;
+ }
}
}
- // Now use the linear array to resolve the asset indexes and plain names
- ResolveFullFileNames(pRootHandler, pSortedEntries, pPackageMap, pbCoreTocFile, dwFileIndexes);
- CASC_FREE(pSortedEntries);
+ return nError;
}
- return ERROR_SUCCESS;
-}
-
-//-----------------------------------------------------------------------------
-// Implementation of Diablo III root file
-
-static int D3Handler_Insert(TRootHandler_Diablo3 * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey)
-{
- ENCODING_KEY EncodingKey;
- DWORD dwFileIndex;
-
- // Don't let the number of items to overflow
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Insert the item
- EncodingKey = *(PENCODING_KEY)pbEncodingKey;
- dwFileIndex = InsertFileEntry(pRootHandler,
- EncodingKey,
- szFileName,
- strlen(szFileName) + 1);
- return (dwFileIndex != INVALID_FILE_INDEX) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
-}
-
-static LPBYTE D3Handler_Search(TRootHandler_Diablo3 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */, PDWORD /* PtrFileDataId */)
-{
- PCASC_FILE_ENTRY pFileEntry;
- const char * szSrcName = NULL;
-
- // Are we still inside the root directory range?
- while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount)
+ // Parse the nameless entries of all folders
+ int ParseDirectory_Phase2(TCascStorage * hs)
{
- // Get the n-th directory and the file name
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1);
- szSrcName = (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName);
+ PATH_BUFFER PathBuffer;
+ char szPathBuffer[MAX_PATH];
- // This is either a full file name or an abbreviated name
- if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME)
+ // Parse each root subdirectory
+ for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++)
{
- CreateFileName(pRootHandler, szSrcName, pSearch->szFileName);
- }
- else
- {
- strcpy(pSearch->szFileName, szSrcName);
+ // Is this root folder loaded?
+ if(RootFolders[i].pbDirectoryData != NULL)
+ {
+ PathBuffer.szBegin = szPathBuffer;
+ PathBuffer.szPtr = szPathBuffer;
+ PathBuffer.szEnd = szPathBuffer + MAX_PATH - 1;
+ szPathBuffer[0] = 0;
+
+ // Retrieve the parent name
+ if(RootFolders[i].dwNodeIndex != 0)
+ {
+ FileTree.PathAt(szPathBuffer, MAX_PATH, RootFolders[i].dwNodeIndex);
+ PathBuffer.szPtr = PathBuffer.szBegin + strlen(szPathBuffer);
+ }
+
+ // Array of DIABLO3_ASSET_ENTRY entries.
+ // These are for files belonging to an asset, without subitem number.
+ // Example: "SoundBank\SoundFile.smp"
+ ParseAssetEntries(hs, RootFolders[i], PathBuffer);
+
+ // Array of DIABLO3_ASSETIDX_ENTRY entries.
+ // These are for files belonging to an asset, with a subitem number.
+ // Example: "SoundBank\SoundFile\0001.smp"
+ ParseAssetAndIdxEntries(hs, RootFolders[i], PathBuffer);
+ }
}
- // Prepare for the next search
- pSearch->IndexLevel1++;
- return pFileEntry->EncodingKey.Value;
+ return ERROR_SUCCESS;
}
- // No more entries
- return NULL;
-}
+ // Creates an array of DIABLO3_CORE_TOC_ENTRY entries indexed by FileIndex
+ // Used as lookup table when we have FileIndex and need Asset+PlainName
+ int CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
+ {
+ PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL;
+ LPBYTE pbCoreTocPtr = pbCoreTocFile;
+ DWORD dwMaxFileIndex = 0;
+ int nError = ERROR_CAN_NOT_COMPLETE;
-static void D3Handler_EndSearch(TRootHandler_Diablo3 * /* pRootHandler */, TCascSearch * /* pSearch */)
-{
- // Do nothing
-}
+ // Load the entire file to memory
+ pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile);
+ if(pbCoreTocFile && cbCoreTocFile)
+ {
+ LPBYTE pbCoreTocEnd = pbCoreTocFile + cbCoreTocFile;
-static LPBYTE D3Handler_GetKey(TRootHandler_Diablo3 * pRootHandler, const char * szFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
- ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
+ // Capture the header
+ if((pbCoreTocPtr = CaptureCoreTocHeader(&pTocHeader, &dwMaxFileIndex, pbCoreTocPtr, pbCoreTocEnd)) == NULL)
+ return ERROR_BAD_FORMAT;
- // Find the file in the name table
- pFileEntry = (PCASC_FILE_ENTRY)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL);
- return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL;
-}
+ // If there are no indices, return NULL
+ if(dwMaxFileIndex == 0)
+ return ERROR_SUCCESS;
-static DWORD D3Handler_GetFileId(TRootHandler_Diablo3 * /* pRootHandler */, const char * /* szFileName */)
-{
- // Not implemented for D3
- return 0;
-}
+ // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
+ pFileIndices = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwMaxFileIndex + 1);
+ if(pFileIndices != NULL)
+ {
+ // Initialize all entries to invalid
+ memset(pFileIndices, 0xFF, (dwMaxFileIndex + 1) * sizeof(DIABLO3_CORE_TOC_ENTRY));
+
+ // Populate the linear array with the file indices
+ for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++)
+ {
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocPtr + pTocHeader->EntryOffsets[i]);
+ LPBYTE pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]);
+
+ // Setup the entries
+ for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
+ {
+ DWORD dwFileIndex = pTocEntry->FileIndex;
+
+ pFileIndices[dwFileIndex].AssetIndex = pTocEntry->AssetIndex;
+ pFileIndices[dwFileIndex].FileIndex = pTocEntry->FileIndex;
+ pFileIndices[dwFileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocPtr) + pTocEntry->NameOffset;
+ pTocEntry++;
+ }
+ }
+
+ // Save the file to the root handler
+ pbCoreTocData = pbCoreTocPtr;
+ nFileIndices = dwMaxFileIndex;
+ nError = ERROR_SUCCESS;
+ }
+ }
+ return nError;
+ }
-static void D3Handler_Close(TRootHandler_Diablo3 * pRootHandler)
-{
- if(pRootHandler != NULL)
+ // Packages.dat contains a list of full file names (without locale prefix).
+ // They are not sorted, nor they correspond to file IDs.
+ // Does the sort order mean something? Perhaps we could use them as listfile?
+ int CreateMapOfRealNames(TCascStorage * hs, const char * szFileName)
{
- // Free the file map
- Map_Free(pRootHandler->pRootMap);
+ DWORD Signature = 0;
+ DWORD NumberOfNames = 0;
- // Free the array of the file entries and file names
- Array_Free(&pRootHandler->FileTable);
- Array_Free(&pRootHandler->FileNames);
+ // Load the entire file to memory
+ pbPackagesDat = LoadFileToMemory(hs, szFileName, &cbPackagesDat);
+ if(pbPackagesDat && cbPackagesDat)
+ {
+ LPBYTE pbPackagesPtr = pbPackagesDat;
+ LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat;
+/*
+ LPBYTE pbPackagesPtr = pbPackagesDat + 8;
+ FILE * fp = fopen("E:\\Packages.dat", "wt");
+ if(fp != NULL)
+ {
+ while(pbPackagesPtr < pbPackagesEnd)
+ {
+ fprintf(fp, "%s\n", pbPackagesPtr);
+ pbPackagesPtr = pbPackagesPtr + strlen((char *)pbPackagesPtr) + 1;
+ }
+ fclose(fp);
+ }
+*/
+ // Get the header. There is just Signature + NumberOfNames
+ if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL)
+ return ERROR_BAD_FORMAT;
+ if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &NumberOfNames)) == NULL)
+ return ERROR_BAD_FORMAT;
+ if(Signature != DIABLO3_PACKAGES_SIGNATURE || NumberOfNames == 0)
+ return ERROR_BAD_FORMAT;
+
+ // Create the map for fast search of the file name
+ if(PackagesMap.Create(NumberOfNames, 0, 0, KeyIsString) == ERROR_SUCCESS)
+ {
+ const char * szPackageName = (const char *)pbPackagesPtr;
+
+ // Go as long as there is something
+ for(DWORD i = 0; i < NumberOfNames; i++)
+ {
+ // Get the file extension
+ if((LPBYTE)szPackageName >= pbPackagesEnd)
+ break;
+
+ // Insert the file name to the map. The file extension is not included
+ PackagesMap.InsertString(szPackageName, true);
+ szPackageName = szPackageName + strlen(szPackageName) + 1;
+ }
+ }
+ }
- // Free the root file itself
- CASC_FREE(pRootHandler);
+ return ERROR_SUCCESS;
}
-}
-/*
-static void DumpRootFile(TDumpContext * dc, LPBYTE pbFileData, LPBYTE pbFileDataEnd)
-{
- char szMD5Buffer[MD5_STRING_SIZE+1];
- DWORD dwSignature;
- DWORD dwItemCount;
- DWORD i;
-
- dwSignature = *(PDWORD)pbFileData;
- if(dwSignature != CASC_DIABLO3_SUBDIR_SIGNATURE)
- return;
- pbFileData += sizeof(DWORD);
-
- // Dump items that contain EncodingKey + AssetId
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
+ int Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
{
- PCASC_DIABLO3_ASSET_ENTRY pEntry = (PCASC_DIABLO3_ASSET_ENTRY)pbFileData;
+ PATH_BUFFER PathBuffer;
+ char szPathBuffer[MAX_PATH];
+ int nError;
+
+ // Initialize path buffer and go parse the directory
+ PathBuffer.szBegin = szPathBuffer;
+ PathBuffer.szPtr = szPathBuffer;
+ PathBuffer.szEnd = szPathBuffer + MAX_PATH;
+ szPathBuffer[0] = 0;
+
+ // Always parse the named entries first. They always point to a file.
+ // These are entries with arbitrary names, and they do not belong to an asset
+ nError = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
+ if(nError == ERROR_SUCCESS)
+ {
+ // The asset entries in the ROOT file don't contain file names, but indices.
+ // To convert a file index to a file name, we need to load and parse the "Base\\CoreTOC.dat" file.
+ nError = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
+ if(nError == ERROR_SUCCESS)
+ {
+ // The file "Base\Data_D3\PC\Misc\Packages.dat" contains the file names
+ // (without level-0 and level-1 directory).
+ // We can use these names for supplying the missing extensions
+ CreateMapOfRealNames(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat");
+
+ // Now parse all folders and resolve the full names
+ ParseDirectory_Phase2(hs);
+ }
- if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd)
- return;
- pbFileData += sizeof(*pEntry);
+ // Free all stuff that was used during loading of the ROOT file
+ FreeLoadingStuff();
+ }
- dump_print(dc, "%s %08X\n", StringFromMD5(pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId);
+ return nError;
}
- // Terminate with two newlines
- dump_print(dc, "\n");
-
- // Dump items that contain EncodingKey + AssetId + FileNumber
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
+ void FreeLoadingStuff()
{
- PCASC_DIABLO3_ASSET_ENTRY2 pEntry = (PCASC_DIABLO3_ASSET_ENTRY2)pbFileData;
+ // Free the captured root sub-directories
+ for(size_t i = 0; i < DIABLO3_MAX_SUBDIRS; i++)
+ CASC_FREE(RootFolders[i].pbDirectoryData);
- if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd)
- return;
- pbFileData += sizeof(*pEntry);
+ // Free the package map
+ PackagesMap.Free();
- dump_print(dc, "%s %08X %08X\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId, pEntry->FileNumber);
- }
-
- // Terminate with two newlines
- dump_print(dc, "\n");
+ // Free the array of file indices
+ CASC_FREE(pFileIndices);
- // Dump items that contain EncodingKey + FileName
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
- {
- PDIABLO3_NAMED_ENTRY pEntry = (PDIABLO3_NAMED_ENTRY)pbFileData;
- DWORD dwEntrySize = VerifyNamedFileEntry(pbFileData, pbFileDataEnd);
+ // Free the loaded CoreTOC.dat file
+ CASC_FREE(pbCoreTocFile);
- if((pbFileData + dwEntrySize) > pbFileDataEnd)
- return;
- pbFileData += dwEntrySize;
-
- dump_print(dc, "%s %s\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->szFileName);
+ // Free the loaded Packages.dat file
+ CASC_FREE(pbPackagesDat);
}
- dump_print(dc, "\n\n");
-}
-*/
+ // Array of root directory subdirectories
+ DIABLO3_DIRECTORY RootFolders[DIABLO3_MAX_ROOT_FOLDERS];
+
+ // Array of DIABLO3_TOC_ENTRY structures, sorted by the file index
+ // Used for converting FileIndex -> Asset+PlainName during loading
+ PDIABLO3_CORE_TOC_ENTRY pFileIndices;
+ LPBYTE pbCoreTocFile;
+ LPBYTE pbCoreTocData;
+ size_t nFileIndices;
+ DWORD cbCoreTocFile;
+
+ // Map for searching a real file extension
+ CASC_MAP PackagesMap;
+ LPBYTE pbPackagesDat;
+ DWORD cbPackagesDat;
+};
+
//-----------------------------------------------------------------------------
// Public functions
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;
+ TDiabloRoot * pRootHandler = NULL;
+ DIABLO3_DIRECTORY RootDirectory;
+ int nError = ERROR_BAD_FORMAT;
- // 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)
+ // Verify the header of the ROOT file
+ if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL)
{
- 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++)
+ // Allocate the root handler object
+ pRootHandler = new TDiabloRoot();
+ if(pRootHandler != NULL)
{
- 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)
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, RootDirectory);
+ if(nError != ERROR_SUCCESS)
{
- nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFile + cbRootFile, i);
- CASC_FREE(pbRootFile);
+ delete pRootHandler;
+ pRootHandler = NULL;
}
}
}
- // 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);
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
return nError;
}