336 lines
9.9 KiB
C++
336 lines
9.9 KiB
C++
/*
|
|
* HLLib
|
|
* Copyright (C) 2006-2010 Ryan Gregg
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later
|
|
* version.
|
|
*/
|
|
|
|
#include "HLLib.h"
|
|
#include "XZPFile.h"
|
|
#include "Streams.h"
|
|
|
|
using namespace HLLib;
|
|
|
|
const char *CXZPFile::lpAttributeNames[] = { "Version", "Preload Bytes" };
|
|
const char *CXZPFile::lpItemAttributeNames[] = { "Created", "Preload Bytes" };
|
|
|
|
CXZPFile::CXZPFile() : CPackage(), pHeaderView(0), pDirectoryEntryView(0), pDirectoryItemView(0), pFooterView(0), pHeader(0), lpDirectoryEntries(0), lpPreloadDirectoryEntries(0), lpPreloadDirectoryMappings(0), lpDirectoryItems(0), pFooter(0)
|
|
{
|
|
|
|
}
|
|
|
|
CXZPFile::~CXZPFile()
|
|
{
|
|
this->Close();
|
|
}
|
|
|
|
HLPackageType CXZPFile::GetType() const
|
|
{
|
|
return HL_PACKAGE_XZP;
|
|
}
|
|
|
|
const hlChar *CXZPFile::GetExtension() const
|
|
{
|
|
return "xzp";
|
|
}
|
|
|
|
const hlChar *CXZPFile::GetDescription() const
|
|
{
|
|
return "XBox Package File";
|
|
}
|
|
|
|
hlBool CXZPFile::MapDataStructures()
|
|
{
|
|
if(sizeof(XZPHeader) > this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for it's header.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(!this->pMapping->Map(this->pHeaderView, 0, sizeof(XZPHeader)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pHeader = (XZPHeader *)this->pHeaderView->GetView();
|
|
|
|
if(memcmp(this->pHeader->lpSignature, "piZx", 4) != 0)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file's header signature does not match.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(this->pHeader->uiVersion != 6)
|
|
{
|
|
LastError.SetErrorMessageFormated("Invalid XZP version (v%u): you have a version of a XZP file that HLLib does not know how to read. Check for product updates.", this->pHeader->uiVersion);
|
|
return hlFalse;
|
|
}
|
|
|
|
if(this->pHeader->uiHeaderLength != sizeof(XZPHeader))
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file's header size does not match.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(!this->pMapping->Map(this->pDirectoryEntryView, sizeof(XZPHeader), this->pHeader->uiPreloadBytes ? (this->pHeader->uiDirectoryEntryCount + this->pHeader->uiPreloadDirectoryEntryCount) * sizeof(XZPDirectoryEntry) + this->pHeader->uiDirectoryEntryCount * sizeof(XZPDirectoryMapping) : this->pHeader->uiDirectoryEntryCount * sizeof(XZPDirectoryEntry)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->lpDirectoryEntries = (XZPDirectoryEntry *)this->pDirectoryEntryView->GetView();
|
|
this->lpPreloadDirectoryEntries = this->pHeader->uiPreloadBytes ? this->lpDirectoryEntries + this->pHeader->uiDirectoryEntryCount : 0;
|
|
this->lpPreloadDirectoryMappings = this->pHeader->uiPreloadBytes ? (XZPDirectoryMapping *)(this->lpPreloadDirectoryEntries + this->pHeader->uiPreloadDirectoryEntryCount) : 0;
|
|
|
|
if(this->pHeader->uiDirectoryItemCount != 0)
|
|
{
|
|
if(!this->pMapping->Map(this->pDirectoryItemView, this->pHeader->uiDirectoryItemOffset, this->pHeader->uiDirectoryItemLength))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->lpDirectoryItems = (XZPDirectoryItem *)this->pDirectoryItemView->GetView();
|
|
}
|
|
|
|
if(!this->pMapping->Map(this->pFooterView, this->pMapping->GetMappingSize() - sizeof(XZPFooter), sizeof(XZPFooter)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pFooter = (XZPFooter *)this->pFooterView->GetView();
|
|
|
|
if(memcmp(this->pFooter->lpSignature, "tFzX", 4) != 0)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file's footer signature does not match.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(this->pFooter->uiFileLength != this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is not within mapping bounds.");
|
|
return hlFalse;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlVoid CXZPFile::UnmapDataStructures()
|
|
{
|
|
this->pFooter = 0;
|
|
this->pMapping->Unmap(this->pFooterView);
|
|
|
|
this->lpDirectoryItems = 0;
|
|
this->pMapping->Unmap(this->pDirectoryItemView);
|
|
|
|
this->lpDirectoryEntries = 0;
|
|
this->lpPreloadDirectoryEntries = 0;
|
|
this->lpPreloadDirectoryMappings = 0;
|
|
this->pMapping->Unmap(this->pDirectoryEntryView);
|
|
|
|
this->pHeader = 0;
|
|
this->pMapping->Unmap(this->pHeaderView);
|
|
}
|
|
|
|
CDirectoryFolder *CXZPFile::CreateRoot()
|
|
{
|
|
CDirectoryFolder *pRoot = new CDirectoryFolder(this);
|
|
|
|
if(this->pHeader->uiDirectoryItemCount != 0)
|
|
{
|
|
// Loop through each file in the XZP file.
|
|
for(hlUInt i = 0; i < this->pHeader->uiDirectoryEntryCount; i++)
|
|
{
|
|
// Find it's info (file name).
|
|
for(hlUInt j = 0; j < this->pHeader->uiDirectoryItemCount; j++)
|
|
{
|
|
if(this->lpDirectoryEntries[i].uiFileNameCRC == this->lpDirectoryItems[j].uiFileNameCRC)
|
|
{
|
|
hlChar lpFileName[256];
|
|
strncpy(lpFileName, (hlChar *)this->lpDirectoryItems + this->lpDirectoryItems[j].uiNameOffset - this->pHeader->uiDirectoryItemOffset, sizeof(lpFileName));
|
|
lpFileName[sizeof(lpFileName) - 1] = '\0';
|
|
|
|
// Check if we have just a file, or if the file has directories we need to create.
|
|
if(strchr(lpFileName, '/') == 0 && strchr(lpFileName, '\\') == 0)
|
|
{
|
|
pRoot->AddFile(lpFileName, i);
|
|
}
|
|
else
|
|
{
|
|
// Tokenize the file path and create the directories.
|
|
CDirectoryFolder *pInsertFolder = pRoot;
|
|
|
|
hlChar lpTemp[256] = "";
|
|
hlChar *lpToken = strtok(lpFileName, "/\\");
|
|
while(lpToken != 0)
|
|
{
|
|
strcpy(lpTemp, lpToken);
|
|
|
|
lpToken = strtok(0, "/\\");
|
|
|
|
if(lpToken != 0)
|
|
{
|
|
// Check if the directory exists.
|
|
CDirectoryItem *pItem = pInsertFolder->GetItem(lpTemp);
|
|
if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
|
|
{
|
|
// It doesn't, create it.
|
|
pInsertFolder = pInsertFolder->AddFolder(lpTemp);
|
|
}
|
|
else
|
|
{
|
|
// It does, use it.
|
|
pInsertFolder = static_cast<CDirectoryFolder *>(pItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
// The file name is the last token, add it.
|
|
pInsertFolder->AddFile(lpTemp, i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No file name information, just file name CRCs.
|
|
for(hlUInt i = 0; i < this->pHeader->uiDirectoryEntryCount; i++)
|
|
{
|
|
hlChar lpTemp[16] = "";
|
|
const hlChar *lpLookup[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
|
for(hlByte *lpCRC = (hlByte *)&this->lpDirectoryEntries[i].uiFileNameCRC; lpCRC < (hlByte *)&this->lpDirectoryEntries[i].uiFileNameCRC + sizeof(hlUInt); lpCRC++)
|
|
{
|
|
strcat(lpTemp, lpLookup[(hlByte)(*lpCRC >> 4)]);
|
|
strcat(lpTemp, lpLookup[(hlByte)(*lpCRC & 0x0F)]);
|
|
|
|
}
|
|
|
|
pRoot->AddFile(lpTemp, i);
|
|
}
|
|
}
|
|
|
|
return pRoot;
|
|
}
|
|
|
|
hlUInt CXZPFile::GetAttributeCountInternal() const
|
|
{
|
|
return HL_XZP_PACKAGE_COUNT;
|
|
}
|
|
|
|
const hlChar *CXZPFile::GetAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_XZP_PACKAGE_COUNT)
|
|
{
|
|
return this->lpAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hlBool CXZPFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
switch(eAttribute)
|
|
{
|
|
case HL_XZP_PACKAGE_VERSION:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiVersion, hlFalse);
|
|
return hlTrue;
|
|
case HL_XZP_PACKAGE_PRELOAD_BYTES:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiPreloadBytes, hlFalse);
|
|
return hlTrue;
|
|
default:
|
|
return hlFalse;
|
|
}
|
|
}
|
|
|
|
hlUInt CXZPFile::GetItemAttributeCountInternal() const
|
|
{
|
|
return HL_XZP_ITEM_COUNT;
|
|
}
|
|
|
|
const hlChar *CXZPFile::GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_XZP_ITEM_COUNT)
|
|
{
|
|
return this->lpItemAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hlBool CXZPFile::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
const XZPDirectoryEntry *pDirectoryEntry = this->lpDirectoryEntries + pFile->GetID();
|
|
switch(eAttribute)
|
|
{
|
|
case HL_XZP_ITEM_CREATED:
|
|
{
|
|
for(hlUInt i = 0; i < this->pHeader->uiDirectoryItemCount; i++)
|
|
{
|
|
if(this->lpDirectoryItems[i].uiFileNameCRC == pDirectoryEntry->uiFileNameCRC)
|
|
{
|
|
time_t Time = (time_t)this->lpDirectoryItems[i].uiTimeCreated;
|
|
tm *pTime = localtime(&Time);
|
|
|
|
hlChar lpTime[128];
|
|
strftime(lpTime, sizeof(lpTime), "%c", pTime);
|
|
|
|
hlAttributeSetString(&Attribute, this->lpItemAttributeNames[eAttribute], lpTime);
|
|
return hlTrue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case HL_XZP_ITEM_PRELOAD_BYTES:
|
|
{
|
|
hlUInt uiSize = 0;
|
|
if(this->lpPreloadDirectoryMappings != 0)
|
|
{
|
|
hlUInt16 uiIndex = this->lpPreloadDirectoryMappings[pFile->GetID()].uiPreloadDirectoryEntryIndex;
|
|
if(uiIndex != 0xffff && this->lpPreloadDirectoryEntries[uiIndex].uiFileNameCRC == pDirectoryEntry->uiFileNameCRC)
|
|
{
|
|
uiSize = this->lpPreloadDirectoryEntries[uiIndex].uiEntryLength;
|
|
}
|
|
}
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpItemAttributeNames[eAttribute], uiSize, hlFalse);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return hlFalse;
|
|
}
|
|
|
|
hlBool CXZPFile::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
uiSize = this->lpDirectoryEntries[pFile->GetID()].uiEntryLength;
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CXZPFile::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
uiSize = this->lpDirectoryEntries[pFile->GetID()].uiEntryLength;
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CXZPFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
|
|
{
|
|
const XZPDirectoryEntry *pDirectoryEntry = this->lpDirectoryEntries + pFile->GetID();
|
|
|
|
pStream = new Streams::CMappingStream(*this->pMapping, pDirectoryEntry->uiEntryOffset, pDirectoryEntry->uiEntryLength);
|
|
|
|
return hlTrue;
|
|
}
|