927 lines
32 KiB
C++
927 lines
32 KiB
C++
/*
|
|
* HLLib
|
|
* Copyright (C) 2006-2012 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 "SGAFile.h"
|
|
#include "Streams.h"
|
|
#include "Checksum.h"
|
|
#include "Utility.h"
|
|
|
|
#if USE_ZLIB
|
|
# ifdef _WIN32
|
|
# define ZLIB_WINAPI
|
|
# endif
|
|
# include <zlib.h>
|
|
#endif
|
|
|
|
using namespace HLLib;
|
|
|
|
#define HL_SGA_CHECKSUM_LENGTH 0x00008000
|
|
|
|
const char *CSGAFile::lpAttributeNames[] = { "Major Version", "Minor Version", "File MD5", "Name", "Header MD5" };
|
|
const char *CSGAFile::lpItemAttributeNames[] = { "Section Alias", "Section Name", "Modified", "Type", "CRC", "Verification" };
|
|
const char *CSGAFile::lpVerificationNames[] = { "None", "CRC", "CRC Blocks", "MD5 Blocks", "SHA1 Blocks" };
|
|
|
|
CSGAFile::CSGAFile() : CPackage(), pHeaderView(0), pHeader(0), pDirectory(0)
|
|
{
|
|
|
|
}
|
|
|
|
CSGAFile::~CSGAFile()
|
|
{
|
|
this->Close();
|
|
}
|
|
|
|
HLPackageType CSGAFile::GetType() const
|
|
{
|
|
return HL_PACKAGE_SGA;
|
|
}
|
|
|
|
const hlChar *CSGAFile::GetExtension() const
|
|
{
|
|
return "sga";
|
|
}
|
|
|
|
const hlChar *CSGAFile::GetDescription() const
|
|
{
|
|
return "Archive File";
|
|
}
|
|
|
|
hlBool CSGAFile::MapDataStructures()
|
|
{
|
|
hlULongLong uiMaxHeaderSize = std::max(sizeof(SGAHeader4), sizeof(SGAHeader6));
|
|
if(uiMaxHeaderSize > 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, uiMaxHeaderSize))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pHeader = static_cast<const SGAHeaderBase *>(this->pHeaderView->GetView());
|
|
|
|
if(memcmp(this->pHeader->lpSignature, "_ARCHIVE", 8) != 0)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file's signature does not match.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if((this->pHeader->uiMajorVersion != 4 || this->pHeader->uiMinorVersion != 0) &&
|
|
(this->pHeader->uiMajorVersion != 5 || this->pHeader->uiMinorVersion != 0) &&
|
|
(this->pHeader->uiMajorVersion != 6 || this->pHeader->uiMinorVersion != 0) &&
|
|
(this->pHeader->uiMajorVersion != 7 || this->pHeader->uiMinorVersion != 0))
|
|
{
|
|
LastError.SetErrorMessageFormated("Invalid SGA version (v%hu.%hu): you have a version of a SGA file that HLLib does not know how to read. Check for product updates.", this->pHeader->uiMajorVersion, this->pHeader->uiMinorVersion);
|
|
return hlFalse;
|
|
}
|
|
|
|
switch(this->pHeader->uiMajorVersion)
|
|
{
|
|
case 4:
|
|
if(static_cast<const CSGADirectory4::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pDirectory = new CSGADirectory4(*this);
|
|
break;
|
|
case 5:
|
|
if(static_cast<const CSGADirectory5::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pDirectory = new CSGADirectory5(*this);
|
|
break;
|
|
case 6:
|
|
if(static_cast<const CSGADirectory6::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pDirectory = new CSGADirectory6(*this);
|
|
break;
|
|
case 7:
|
|
if(static_cast<const CSGADirectory7::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pDirectory = new CSGADirectory7(*this);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return hlFalse;
|
|
}
|
|
|
|
if(!this->pDirectory->MapDataStructures())
|
|
{
|
|
return hlFalse;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlVoid CSGAFile::UnmapDataStructures()
|
|
{
|
|
delete this->pDirectory;
|
|
this->pDirectory = 0;
|
|
|
|
this->pHeader = 0;
|
|
|
|
this->pMapping->Unmap(this->pHeaderView);
|
|
}
|
|
|
|
CDirectoryFolder *CSGAFile::CreateRoot()
|
|
{
|
|
return this->pDirectory->CreateRoot();
|
|
}
|
|
|
|
hlBool CSGAFile::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
return this->pDirectory->GetItemAttributeInternal(pItem, eAttribute, Attribute);
|
|
}
|
|
|
|
hlBool CSGAFile::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const
|
|
{
|
|
return this->pDirectory->GetFileExtractableInternal(pFile, bExtractable);
|
|
}
|
|
|
|
hlBool CSGAFile::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
|
|
{
|
|
return this->pDirectory->GetFileValidationInternal(pFile, eValidation);
|
|
}
|
|
|
|
hlBool CSGAFile::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
return this->pDirectory->GetFileSizeInternal(pFile, uiSize);
|
|
}
|
|
|
|
hlBool CSGAFile::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
return this->pDirectory->GetFileSizeOnDiskInternal(pFile, uiSize);
|
|
}
|
|
|
|
hlBool CSGAFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
|
|
{
|
|
return this->pDirectory->CreateStreamInternal(pFile, pStream);
|
|
}
|
|
|
|
hlVoid CSGAFile::ReleaseStreamInternal(Streams::IStream &Stream) const
|
|
{
|
|
return this->pDirectory->ReleaseStreamInternal(Stream);
|
|
}
|
|
|
|
|
|
hlUInt CSGAFile::GetAttributeCountInternal() const
|
|
{
|
|
return HL_SGA_PACKAGE_COUNT;
|
|
}
|
|
|
|
const hlChar *CSGAFile::GetAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_SGA_PACKAGE_COUNT)
|
|
{
|
|
return this->lpAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hlBool CSGAFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
hlChar lpBuffer[64];
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_PACKAGE_VERSION_MAJOR:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMajorVersion, hlFalse);
|
|
return hlTrue;
|
|
case HL_SGA_PACKAGE_VERSION_MINOR:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMinorVersion, hlFalse);
|
|
return hlTrue;
|
|
case HL_SGA_PACKAGE_MD5_FILE:
|
|
if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
|
|
{
|
|
BufferToHexString(static_cast<const SGAHeader4 *>(this->pHeader)->lpFileMD5, 16, lpBuffer, sizeof(lpBuffer));
|
|
hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
|
|
return hlTrue;
|
|
}
|
|
return hlFalse;
|
|
case HL_SGA_PACKAGE_NAME:
|
|
if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
|
|
{
|
|
WStringToString(static_cast<const SGAHeader4 *>(this->pHeader)->lpName, lpBuffer, sizeof(lpBuffer));
|
|
hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
|
|
return hlTrue;
|
|
}
|
|
if(this->pHeader->uiMajorVersion >= 6 && this->pHeader->uiMajorVersion <= 6)
|
|
{
|
|
WStringToString(static_cast<const SGAHeader6 *>(this->pHeader)->lpName, lpBuffer, sizeof(lpBuffer));
|
|
hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
|
|
return hlTrue;
|
|
}
|
|
return hlFalse;
|
|
case HL_SGA_PACKAGE_MD5_HEADER:
|
|
if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
|
|
{
|
|
BufferToHexString(static_cast<const SGAHeader4 *>(this->pHeader)->lpHeaderMD5, 16, lpBuffer, sizeof(lpBuffer));
|
|
hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
|
|
return hlTrue;
|
|
}
|
|
return hlFalse;
|
|
default:
|
|
return hlFalse;
|
|
}
|
|
}
|
|
|
|
hlUInt CSGAFile::GetItemAttributeCountInternal() const
|
|
{
|
|
return HL_SGA_ITEM_COUNT;
|
|
}
|
|
|
|
const hlChar *CSGAFile::GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_SGA_ITEM_COUNT)
|
|
{
|
|
return this->lpItemAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CSGAFile::ISGADirectory::~ISGADirectory()
|
|
{
|
|
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
|
|
{
|
|
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
|
|
{
|
|
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
|
|
{
|
|
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CSGADirectory(CSGAFile& File) : CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>(File)
|
|
{
|
|
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::~CSGADirectory()
|
|
{
|
|
this->UnmapDataStructures();
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::MapDataStructures()
|
|
{
|
|
if(!this->File.pMapping->Map(this->pHeaderDirectoryView, sizeof(SGAHeader), static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pDirectoryHeader = static_cast<const SGADirectoryHeader *>(this->pHeaderDirectoryView->GetView());
|
|
|
|
if(this->pDirectoryHeader->uiSectionCount > 0 && this->pDirectoryHeader->uiSectionOffset + sizeof(SGASection) * this->pDirectoryHeader->uiSectionCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for section data.");
|
|
return hlFalse;
|
|
}
|
|
if(this->pDirectoryHeader->uiFolderCount > 0 && this->pDirectoryHeader->uiFolderOffset + sizeof(SGAFolder) * this->pDirectoryHeader->uiFolderCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for folder data.");
|
|
return hlFalse;
|
|
}
|
|
if(this->pDirectoryHeader->uiFileCount > 0 && this->pDirectoryHeader->uiFileOffset + sizeof(SGAFile) * this->pDirectoryHeader->uiFileCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for file data.");
|
|
return hlFalse;
|
|
}
|
|
if(this->pDirectoryHeader->uiStringTableOffset > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file map is too small for string table data.");
|
|
return hlFalse;
|
|
}
|
|
|
|
this->lpSections = reinterpret_cast<const SGASection *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiSectionOffset);
|
|
this->lpFolders = reinterpret_cast<const SGAFolder *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiFolderOffset);
|
|
this->lpFiles = reinterpret_cast<const SGAFile *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiFileOffset);
|
|
this->lpStringTable = reinterpret_cast<const hlChar *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiStringTableOffset);
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::UnmapDataStructures()
|
|
{
|
|
this->pDirectoryHeader = 0;
|
|
this->lpSections = 0;
|
|
this->lpFolders = 0;
|
|
this->lpFiles = 0;
|
|
this->lpStringTable = 0;
|
|
|
|
this->File.pMapping->Unmap(this->pHeaderDirectoryView);
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
CDirectoryFolder *CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateRoot()
|
|
{
|
|
CDirectoryFolder *pRoot = new CDirectoryFolder(&this->File);
|
|
|
|
for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
|
|
{
|
|
CDirectoryFolder* pSection;
|
|
// Check if folder exists.
|
|
CDirectoryItem *pItem = pRoot->GetItem(this->lpSections[i].lpAlias);
|
|
if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
|
|
{
|
|
// It doesn't, create it.
|
|
pSection = pRoot->AddFolder(this->lpSections[i].lpAlias);
|
|
}
|
|
else
|
|
{
|
|
// It does, use it.
|
|
pSection = static_cast<CDirectoryFolder *>(pItem);
|
|
}
|
|
this->CreateFolder(pSection, this->lpSections[i].uiFolderRootIndex);
|
|
}
|
|
|
|
return pRoot;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex)
|
|
{
|
|
const hlChar* lpName = this->lpStringTable + this->lpFolders[uiFolderIndex].uiNameOffset;
|
|
if(*lpName != '\0')
|
|
{
|
|
// Strip parent folder names.
|
|
const hlChar* lpTemp = strrchr(lpName, '/');
|
|
if(lpTemp != 0)
|
|
{
|
|
lpName = lpTemp + 1;
|
|
}
|
|
lpTemp = strrchr(lpName, '\\');
|
|
if(lpTemp != 0)
|
|
{
|
|
lpName = lpTemp + 1;
|
|
}
|
|
// Check if folder exists.
|
|
CDirectoryItem *pItem = pParent->GetItem(lpName);
|
|
if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
|
|
{
|
|
// It doesn't, create it.
|
|
pParent = pParent->AddFolder(lpName);
|
|
}
|
|
else
|
|
{
|
|
// It does, use it.
|
|
pParent = static_cast<CDirectoryFolder *>(pItem);
|
|
}
|
|
}
|
|
for(hlUInt i = this->lpFolders[uiFolderIndex].uiFolderStartIndex; i < this->lpFolders[uiFolderIndex].uiFolderEndIndex; i++)
|
|
{
|
|
CreateFolder(pParent, i);
|
|
}
|
|
for(hlUInt i = this->lpFolders[uiFolderIndex].uiFileStartIndex; i < this->lpFolders[uiFolderIndex].uiFileEndIndex; i++)
|
|
{
|
|
const hlChar* lpName = this->lpStringTable + this->lpFiles[i].uiNameOffset;
|
|
pParent->AddFile(lpName, i);
|
|
}
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
if(pItem->GetID() != HL_ID_INVALID)
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_ITEM_CRC:
|
|
{
|
|
Mapping::CView *pFileHeaderView = 0;
|
|
if(this->File.pMapping->Map(pFileHeaderView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), sizeof(SGAFileHeader)))
|
|
{
|
|
const SGAFileHeader* pFileHeader = static_cast<const SGAFileHeader *>(pFileHeaderView->GetView());
|
|
hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], pFileHeader->uiCRC32, hlTrue);
|
|
this->File.pMapping->Unmap(pFileHeaderView);
|
|
return hlTrue;
|
|
}
|
|
return hlFalse;
|
|
}
|
|
case HL_SGA_ITEM_VERIFICATION:
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[CSGAFile::VERIFICATION_CRC]);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
if(pItem->GetID() != HL_ID_INVALID)
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_ITEM_CRC:
|
|
{
|
|
hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiCRC32, hlTrue);
|
|
return hlTrue;
|
|
}
|
|
case HL_SGA_ITEM_VERIFICATION:
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[CSGAFile::VERIFICATION_CRC]);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
if(pItem->GetID() != HL_ID_INVALID)
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_ITEM_CRC:
|
|
{
|
|
hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiCRC32, hlTrue);
|
|
return hlTrue;
|
|
}
|
|
case HL_SGA_ITEM_VERIFICATION:
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[File.uiDummy0 < CSGAFile::VERIFICATION_COUNT ? File.uiDummy0 : CSGAFile::VERIFICATION_NONE]);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
if(pItem->GetID() != HL_ID_INVALID)
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FOLDER:
|
|
{
|
|
const CDirectoryFolder *pFolder = static_cast<const CDirectoryFolder *>(pItem);
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_ITEM_SECTION_ALIAS:
|
|
{
|
|
for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
|
|
{
|
|
if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex)
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias);
|
|
return hlTrue;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
case HL_SGA_ITEM_SECTION_NAME:
|
|
{
|
|
for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
|
|
{
|
|
if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex)
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName);
|
|
return hlTrue;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
switch(eAttribute)
|
|
{
|
|
case HL_SGA_ITEM_SECTION_ALIAS:
|
|
{
|
|
for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
|
|
{
|
|
if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex)
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias);
|
|
return hlTrue;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
case HL_SGA_ITEM_SECTION_NAME:
|
|
{
|
|
for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
|
|
{
|
|
if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex)
|
|
{
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName);
|
|
return hlTrue;
|
|
}
|
|
}
|
|
return hlFalse;
|
|
}
|
|
case HL_SGA_ITEM_MODIFIED:
|
|
{
|
|
time_t Time = (time_t)File.uiTimeModified;
|
|
tm *pTime = localtime(&Time);
|
|
|
|
hlChar lpTime[128];
|
|
strftime(lpTime, sizeof(lpTime), "%c", pTime);
|
|
|
|
hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], lpTime);
|
|
return hlTrue;
|
|
}
|
|
case HL_SGA_ITEM_TYPE:
|
|
{
|
|
hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiType, hlFalse);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(pItem, eAttribute, Attribute);
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const
|
|
{
|
|
#if !USE_ZLIB
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
bExtractable = File.uiType == 0;
|
|
#else
|
|
bExtractable = true;
|
|
#endif
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
#if !USE_ZLIB
|
|
if(File.uiType != 0)
|
|
{
|
|
eValidation = HL_VALIDATES_ASSUMED_OK;
|
|
return hlTrue;
|
|
}
|
|
#endif
|
|
|
|
Mapping::CView *pFileHeaderDataView = 0;
|
|
if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), File.uiSizeOnDisk + sizeof(SGAFileHeader)))
|
|
{
|
|
hlULong uiChecksum = 0;
|
|
const SGAFileHeader* pFileHeader = static_cast<const SGAFileHeader*>(pFileHeaderDataView->GetView());
|
|
const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeader) + sizeof(SGAFileHeader);
|
|
#if USE_ZLIB
|
|
hlByte *lpInflateBuffer = 0;
|
|
if(File.uiType != 0)
|
|
{
|
|
lpInflateBuffer = new hlByte[File.uiSize];
|
|
uLongf iInflateSize = File.uiSize;
|
|
switch(uncompress(lpInflateBuffer, &iInflateSize, lpBuffer, static_cast<uLong>(File.uiSizeOnDisk)))
|
|
{
|
|
case Z_OK:
|
|
lpBuffer = lpInflateBuffer;
|
|
break;
|
|
default:
|
|
delete []lpInflateBuffer;
|
|
lpInflateBuffer = 0;
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
if(File.uiType == 0 || lpInflateBuffer != 0)
|
|
#endif
|
|
{
|
|
hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSize;
|
|
|
|
hlBool bCancel = hlFalse;
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
|
|
while(uiTotalBytes < uiFileBytes)
|
|
{
|
|
if(bCancel)
|
|
{
|
|
eValidation = HL_VALIDATES_CANCELED;
|
|
break;
|
|
}
|
|
|
|
hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes);
|
|
uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
|
|
|
|
lpBuffer += uiBufferSize;
|
|
uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
|
|
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
}
|
|
}
|
|
#if USE_ZLIB
|
|
delete []lpInflateBuffer;
|
|
#endif
|
|
if(eValidation == HL_VALIDATES_ASSUMED_OK)
|
|
{
|
|
eValidation = static_cast<hlULong>(pFileHeader->uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
|
|
}
|
|
|
|
this->File.pMapping->Unmap(pFileHeaderDataView);
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
Mapping::CView *pFileHeaderDataView = 0;
|
|
if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
|
|
{
|
|
hlULong uiChecksum = 0;
|
|
const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeaderDataView->GetView());
|
|
hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSizeOnDisk;
|
|
|
|
hlBool bCancel = hlFalse;
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
|
|
while(uiTotalBytes < uiFileBytes)
|
|
{
|
|
if(bCancel)
|
|
{
|
|
eValidation = HL_VALIDATES_CANCELED;
|
|
break;
|
|
}
|
|
|
|
hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes);
|
|
uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
|
|
|
|
lpBuffer += uiBufferSize;
|
|
uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
|
|
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
}
|
|
if(eValidation == HL_VALIDATES_ASSUMED_OK)
|
|
{
|
|
eValidation = static_cast<hlULong>(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
|
|
}
|
|
|
|
this->File.pMapping->Unmap(pFileHeaderDataView);
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
Mapping::CView *pFileHeaderDataView = 0;
|
|
if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
|
|
{
|
|
hlULong uiChecksum = 0;
|
|
const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeaderDataView->GetView());
|
|
hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSizeOnDisk;
|
|
hlULongLong uiBlockSize = this->pDirectoryHeader->uiBlockSize;
|
|
if(uiBlockSize == 0)
|
|
{
|
|
uiBlockSize = HL_SGA_CHECKSUM_LENGTH;
|
|
}
|
|
|
|
Checksum* checksum = 0;
|
|
switch(File.uiDummy0)
|
|
{
|
|
case CSGAFile::VERIFICATION_CRC_BLOCKS:
|
|
checksum = new CRC32Checksum();
|
|
break;
|
|
case CSGAFile::VERIFICATION_MD5_BLOCKS:
|
|
checksum = new MD5Checksum();
|
|
break;
|
|
case CSGAFile::VERIFICATION_SHA1_BLOCKS:
|
|
checksum = new SHA1Checksum();
|
|
break;
|
|
}
|
|
const hlByte *lpHashTable = reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiHashTableOffset + File.uiHashOffset;
|
|
|
|
hlBool bCancel = hlFalse;
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
|
|
while(uiTotalBytes < uiFileBytes)
|
|
{
|
|
if(bCancel)
|
|
{
|
|
eValidation = HL_VALIDATES_CANCELED;
|
|
break;
|
|
}
|
|
|
|
hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + uiBlockSize <= uiFileBytes ? uiBlockSize : uiFileBytes - uiTotalBytes);
|
|
uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
|
|
if(checksum != 0)
|
|
{
|
|
checksum->Initialize();
|
|
checksum->Update(lpBuffer, uiBufferSize);
|
|
if(!checksum->Finalize(lpHashTable))
|
|
{
|
|
eValidation = HL_VALIDATES_CORRUPT;
|
|
break;
|
|
}
|
|
lpHashTable += checksum->GetDigestSize();
|
|
}
|
|
|
|
lpBuffer += uiBufferSize;
|
|
uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
|
|
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
}
|
|
if(eValidation == HL_VALIDATES_ASSUMED_OK)
|
|
{
|
|
eValidation = static_cast<hlULong>(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
|
|
}
|
|
|
|
delete checksum;
|
|
|
|
this->File.pMapping->Unmap(pFileHeaderDataView);
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
uiSize = File.uiSize;
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
uiSize = File.uiSizeOnDisk;
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
|
|
{
|
|
const SGAFile &File = this->lpFiles[pFile->GetID()];
|
|
|
|
if(File.uiType == 0)
|
|
{
|
|
pStream = new Streams::CMappingStream(*this->File.pMapping, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk);
|
|
return hlTrue;
|
|
}
|
|
else
|
|
{
|
|
#if USE_ZLIB
|
|
Mapping::CView *pFileDataView = 0;
|
|
if(this->File.pMapping->Map(pFileDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
|
|
{
|
|
hlBool bResult = hlFalse;
|
|
hlByte *lpInflateBuffer = new hlByte[File.uiSize];
|
|
uLongf iInflateSize = File.uiSize;
|
|
switch(uncompress(lpInflateBuffer, &iInflateSize, static_cast<const hlByte *>(pFileDataView->GetView()), (uLong)File.uiSizeOnDisk))
|
|
{
|
|
case Z_OK:
|
|
pStream = new Streams::CMemoryStream(lpInflateBuffer, iInflateSize);
|
|
bResult = hlTrue;
|
|
break;
|
|
case Z_MEM_ERROR:
|
|
delete []lpInflateBuffer;
|
|
LastError.SetErrorMessage("Deflate Error: Z_MEM_ERROR.");
|
|
break;
|
|
case Z_BUF_ERROR:
|
|
delete []lpInflateBuffer;
|
|
LastError.SetErrorMessage("Deflate Error: Z_BUF_ERROR.");
|
|
break;
|
|
case Z_DATA_ERROR:
|
|
delete []lpInflateBuffer;
|
|
LastError.SetErrorMessage("Deflate Error: Z_DATA_ERROR.");
|
|
break;
|
|
default:
|
|
delete []lpInflateBuffer;
|
|
LastError.SetErrorMessage("Deflate Error: Unknown.");
|
|
break;
|
|
}
|
|
this->File.pMapping->Unmap(pFileDataView);
|
|
return bResult;
|
|
}
|
|
#endif
|
|
return hlFalse;
|
|
}
|
|
}
|
|
|
|
template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
|
|
hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::ReleaseStreamInternal(Streams::IStream &Stream) const
|
|
{
|
|
if(Stream.GetType() == HL_STREAM_MEMORY)
|
|
{
|
|
delete []static_cast<const hlByte *>(static_cast<Streams::CMemoryStream &>(Stream).GetBuffer());
|
|
}
|
|
}
|