aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascDumpData.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/CascLib/src/CascDumpData.cpp')
-rw-r--r--dep/CascLib/src/CascDumpData.cpp524
1 files changed, 524 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp
new file mode 100644
index 00000000000..d3871709fce
--- /dev/null
+++ b/dep/CascLib/src/CascDumpData.cpp
@@ -0,0 +1,524 @@
+/*****************************************************************************/
+/* CascDumpData.cpp Copyright (c) Ladislav Zezula 2014 */
+/*---------------------------------------------------------------------------*/
+/* System-dependent directory functions for CascLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 07.05.14 1.00 Lad The first version of CascDumpData.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+#include "CascMndxRoot.h"
+
+#ifdef _DEBUG // The entire file is only valid for debug purposes
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static char * StringFromIndexKey(LPBYTE md5, char * szBuffer)
+{
+ return StringFromBinary(md5, 9, szBuffer);
+}
+
+static char * StringFromMD5(LPBYTE md5, char * szBuffer)
+{
+ return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
+}
+
+static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, const void * pvIndexEntry2)
+{
+ PCASC_INDEX_ENTRY pIndexEntry1 = (PCASC_INDEX_ENTRY)pvIndexEntry1;
+ PCASC_INDEX_ENTRY pIndexEntry2 = (PCASC_INDEX_ENTRY)pvIndexEntry2;
+ ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffset);
+ ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffset);
+ DWORD ArchIndex1 = (DWORD)(FileOffset1 >> 0x1E);
+ DWORD ArchIndex2 = (DWORD)(FileOffset2 >> 0x1E);
+
+ // First, compare the archive index
+ if(ArchIndex1 < ArchIndex2)
+ return -1;
+ if(ArchIndex1 > ArchIndex2)
+ return +1;
+
+ // Second, compare the archive offset
+ FileOffset1 &= 0x3FFFFFFF;
+ FileOffset2 &= 0x3FFFFFFF;
+ if(FileOffset1 < FileOffset2)
+ return -1;
+ if(FileOffset1 > FileOffset2)
+ return +1;
+
+ return 0;
+}
+
+
+static char ** CreateFileNameArray(TCascStorage * hs, const TCHAR * szListFile)
+{
+ PCASC_ROOT_ENTRY pRootEntry;
+ char ** FileNameArray = NULL;
+ void * pvListFile;
+ size_t nRootIndex;
+ char szFileName1[MAX_PATH+1];
+ char szFileName2[MAX_PATH+1];
+
+ // Open the listfile stream and initialize the listfile cache
+ pvListFile = ListFile_OpenExternal(szListFile);
+ if(pvListFile != NULL)
+ {
+ // Allocate the array of file names
+ FileNameArray = CASC_ALLOC(char*, hs->nRootEntries);
+ if(FileNameArray != NULL)
+ {
+ // Zero the name array
+ memset(FileNameArray, 0, hs->nRootEntries * sizeof(char *));
+
+ // Perform search
+ while(ListFile_GetNext(pvListFile, "*", szFileName1, MAX_PATH))
+ {
+ // Create normalized name
+ strcpy(szFileName2, szFileName1);
+ NormalizeFileName_UpperBkSlash(szFileName2);
+
+ // Try to find the root entry
+ pRootEntry = FindFirstRootEntry(hs, szFileName2, &nRootIndex);
+ if(pRootEntry != NULL)
+ {
+ assert(nRootIndex < hs->nRootEntries);
+ if(FileNameArray[nRootIndex] == NULL)
+ FileNameArray[nRootIndex] = NewStr(szFileName1, 0);
+ }
+ }
+ }
+
+ // Close the listfile cache
+ ListFile_Free(pvListFile);
+ }
+
+ return FileNameArray;
+}
+
+static void FreeFileNameArray(TCascStorage * hs, char ** FileNameArray)
+{
+ if(FileNameArray != NULL)
+ {
+ // Free all sub-entries
+ for(size_t i = 0; i < hs->nRootEntries; i++)
+ {
+ if(FileNameArray[i] != NULL)
+ CASC_FREE(FileNameArray[i]);
+ }
+
+ // Free the array itself
+ CASC_FREE(FileNameArray);
+ }
+}
+
+static void DumpIndexKey(
+ FILE * fp,
+ TCascStorage * hs,
+ LPBYTE pbEncodingKey,
+ PCASC_INDEX_ENTRY * ppIndexEntries)
+{
+ PCASC_INDEX_ENTRY pIndexEntry;
+ QUERY_KEY QueryKey;
+ size_t EntryIndex = 0;
+ char szMd5[MD5_STRING_SIZE];
+
+ QueryKey.pbData = pbEncodingKey;
+ QueryKey.cbData = MD5_HASH_SIZE;
+ pIndexEntry = FindIndexEntry(hs, &QueryKey);
+ if(pIndexEntry != NULL)
+ {
+ ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
+ DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
+ DWORD FileSize = *(PDWORD)pIndexEntry->FileSize;
+
+ // Mark the index entry as dumped
+ ppIndexEntries[EntryIndex] = NULL;
+
+ // Mask the file offset
+ FileOffset &= 0x3FFFFFFF;
+ fprintf(fp, " Index: %s, ArchIdx: %02x FileOffset %08x FileSize: %lx\n",
+ StringFromIndexKey(pIndexEntry->IndexKey, szMd5),
+ ArchIndex,
+ (DWORD)FileOffset,
+ FileSize);
+ }
+ else
+ {
+ fprintf(fp, " NO INDEX ENTRY\n");
+ }
+}
+
+static void DumpEncodingEntry(
+ FILE * fp,
+ TCascStorage * hs,
+ PCASC_ENCODING_ENTRY pEncodingEntry,
+ PCASC_INDEX_ENTRY * ppIndexEntries)
+{
+ LPBYTE pbEncodingKey;
+ char szMd5[MD5_STRING_SIZE];
+
+ fprintf(fp, " Encoding Key: %s Key Count: %u Size: %lx\n",
+ StringFromMD5(pEncodingEntry->EncodingKey, szMd5),
+ pEncodingEntry->KeyCount,
+ ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes));
+
+ // If there is a file key
+ if(pEncodingEntry->KeyCount != 0)
+ {
+ // Get the first encoding key
+ pbEncodingKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
+
+ // Dump all encoding keys
+ for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++)
+ {
+ fprintf(fp, " Index Key: %s\n", StringFromMD5(pbEncodingKey, szMd5));
+ DumpIndexKey(fp, hs, pbEncodingKey, ppIndexEntries);
+ pbEncodingKey += MD5_HASH_SIZE;
+ }
+ }
+ else
+ {
+ fprintf(fp, " ZERO FILE KEYS\n");
+ return;
+ }
+}
+
+static void DumpEncodingEntry(
+ FILE * fp,
+ TCascStorage * hs,
+ PCASC_ROOT_ENTRY pRootEntry,
+ PCASC_ENCODING_ENTRY * ppEncodingKeys,
+ PCASC_INDEX_ENTRY * ppIndexEntries)
+{
+ PCASC_ENCODING_ENTRY pEncodingKey;
+ QUERY_KEY QueryKey;
+ size_t EntryIndex = 0;
+
+ // Find the encoding key
+ QueryKey.pbData = pRootEntry->EncodingKey;
+ QueryKey.cbData = MD5_HASH_SIZE;
+ pEncodingKey = FindEncodingEntry(hs, &QueryKey, &EntryIndex);
+ if(pEncodingKey == NULL)
+ {
+ fprintf(fp, " NO ENCODING KEY\n");
+ return;
+ }
+
+ // Get the file key, clear the encoding key
+ ppEncodingKeys[EntryIndex] = NULL;
+ DumpEncodingEntry(fp, hs, pEncodingKey, ppIndexEntries);
+}
+
+static void DumpRootEntries(FILE * fp, TCascStorage * hs, char ** FileNameArray)
+{
+ PCASC_ENCODING_ENTRY * ppEncodingEntries; // Array of encoding entries
+ PCASC_INDEX_ENTRY * ppIndexEntries; // Complete list of key entries for all files
+ const char * szFileName = NULL;
+ ULONGLONG PrevNameHash = (ULONGLONG)-1;
+ char szMd5[MD5_STRING_SIZE];
+
+ // Create copy of the encoding keys and file keys
+ ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, hs->nEncodingEntries);
+ ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, hs->pIndexEntryMap->ItemCount);
+ if(ppEncodingEntries && ppIndexEntries)
+ {
+ // Copy all pointers
+ memcpy(ppEncodingEntries, hs->ppEncodingEntries, hs->nEncodingEntries * sizeof(PCASC_ENCODING_ENTRY));
+ Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
+
+ // Parse all entries
+ for(size_t i = 0; i < hs->nRootEntries; i++)
+ {
+ PCASC_ROOT_ENTRY pRootEntry = hs->ppRootEntries[i];
+ const char * szDuplicate = "";
+
+ // Check duplicates
+ if(pRootEntry->FileNameHash != PrevNameHash)
+ szFileName = FileNameArray[i];
+ else
+ szDuplicate = "(DUPLICATE) ";
+
+ // Dump the root entry
+ fprintf(fp, "NameHash: %016llx Locales: %08lx MD5: %s %sFileName: %s\n",
+ pRootEntry->FileNameHash,
+ pRootEntry->Locales,
+ StringFromMD5(pRootEntry->EncodingKey, szMd5),
+ szDuplicate,
+ szFileName);
+
+ DumpEncodingEntry(fp, hs, pRootEntry, ppEncodingEntries, ppIndexEntries);
+ PrevNameHash = pRootEntry->FileNameHash;
+ fprintf(fp, "\n");
+ }
+
+ // Dump all orphaned encoding keys
+ for(size_t i = 0; i < hs->nEncodingEntries; i++)
+ {
+ if(ppEncodingEntries[i] != NULL)
+ {
+ fprintf(fp, "[NO ROOT KEY]\n");
+
+ DumpEncodingEntry(fp, hs, ppEncodingEntries[i], ppIndexEntries);
+ ppEncodingEntries[i] = NULL;
+
+ fprintf(fp, "\n");
+ }
+ }
+
+
+ CASC_FREE(ppIndexEntries);
+ CASC_FREE(ppEncodingEntries);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+void CascDumpSparseArray(const char * szFileName, void * pvSparseArray)
+{
+ TSparseArray * pSparseArray = (TSparseArray *)pvSparseArray;
+ FILE * fp;
+
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ // Write header
+ fprintf(fp, "## Value\n-- -----\n");
+
+ // Write the values
+ for(DWORD i = 0; i < pSparseArray->TotalItemCount; i++)
+ {
+ DWORD Value = 0;
+
+ if(pSparseArray->IsItemPresent(i))
+ {
+ Value = pSparseArray->GetItemValue(i);
+ fprintf(fp, "%02X %02X\n", i, Value);
+ }
+ else
+ {
+ fprintf(fp, "%02X --\n", i);
+ }
+ }
+
+ fclose(fp);
+ }
+}
+
+void CascDumpNameFragTable(const char * szFileName, void * pMarFile)
+{
+ TFileNameDatabase * pDB = ((PMAR_FILE)pMarFile)->pDatabasePtr->pDB;
+ FILE * fp;
+
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ PNAME_FRAG pNameTable = pDB->NameFragTable.NameFragArray;
+ const char * szNames = pDB->IndexStruct_174.NameFragments.CharArray;
+ const char * szLastEntry;
+ char szMatchType[0x100];
+
+ // Dump the table header
+ fprintf(fp, "Indx ThisHash NextHash FragOffs\n");
+ fprintf(fp, "---- -------- -------- --------\n");
+
+ // Dump all name entries
+ for(DWORD i = 0; i < pDB->NameFragTable.ItemCount; i++)
+ {
+ // Reset both match types
+ szMatchType[0] = 0;
+ szLastEntry = "";
+
+ // Only if the table entry is not empty
+ if(pNameTable->ItemIndex != 0xFFFFFFFF)
+ {
+ // Prepare the entry
+ if(IS_SINGLE_CHAR_MATCH(pDB->NameFragTable, i))
+ sprintf(szMatchType, "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF));
+ else
+ sprintf(szMatchType, "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs);
+ }
+
+ // Dump the entry
+ fprintf(fp, "0x%02X %08x %08x %08x %s%s\n", i, pNameTable->ItemIndex,
+ pNameTable->NextIndex,
+ pNameTable->FragOffs,
+ szMatchType,
+ szLastEntry);
+ pNameTable++;
+ }
+ fclose(fp);
+ }
+}
+
+void CascDumpFileNames(const char * szFileName, void * pvMarFile)
+{
+ TMndxFindResult Struct1C;
+ PMAR_FILE pMarFile = (PMAR_FILE)pvMarFile;
+ FILE * fp;
+ char szNameBuff[0x200];
+ bool bFindResult;
+
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ // Set an empty path as search mask (?)
+ Struct1C.SetSearchPath("", 0);
+
+ // Keep searching
+ for(;;)
+ {
+ // Search the next file name
+ pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult);
+
+ // Stop the search in case of failure
+ if(!bFindResult)
+ break;
+
+ // Printf the found file name
+ memcpy(szNameBuff, Struct1C.szFoundPath, Struct1C.cchFoundPath);
+ szNameBuff[Struct1C.cchFoundPath] = 0;
+ fprintf(fp, "%s\n", szNameBuff);
+ }
+
+ fclose(fp);
+ }
+
+ // Free the search structures
+ Struct1C.FreeStruct40();
+}
+
+void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo)
+{
+ PCASC_MNDX_ENTRY pMndxEntry;
+ FILE * fp;
+ char szMd5[MD5_STRING_SIZE];
+
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n");
+ for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++)
+ {
+ pMndxEntry = pMndxInfo->ppValidEntries[i];
+
+ fprintf(fp, "%04X %08X %s %08X\n", i,
+ pMndxEntry->Flags,
+ StringFromMD5(pMndxEntry->EncodingKey, szMd5),
+ pMndxEntry->FileSize);
+ }
+ fclose(fp);
+ }
+}
+
+void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
+{
+ PCASC_INDEX_ENTRY * ppIndexEntries;
+ FILE * fp;
+ size_t nIndexEntries = hs->pIndexEntryMap->ItemCount;
+ char szIndexKey[0x40];
+
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ // Create linear aray
+ ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, nIndexEntries);
+ if(ppIndexEntries != NULL)
+ {
+ // Obtain the linear array of index entries
+ Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
+
+ // Sort the array by archive number and archive offset
+ qsort_pointer_array((void **)ppIndexEntries, nIndexEntries, CompareIndexEntries_FilePos, NULL);
+
+ // Parse the array
+ fprintf(fp, "ArNo ArOffset FileSize IndexKey\n==== ======== ======== ================================\n");
+ for(size_t i = 0; i < nIndexEntries; i++)
+ {
+ PCASC_INDEX_ENTRY pIndexEntry = ppIndexEntries[i];
+ ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
+ DWORD ArchIndex = (DWORD)(ArchOffset >> 0x1E);
+ DWORD FileSize;
+
+ FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize);
+ ArchOffset &= 0x3FFFFFFF;
+
+ fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
+ }
+
+ CASC_FREE(ppIndexEntries);
+ }
+
+ fclose(fp);
+ }
+}
+
+void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile)
+{
+ char ** FileNameArray = NULL;
+ FILE * fp;
+
+ // Validate the storage handle
+ if(hs != NULL)
+ {
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ if(fp != NULL)
+ {
+ // If we also have listfile, open it
+ if(szListFile != NULL)
+ FileNameArray = CreateFileNameArray(hs, szListFile);
+
+ // Dump all root keys
+ fprintf(fp, "Root Entries\n=========\n\n");
+ DumpRootEntries(fp, hs, FileNameArray);
+
+ FreeFileNameArray(hs, FileNameArray);
+ fclose(fp);
+ }
+ }
+}
+
+
+void CascDumpFile(const char * szFileName, HANDLE hFile)
+{
+ FILE * fp;
+ DWORD dwBytesRead = 1;
+ DWORD dwFilePos = CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
+ BYTE Buffer[0x1000];
+
+ // Create the dump file
+ fp = fopen(szFileName, "wb");
+ if(fp != NULL)
+ {
+ // Read data as long as we can, write as long as we can
+ while(dwBytesRead != 0)
+ {
+ // Read from the source file
+ if(!CascReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead))
+ break;
+
+ // Write to the destination file
+ if(fwrite(Buffer, 1, dwBytesRead, fp) != dwBytesRead)
+ break;
+ }
+
+ // Close the local file
+ fclose(fp);
+
+ // Restore the file pointer
+ CascSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN);
+ }
+}
+
+#endif // _DEBUG