diff --git a/HLExtract.Net/HLLib.cs b/HLExtract.Net/HLLib.cs index 2b151a4..2e31431 100644 --- a/HLExtract.Net/HLLib.cs +++ b/HLExtract.Net/HLLib.cs @@ -19,8 +19,8 @@ using System.Runtime.InteropServices; public sealed class HLLib { #region Constants - public const int HL_VERSION_NUMBER = ((2 << 24) | (4 << 16) | (4 << 8) | 0); - public const string HL_VERSION_STRING = "2.4.4"; + public const int HL_VERSION_NUMBER = ((2 << 24) | (4 << 16) | (5 << 8) | 0); + public const string HL_VERSION_STRING = "2.4.5"; public const uint HL_ID_INVALID = 0xffffffff; @@ -206,6 +206,7 @@ public sealed class HLLib HL_SGA_ITEM_MODIFIED, HL_SGA_ITEM_TYPE, HL_SGA_ITEM_CRC, + HL_SGA_ITEM_VERIFICATION, HL_SGA_ITEM_COUNT, HL_VBSP_PACKAGE_VERSION = 0, diff --git a/HLExtract.Net/Properties/AssemblyInfo.cs b/HLExtract.Net/Properties/AssemblyInfo.cs index 2842685..b355ce5 100644 --- a/HLExtract.Net/Properties/AssemblyInfo.cs +++ b/HLExtract.Net/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("2.4.4.0")] -[assembly: AssemblyFileVersion("2.4.4.0")] +[assembly: AssemblyVersion("2.4.5.0")] +[assembly: AssemblyFileVersion("2.4.5.0")] diff --git a/HLExtract/HLExtract.rc b/HLExtract/HLExtract.rc index bbbdf38..60109b3 100644 --- a/HLExtract/HLExtract.rc +++ b/HLExtract/HLExtract.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,4,4,0 - PRODUCTVERSION 2,4,4,0 + FILEVERSION 2,4,5,0 + PRODUCTVERSION 2,4,5,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "Comments", "Half-Life Package Extraction Utility" VALUE "FileDescription", "HLExtract Application" - VALUE "FileVersion", "2.4.4" + VALUE "FileVersion", "2.4.5" VALUE "InternalName", "HLExtract" VALUE "LegalCopyright", "Copyright (C) 2006-2013 Ryan Gregg" VALUE "OriginalFilename", "HLExtract.exe" VALUE "ProductName", " HLExtract Application" - VALUE "ProductVersion", "2.4.4" + VALUE "ProductVersion", "2.4.5" END END BLOCK "VarFileInfo" diff --git a/HLLib/Checksum.cpp b/HLLib/Checksum.cpp index 85faa3b..0131fc1 100644 --- a/HLLib/Checksum.cpp +++ b/HLLib/Checksum.cpp @@ -207,6 +207,17 @@ hlULong HLLib::CRC32(const hlByte *lpBuffer, hlUInt uiBufferSize, hlULong uiCRC) return uiCRC ^ 0xffffffffUL; } +inline hlULong LeftRoate(hlULong value, hlUInt bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + +inline hlULong SwapEndian(hlULong value) +{ + value = ((value << 8) & 0xFF00FF00UL) | ((value >> 8) & 0x00FF00FFUL); + return (value << 16) | (value >> 16); +} + const hlULong lpMD5Table[4][16] = { { @@ -251,11 +262,6 @@ const hlByte lpMD5Padding[64] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -hlULong LeftRoate(hlULong value, hlUInt bits) -{ - return (value << bits) | (value >> (32 - bits)); -} - hlVoid HLLib::MD5_Initialize(MD5Context& context) { context.lpState[0] = 0x67452301UL; @@ -286,7 +292,7 @@ hlVoid HLLib::MD5_Update(MD5Context& context, const hlByte *lpBuffer, hlUInt uiB // Round 1. for(hlULong i = 0; i < 16; ++i) { - hlULong x = (b & c) | ((~b) & d); + hlULong x = d ^ (b & (c ^ d)); hlULong t = d; d = c; @@ -360,5 +366,144 @@ hlVoid HLLib::MD5_Finalize(MD5Context& context, hlByte (&lpDigest)[16]) MD5_Update(context, reinterpret_cast(&uiLengthInBits), sizeof(uiLengthInBits)); - memcpy(lpDigest, context.lpState, sizeof(lpDigest)); + for(hlULong i = 0; i < sizeof(context.lpState) / sizeof(context.lpState[0]); ++i) + { + reinterpret_cast(lpDigest)[i] = context.lpState[i]; + } } + +hlVoid HLLib::SHA1_Initialize(SHA1Context& context) +{ + context.lpState[0] = 0x67452301UL; + context.lpState[1] = 0xEFCDAB89UL; + context.lpState[2] = 0x98BADCFEUL; + context.lpState[3] = 0x10325476UL; + context.lpState[4] = 0xC3D2E1F0UL; + context.uiLength = 0; +} + +hlVoid HLLib::SHA1_Update(SHA1Context& context, const hlByte *lpBuffer, hlUInt uiBufferSize) +{ + hlULong uiBlockLength = context.uiLength % sizeof(context.lpBlock); + while(uiBlockLength + uiBufferSize >= sizeof(context.lpBlock)) + { + hlULong uiCopyLength = std::min(static_cast(uiBufferSize), static_cast(sizeof(context.lpBlock) - uiBlockLength)); + memcpy(reinterpret_cast(context.lpBlock) + uiBlockLength, lpBuffer, uiCopyLength); + context.uiLength += uiCopyLength; + + lpBuffer += uiCopyLength; + uiBufferSize -= uiCopyLength; + + { + hlULong a = context.lpState[0]; + hlULong b = context.lpState[1]; + hlULong c = context.lpState[2]; + hlULong d = context.lpState[3]; + hlULong e = context.lpState[4]; + + hlULong i; + hlULong lpExtendedBlock[80]; + + // Input needs to be big-endian. + for(i = 0; i < 16; ++i) + { + lpExtendedBlock[i] = SwapEndian(context.lpBlock[i]); + } + + // Extend the 16 dwords to 80. + for(; i < 80; ++i) + { + lpExtendedBlock[i] = LeftRoate(lpExtendedBlock[i - 3] ^ lpExtendedBlock[i - 8] ^ lpExtendedBlock[i - 14] ^ lpExtendedBlock[i - 16], 1); + } + + // Round 1. + for(i = 0; i < 20; ++i) + { + hlULong x = d ^ (b & (c ^ d)); + + hlULong t = LeftRoate(a, 5) + x + e + 0x5A827999UL + lpExtendedBlock[i]; + e = d; + d = c; + c = LeftRoate(b, 30); + b = a; + a = t; + } + + // Round 2. + for(; i < 40; ++i) + { + hlULong x = b ^ c ^ d; + + hlULong t = LeftRoate(a, 5) + x + e + 0x6ED9EBA1UL + lpExtendedBlock[i]; + e = d; + d = c; + c = LeftRoate(b, 30); + b = a; + a = t; + } + + // Round 3. + for(; i < 60; ++i) + { + hlULong x = (b & c) | ((b | c) & d); + + hlULong t = LeftRoate(a, 5) + x + e + 0x8F1BBCDCUL + lpExtendedBlock[i]; + e = d; + d = c; + c = LeftRoate(b, 30); + b = a; + a = t; + } + + // Round 4. + for(; i < 80; ++i) + { + hlULong x = b ^ c ^ d; + + hlULong t = LeftRoate(a, 5) + x + e + 0xCA62C1D6UL + lpExtendedBlock[i]; + e = d; + d = c; + c = LeftRoate(b, 30); + b = a; + a = t; + } + + context.lpState[0] += a; + context.lpState[1] += b; + context.lpState[2] += c; + context.lpState[3] += d; + context.lpState[4] += e; + } + + uiBlockLength = 0; + } + + memcpy(reinterpret_cast(context.lpBlock) + uiBlockLength, lpBuffer, uiBufferSize); + context.uiLength += uiBufferSize; +} + +hlVoid HLLib::SHA1_Finalize(SHA1Context& context, hlByte (&lpDigest)[20]) +{ + hlULongLong uiLengthInBits = 8ULL * static_cast(context.uiLength); + + hlULong uiBlockLength = context.uiLength % sizeof(context.lpBlock); + if(uiBlockLength < sizeof(context.lpBlock) - sizeof(hlULongLong)) + { + SHA1_Update(context, lpMD5Padding, sizeof(context.lpBlock) - sizeof(uiLengthInBits) - uiBlockLength); + } + else + { + SHA1_Update(context, lpMD5Padding, 2 * sizeof(context.lpBlock) - sizeof(uiLengthInBits) - uiBlockLength); + } + + // Length needs to be big-endian. + uiLengthInBits = (uiLengthInBits << 32) | (uiLengthInBits >> 32); + uiLengthInBits = static_cast(SwapEndian(static_cast(uiLengthInBits & 0xFFFFFFFFULL))) | static_cast(SwapEndian(static_cast(uiLengthInBits >> 32))) << 32; + SHA1_Update(context, reinterpret_cast(&uiLengthInBits), sizeof(uiLengthInBits)); + + for(hlULong i = 0; i < sizeof(context.lpState) / sizeof(context.lpState[0]); ++i) + { + // Output needs to be big-endian. + reinterpret_cast(lpDigest)[i] = SwapEndian(context.lpState[i]); + } +} \ No newline at end of file diff --git a/HLLib/Checksum.h b/HLLib/Checksum.h index 25ff172..acfdc0d 100644 --- a/HLLib/Checksum.h +++ b/HLLib/Checksum.h @@ -30,6 +30,130 @@ namespace HLLib hlVoid MD5_Initialize(MD5Context& context); hlVoid MD5_Update(MD5Context& context, const hlByte *lpBuffer, hlUInt uiBufferSize); hlVoid MD5_Finalize(MD5Context& context, hlByte (&lpDigest)[16]); + + struct SHA1Context + { + hlULong lpState[5]; + hlULong lpBlock[16]; + hlULong uiLength; + }; + + hlVoid SHA1_Initialize(SHA1Context& context); + hlVoid SHA1_Update(SHA1Context& context, const hlByte *lpBuffer, hlUInt uiBufferSize); + hlVoid SHA1_Finalize(SHA1Context& context, hlByte (&lpDigest)[20]); + + class Checksum + { + public: + virtual ~Checksum() + { + } + + virtual hlULong GetDigestSize() const = 0; + virtual void Initialize() = 0; + virtual void Update(const hlByte *lpBuffer, hlUInt uiBufferSize) = 0; + virtual bool Finalize(const hlByte *lpHash) = 0; + }; + + class CRC32Checksum : public Checksum + { + public: + CRC32Checksum() + { + Initialize(); + } + + virtual hlULong GetDigestSize() const + { + return sizeof(this->uiChecksum); + } + + virtual void Initialize() + { + this->uiChecksum = 0; + } + + virtual void Update(const hlByte *lpBuffer, hlUInt uiBufferSize) + { + this->uiChecksum = CRC32(lpBuffer, uiBufferSize, this->uiChecksum); + } + + virtual bool Finalize(const hlByte *lpHash) + { + return *reinterpret_cast(lpHash) == this->uiChecksum; + } + + private: + hlULong uiChecksum; + }; + + class MD5Checksum : public Checksum + { + public: + MD5Checksum() + { + Initialize(); + } + + virtual hlULong GetDigestSize() const + { + return sizeof(this->context.lpState); + } + + virtual void Initialize() + { + MD5_Initialize(this->context); + } + + virtual void Update(const hlByte *lpBuffer, hlUInt uiBufferSize) + { + MD5_Update(this->context, lpBuffer, uiBufferSize); + } + + virtual bool Finalize(const hlByte *lpHash) + { + hlByte lpDigest[16]; + MD5_Finalize(this->context, lpDigest); + return memcmp(lpHash, lpDigest, sizeof(lpDigest)) == 0; + } + + private: + MD5Context context; + }; + + class SHA1Checksum : public Checksum + { + public: + SHA1Checksum() + { + Initialize(); + } + + virtual hlULong GetDigestSize() const + { + return sizeof(this->context.lpState); + } + + virtual void Initialize() + { + SHA1_Initialize(this->context); + } + + virtual void Update(const hlByte *lpBuffer, hlUInt uiBufferSize) + { + SHA1_Update(this->context, lpBuffer, uiBufferSize); + } + + virtual bool Finalize(const hlByte *lpHash) + { + hlByte lpDigest[20]; + SHA1_Finalize(this->context, lpDigest); + return memcmp(lpHash, lpDigest, sizeof(lpDigest)) == 0; + } + + private: + SHA1Context context; + }; } #endif diff --git a/HLLib/HLLib.rc b/HLLib/HLLib.rc index 56191c2..e24007b 100644 --- a/HLLib/HLLib.rc +++ b/HLLib/HLLib.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,4,4,0 - PRODUCTVERSION 2,4,4,0 + FILEVERSION 2,4,5,0 + PRODUCTVERSION 2,4,5,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "Comments", "Half-Life Package Library" VALUE "FileDescription", "HLLib Dynamic Link Library" - VALUE "FileVersion", "2.4.4" + VALUE "FileVersion", "2.4.5" VALUE "InternalName", "HLLib" VALUE "LegalCopyright", "Copyright (C) 2006-2013 Ryan Gregg" VALUE "OriginalFilename", "HLLib.dll" VALUE "ProductName", " HLLib Dynamic Link Library" - VALUE "ProductVersion", "2.4.4" + VALUE "ProductVersion", "2.4.5" END END BLOCK "VarFileInfo" diff --git a/HLLib/SGAFile.cpp b/HLLib/SGAFile.cpp index 3ae2513..4d35013 100644 --- a/HLLib/SGAFile.cpp +++ b/HLLib/SGAFile.cpp @@ -27,7 +27,8 @@ 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" }; +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) { @@ -56,17 +57,18 @@ const hlChar *CSGAFile::GetDescription() const hlBool CSGAFile::MapDataStructures() { - if(sizeof(SGAHeader) > this->pMapping->GetMappingSize()) + 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, sizeof(SGAHeader))) + if(!this->pMapping->Map(this->pHeaderView, 0, uiMaxHeaderSize)) { return hlFalse; } - this->pHeader = static_cast(this->pHeaderView->GetView()); + this->pHeader = static_cast(this->pHeaderView->GetView()); if(memcmp(this->pHeader->lpSignature, "_ARCHIVE", 8) != 0) { @@ -74,26 +76,53 @@ hlBool CSGAFile::MapDataStructures() return hlFalse; } - if((this->pHeader->uiMajorVersion != 4 || this->pHeader->uiMinorVersion != 0) && (this->pHeader->uiMajorVersion != 5 || this->pHeader->uiMinorVersion != 0)) + 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; } - if(this->pHeader->uiHeaderLength > this->pMapping->GetMappingSize()) - { - LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header."); - return hlFalse; - } - switch(this->pHeader->uiMajorVersion) { case 4: + if(static_cast(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(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(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(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; @@ -185,17 +214,35 @@ hlBool CSGAFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMinorVersion, hlFalse); return hlTrue; case HL_SGA_PACKAGE_MD5_FILE: - BufferToHexString(this->pHeader->lpFileMD5, 16, lpBuffer, sizeof(lpBuffer)); - hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); - return hlTrue; + if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5) + { + BufferToHexString(static_cast(this->pHeader)->lpFileMD5, 16, lpBuffer, sizeof(lpBuffer)); + hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); + return hlTrue; + } + return hlFalse; case HL_SGA_PACKAGE_NAME: - WStringToString(this->pHeader->lpName, lpBuffer, sizeof(lpBuffer)); - hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); - return hlTrue; + if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5) + { + WStringToString(static_cast(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(this->pHeader)->lpName, lpBuffer, sizeof(lpBuffer)); + hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); + return hlTrue; + } + return hlFalse; case HL_SGA_PACKAGE_MD5_HEADER: - BufferToHexString(this->pHeader->lpHeaderMD5, 16, lpBuffer, sizeof(lpBuffer)); - hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); - return hlTrue; + if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5) + { + BufferToHexString(static_cast(this->pHeader)->lpHeaderMD5, 16, lpBuffer, sizeof(lpBuffer)); + hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer); + return hlTrue; + } + return hlFalse; default: return hlFalse; } @@ -221,59 +268,77 @@ CSGAFile::ISGADirectory::~ISGADirectory() } -template -CSGAFile::CSGADirectory::CSGADirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0) +template +CSGAFile::CSGASpecializedDirectory::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0) { } -template -CSGAFile::CSGADirectory::~CSGADirectory() +template +CSGAFile::CSGASpecializedDirectory::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0) +{ + +} + +template +CSGAFile::CSGASpecializedDirectory::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0) +{ + +} + +template +CSGAFile::CSGADirectory::CSGADirectory(CSGAFile& File) : CSGASpecializedDirectory(File) +{ + +} + +template +CSGAFile::CSGADirectory::~CSGADirectory() { this->UnmapDataStructures(); } -template -hlBool CSGAFile::CSGADirectory::MapDataStructures() +template +hlBool CSGAFile::CSGADirectory::MapDataStructures() { - if(!this->File.pMapping->Map(this->pHeaderDirectoryView, sizeof(SGAHeader), this->File.pHeader->uiHeaderLength)) + if(!this->File.pMapping->Map(this->pHeaderDirectoryView, sizeof(SGAHeader), static_cast(this->File.pHeader)->uiHeaderLength)) { return hlFalse; } - this->pDirectoryHeader = static_cast(this->pHeaderDirectoryView->GetView()); + this->pDirectoryHeader = static_cast(this->pHeaderDirectoryView->GetView()); - if(this->pDirectoryHeader->uiSectionCount > 0 && this->pDirectoryHeader->uiSectionOffset + sizeof(TSGASection) * this->pDirectoryHeader->uiSectionCount > this->File.pHeader->uiHeaderLength) + if(this->pDirectoryHeader->uiSectionCount > 0 && this->pDirectoryHeader->uiSectionOffset + sizeof(SGASection) * this->pDirectoryHeader->uiSectionCount > static_cast(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(TSGAFolder) * this->pDirectoryHeader->uiFolderCount > this->File.pHeader->uiHeaderLength) + if(this->pDirectoryHeader->uiFolderCount > 0 && this->pDirectoryHeader->uiFolderOffset + sizeof(SGAFolder) * this->pDirectoryHeader->uiFolderCount > static_cast(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(TSGAFile) * this->pDirectoryHeader->uiFileCount > this->File.pHeader->uiHeaderLength) + if(this->pDirectoryHeader->uiFileCount > 0 && this->pDirectoryHeader->uiFileOffset + sizeof(SGAFile) * this->pDirectoryHeader->uiFileCount > static_cast(this->File.pHeader)->uiHeaderLength) { LastError.SetErrorMessage("Invalid file: the file map is too small for file data."); return hlFalse; } - if(this->pDirectoryHeader->uiStringTableOffset > this->File.pHeader->uiHeaderLength) + if(this->pDirectoryHeader->uiStringTableOffset > static_cast(this->File.pHeader)->uiHeaderLength) { LastError.SetErrorMessage("Invalid file: the file map is too small for string table data."); return hlFalse; } - this->lpSections = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiSectionOffset); - this->lpFolders = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiFolderOffset); - this->lpFiles = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiFileOffset); + this->lpSections = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiSectionOffset); + this->lpFolders = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiFolderOffset); + this->lpFiles = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiFileOffset); this->lpStringTable = reinterpret_cast(reinterpret_cast(this->pDirectoryHeader) + this->pDirectoryHeader->uiStringTableOffset); return hlTrue; } -template -hlVoid CSGAFile::CSGADirectory::UnmapDataStructures() +template +hlVoid CSGAFile::CSGADirectory::UnmapDataStructures() { this->pDirectoryHeader = 0; this->lpSections = 0; @@ -284,8 +349,8 @@ hlVoid CSGAFile::CSGADirectoryFile.pMapping->Unmap(this->pHeaderDirectoryView); } -template -CDirectoryFolder *CSGAFile::CSGADirectory::CreateRoot() +template +CDirectoryFolder *CSGAFile::CSGADirectory::CreateRoot() { CDirectoryFolder *pRoot = new CDirectoryFolder(&File); @@ -310,8 +375,8 @@ CDirectoryFolder *CSGAFile::CSGADirectory -hlVoid CSGAFile::CSGADirectory::CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex) +template +hlVoid CSGAFile::CSGADirectory::CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex) { const hlChar* lpName = this->lpStringTable + this->lpFolders[uiFolderIndex].uiNameOffset; if(*lpName != '\0') @@ -351,8 +416,108 @@ hlVoid CSGAFile::CSGADirectory -hlBool CSGAFile::CSGADirectory::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const +template +hlBool CSGAFile::CSGASpecializedDirectory::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(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(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), sizeof(SGAFileHeader))) + { + const SGAFileHeader* pFileHeader = static_cast(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; + } + } + break; + } + } + } + return hlFalse; +} + +template +hlBool CSGAFile::CSGASpecializedDirectory::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(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; + } + } + break; + } + } + } + return hlFalse; +} + +template +hlBool CSGAFile::CSGASpecializedDirectory::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(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; + } + } + break; + } + } + } + return hlFalse; +} + +template +hlBool CSGAFile::CSGADirectory::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const { if(pItem->GetID() != HL_ID_INVALID) { @@ -393,7 +558,7 @@ hlBool CSGAFile::CSGADirectory(pItem); - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; switch(eAttribute) { case HL_SGA_ITEM_SECTION_ALIAS: @@ -436,31 +601,19 @@ hlBool CSGAFile::CSGADirectoryFile.pMapping->Map(pFileHeaderView, this->File.pHeader->uiFileDataOffset + File.uiOffset - sizeof(TSGAFileHeader), sizeof(TSGAFileHeader))) - { - const TSGAFileHeader* pFileHeader = static_cast(pFileHeaderView->GetView()); - hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], pFileHeader->uiCRC32, hlTrue); - this->File.pMapping->Unmap(pFileHeaderView); - return hlTrue; - } - return hlFalse; - } } break; } } } - return hlFalse; + return CSGASpecializedDirectory::GetItemAttributeInternal(pItem, eAttribute, Attribute); } -template -hlBool CSGAFile::CSGADirectory::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const +template +hlBool CSGAFile::CSGADirectory::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const { #if !USE_ZLIB - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; bExtractable = File.uiType == 0; #else @@ -470,10 +623,10 @@ hlBool CSGAFile::CSGADirectory -hlBool CSGAFile::CSGADirectory::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const +template +hlBool CSGAFile::CSGASpecializedDirectory::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const { - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; #if !USE_ZLIB if(File.uiType != 0) @@ -484,11 +637,11 @@ hlBool CSGAFile::CSGADirectoryFile.pMapping->Map(pFileHeaderDataView, this->File.pHeader->uiFileDataOffset + File.uiOffset - sizeof(TSGAFileHeader), File.uiSizeOnDisk + sizeof(TSGAFileHeader))) + if(this->File.pMapping->Map(pFileHeaderDataView, static_cast(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), File.uiSizeOnDisk + sizeof(SGAFileHeader))) { hlULong uiChecksum = 0; - const TSGAFileHeader* pFileHeader = static_cast(pFileHeaderDataView->GetView()); - const hlByte* lpBuffer = reinterpret_cast(pFileHeader) + sizeof(TSGAFileHeader); + const SGAFileHeader* pFileHeader = static_cast(pFileHeaderDataView->GetView()); + const hlByte* lpBuffer = reinterpret_cast(pFileHeader) + sizeof(SGAFileHeader); #if USE_ZLIB hlByte *lpInflateBuffer = 0; if(File.uiType != 0) @@ -535,7 +688,10 @@ hlBool CSGAFile::CSGADirectory(pFileHeader->uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + if(eValidation == HL_VALIDATES_ASSUMED_OK) + { + eValidation = static_cast(pFileHeader->uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + } this->File.pMapping->Unmap(pFileHeaderDataView); } @@ -547,41 +703,166 @@ hlBool CSGAFile::CSGADirectory -hlBool CSGAFile::CSGADirectory::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const +template +hlBool CSGAFile::CSGASpecializedDirectory::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const { - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; + + Mapping::CView *pFileHeaderDataView = 0; + if(this->File.pMapping->Map(pFileHeaderDataView, static_cast(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk)) + { + hlULong uiChecksum = 0; + const hlByte* lpBuffer = reinterpret_cast(pFileHeaderDataView->GetView()); + hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSizeOnDisk; + + hlBool bCancel = hlFalse; + hlValidateFileProgress(const_cast(pFile), uiTotalBytes, uiFileBytes, &bCancel); + + while(uiTotalBytes < uiFileBytes) + { + if(bCancel) + { + eValidation = HL_VALIDATES_CANCELED; + break; + } + + hlUInt uiBufferSize = static_cast(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes); + uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum); + + lpBuffer += uiBufferSize; + uiTotalBytes += static_cast(uiBufferSize); + + hlValidateFileProgress(const_cast(pFile), uiTotalBytes, uiFileBytes, &bCancel); + } + if(eValidation == HL_VALIDATES_ASSUMED_OK) + { + eValidation = static_cast(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + } + + this->File.pMapping->Unmap(pFileHeaderDataView); + } + else + { + eValidation = HL_VALIDATES_ERROR; + } + + return hlTrue; +} + +template +hlBool CSGAFile::CSGASpecializedDirectory::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(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk)) + { + hlULong uiChecksum = 0; + const hlByte* lpBuffer = reinterpret_cast(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(this->pDirectoryHeader) + this->pDirectoryHeader->uiHashTableOffset + File.uiHashOffset; + + hlBool bCancel = hlFalse; + hlValidateFileProgress(const_cast(pFile), uiTotalBytes, uiFileBytes, &bCancel); + + while(uiTotalBytes < uiFileBytes) + { + if(bCancel) + { + eValidation = HL_VALIDATES_CANCELED; + break; + } + + hlUInt uiBufferSize = static_cast(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(uiBufferSize); + + hlValidateFileProgress(const_cast(pFile), uiTotalBytes, uiFileBytes, &bCancel); + } + if(eValidation == HL_VALIDATES_ASSUMED_OK) + { + eValidation = static_cast(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + } + + delete checksum; + + this->File.pMapping->Unmap(pFileHeaderDataView); + } + else + { + eValidation = HL_VALIDATES_ERROR; + } + + return hlTrue; +} + +template +hlBool CSGAFile::CSGADirectory::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const +{ + const SGAFile &File = this->lpFiles[pFile->GetID()]; uiSize = File.uiSize; return hlTrue; } -template -hlBool CSGAFile::CSGADirectory::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const +template +hlBool CSGAFile::CSGADirectory::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const { - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; uiSize = File.uiSizeOnDisk; return hlTrue; } -template -hlBool CSGAFile::CSGADirectory::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const +template +hlBool CSGAFile::CSGADirectory::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const { - const TSGAFile &File = this->lpFiles[pFile->GetID()]; + const SGAFile &File = this->lpFiles[pFile->GetID()]; if(File.uiType == 0) { - pStream = new Streams::CMappingStream(*this->File.pMapping, this->File.pHeader->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk); + pStream = new Streams::CMappingStream(*this->File.pMapping, static_cast(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk); return hlTrue; } else { #if USE_ZLIB Mapping::CView *pFileDataView = 0; - if(this->File.pMapping->Map(pFileDataView, this->File.pHeader->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk)) + if(this->File.pMapping->Map(pFileDataView, static_cast(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk)) { hlBool bResult = hlFalse; hlByte *lpInflateBuffer = new hlByte[File.uiSize]; @@ -617,8 +898,8 @@ hlBool CSGAFile::CSGADirectory -hlVoid CSGAFile::CSGADirectory::ReleaseStreamInternal(Streams::IStream &Stream) const +template +hlVoid CSGAFile::CSGADirectory::ReleaseStreamInternal(Streams::IStream &Stream) const { if(Stream.GetType() == HL_STREAM_MEMORY) { diff --git a/HLLib/SGAFile.h b/HLLib/SGAFile.h index 65a9142..9532dda 100644 --- a/HLLib/SGAFile.h +++ b/HLLib/SGAFile.h @@ -22,11 +22,15 @@ namespace HLLib private: #pragma pack(1) - struct SGAHeader + struct SGAHeaderBase { hlChar lpSignature[8]; hlUShort uiMajorVersion; hlUShort uiMinorVersion; + }; + + struct SGAHeader4 : public SGAHeaderBase + { hlByte lpFileMD5[16]; hlWChar lpName[64]; hlByte lpHeaderMD5[16]; @@ -35,6 +39,14 @@ namespace HLLib hlUInt uiDummy0; }; + struct SGAHeader6 : public SGAHeaderBase + { + hlWChar lpName[64]; + hlUInt uiHeaderLength; + hlUInt uiFileDataOffset; + hlUInt uiDummy0; + }; + template struct SGADirectoryHeader { @@ -51,6 +63,12 @@ namespace HLLib typedef SGADirectoryHeader SGADirectoryHeader4; typedef SGADirectoryHeader SGADirectoryHeader5; + struct SGADirectoryHeader7 : public SGADirectoryHeader5 + { + hlUInt uiHashTableOffset; + hlUInt uiBlockSize; + }; + template struct SGASection { @@ -79,7 +97,7 @@ namespace HLLib typedef SGAFolder SGAFolder4; typedef SGAFolder SGAFolder5; - struct SGAFile + struct SGAFile4 { hlUInt uiNameOffset; hlUInt uiOffset; @@ -90,12 +108,32 @@ namespace HLLib hlByte uiType; }; + struct SGAFile6 : public SGAFile4 + { + hlUInt uiCRC32; + }; + + struct SGAFile7 : public SGAFile6 + { + hlUInt uiHashOffset; + }; + struct SGAFileHeader { hlChar lpName[256]; hlUInt uiCRC32; }; + enum SGAFileVerification + { + VERIFICATION_NONE, + VERIFICATION_CRC, + VERIFICATION_CRC_BLOCKS, + VERIFICATION_MD5_BLOCKS, + VERIFICATION_SHA1_BLOCKS, + VERIFICATION_COUNT, + }; + #pragma pack() class ISGADirectory @@ -120,23 +158,100 @@ namespace HLLib virtual hlVoid ReleaseStreamInternal(Streams::IStream &Stream) const = 0; }; - template - class CSGADirectory : public ISGADirectory + // Specialization SGAFile7 and up where the CRC moved to the header and the CRC is of the compressed data and there are stronger hashes. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef TSGAFile SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + // Specialization SGAFile4 where the CRC was stored in a SGAFileHeader located before the file data. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef CSGAFile::SGAFile4 SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + // Specialization SGAFile6 where the CRC moved to the header and the CRC is of the compressed data. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef CSGAFile::SGAFile6 SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + template + class CSGADirectory : public CSGASpecializedDirectory { public: CSGADirectory(CSGAFile& File); virtual ~CSGADirectory(); - private: - CSGAFile& File; - - Mapping::CView *pHeaderDirectoryView; - const TSGADirectoryHeader *pDirectoryHeader; - const TSGASection *lpSections; - const TSGAFolder *lpFolders; - const TSGAFile *lpFiles; - const hlChar *lpStringTable; - public: virtual hlBool MapDataStructures(); virtual hlVoid UnmapDataStructures(); @@ -146,7 +261,6 @@ namespace HLLib virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; virtual hlBool GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const; - virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; virtual hlBool GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const; virtual hlBool GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const; @@ -157,18 +271,23 @@ namespace HLLib hlVoid CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex); }; - typedef CSGADirectory CSGADirectory4; - typedef CSGADirectory CSGADirectory5; + typedef CSGADirectory CSGADirectory4; + typedef CSGADirectory CSGADirectory5; + typedef CSGADirectory CSGADirectory6; + typedef CSGADirectory CSGADirectory7; friend CSGADirectory4; friend CSGADirectory5; + friend CSGADirectory6; + friend CSGADirectory7; private: static const char *lpAttributeNames[]; static const char *lpItemAttributeNames[]; + static const char *lpVerificationNames[]; Mapping::CView *pHeaderView; - const SGAHeader *pHeader; + const SGAHeaderBase *pHeader; ISGADirectory* pDirectory; diff --git a/HLLib/VBSPFile.cpp b/HLLib/VBSPFile.cpp index 6fe4b91..52a9575 100644 --- a/HLLib/VBSPFile.cpp +++ b/HLLib/VBSPFile.cpp @@ -539,7 +539,10 @@ hlBool CVBSPFile::GetFileValidationInternal(const CDirectoryFile *pFile, HLValid delete pStream; } - eValidation = (hlULong)pDirectoryItem->uiCRC32 == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + if(eValidation == HL_VALIDATES_ASSUMED_OK) + { + eValidation = (hlULong)pDirectoryItem->uiCRC32 == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + } } else { diff --git a/HLLib/ZIPFile.cpp b/HLLib/ZIPFile.cpp index af7e01b..e840617 100644 --- a/HLLib/ZIPFile.cpp +++ b/HLLib/ZIPFile.cpp @@ -467,7 +467,10 @@ hlBool CZIPFile::GetFileValidationInternal(const CDirectoryFile *pFile, HLValida delete pStream; } - eValidation = (hlULong)pDirectoryItem->uiCRC32 == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + if(eValidation == HL_VALIDATES_ASSUMED_OK) + { + eValidation = (hlULong)pDirectoryItem->uiCRC32 == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; + } return hlTrue; } diff --git a/HLLib/stdafx.h b/HLLib/stdafx.h index bfb9c71..9bd1f72 100644 --- a/HLLib/stdafx.h +++ b/HLLib/stdafx.h @@ -65,8 +65,8 @@ typedef hlSingle hlFloat; #define hlFalse 0 #define hlTrue 1 -#define HL_VERSION_NUMBER ((2 << 24) | (4 << 16) | (4 << 8) | 0) -#define HL_VERSION_STRING "2.4.4" +#define HL_VERSION_NUMBER ((2 << 24) | (4 << 16) | (5 << 8) | 0) +#define HL_VERSION_STRING "2.4.5" #define HL_ID_INVALID 0xffffffff @@ -254,6 +254,7 @@ typedef enum HL_SGA_ITEM_MODIFIED, HL_SGA_ITEM_TYPE, HL_SGA_ITEM_CRC, + HL_SGA_ITEM_VERIFICATION, HL_SGA_ITEM_COUNT, HL_VBSP_PACKAGE_VERSION = 0, diff --git a/Readme.txt b/Readme.txt index e794195..e7e65fc 100644 --- a/Readme.txt +++ b/Readme.txt @@ -9,11 +9,11 @@ Library/Author Information: =========================== ---- General Library Information ---- -Date : January 27th, 2013 +Date : October 17th, 2013 Author : Ryan Gregg Michael Mohr (Linux Port) Title : HLLib -Build : 2.4.4 +Build : 2.4.5 Email address : ryansgregg@hotmail.com (Ryan Gregg) m.mohr@laposte.net (Michael Mohr) Home page / Website : http://nemesis.thewavelength.net/ @@ -93,6 +93,10 @@ Console Commands (Interactive console mode): Library Changelog: ================== + v2.4.5 + - Added support for new SGA file format (v6). + - Added support for new SGA file format (v7). + v2.4.4 - Fixed support for VPK file format (v1). diff --git a/lib/HLLib.h b/lib/HLLib.h index 6c08c88..a706a8d 100644 --- a/lib/HLLib.h +++ b/lib/HLLib.h @@ -65,8 +65,8 @@ typedef hlSingle hlFloat; #define hlFalse 0 #define hlTrue 1 -#define HL_VERSION_NUMBER ((2 << 24) | (4 << 16) | (4 << 8) | 0) -#define HL_VERSION_STRING "2.4.4" +#define HL_VERSION_NUMBER ((2 << 24) | (4 << 16) | (5 << 8) | 0) +#define HL_VERSION_STRING "2.4.5" #define HL_ID_INVALID 0xffffffff @@ -258,6 +258,7 @@ typedef enum HL_SGA_ITEM_MODIFIED, HL_SGA_ITEM_TYPE, HL_SGA_ITEM_CRC, + HL_SGA_ITEM_VERIFICATION, HL_SGA_ITEM_COUNT, HL_VBSP_PACKAGE_VERSION = 0, @@ -1884,16 +1885,20 @@ namespace HLLib // CSGAFile // - class HLLIB_API CSGAFile : public CPackage +class HLLIB_API CSGAFile : public CPackage { private: #pragma pack(1) - struct SGAHeader + struct SGAHeaderBase { hlChar lpSignature[8]; hlUShort uiMajorVersion; hlUShort uiMinorVersion; + }; + + struct SGAHeader4 : public SGAHeaderBase + { hlByte lpFileMD5[16]; hlWChar lpName[64]; hlByte lpHeaderMD5[16]; @@ -1902,6 +1907,14 @@ namespace HLLib hlUInt uiDummy0; }; + struct SGAHeader6 : public SGAHeaderBase + { + hlWChar lpName[64]; + hlUInt uiHeaderLength; + hlUInt uiFileDataOffset; + hlUInt uiDummy0; + }; + template struct SGADirectoryHeader { @@ -1918,6 +1931,12 @@ namespace HLLib typedef SGADirectoryHeader SGADirectoryHeader4; typedef SGADirectoryHeader SGADirectoryHeader5; + struct SGADirectoryHeader7 : public SGADirectoryHeader5 + { + hlUInt uiHashTableOffset; + hlUInt uiBlockSize; + }; + template struct SGASection { @@ -1946,7 +1965,7 @@ namespace HLLib typedef SGAFolder SGAFolder4; typedef SGAFolder SGAFolder5; - struct SGAFile + struct SGAFile4 { hlUInt uiNameOffset; hlUInt uiOffset; @@ -1957,12 +1976,32 @@ namespace HLLib hlByte uiType; }; + struct SGAFile6 : public SGAFile4 + { + hlUInt uiCRC32; + }; + + struct SGAFile7 : public SGAFile6 + { + hlUInt uiHashOffset; + }; + struct SGAFileHeader { hlChar lpName[256]; hlUInt uiCRC32; }; + enum SGAFileVerification + { + VERIFICATION_NONE, + VERIFICATION_CRC, + VERIFICATION_CRC_BLOCKS, + VERIFICATION_MD5_BLOCKS, + VERIFICATION_SHA1_BLOCKS, + VERIFICATION_COUNT, + }; + #pragma pack() class ISGADirectory @@ -1987,23 +2026,100 @@ namespace HLLib virtual hlVoid ReleaseStreamInternal(Streams::IStream &Stream) const = 0; }; - template - class CSGADirectory : public ISGADirectory + // Specialization SGAFile7 and up where the CRC moved to the header and the CRC is of the compressed data and there are stronger hashes. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef TSGAFile SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + // Specialization SGAFile4 where the CRC was stored in a SGAFileHeader located before the file data. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef CSGAFile::SGAFile4 SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + // Specialization SGAFile6 where the CRC moved to the header and the CRC is of the compressed data. + template + class CSGASpecializedDirectory : public ISGADirectory + { + public: + typedef TSGAHeader SGAHeader; + typedef TSGADirectoryHeader SGADirectoryHeader; + typedef TSGASection SGASection; + typedef TSGAFolder SGAFolder; + typedef CSGAFile::SGAFile6 SGAFile; + + CSGASpecializedDirectory(CSGAFile& File); + + protected: + CSGAFile& File; + + Mapping::CView *pHeaderDirectoryView; + const SGADirectoryHeader *pDirectoryHeader; + const SGASection *lpSections; + const SGAFolder *lpFolders; + const SGAFile *lpFiles; + const hlChar *lpStringTable; + + public: + virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; + + virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; + }; + + template + class CSGADirectory : public CSGASpecializedDirectory { public: CSGADirectory(CSGAFile& File); virtual ~CSGADirectory(); - private: - CSGAFile& File; - - Mapping::CView *pHeaderDirectoryView; - const TSGADirectoryHeader *pDirectoryHeader; - const TSGASection *lpSections; - const TSGAFolder *lpFolders; - const TSGAFile *lpFiles; - const hlChar *lpStringTable; - public: virtual hlBool MapDataStructures(); virtual hlVoid UnmapDataStructures(); @@ -2013,7 +2129,6 @@ namespace HLLib virtual hlBool GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const; virtual hlBool GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const; - virtual hlBool GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const; virtual hlBool GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const; virtual hlBool GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const; @@ -2024,18 +2139,23 @@ namespace HLLib hlVoid CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex); }; - typedef CSGADirectory CSGADirectory4; - typedef CSGADirectory CSGADirectory5; + typedef CSGADirectory CSGADirectory4; + typedef CSGADirectory CSGADirectory5; + typedef CSGADirectory CSGADirectory6; + typedef CSGADirectory CSGADirectory7; friend CSGADirectory4; friend CSGADirectory5; + friend CSGADirectory6; + friend CSGADirectory7; private: static const char *lpAttributeNames[]; static const char *lpItemAttributeNames[]; + static const char *lpVerificationNames[]; Mapping::CView *pHeaderView; - const SGAHeader *pHeader; + const SGAHeaderBase *pHeader; ISGADirectory* pDirectory;