From 810c6b9f95775ce3359bce8fff28b3f5dc7ecb55 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 4 Apr 2025 20:36:58 -0400 Subject: [PATCH] Update fastfile class logic. --- libs/fastfile/fastfile_cod2.cpp | 11 +- libs/fastfile/fastfile_cod2.h | 2 + libs/fastfile/fastfile_cod5.cpp | 10 +- libs/fastfile/fastfile_cod5.h | 2 + libs/fastfile/fastfile_cod7.cpp | 172 ++++++++++++++++++-------------- libs/fastfile/fastfile_cod7.h | 2 + libs/fastfile/fastfile_cod9.cpp | 11 +- libs/fastfile/fastfile_cod9.h | 2 + 8 files changed, 129 insertions(+), 83 deletions(-) diff --git a/libs/fastfile/fastfile_cod2.cpp b/libs/fastfile/fastfile_cod2.cpp index 473b7bf..8c58687 100644 --- a/libs/fastfile/fastfile_cod2.cpp +++ b/libs/fastfile/fastfile_cod2.cpp @@ -1,7 +1,7 @@ #include "fastfile_cod2.h" #include "utils.h" -#include "compressor.h" +#include "compression.h" #include "zonefile_cod2.h" #include @@ -15,6 +15,10 @@ FastFile_COD2::~FastFile_COD2() { } +QByteArray FastFile_COD2::GetBinaryData() { + return QByteArray(); +} + bool FastFile_COD2::Load(const QString aFilePath) { if (aFilePath.isEmpty()) { return false; @@ -58,7 +62,7 @@ bool FastFile_COD2::Load(const QByteArray aData) { Utils::ReadUntilHex(&fastFileStream, "78"); QByteArray compressedData = aData.mid(fastFileStream.device()->pos()); - QByteArray decompressedData = Compressor::DecompressZLIB(compressedData); + QByteArray decompressedData = Compression::DecompressZLIB(compressedData); QDir exportsDir(QDir::currentPath()); exportsDir.mkdir("exports"); @@ -71,7 +75,8 @@ bool FastFile_COD2::Load(const QByteArray aData) { // Load the zone file with the decompressed data (using an Xbox platform flag). ZoneFile_COD2 zoneFile; - zoneFile.Load(decompressedData, GetStem() + ".zone", FF_PLATFORM_XBOX); + zoneFile.SetStem(GetStem()); + zoneFile.Load(decompressedData, FF_PLATFORM_XBOX); SetZoneFile(std::make_shared(zoneFile)); return true; diff --git a/libs/fastfile/fastfile_cod2.h b/libs/fastfile/fastfile_cod2.h index b8dd02d..91adfe9 100644 --- a/libs/fastfile/fastfile_cod2.h +++ b/libs/fastfile/fastfile_cod2.h @@ -9,6 +9,8 @@ public: FastFile_COD2(); ~FastFile_COD2(); + QByteArray GetBinaryData() override; + bool Load(const QString aFilePath) override; bool Load(const QByteArray aData) override; }; diff --git a/libs/fastfile/fastfile_cod5.cpp b/libs/fastfile/fastfile_cod5.cpp index 71778eb..50d11f2 100644 --- a/libs/fastfile/fastfile_cod5.cpp +++ b/libs/fastfile/fastfile_cod5.cpp @@ -2,7 +2,7 @@ #include "zonefile_cod5.h" #include "utils.h" -#include "compressor.h" +#include "compression.h" #include "statusbarmanager.h" #include @@ -16,6 +16,10 @@ FastFile_COD5::~FastFile_COD5() { } +QByteArray FastFile_COD5::GetBinaryData() { + return QByteArray(); +} + bool FastFile_COD5::Load(const QString aFilePath) { StatusBarManager::instance().updateStatus("Loading COD5 Fast File w/path", 1000); @@ -64,7 +68,7 @@ bool FastFile_COD5::Load(const QByteArray aData) { SetGame("COD5"); // For COD5, simply decompress from offset 12. - decompressedData = Compressor::DecompressZLIB(aData.mid(12)); + decompressedData = Compression::DecompressZLIB(aData.mid(12)); Utils::ExportData(GetStem() + ".zone", decompressedData); @@ -77,7 +81,7 @@ bool FastFile_COD5::Load(const QByteArray aData) { ZoneFile_COD5 zoneFile; zoneFile.SetStem(GetStem()); - //zoneFile.Load(decompressedData, GetStem() + ".zone", platform); + zoneFile.Load(decompressedData, platform); SetZoneFile(std::make_shared(zoneFile)); return true; diff --git a/libs/fastfile/fastfile_cod5.h b/libs/fastfile/fastfile_cod5.h index aee2e9e..0622ce3 100644 --- a/libs/fastfile/fastfile_cod5.h +++ b/libs/fastfile/fastfile_cod5.h @@ -9,6 +9,8 @@ public: FastFile_COD5(); ~FastFile_COD5(); + QByteArray GetBinaryData() override; + bool Load(const QString aFilePath) override; bool Load(const QByteArray aData) override; }; diff --git a/libs/fastfile/fastfile_cod7.cpp b/libs/fastfile/fastfile_cod7.cpp index 6d1c977..92f34c2 100644 --- a/libs/fastfile/fastfile_cod7.cpp +++ b/libs/fastfile/fastfile_cod7.cpp @@ -2,7 +2,8 @@ #include "zonefile_cod7.h" #include "utils.h" -#include "compressor.h" +#include "compression.h" +#include "encryption.h" #include #include @@ -15,6 +16,10 @@ FastFile_COD7::~FastFile_COD7() { } +QByteArray FastFile_COD7::GetBinaryData() { + return QByteArray(); +} + bool FastFile_COD7::Load(const QString aFilePath) { if (aFilePath.isEmpty()) { return false; @@ -58,84 +63,105 @@ bool FastFile_COD7::Load(const QByteArray aData) { SetPlatform(pCalculateFFPlatform(version)); SetGame("COD7"); + // Load the zone file with the decompressed data (using an Xbox platform flag). + ZoneFile_COD7 zoneFile; + zoneFile.SetStem(GetStem()); + // For COD7/COD9, use BigEndian. fastFileStream.setByteOrder(QDataStream::BigEndian); if (GetPlatform() == "PC") { fastFileStream.setByteOrder(QDataStream::LittleEndian); + + // Select key based on game. + QByteArray key; + fastFileStream.skipRawData(4); + if (GetPlatform() == "360") { + key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d"); + } else if (GetPlatform() == "PS3") { + key = QByteArray::fromHex("46D3F997F29C9ACE175B0DAE3AB8C0C1B8E423E2E3BF7E3C311EA35245BF193A"); + // or + // key = QByteArray::fromHex("0C99B3DDB8D6D0845D1147E470F28A8BF2AE69A8A9F534767B54E9180FF55370"); + } + + // Read the 8-byte magic. + QByteArray fileMagic(8, Qt::Uninitialized); + fastFileStream.readRawData(fileMagic.data(), 8); + if (fileMagic != "PHEEBs71") { + qWarning() << "Invalid fast file magic!"; + return false; + } + fastFileStream.skipRawData(4); + + // Read IV table name (32 bytes). + QByteArray fileName(32, Qt::Uninitialized); + fastFileStream.readRawData(fileName.data(), 32); + + // Build the IV table from the fileName. + QByteArray ivTable = Encryption::InitIVTable(fileName); + + // Skip the RSA signature (256 bytes). + QByteArray rsaSignature(256, Qt::Uninitialized); + fastFileStream.readRawData(rsaSignature.data(), 256); + + // Now the stream should be positioned at 0x13C, where sections begin. + int sectionIndex = 0; + while (true) { + qint32 sectionSize = 0; + fastFileStream >> sectionSize; + qDebug() << "Section index:" << sectionIndex << "Size:" << sectionSize + << "Pos:" << fastFileStream.device()->pos(); + if (sectionSize == 0) + break; + + // Read the section data. + QByteArray sectionData; + sectionData.resize(sectionSize); + fastFileStream.readRawData(sectionData.data(), sectionSize); + + // Compute the IV for this section. + QByteArray iv = Encryption::GetIV(ivTable, sectionIndex); + + // Decrypt the section using Salsa20. + QByteArray decData = Encryption::salsa20DecryptSection(sectionData, key, iv); + + // Compute SHA1 hash of the decrypted data. + QByteArray sectionHash = QCryptographicHash::hash(decData, QCryptographicHash::Sha1); + + // Update the IV table based on the section hash. + Encryption::UpdateIVTable(ivTable, sectionIndex, sectionHash); + + // Build a compressed data buffer by prepending the two-byte zlib header. + QByteArray compressedData; + compressedData.append(char(0x78)); + compressedData.append(char(0x01)); + compressedData.append(decData); + + decompressedData.append(Compression::DecompressZLIB(compressedData)); + + sectionIndex++; + } + + zoneFile.Load(decompressedData, FF_PLATFORM_XBOX); + } else if (GetPlatform() == "Wii") { + // For COD7, simply decompress from offset 12. + decompressedData = Compression::DecompressZLIB(aData.mid(12)); + + Utils::ExportData(GetStem() + ".zone", decompressedData); + + QDir workingDir = QDir::currentPath(); + workingDir.mkdir("exports"); + + QFile outputFile("exports/" + GetStem() + ".zone"); + if (!outputFile.open(QIODevice::WriteOnly)) { + qDebug() << "Failed to extract IPAK file."; + } + qDebug() << " - File Name: " << outputFile.fileName(); + outputFile.write(decompressedData); + outputFile.close(); + + zoneFile.Load(decompressedData, FF_PLATFORM_WII); } - // Select key based on game. - QByteArray key; - fastFileStream.skipRawData(4); - if (GetPlatform() == "360") { - key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d"); - } else if (GetPlatform() == "PS3") { - key = QByteArray::fromHex("46D3F997F29C9ACE175B0DAE3AB8C0C1B8E423E2E3BF7E3C311EA35245BF193A"); - // or - // key = QByteArray::fromHex("0C99B3DDB8D6D0845D1147E470F28A8BF2AE69A8A9F534767B54E9180FF55370"); - } - - // Read the 8-byte magic. - QByteArray fileMagic(8, Qt::Uninitialized); - fastFileStream.readRawData(fileMagic.data(), 8); - if (fileMagic != "PHEEBs71") { - qWarning() << "Invalid fast file magic!"; - return false; - } - fastFileStream.skipRawData(4); - - // Read IV table name (32 bytes). - QByteArray fileName(32, Qt::Uninitialized); - fastFileStream.readRawData(fileName.data(), 32); - - // Build the IV table from the fileName. - QByteArray ivTable = Encryption::InitIVTable(fileName); - - // Skip the RSA signature (256 bytes). - QByteArray rsaSignature(256, Qt::Uninitialized); - fastFileStream.readRawData(rsaSignature.data(), 256); - - // Now the stream should be positioned at 0x13C, where sections begin. - int sectionIndex = 0; - while (true) { - qint32 sectionSize = 0; - fastFileStream >> sectionSize; - qDebug() << "Section index:" << sectionIndex << "Size:" << sectionSize - << "Pos:" << fastFileStream.device()->pos(); - if (sectionSize == 0) - break; - - // Read the section data. - QByteArray sectionData; - sectionData.resize(sectionSize); - fastFileStream.readRawData(sectionData.data(), sectionSize); - - // Compute the IV for this section. - QByteArray iv = Encryption::GetIV(ivTable, sectionIndex); - - // Decrypt the section using Salsa20. - QByteArray decData = Encryption::salsa20DecryptSection(sectionData, key, iv); - - // Compute SHA1 hash of the decrypted data. - QByteArray sectionHash = QCryptographicHash::hash(decData, QCryptographicHash::Sha1); - - // Update the IV table based on the section hash. - Encryption::UpdateIVTable(ivTable, sectionIndex, sectionHash); - - // Build a compressed data buffer by prepending the two-byte zlib header. - QByteArray compressedData; - compressedData.append(char(0x78)); - compressedData.append(char(0x01)); - compressedData.append(decData); - - decompressedData.append(Compressor::DecompressZLIB(compressedData)); - - sectionIndex++; - } - - // Load the zone file with the decompressed data (using an Xbox platform flag). - ZoneFile_COD7 zoneFile; - zoneFile.Load(decompressedData, GetStem() + ".zone", FF_PLATFORM_XBOX); SetZoneFile(std::make_shared(zoneFile)); return true; diff --git a/libs/fastfile/fastfile_cod7.h b/libs/fastfile/fastfile_cod7.h index 0bcb016..ae2d478 100644 --- a/libs/fastfile/fastfile_cod7.h +++ b/libs/fastfile/fastfile_cod7.h @@ -9,6 +9,8 @@ public: FastFile_COD7(); ~FastFile_COD7(); + QByteArray GetBinaryData() override; + bool Load(const QString aFilePath) override; bool Load(const QByteArray aData) override; }; diff --git a/libs/fastfile/fastfile_cod9.cpp b/libs/fastfile/fastfile_cod9.cpp index c25f1f2..353b0a0 100644 --- a/libs/fastfile/fastfile_cod9.cpp +++ b/libs/fastfile/fastfile_cod9.cpp @@ -1,8 +1,6 @@ #include "fastfile_cod9.h" #include "zonefile_cod9.h" - -#include "utils.h" -#include "compressor.h" +#include "encryption.h" #include #include @@ -15,6 +13,10 @@ FastFile_COD9::~FastFile_COD9() { } +QByteArray FastFile_COD9::GetBinaryData() { + return QByteArray(); +} + bool FastFile_COD9::Load(const QString aFilePath) { if (aFilePath.isEmpty()) { return false; @@ -104,7 +106,8 @@ bool FastFile_COD9::Load(const QByteArray aData) { // Load the zone file with the decompressed data (using an Xbox platform flag). ZoneFile_COD9 zoneFile; - zoneFile.Load(decompressedData, GetStem() + ".zone", FF_PLATFORM_PC); + zoneFile.SetStem(GetStem()); + zoneFile.Load(decompressedData, FF_PLATFORM_PC); SetZoneFile(std::make_shared(zoneFile)); return true; diff --git a/libs/fastfile/fastfile_cod9.h b/libs/fastfile/fastfile_cod9.h index e5293d9..fdc3d16 100644 --- a/libs/fastfile/fastfile_cod9.h +++ b/libs/fastfile/fastfile_cod9.h @@ -9,6 +9,8 @@ public: FastFile_COD9(); ~FastFile_COD9(); + QByteArray GetBinaryData() override; + bool Load(const QString aFilePath) override; bool Load(const QByteArray aData) override; };