mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-02-15 06:29:13 +01:00
Dep/CascLib: Update to ladislav-zezula/CascLib@a1197edf0b
This commit is contained in:
634
dep/CascLib/src/CascRootFile_TVFS.cpp
Normal file
634
dep/CascLib/src/CascRootFile_TVFS.cpp
Normal file
@@ -0,0 +1,634 @@
|
||||
/*****************************************************************************/
|
||||
/* CascRootFile_TVFS.cpp Copyright (c) Ladislav Zezula 2018 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* ROOT handler for TACT VFS manifest format (root) */
|
||||
/* Note: TACT = Trusted Application Content Transfer */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 24.05.18 1.00 Lad The first version of CascRootFile_TVFS.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local defines
|
||||
|
||||
#define TVFS_FLAG_INCLUDE_CKEY 0x0001 // Include C-key in content file record
|
||||
#define TVFS_FLAG_WRITE_SUPPORT 0x0002 // Write support. Include a table of encoding specifiers. This is required for writing files to the underlying storage. This bit is implied by the patch-support bit
|
||||
#define TVFS_FLAG_PATCH_SUPPORT 0x0004 // Patch support. Include patch records in the content file records.
|
||||
#define TVFS_FLAG_LOWERCASE_MANIFEST 0x0008 // Lowercase manifest. All paths in the path table have been converted to ASCII lowercase (i.e. [A-Z] converted to [a-z])
|
||||
|
||||
#define TVFS_PTE_PATH_SEPARATOR_PRE 0x0001 // There is path separator before the name
|
||||
#define TVFS_PTE_PATH_SEPARATOR_POST 0x0002 // There is path separator after the name
|
||||
#define TVFS_PTE_NODE_VALUE 0x0004 // The NodeValue in path table entry is valid
|
||||
|
||||
#define TVFS_FOLDER_NODE 0x80000000 // Highest bit is set if a file node is a folder
|
||||
#define TVFS_FOLDER_SIZE_MASK 0x7FFFFFFF // Mask to get length of the folder
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
// In-memory layout of the TVFS file header
|
||||
typedef struct _TVFS_DIRECTORY_HEADER
|
||||
{
|
||||
DWORD Signature; // Must be CASC_TVFS_ROOT_SIGNATURE
|
||||
BYTE FormatVersion; // Version of the format. Should be 1.
|
||||
BYTE HeaderSize; // Size of the header, in bytes
|
||||
BYTE EKeySize; // Size of an E-Key. TACT uses 9-byte E-keys
|
||||
BYTE PatchKeySize; // Size of a patch key. TACT uses 9-byte P-keys
|
||||
DWORD Flags; // Flags. See TVFS_FLAG_XXX
|
||||
|
||||
// Followed by the offset table (variable length)
|
||||
DWORD PathTableOffset; // Offset of the path table
|
||||
DWORD PathTableSize; // Size of the path table
|
||||
DWORD VfsTableOffset; // Offset of the VFS table
|
||||
DWORD VfsTableSize; // Size of the VFS table
|
||||
DWORD CftTableOffset; // Offset of the container file table
|
||||
DWORD CftTableSize; // Size of the container file table
|
||||
USHORT MaxDepth; // The maximum depth of the path prefix tree stored in the path table
|
||||
DWORD EstTableOffset; // The offset of the encoding specifier table. Only if the write-support bit is set in the header flag
|
||||
DWORD EstTableSize; // The size of the encoding specifier table. Only if the write-support bit is set in the header flag
|
||||
|
||||
DWORD CftOffsSize; // Byte length of the offset in the Content File Table entry
|
||||
DWORD EstOffsSize; // Byte length of the offset in the Encoding Specifier Table entry
|
||||
|
||||
LPBYTE pbDirectoryData; // Pointer to the begin of directory data
|
||||
LPBYTE pbDirectoryEnd; // Pointer to the end of directory data
|
||||
|
||||
// LPBYTE pbPathFileTable; // Begin and end of the path table
|
||||
// LPBYTE pbPathTableEnd;
|
||||
|
||||
// LPBYTE pbVfsFileTable; // Begin and end of the VFS file table
|
||||
// LPBYTE pbVfsTableEnd;
|
||||
|
||||
// LPBYTE pbCftFileTable; // Begin and end of the content file table
|
||||
// LPBYTE pbCftTableEnd;
|
||||
|
||||
} TVFS_DIRECTORY_HEADER, *PTVFS_DIRECTORY_HEADER;
|
||||
|
||||
/*
|
||||
// Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD
|
||||
#define TVFS_HEADER_LENGTH FIELD_OFFSET(TVFS_DIRECTORY_HEADER, CftOffsSize)
|
||||
|
||||
// Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD
|
||||
#define TVFS_MIN_PATH_ENTRY (1 + 1 + 1 + sizeof(DWORD))
|
||||
|
||||
// Minimum size of the VFS entry (SpanCount + FileOffset + SpanLength + CftOffset)
|
||||
#define TVFS_MIN_VFS_ENTRY (1 + sizeof(DWORD) + sizeof(DWORD) + 1)
|
||||
|
||||
// Minimum size of the Content File Table entry (CASC_EKEY_SIZE + EncodedSize + ContentSize)
|
||||
#define TVFS_MIN_CFT_ENTRY (CASC_EKEY_SIZE + sizeof(DWORD) + sizeof(DWORD))
|
||||
|
||||
// Minimum size of the TVFS folder data
|
||||
#define TVFS_MIN_FILE_SIZE (TVFS_HEADER_LENGTH + TVFS_MIN_PATH_ENTRY + TVFS_MIN_VFS_ENTRY + TVFS_MIN_CFT_ENTRY)
|
||||
|
||||
// Maximum estimated file table. Empirically set to 8 MB, increase if needed.
|
||||
#define TVFS_MAX_FILE_SIZE 0x00800000
|
||||
*/
|
||||
|
||||
// In-memory layout of the path table entry
|
||||
typedef struct _TVFS_PATH_TABLE_ENTRY
|
||||
{
|
||||
LPBYTE pbNamePtr; // Pointer to the begin of the node name
|
||||
LPBYTE pbNameEnd; // Pointer to the end of the file name
|
||||
DWORD NodeFlags; // TVFS_PTE_XXX
|
||||
DWORD NodeValue; // Node value
|
||||
} TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handler definition for TVFS root file
|
||||
|
||||
//static FILE * fp = NULL;
|
||||
|
||||
// Structure for the root handler
|
||||
struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
{
|
||||
public:
|
||||
|
||||
TRootHandler_TVFS() : TFileTreeRoot(0)
|
||||
{
|
||||
// TVFS supports file names, but DOESN'T support CKeys.
|
||||
dwFeatures |= CASC_FEATURE_FILE_NAMES;
|
||||
dwNestLevel = 0;
|
||||
}
|
||||
|
||||
// Returns size of "container file table offset" fiels in the VFS.
|
||||
// - If the container file table is larger than 0xffffff bytes, it's 4 bytes
|
||||
// - If the container file table is larger than 0xffff bytes, it's 3 bytes
|
||||
// - If the container file table is larger than 0xff bytes, it's 2 bytes
|
||||
// - If the container file table is smaller than 0xff bytes, it's 1 byte
|
||||
static DWORD GetOffsetFieldSize(DWORD dwTableSize)
|
||||
{
|
||||
if(dwTableSize > 0xffffff)
|
||||
return 4;
|
||||
if(dwTableSize > 0xffff)
|
||||
return 3;
|
||||
if(dwTableSize > 0xff)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool PathBuffer_AddChar(PATH_BUFFER & PathBuffer, char chOneChar)
|
||||
{
|
||||
if(PathBuffer.szPtr >= PathBuffer.szEnd)
|
||||
return false;
|
||||
|
||||
*PathBuffer.szPtr++ = chOneChar;
|
||||
*PathBuffer.szPtr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathBuffer_AddString(PATH_BUFFER & PathBuffer, LPBYTE pbNamePtr, LPBYTE pbNameEnd)
|
||||
{
|
||||
size_t nLength = (pbNameEnd - pbNamePtr);
|
||||
|
||||
// Check whether we have enough space
|
||||
if ((PathBuffer.szPtr + nLength) > PathBuffer.szEnd)
|
||||
return false;
|
||||
|
||||
// Copy the node name
|
||||
memcpy(PathBuffer.szPtr, pbNamePtr, nLength);
|
||||
PathBuffer.szPtr += nLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathBuffer_AppendNode(PATH_BUFFER & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
|
||||
{
|
||||
// Append the prefix separator, if needed
|
||||
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_PRE)
|
||||
PathBuffer_AddChar(PathBuffer, '/');
|
||||
|
||||
// Append the name fragment, if any
|
||||
if (PathEntry.pbNameEnd > PathEntry.pbNamePtr)
|
||||
PathBuffer_AddString(PathBuffer, PathEntry.pbNamePtr, PathEntry.pbNameEnd);
|
||||
|
||||
// Append the postfix separator, if needed
|
||||
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST)
|
||||
PathBuffer_AddChar(PathBuffer, '/');
|
||||
|
||||
// Always end the buffer with zero
|
||||
PathBuffer.szPtr[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
|
||||
{
|
||||
// Fill the header structure with zeros
|
||||
memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
|
||||
DirHeader.pbDirectoryData = pbDataPtr;
|
||||
DirHeader.pbDirectoryEnd = pbDataEnd;
|
||||
|
||||
// Capture the signature
|
||||
pbDataPtr = CaptureInteger32(pbDataPtr, pbDataEnd, &DirHeader.Signature);
|
||||
if(pbDataPtr == NULL || DirHeader.Signature != CASC_TVFS_ROOT_SIGNATURE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the other four integers
|
||||
pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, 4, &DirHeader.FormatVersion);
|
||||
if(pbDataPtr == NULL || DirHeader.FormatVersion != 1 || DirHeader.EKeySize != 9 || DirHeader.PatchKeySize != 9 || DirHeader.HeaderSize < 8)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the rest
|
||||
pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, DirHeader.HeaderSize - FIELD_OFFSET(TVFS_DIRECTORY_HEADER, Flags), (LPBYTE)(&DirHeader.Flags));
|
||||
if(pbDataPtr == NULL)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Swap the header values
|
||||
DirHeader.Flags = ConvertBytesToInteger_4_LE((LPBYTE)(&DirHeader.Flags));
|
||||
|
||||
// Swap the offset table values
|
||||
DirHeader.PathTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableOffset));
|
||||
DirHeader.PathTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableSize));
|
||||
DirHeader.VfsTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableOffset));
|
||||
DirHeader.VfsTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableSize));
|
||||
DirHeader.CftTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableOffset));
|
||||
DirHeader.CftTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableSize));
|
||||
DirHeader.MaxDepth = (USHORT)ConvertBytesToInteger_2((LPBYTE)(&DirHeader.MaxDepth));
|
||||
DirHeader.EstTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableOffset));
|
||||
DirHeader.EstTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableSize));
|
||||
|
||||
// Determine size of file table offsets
|
||||
DirHeader.CftOffsSize = GetOffsetFieldSize(DirHeader.CftTableSize);
|
||||
DirHeader.EstOffsSize = GetOffsetFieldSize(DirHeader.EstTableSize);
|
||||
|
||||
// Capture the path table
|
||||
// DirHeader.pbPathFileTable = pbDirectory + DirHeader.PathTableOffset;
|
||||
// DirHeader.pbPathTableEnd = pbDirectory + DirHeader.PathTableOffset + DirHeader.PathTableSize;
|
||||
// if(DirHeader.pbPathTableEnd > pbDataEnd)
|
||||
// return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the VFS file table
|
||||
// DirHeader.pbVfsFileTable = pbDirectory + DirHeader.VfsTableOffset;
|
||||
// DirHeader.pbVfsTableEnd = pbDirectory + DirHeader.VfsTableOffset + DirHeader.VfsTableSize;
|
||||
// if(DirHeader.pbVfsTableEnd > pbDataEnd)
|
||||
// return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the container file table
|
||||
// DirHeader.pbCftFileTable = pbDirectory + DirHeader.CftTableOffset;
|
||||
// DirHeader.pbCftTableEnd = pbDirectory + DirHeader.CftTableOffset + DirHeader.CftTableSize;
|
||||
// if(DirHeader.pbCftTableEnd > pbDataEnd)
|
||||
// return ERROR_BAD_FORMAT;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int CaptureVfsSpanEntries(TVFS_DIRECTORY_HEADER & DirHeader, PENCODED_KEY pEKey, PDWORD PtrSpanSize, DWORD dwVfsOffset)
|
||||
{
|
||||
LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset;
|
||||
LPBYTE pbVfsFileEntry = pbVfsFileTable + dwVfsOffset;
|
||||
LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize;
|
||||
LPBYTE pbCftFileTable;
|
||||
LPBYTE pbCftFileEntry;
|
||||
LPBYTE pbCftFileEnd;
|
||||
LPBYTE pbTemp = NULL;
|
||||
size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize;
|
||||
DWORD dwCftOffset;
|
||||
DWORD dwSpanCount;
|
||||
DWORD dwSpanSize;
|
||||
|
||||
// Get the number of span entries
|
||||
if(!(pbVfsFileTable <= pbVfsFileEntry && pbVfsFileEntry <= pbVfsFileEnd))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
dwSpanCount = *pbVfsFileEntry++;
|
||||
|
||||
// 1 - 224 = valid file, 225-254 = other file, 255 = deleted file
|
||||
// We will ignore all files with unsupported span count
|
||||
if(dwSpanCount == 0 || dwSpanCount > 224)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// So far we've only saw entries with 1 span.
|
||||
// Need to test files with multiple spans. Ignore such files for now.
|
||||
assert(dwSpanCount == 1);
|
||||
|
||||
// Capture the array of span items
|
||||
if(CaptureArray_(pbVfsFileEntry, pbVfsFileEnd, &pbTemp, ItemSize, dwSpanCount) == NULL)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
//
|
||||
// Structure of the span entry:
|
||||
// (4bytes): Offset into the referenced file (big endian)
|
||||
// (4bytes): Size of the span (big endian)
|
||||
// (?bytes): Offset into Container File Table. Length depends on container file table size
|
||||
//
|
||||
|
||||
// Get the offset to the Container File Table
|
||||
dwCftOffset = ConvertBytesToInteger_X(pbVfsFileEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
|
||||
dwSpanSize = ConvertBytesToInteger_4(pbVfsFileEntry + sizeof(DWORD));
|
||||
|
||||
// Go to the Container File Table and fetch the EKey from there
|
||||
pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
|
||||
pbCftFileEntry = pbCftFileTable + dwCftOffset;
|
||||
pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize;
|
||||
if((pbCftFileEntry + DirHeader.EKeySize) > pbCftFileEnd)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Give the pointer to the EKey
|
||||
memcpy(pEKey->Value, pbCftFileEntry, DirHeader.EKeySize);
|
||||
PtrSpanSize[0] = dwSpanSize;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Structure of the path table entry:
|
||||
// (1byte) 0x00 (optional) - means that there will be prefix path separator
|
||||
// (1byte) File name length
|
||||
// (?byte) File name
|
||||
// (1byte) 0x00 (optional) - means that there will be postfix path separator
|
||||
// (1byte) 0xFF (optional) - node value identifier
|
||||
// (4byte) - node value
|
||||
//
|
||||
// Note: The path "data\archive\maps\file.bmp" could be cut into nodes like:
|
||||
// data\0 (or data with subdirectory)
|
||||
// arc
|
||||
// hive\0
|
||||
// maps\0 (or folder data)
|
||||
// file.bmp
|
||||
//
|
||||
|
||||
LPBYTE CapturePathEntry(TVFS_PATH_TABLE_ENTRY & PathEntry, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
|
||||
{
|
||||
// Reset the path entry structure
|
||||
PathEntry.pbNamePtr = pbPathTablePtr;
|
||||
PathEntry.pbNameEnd = pbPathTablePtr;
|
||||
PathEntry.NodeFlags = 0;
|
||||
PathEntry.NodeValue = 0;
|
||||
|
||||
// Zero before the name means prefix path separator
|
||||
if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0)
|
||||
{
|
||||
PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_PRE;
|
||||
pbPathTablePtr++;
|
||||
}
|
||||
|
||||
// Capture the length of the name fragment
|
||||
if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] != 0xFF)
|
||||
{
|
||||
// Capture length of the name fragment
|
||||
size_t nLength = *pbPathTablePtr++;
|
||||
|
||||
if ((pbPathTablePtr + nLength) > pbPathTableEnd)
|
||||
return NULL;
|
||||
PathEntry.pbNamePtr = pbPathTablePtr;
|
||||
PathEntry.pbNameEnd = pbPathTablePtr + nLength;
|
||||
pbPathTablePtr += nLength;
|
||||
}
|
||||
|
||||
// Zero after the name means postfix path separator
|
||||
if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0)
|
||||
{
|
||||
PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST;
|
||||
pbPathTablePtr++;
|
||||
}
|
||||
|
||||
if (pbPathTablePtr < pbPathTableEnd)
|
||||
{
|
||||
// Check for node value
|
||||
if (pbPathTablePtr[0] == 0xFF)
|
||||
{
|
||||
if ((pbPathTablePtr + 1 + sizeof(DWORD)) > pbPathTableEnd)
|
||||
return NULL;
|
||||
PathEntry.NodeValue = ConvertBytesToInteger_4(pbPathTablePtr + 1);
|
||||
PathEntry.NodeFlags |= TVFS_PTE_NODE_VALUE;
|
||||
pbPathTablePtr = pbPathTablePtr + 1 + sizeof(DWORD);
|
||||
}
|
||||
|
||||
// Non-0xFF after the name means path separator after
|
||||
else
|
||||
{
|
||||
PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST;
|
||||
assert(pbPathTablePtr[0] != 0);
|
||||
}
|
||||
}
|
||||
|
||||
return pbPathTablePtr;
|
||||
}
|
||||
|
||||
bool IsVfsFileEKey(TCascStorage * hs, ENCODED_KEY & EKey, size_t EKeyLength)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
size_t ItemCount = hs->VfsRootList.ItemCount();
|
||||
|
||||
// Search the array
|
||||
for (size_t i = 0; i < ItemCount; i++)
|
||||
{
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
|
||||
if (pCKeyEntry != NULL)
|
||||
{
|
||||
if (!memcmp(pCKeyEntry->EKey, EKey.Value, EKeyLength))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found in the VFS list
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function verifies whether a file is actually a sub-directory.
|
||||
// If yes, it contains just another "TVFS" virtual file system, just like the ROOT file.
|
||||
int IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, ENCODED_KEY & EKey, DWORD dwFileSize)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
LPBYTE pbVfsData = NULL;
|
||||
DWORD cbVfsData = dwFileSize;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify whether the EKey is in the list of VFS root files
|
||||
if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize))
|
||||
{
|
||||
// Locate the CKey entry
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
{
|
||||
// Load the entire file into memory
|
||||
pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData);
|
||||
if (pbVfsData && cbVfsData)
|
||||
{
|
||||
// Capture the file folder. This also serves as test
|
||||
nError = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
|
||||
if (nError == ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Clear the captured header
|
||||
memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
|
||||
CASC_FREE(pbVfsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
char szFileName[0x20];
|
||||
|
||||
// The CKey entry must exist
|
||||
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pbCKey)) != NULL)
|
||||
{
|
||||
CascStrPrintf(szFileName, _countof(szFileName), szFormat, nIndex);
|
||||
Insert(szFileName, pCKeyEntry);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
|
||||
{
|
||||
TVFS_DIRECTORY_HEADER SubHeader;
|
||||
TVFS_PATH_TABLE_ENTRY PathEntry;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
ENCODED_KEY EKey;
|
||||
char * szSavePathPtr = PathBuffer.szPtr;
|
||||
DWORD dwSpanSize = 0;
|
||||
int nError;
|
||||
|
||||
// Prepare the EKey structure to be filled with zeros
|
||||
memset(&EKey, 0, sizeof(ENCODED_KEY));
|
||||
|
||||
// Parse the file table
|
||||
while(pbPathTablePtr < pbPathTableEnd)
|
||||
{
|
||||
// Capture the single path table entry
|
||||
pbPathTablePtr = CapturePathEntry(PathEntry, pbPathTablePtr, pbPathTableEnd);
|
||||
if(pbPathTablePtr == NULL)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Append the node name to the total path. Also add backslash, if it's a folder
|
||||
PathBuffer_AppendNode(PathBuffer, PathEntry);
|
||||
|
||||
// Folder component
|
||||
if (PathEntry.NodeFlags & TVFS_PTE_NODE_VALUE)
|
||||
{
|
||||
// If the TVFS_FOLDER_NODE is set, then the path node is a directory,
|
||||
// with its data immediately following the path node. Lower 31 bits of NodeValue
|
||||
// contain the length of the directory (including the NodeValue!)
|
||||
if (PathEntry.NodeValue & TVFS_FOLDER_NODE)
|
||||
{
|
||||
LPBYTE pbDirectoryEnd = pbPathTablePtr + (PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) - sizeof(DWORD);
|
||||
|
||||
// Check the available data
|
||||
assert((PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) >= sizeof(DWORD));
|
||||
|
||||
// Recursively call the folder parser on the same file
|
||||
nError = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Skip the directory data
|
||||
pbPathTablePtr = pbDirectoryEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Capture the VFS and Container Table Entry in order to get the file EKey
|
||||
nError = CaptureVfsSpanEntries(DirHeader, &EKey, &dwSpanSize, PathEntry.NodeValue);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// We need to check whether this is another TVFS directory file
|
||||
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, EKey, dwSpanSize) == ERROR_SUCCESS)
|
||||
{
|
||||
// Add colon (':')
|
||||
PathBuffer_AddChar(PathBuffer, ':');
|
||||
|
||||
// Insert the file to the file tree
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
{
|
||||
// The file content size should already be there
|
||||
assert(pCKeyEntry->ContentSize == dwSpanSize);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
}
|
||||
|
||||
ParseDirectoryData(hs, SubHeader, PathBuffer);
|
||||
CASC_FREE(SubHeader.pbDirectoryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (fp != NULL)
|
||||
// {
|
||||
// fwrite(PathBuffer.szBegin, 1, (PathBuffer.szPtr - PathBuffer.szBegin), fp);
|
||||
// fprintf(fp, "\n");
|
||||
// }
|
||||
|
||||
// Insert the file to the file tree
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
{
|
||||
// If the file content is not there, supply it now
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = dwSpanSize;
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the position of the path buffer
|
||||
PathBuffer.szPtr = szSavePathPtr;
|
||||
PathBuffer.szPtr[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the total number of entries
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer)
|
||||
{
|
||||
LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset;
|
||||
LPBYTE pbRootDirPtr = pbRootDirectory;
|
||||
LPBYTE pbRootDirEnd = pbRootDirPtr + DirHeader.PathTableSize;
|
||||
DWORD dwNodeValue = 0;
|
||||
|
||||
// Most usually, there is a root directory in the folder
|
||||
if((pbRootDirPtr + 1 + sizeof(DWORD)) < pbRootDirEnd)
|
||||
{
|
||||
//
|
||||
// The structure of the root directory
|
||||
// -----------------------------------
|
||||
// 1byte 0xFF
|
||||
// 4bytes NodeValue (BigEndian). The most significant bit is set
|
||||
// - Lower 31 bits contain length of the directory data, including NodeValue
|
||||
//
|
||||
|
||||
if(pbRootDirPtr[0] == 0xFF)
|
||||
{
|
||||
// Get the NodeValue and check its highest bit
|
||||
if(CaptureInteger32_BE(pbRootDirPtr + 1, pbRootDirEnd, &dwNodeValue) == NULL || (dwNodeValue & TVFS_FOLDER_NODE) == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Get the range of the root directory
|
||||
pbRootDirEnd = pbRootDirPtr + 1 + (dwNodeValue & TVFS_FOLDER_SIZE_MASK);
|
||||
pbRootDirPtr = pbRootDirPtr + 1 + sizeof(DWORD);
|
||||
|
||||
// Check the directory
|
||||
if(pbRootDirEnd > (pbRootDirectory + DirHeader.PathTableSize))
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
// Now go parse the path file table
|
||||
return ParsePathFileTable(hs, DirHeader, PathBuffer, pbRootDirPtr, pbRootDirEnd);
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
|
||||
{
|
||||
// PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PATH_BUFFER PathBuffer;
|
||||
char szPathBuffer[MAX_PATH];
|
||||
|
||||
// Initialize the path buffer
|
||||
memset(szPathBuffer, 0, sizeof(szPathBuffer));
|
||||
PathBuffer.szBegin =
|
||||
PathBuffer.szPtr = szPathBuffer;
|
||||
PathBuffer.szEnd = szPathBuffer + MAX_PATH;
|
||||
|
||||
// Save the length of the key
|
||||
FileTree.SetKeyLength(RootHeader.EKeySize);
|
||||
|
||||
// Insert the main VFS root file as named entry
|
||||
InsertRootVfsEntry(hs, hs->VfsRoot.CKey, "vfs-root", 0);
|
||||
|
||||
// Insert all VFS roots folders as files
|
||||
//for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
|
||||
//{
|
||||
// pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
|
||||
// InsertRootVfsEntry(hs, pCKeyEntry->CKey, "vfs-%u", i+1);
|
||||
//}
|
||||
|
||||
// Parse the entire directory data
|
||||
return ParseDirectoryData(hs, RootHeader, PathBuffer);
|
||||
}
|
||||
|
||||
DWORD dwNestLevel;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions - TVFS root
|
||||
|
||||
int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_TVFS * pRootHandler = NULL;
|
||||
TVFS_DIRECTORY_HEADER RootHeader;
|
||||
int nError;
|
||||
|
||||
// Capture the entire root directory
|
||||
nError = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate the root handler object
|
||||
pRootHandler = new TRootHandler_TVFS();
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, RootHeader);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
}
|
||||
Reference in New Issue
Block a user