Compare commits

...

11 Commits

97 changed files with 1056 additions and 900 deletions

View File

@ -2,9 +2,9 @@ TEMPLATE = subdirs
SUBDIRS += libs \
app \
tools \
tests
#tools \
#tests
tests.depends = libs
#tests.depends = libs
app.depends = libs
tools.depends = libs
#tools.depends = libs

View File

@ -533,7 +533,7 @@ bool MainWindow::OpenFastFile(const QString aFastFilePath) {
return false;
}
FastFile* fastFile = FastFileFactory::Create(aFastFilePath);
FastFile* fastFile = FastFile::Open(aFastFilePath);
fastFile->SetStem(fastFileStem);
mTreeWidget->AddFastFile(fastFile);
@ -548,7 +548,7 @@ bool MainWindow::OpenFastFile(const QByteArray& aFastFileData, const QString aFa
return false;
}
FastFile* fastFile = FastFileFactory::Create(aFastFileData);
FastFile* fastFile = FastFile::Open(aFastFileData);
fastFile->SetStem(fastFileStem);
mTreeWidget->AddFastFile(fastFile);

View File

@ -31,7 +31,8 @@ QByteArray Compression::CompressXMem(const QByteArray &data)
return output;
}
QByteArray Compression::DecompressXMem(const QByteArray &data, int flags, int windowSize, int partSize)
QByteArray Compression::DecompressXMem(const QByteArray &data,
int flags, int windowSize, int partSize)
{
if (data.isEmpty())
return {};
@ -41,27 +42,49 @@ QByteArray Compression::DecompressXMem(const QByteArray &data, int flags, int wi
lzxParams.WindowSize = windowSize;
lzxParams.CompressionPartitionSize = partSize;
XMEMDECOMPRESSION_CONTEXT ctx = nullptr;
if (FAILED(XMemCreateDecompressionContext(XMEMCODEC_LZX, &lzxParams, 0, &ctx)) || !ctx)
return {};
QByteArray internalState(0x94933, Qt::Uninitialized);
// Allocate large enough buffer for decompression (16 MB is a safe upper bound)
const SIZE_T kMaxOutSize = 16 * 1024 * 1024;
QByteArray output(static_cast<int>(kMaxOutSize), Qt::Uninitialized);
SIZE_T actualSize = kMaxOutSize;
XMEMDECOMPRESSION_CONTEXT ctx = XMemInitializeDecompressionContext(
XMEMCODEC_LZX, &lzxParams, 1,
internalState.data(), internalState.size());
HRESULT hr = XMemDecompress(ctx,
output.data(), &actualSize,
data.constData(), data.size() + 16);
XMemDestroyDecompressionContext(ctx);
if (FAILED(hr)) {
qWarning() << "XMemDecompress failed with HRESULT:" << hr;
if (!ctx || XMemResetDecompressionContext(ctx)) {
qWarning() << "Failed to init LZX context";
return {};
}
output.resize(static_cast<int>(actualSize));
QByteArray output;
output.reserve(16 * 1024 * 1024); // rough guess
const quint8 *nextIn = reinterpret_cast<const quint8*>(data.constData());
SIZE_T availIn = data.size();
QByteArray scratch(0x10000, Qt::Uninitialized); // 64 KB chunks
while (availIn > 0) {
SIZE_T inSize = availIn; // let XMem tell us how much it will consume
SIZE_T outSize = scratch.size(); // max 64 KB per call
HRESULT hr = XMemDecompressStream(ctx,
scratch.data(), &outSize,
nextIn, &inSize);
if (FAILED(hr)) {
qWarning() << "XMemDecompressStream failed, hr=" << hr;
XMemDestroyDecompressionContext(ctx);
return {};
}
if (inSize == 0 && outSize == 0)
break; // no progress
output.append(scratch.constData(), static_cast<int>(outSize));
nextIn += inSize;
availIn -= inSize;
}
XMemDestroyDecompressionContext(ctx);
return output;
}
@ -77,19 +100,18 @@ quint32 Compression::CalculateAdler32Checksum(const QByteArray &data) {
qint64 Compression::FindZlibOffset(const QByteArray &bytes)
{
static const QByteArray iwffs("IWffs");
auto idx = bytes.indexOf(iwffs);
if (idx != -1)
return idx + 0x4000;
QDataStream stream(bytes);
const char header = 0x78; // z-lib: 0x78 [FLG]
int pos = -1;
while ((pos = bytes.indexOf(header, pos + 1)) != -1)
while (!stream.atEnd())
{
QByteArray window = bytes.mid(pos, 0x20);
if (!window.contains(QByteArray::fromHex("000000")) &&
!window.contains(QByteArray::fromHex("FFFFFF")))
return pos;
QByteArray testSegment = stream.device()->peek(2).toHex().toUpper();
if (testSegment == "7801" ||
testSegment == "785E" ||
testSegment == "789C" ||
testSegment == "78DA") {
return stream.device()->pos();
}
stream.skipRawData(1);
}
return -1;
}

View File

@ -25,12 +25,6 @@
http://www.oberhumer.com/opensource/lzo/
*/
/*
* NOTE:
* the full LZO package can be found at
* http://www.oberhumer.com/opensource/lzo/
*/
#define __LZO_IN_MINILZO 1
#if defined(LZO_CFG_FREESTANDING)

View File

@ -223,3 +223,21 @@ double XDataStream::ParseDouble(const QString& aDebugString)
}
return val;
}
bool XDataStream::ParseBool(const QString &aDebugString)
{
qint64 start = this->device()->pos();
char val;
*this >> val;
if (mDebug)
{
qDebug() << QString("[%1-%2] Parsed %3: %4")
.arg(start, 10, 10, QChar('0'))
.arg(this->device()->pos(), 10, 10, QChar('0'))
.arg(aDebugString)
.arg(val);
}
return val;
}

View File

@ -25,6 +25,7 @@ public:
quint64 ParseUInt64(const QString& aDebugString = "");
float ParseSingle(const QString& aDebugString = "");
double ParseDouble(const QString& aDebugString = "");
bool ParseBool(const QString& aDebugString = "");
private:
bool mDebug;

View File

@ -1,27 +1,35 @@
#include "encryption.h"
#include <QtCore>
#include "QtZlib/zlib.h"
#include "ecrypt-sync.h"
#include "sha1.h"
#include "encryption.h"
#include "compression.h"
void Encryption::Convert32BitTo8Bit(quint32 value, quint8 *array) {
static QVector<quint32> ivCounter(4, 1); // start all counters at 1
void Encryption::Convert32BitTo8Bit(quint32 value, quint8 *array)
{
array[0] = static_cast<quint8>(value >> 0);
array[1] = static_cast<quint8>(value >> 8);
array[2] = static_cast<quint8>(value >> 16);
array[3] = static_cast<quint8>(value >> 24);
}
quint32 Encryption::ConvertArrayTo32Bit(const QByteArray &array) {
quint32 Encryption::ConvertArrayTo32Bit(const QByteArray &array)
{
return ((static_cast<quint32>(static_cast<uchar>(array[0])) << 0) |
(static_cast<quint32>(static_cast<uchar>(array[1])) << 8) |
(static_cast<quint32>(static_cast<uchar>(array[2])) << 16) |
(static_cast<quint32>(static_cast<uchar>(array[3])) << 24));
}
quint32 Encryption::Rotate(quint32 value, quint32 numBits) {
quint32 Encryption::Rotate(quint32 value, quint32 numBits)
{
return (value << numBits) | (value >> (32 - numBits));
}
QByteArray Encryption::InitIVTable(const QByteArray &feed) {
QByteArray Encryption::InitIVTable(const QByteArray &feed)
{
const int tableSize = 0xFB0;
QByteArray table;
table.resize(tableSize);
@ -48,22 +56,23 @@ QByteArray Encryption::InitIVTable(const QByteArray &feed) {
return table;
}
int Encryption::unk(quint64 arg1, quint8 arg2) {
int Encryption::unk(quint64 arg1, quint8 arg2)
{
if (arg2 >= 0x40)
return 0;
return static_cast<int>(arg1 >> arg2);
}
QByteArray Encryption::GetIV(const QByteArray &table, int index) {
int num1 = 0xFA0 + index;
QByteArray Encryption::GetIV(const QByteArray &table, int index)
{
int num1 = (4 * index % 4 + 0xFA0) + index % 4 + (index - (index % 4));
int num2 = unk(0x51EB851FLL * num1, 0x20);
int adjust = ((num2 >> 6) + (num2 >> 31));
int startIndex = 20 * (num1 - 200 * adjust);
// Return 8 bytes from that location.
int startIndex = 20 * (num1 - 200 * ((num2 >> 6) + (num2 >> 31)));
return table.mid(startIndex, 8);
}
void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray &sectionHash) {
void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray &sectionHash)
{
int blockNumIndex = index % 4;
int baseOffset = 0xFA0 + blockNumIndex * 4;
quint32 blockNumVal = (static_cast<uchar>(table.at(baseOffset)) ) |
@ -86,7 +95,8 @@ void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray &s
}
}
quint32 Encryption::ToUInt32(const QByteArray &data, int offset) {
quint32 Encryption::ToUInt32(const QByteArray &data, int offset)
{
// Converts 4 bytes (starting at offset) from data into a 32-bit quint32eger (little-endian)
return ((static_cast<quint32>(static_cast<uchar>(data[offset])) ) |
(static_cast<quint32>(static_cast<uchar>(data[offset+1])) << 8 ) |
@ -351,96 +361,8 @@ void Encryption::generateNewIV(int index, const QByteArray &hash, QByteArray &iv
ivCounter[index]++;
}
QByteArray Encryption::decryptFastFile_BO2(const QByteArray &fastFileData)
QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData)
{
const QByteArray bo2_salsa20_key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
QByteArray fileData = fastFileData;
QByteArray finalFastFile;
QByteArray ivTable(16000, 0);
fillIVTable(fileData, ivTable, 16000 - 1);
QVector<quint32> ivCounter(4, 1);
QDataStream stream(fileData);
stream.setByteOrder(QDataStream::LittleEndian);
stream.skipRawData(0x138);
QByteArray sha1Hash(20, 0);
QByteArray ivPtr(8, 0);
int chunkIndex = 0;
while (!stream.atEnd()) {
quint32 dataLength;
stream >> dataLength;
if (dataLength == 0 || dataLength > fileData.size() - stream.device()->pos()) {
qWarning() << "Invalid data length at offset: " << stream.device()->pos();
break;
}
fillIV(chunkIndex % 4, ivPtr, ivTable, ivCounter);
ECRYPT_ctx x;
ECRYPT_keysetup(&x, reinterpret_cast<const u8*>(bo2_salsa20_key.constData()), 256, 0);
ECRYPT_ivsetup(&x, reinterpret_cast<const u8*>(ivPtr.constData()));
QByteArray encryptedBlock = fileData.mid(stream.device()->pos(), dataLength);
QByteArray decryptedBlock;
decryptedBlock.resize(dataLength);
ECRYPT_decrypt_bytes(&x, reinterpret_cast<const u8*>(encryptedBlock.constData()),
reinterpret_cast<u8*>(decryptedBlock.data()), dataLength);
QCryptographicHash sha1(QCryptographicHash::Sha1);
sha1.addData(decryptedBlock);
sha1Hash = sha1.result();
z_stream strm = {};
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = static_cast<uInt>(decryptedBlock.size());
strm.next_in = reinterpret_cast<Bytef*>(decryptedBlock.data());
QByteArray decompressedData;
decompressedData.resize(fmax(dataLength * 2, 4096));
strm.avail_out = decompressedData.size();
strm.next_out = reinterpret_cast<Bytef*>(decompressedData.data());
int zReturn = inflateInit2(&strm, -15);
if (zReturn != Z_OK) {
qWarning() << "inflateInit2 failed with error code" << zReturn;
break;
}
zReturn = inflate(&strm, Z_FINISH);
inflateEnd(&strm);
if (zReturn != Z_STREAM_END) {
qDebug() << "Error decompressing at offset: " << stream.device()->pos() << " : " << zReturn;
decompressedData.clear();
} else {
decompressedData.resize(strm.total_out);
}
finalFastFile.append(decompressedData);
generateNewIV(chunkIndex % 4, sha1Hash, ivTable, ivCounter);
if (stream.device()->pos() + static_cast<qint64>(dataLength) > fileData.size()) {
qWarning() << "Skipping past file size!";
break;
}
stream.skipRawData(dataLength);
chunkIndex++;
}
return finalFastFile;
}
QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData) {
const QByteArray salsaKey = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
QByteArray ivTable(0xFB0, 0);
@ -504,3 +426,71 @@ QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData) {
return finalFastFile;
}
QByteArray Encryption::DecryptFile(const QByteArray &fastFileData, const QString &aFileName, const QByteArray &aKey)
{
Q_UNUSED(aFileName);
const QByteArray salsaKey = QByteArray::fromHex(aKey);
QByteArray ivTable(0xFB0, 0);
fillIVTable(fastFileData, ivTable, 0xFB0 - 1);
QVector<quint32> ivCounter(4, 1);
QDataStream stream(fastFileData);
stream.setByteOrder(QDataStream::LittleEndian);
QByteArray finalFastFile;
QByteArray sha1Hash(20, 0);
QByteArray ivPtr(8, 0);
int chunkIndex = 0;
while (!stream.atEnd()) {
if (stream.device()->bytesAvailable() < 4) {
qWarning() << "No sufficient data for chunk size at offset:" << stream.device()->pos();
break;
}
quint32 dataLength;
stream >> dataLength;
if (dataLength == 0 || dataLength > fastFileData.size() - stream.device()->pos()) {
qWarning() << "Invalid data length at offset:" << stream.device()->pos();
break;
}
fillIV(chunkIndex % 4, ivPtr, ivTable, ivCounter);
ECRYPT_ctx x;
ECRYPT_keysetup(&x, reinterpret_cast<const u8*>(salsaKey.constData()), 256, 0);
ECRYPT_ivsetup(&x, reinterpret_cast<const u8*>(ivPtr.constData()));
QByteArray encryptedBlock = fastFileData.mid(stream.device()->pos(), dataLength);
QByteArray decryptedBlock(dataLength, Qt::Uninitialized);
ECRYPT_decrypt_bytes(&x,
reinterpret_cast<const u8*>(encryptedBlock.constData()),
reinterpret_cast<u8*>(decryptedBlock.data()),
dataLength);
// SHA1 hash update
sha1Hash = QCryptographicHash::hash(decryptedBlock, QCryptographicHash::Sha1);
// Decompress (ZLIB raw DEFLATE)
QByteArray decompressedData = Compression::DecompressDeflate(decryptedBlock);
if (decompressedData.isEmpty()) {
qWarning() << "Failed decompression at chunk index:" << chunkIndex;
return QByteArray();
}
finalFastFile.append(decompressedData);
// Update IV table using SHA1
generateNewIV(chunkIndex % 4, sha1Hash, ivTable, ivCounter);
stream.skipRawData(dataLength);
chunkIndex++;
}
return finalFastFile;
}

View File

@ -46,8 +46,9 @@ public:
static void generateNewIV(int index, const QByteArray& hash, QByteArray& ivTable, QVector<quint32>& ivCounter);
static QByteArray decryptFastFile_BO2(const QByteArray& fastFileData);
//static QByteArray decryptFastFile_BO2(const QByteArray& fastFileData);
static QByteArray decryptFastFile_BO3(const QByteArray& fastFileData);
static QByteArray DecryptFile(const QByteArray& fastFileData, const QString& aFileName, const QByteArray& aKey);
};
#endif // ENCRYPTION_H

View File

@ -73,12 +73,6 @@ bool FastFile_COD10_360::Load(const QByteArray aData) {
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
// Select key based on game.
QByteArray key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
@ -96,7 +90,7 @@ bool FastFile_COD10_360::Load(const QByteArray aData) {
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
decompressedData = Encryption::decryptFastFile_BO2(aData);
decompressedData = Encryption::DecryptFile(aData, fileName, "0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");
@ -104,6 +98,7 @@ bool FastFile_COD10_360::Load(const QByteArray aData) {
testFile.write(decompressedData);
testFile.close();
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with the decompressed data (using an Xbox platform flag).
ZoneFile_COD10_360* zoneFile = new ZoneFile_COD10_360();

View File

@ -68,29 +68,74 @@ bool FastFile_COD11_360::Load(const QString aFilePath) {
return true;
}
enum DB_CompressorType : qint32
{
DB_COMPRESSOR_INVALID = 0x0,
DB_COMPRESSOR_ZLIB = 0x1,
DB_COMPRESSOR_LZX = 0x2,
DB_COMPRESSOR_PASSTHROUGH = 0x3,
};
bool FastFile_COD11_360::Load(const QByteArray aData) {
QByteArray decompressedData;
// Prepare data stream for parsing
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
fastFileStream.setByteOrder(XDataStream::BigEndian);
// Verify magic header
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
if (fileMagic != "TAff0000") {
qWarning() << "Invalid fast file magic for COD12!";
quint32 version = fastFileStream.ParseUInt32();
fastFileStream.skipRawData(1);
DB_CompressorType compressorType = (DB_CompressorType)fastFileStream.ParseInt8();
fastFileStream.skipRawData(10);
qint32 blockCount = fastFileStream.ParseInt32();
if (version != 1838)
{
qWarning() << "Invalid fast file version:" << version << "!";
return false;
}
// Skip: File size (4 bytes), flags/version (4 bytes), unknown (8 bytes), build tag (32 bytes), RSA signature (256 bytes)
fastFileStream.skipRawData(4 + 4 + 8 + 32 + 256); // total 304 bytes skipped so far + 8 bytes magic = 312 bytes at correct position.
if (blockCount > 17280)
{
qWarning() << "Fast file has too many blocks:" << blockCount << "> 17280!";
return false;
}
fastFileStream.skipRawData(12 * blockCount);
// Correctly positioned at 0x138
QByteArray encryptedData = aData.mid(0x138);
decompressedData = Encryption::decryptFastFile_BO3(encryptedData);
qint32 startPos = fastFileStream.ParseInt32();
Q_UNUSED(startPos);
// Output for verification/testing
qint32 endPos = fastFileStream.ParseInt32();
Q_UNUSED(endPos);
if (fileMagic == "S1ffu100")
{
QByteArray compressedData = aData.mid(fastFileStream.device()->pos());
if (compressorType == DB_COMPRESSOR_ZLIB)
{
decompressedData = Compression::DecompressZLIB(compressedData);
}
else if (compressorType == DB_COMPRESSOR_LZX)
{
decompressedData = Compression::DecompressXMem(compressedData, 0, 0x80000, 0);
}
}
else if (fileMagic == "S1ff0100")
{
}
else
{
qWarning() << "Invalid fast file magic:" << fileMagic << "!";
return false;
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with decompressed data

View File

@ -75,21 +75,66 @@ bool FastFile_COD12_360::Load(const QByteArray aData) {
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
// Verify magic header
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
if (fileMagic != "TAff0000") {
qWarning() << "Invalid fast file magic for COD12!";
// Skip header magic
fastFileStream.skipRawData(8);
quint32 version;
fastFileStream >> version;
quint8 unknownFlag, compressionFlag, platformFlag, encryptionFlag;
fastFileStream >> unknownFlag >> compressionFlag >> platformFlag >> encryptionFlag;
if (compressionFlag != 1) {
qDebug() << "Invalid fastfile compression: " << compressionFlag;
return false;
} else if (platformFlag != 4) {
qDebug() << "Invalid platform: " << platformFlag;
return false;
} else if (encryptionFlag != 0) {
qDebug() << "Decryption not supported yet!";
return false;
}
// Skip: File size (4 bytes), flags/version (4 bytes), unknown (8 bytes), build tag (32 bytes), RSA signature (256 bytes)
fastFileStream.skipRawData(4 + 4 + 8 + 32 + 256); // total 304 bytes skipped so far + 8 bytes magic = 312 bytes at correct position.
fastFileStream.skipRawData(128);
// Correctly positioned at 0x138
QByteArray encryptedData = aData.mid(0x138);
decompressedData = Encryption::decryptFastFile_BO3(encryptedData);
quint64 size;
fastFileStream >> size;
fastFileStream.skipRawData(432);
int consumed = 0;
while(consumed < size)
{
// Read Block Header
quint32 compressedSize, decompressedSize, blockSize, blockPosition;
fastFileStream >> compressedSize >> decompressedSize >> blockSize >> blockPosition;
// Validate the block position, it should match
if(blockPosition != fastFileStream.device()->pos() - 16)
{
qDebug() << "Block Position does not match Stream Position.";
return false;
}
// Check for padding blocks
if(decompressedSize == 0)
{
fastFileStream.device()->read((((fastFileStream.device()->pos()) + ((0x800000) - 1)) & ~((0x800000) - 1)) - fastFileStream.device()->pos());
continue;
}
fastFileStream.device()->read(2);
QByteArray compressedData(compressedSize - 2, Qt::Uninitialized);
qDebug() << "Data position: " << fastFileStream.device()->pos() << " - Size: " << compressedSize;
fastFileStream.readRawData(compressedData.data(), compressedSize - 2);
decompressedData.append(Compression::DecompressDeflate(compressedData));
consumed += decompressedSize;
// Sinze Fast Files are aligns, we must skip the full block
fastFileStream.device()->seek(blockPosition + 16 + blockSize);
}
// Output for verification/testing
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);

View File

@ -70,30 +70,47 @@ bool FastFile_COD6_360::Load(const QString aFilePath) {
}
bool FastFile_COD6_360::Load(const QByteArray aData) {
const qint64 zlibOffset = Compression::FindZlibOffset(aData);
if (zlibOffset == -1)
{
qWarning() << "Z-Lib stream not found";
return false;
}
QByteArray compressed = aData.mid(zlibOffset);
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::BigEndian);
QByteArray decompressedData = Compression::DecompressZLIB(compressed);
QByteArray magic(8, Qt::Uninitialized);
fastFileStream.readRawData(magic.data(), 8);
if (decompressedData.isEmpty() || decompressedData.size() < 1024)
{
QByteArray stripped = Compression::StripHashBlocks(compressed);
QByteArray retry = Compression::DecompressZLIB(stripped);
if (!retry.isEmpty())
decompressedData.swap(retry);
}
quint32 version = fastFileStream.ParseUInt32();
if (decompressedData.isEmpty())
if (version != 269)
{
qWarning() << "Unable to decompress fast-file";
qDebug() << QString("Invalid version: %1!").arg(version);
return false;
}
bool localPatch = fastFileStream.ParseBool();
Q_UNUSED(localPatch);
quint8 compressor = fastFileStream.ParseUInt8();
Q_UNUSED(compressor);
// Skip fastfile date/time
fastFileStream.skipRawData(11);
quint32 hashCount = fastFileStream.ParseUInt32();
fastFileStream.skipRawData(12 * hashCount);
fastFileStream.skipRawData(8);
QByteArray decompressedData;
if (magic == "IWff0100")
{
}
else if (magic == "IWffu100")
{
quint32 zlibSize = aData.size() - fastFileStream.device()->pos();
QByteArray zlibData(zlibSize, Qt::Uninitialized);
fastFileStream.readRawData(zlibData.data(), zlibSize);
decompressedData = Compression::DecompressZLIB(zlibData);
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
ZoneFile_COD6_360* zoneFile = new ZoneFile_COD6_360();

View File

@ -73,14 +73,9 @@ bool FastFile_COD7_360::Load(const QByteArray aData) {
// Create a XDataStream on the input data.
XDataStream fastFileStream(aData);
fastFileStream.skipRawData(12);
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
// Select key based on game.
QByteArray key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d");
fastFileStream.skipRawData(4);
fastFileStream.skipRawData(16);
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
@ -102,6 +97,8 @@ bool FastFile_COD7_360::Load(const QByteArray aData) {
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
QByteArray key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d");
// Now the stream should be positioned at 0x13C, where sections begin.
int sectionIndex = 0;
while (true) {

View File

@ -0,0 +1,148 @@
#include "fastfile_cod7_5_360.h"
#include "zonefile_cod7_360.h"
#include "compression.h"
#include "encryption.h"
#include <QFile>
#include <QDebug>
FastFile_COD7_5_360::FastFile_COD7_5_360()
: FastFile() {
SetCompany(COMPANY_INFINITY_WARD);
SetType(FILETYPE_FAST_FILE);
SetSignage(SIGNAGE_UNSIGNED);
SetMagic(0);
SetVersion(0);
SetPlatform("360");
SetGame("COD7.5");
}
FastFile_COD7_5_360::FastFile_COD7_5_360(const QByteArray& aData)
: FastFile_COD7_5_360() {
if (!aData.isEmpty()) {
Load(aData);
}
}
FastFile_COD7_5_360::FastFile_COD7_5_360(const QString aFilePath)
: FastFile_COD7_5_360() {
if (!aFilePath.isEmpty()) {
Load(aFilePath);
}
}
FastFile_COD7_5_360::~FastFile_COD7_5_360() {
}
QByteArray FastFile_COD7_5_360::GetBinaryData() const {
return QByteArray();
}
bool FastFile_COD7_5_360::Load(const QString aFilePath) {
if (aFilePath.isEmpty()) {
return false;
}
// Check fastfile can be read
QFile *file = new QFile(aFilePath);
if (!file->open(QIODevice::ReadOnly)) {
qDebug() << QString("Error: Failed to open FastFile: %1!").arg(aFilePath);
return false;
}
// Decompress fastfile and close
const QString fastFileStem = aFilePath.section("/", -1, -1);
SetStem(fastFileStem);
if (!Load(file->readAll())) {
qDebug() << "Error: Failed to load fastfile: " << fastFileStem;
return false;
}
file->close();
// Open zone file after decompressing ff and writing
return true;
}
enum NX_Language : qint32
{
LANGUAGE_ENGLISH = 0x0,
LANGUAGE_FRENCH = 0x1,
LANGUAGE_GERMAN = 0x2,
LANGUAGE_ITALIAN = 0x3,
LANGUAGE_SPANISH = 0x4,
LANGUAGE_BRITISH = 0x5,
LANGUAGE_RUSSIAN = 0x6,
LANGUAGE_POLISH = 0x7,
LANGUAGE_KOREAN = 0x8,
LANGUAGE_TAIWANESE = 0x9,
LANGUAGE_JAPANESE = 0xA,
LANGUAGE_CHINESE = 0xB,
LANGUAGE_THAI = 0xC,
LANGUAGE_LEET = 0xD,
LANGUAGE_CZECH = 0xE,
MAX_LANGUAGES = 0xF,
};
bool FastFile_COD7_5_360::Load(const QByteArray aData) {
// Create a XDataStream on the input data.
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::BigEndian);
QByteArray magic(8, Qt::Uninitialized);
fastFileStream.readRawData(magic.data(), 8);
quint32 version = fastFileStream.ParseUInt32();
if (version != 357)
{
qDebug() << QString("Invalid version: %1!").arg(version);
return false;
}
bool localPatch = fastFileStream.ParseBool();
Q_UNUSED(localPatch);
quint8 compressor = fastFileStream.ParseUInt8();
Q_UNUSED(compressor);
// Skip fastfile date/time
fastFileStream.skipRawData(8);
NX_Language language = (NX_Language)fastFileStream.ParseInt32();
Q_UNUSED(language);
quint32 hashCount = fastFileStream.ParseUInt32();
fastFileStream.skipRawData(12 * hashCount);
fastFileStream.skipRawData(8);
QByteArray decompressedData;
if (magic == "NXff0100")
{
}
else if (magic == "NXffu100")
{
quint32 zlibSize = aData.size() - fastFileStream.device()->pos();
QByteArray zlibData(zlibSize, Qt::Uninitialized);
fastFileStream.readRawData(zlibData.data(), zlibSize);
decompressedData = Compression::DecompressZLIB(zlibData);
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
ZoneFile_COD7_360* zoneFile = new ZoneFile_COD7_360();
zoneFile->SetStem(GetBaseStem() + ".zone");
if (!zoneFile->Load(decompressedData)) {
qWarning() << "Failed to load ZoneFile!";
return false;
}
SetZoneFile(zoneFile);
return true;
}

View File

@ -0,0 +1,20 @@
#ifndef FASTFILE_COD7_5_360_H
#define FASTFILE_COD7_5_360_H
#include "fastfile.h"
class FastFile_COD7_5_360 : public FastFile
{
public:
FastFile_COD7_5_360();
FastFile_COD7_5_360(const QByteArray& aData);
FastFile_COD7_5_360(const QString aFilePath);
~FastFile_COD7_5_360();
QByteArray GetBinaryData() const override;
bool Load(const QString aFilePath) override;
bool Load(const QByteArray aData) override;
};
#endif // FASTFILE_COD7_5_360_H

View File

@ -76,9 +76,6 @@ bool FastFile_COD8_360::Load(const QByteArray aData) {
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
// Select key based on game.
QByteArray key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
@ -92,11 +89,7 @@ bool FastFile_COD8_360::Load(const QByteArray aData) {
QByteArray fileName(32, Qt::Uninitialized);
fastFileStream.readRawData(fileName.data(), 32);
// Skip the RSA signature (256 bytes).
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
decompressedData = Encryption::decryptFastFile_BO2(aData);
decompressedData = Encryption::DecryptFile(aData, fileName, "0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");

View File

@ -76,9 +76,6 @@ bool FastFile_COD9_360::Load(const QByteArray aData) {
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
// Select key based on game.
QByteArray key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
@ -96,7 +93,7 @@ bool FastFile_COD9_360::Load(const QByteArray aData) {
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
decompressedData = Encryption::decryptFastFile_BO2(aData);
decompressedData = Encryption::DecryptFile(aData, fileName, "0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");

View File

@ -90,12 +90,7 @@ bool FastFile_COD10_PC::Load(const QByteArray aData) {
}
// Select key based on game.
QByteArray key;
if (GetPlatform() == "360") {
key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
} else if (GetPlatform() == "PC") {
key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
}
QByteArray key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
@ -114,11 +109,7 @@ bool FastFile_COD10_PC::Load(const QByteArray aData) {
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
}
//decompressedData = Encryption::decryptFastFile_BO2(aData);
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");
@ -126,6 +117,7 @@ bool FastFile_COD10_PC::Load(const QByteArray aData) {
testFile.write(decompressedData);
testFile.close();
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with the decompressed data (using an Xbox platform flag).
ZoneFile_COD10_PC* zoneFile = new ZoneFile_COD10_PC();

View File

@ -80,22 +80,11 @@ bool FastFile_COD11_PC::Load(const QByteArray aData) {
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
SetPlatform(pCalculateFFPlatform(version));
SetGame("COD9");
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
if (GetPlatform() == "PC") {
fastFileStream.setByteOrder(XDataStream::LittleEndian);
}
fastFileStream.setByteOrder(XDataStream::LittleEndian);
// Select key based on game.
QByteArray key;
if (GetPlatform() == "360") {
key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
} else if (GetPlatform() == "PC") {
key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
}
QByteArray key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
// Read the 8-byte magic.
QByteArray fileMagic(8, Qt::Uninitialized);
@ -114,11 +103,7 @@ bool FastFile_COD11_PC::Load(const QByteArray aData) {
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
}
//decompressedData = Encryption::decryptFastFile_BO2(aData);
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");
@ -126,6 +111,7 @@ bool FastFile_COD11_PC::Load(const QByteArray aData) {
testFile.write(decompressedData);
testFile.close();
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with the decompressed data (using an Xbox platform flag).
ZoneFile_COD11_PC* zoneFile = new ZoneFile_COD11_PC();

View File

@ -136,7 +136,6 @@ bool FastFile_COD12_PC::Load(const QByteArray aData) {
// Sinze Fast Files are aligns, we must skip the full block
fastFileStream.device()->seek(blockPosition + 16 + blockSize);
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with the decompressed data (using an Xbox platform flag).

View File

@ -72,12 +72,30 @@ bool FastFile_COD6_PC::Load(const QString aFilePath) {
}
bool FastFile_COD6_PC::Load(const QByteArray aData) {
StatusBarManager::instance().updateStatus("Loading COD5 Fast File w/data", 1000);
QByteArray decompressedData;
const qint64 zlibOffset = Compression::FindZlibOffset(aData);
qDebug() << "ZLib Offset: " << zlibOffset;
if (zlibOffset == -1)
{
qWarning() << "Z-Lib stream not found";
return false;
}
QByteArray compressed = aData.mid(zlibOffset);
// Assume the first 12 bytes are a header; the rest is zlib-compressed zone data.
const QByteArray compressedData = aData.mid(21);
decompressedData = Compression::DecompressZLIB(compressedData);
QByteArray decompressedData = Compression::DecompressZLIB(compressed);
if (decompressedData.isEmpty() || decompressedData.size() < 1024)
{
QByteArray stripped = Compression::StripHashBlocks(compressed);
QByteArray retry = Compression::DecompressZLIB(stripped);
if (!retry.isEmpty())
decompressedData.swap(retry);
}
if (decompressedData.isEmpty())
{
qWarning() << "Unable to decompress fast-file";
return false;
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);

View File

@ -81,82 +81,10 @@ bool FastFile_COD8_PC::Load(const QByteArray aData) {
SetType(pParseFFFileType(&fastFileStream));
SetSignage(pParseFFSignage(&fastFileStream));
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
SetPlatform(pCalculateFFPlatform(version));
SetGame("COD7");
SetVersion(pParseFFVersion(&fastFileStream));
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::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++;
}
decompressedData = Compression::DecompressZLIB(aData.mid(21));
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
ZoneFile_COD8_PC* zoneFile = new ZoneFile_COD8_PC();
zoneFile->SetStem(GetBaseStem() + ".zone");

View File

@ -67,8 +67,6 @@ bool FastFile_COD9_PC::Load(const QString aFilePath) {
}
bool FastFile_COD9_PC::Load(const QByteArray aData) {
QByteArray decompressedData;
// Create a XDataStream on the input data.
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
@ -78,26 +76,9 @@ bool FastFile_COD9_PC::Load(const QByteArray aData) {
SetType(pParseFFFileType(&fastFileStream));
SetSignage(pParseFFSignage(&fastFileStream));
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
SetPlatform(pCalculateFFPlatform(version));
SetGame("COD9");
SetVersion(pParseFFVersion(&fastFileStream));
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
if (GetPlatform() == "PC") {
fastFileStream.setByteOrder(XDataStream::LittleEndian);
}
// Select key based on game.
QByteArray key;
if (GetPlatform() == "360") {
key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
} else if (GetPlatform() == "PC") {
key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
}
// Read the 8-byte magic.
// Validate the fastfile magic.
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
if (fileMagic != "PHEEBs71") {
@ -106,35 +87,100 @@ bool FastFile_COD9_PC::Load(const QByteArray aData) {
}
fastFileStream.skipRawData(4);
// Read IV table name (32 bytes).
QByteArray fileName(32, Qt::Uninitialized);
fastFileStream.readRawData(fileName.data(), 32);
// Read IV seed name (32 bytes).
QByteArray nameKey(32, Qt::Uninitialized);
fastFileStream.readRawData(nameKey.data(), 32);
// Skip the RSA signature (256 bytes).
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
// --- Salsa20 + IV setup ---
static QVector<quint32> ivCounter(4, 1);
QByteArray ivTable = Encryption::InitIVTable(nameKey);
ivCounter.fill(1); // reset global counters
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
// Skip RSA signature (0x100)
fastFileStream.skipRawData(0x100);
// Decrypt + decompress loop
QByteArray finalZone;
int chunkIndex = 0;
while (!fastFileStream.atEnd()) {
quint32 dataLength = 0;
fastFileStream >> dataLength;
if (dataLength == 0)
break;
QByteArray encryptedBlock(dataLength, Qt::Uninitialized);
if (fastFileStream.readRawData(encryptedBlock.data(), dataLength) != dataLength) {
qWarning() << "Unexpected EOF while reading block";
break;
}
// Derive IV for this chunk
QByteArray iv = Encryption::GetIV(ivTable, chunkIndex % 4);
// Salsa20 decryption
QByteArray decryptedBlock = Encryption::salsa20DecryptSection(
encryptedBlock,
QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE"),
iv,
64
);
// SHA1 hash of decrypted block
QCryptographicHash sha1(QCryptographicHash::Sha1);
sha1.addData(decryptedBlock);
QByteArray sha1Hash = sha1.result();
// Inflate into buffer
z_stream strm{};
strm.avail_in = static_cast<uInt>(decryptedBlock.size());
strm.next_in = reinterpret_cast<Bytef*>(decryptedBlock.data());
QByteArray decompressedData;
QByteArray buffer(0x10000, Qt::Uninitialized);
inflateInit2(&strm, -15);
int ret;
do {
strm.avail_out = buffer.size();
strm.next_out = reinterpret_cast<Bytef*>(buffer.data());
ret = inflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
qWarning() << "inflate failed with code" << ret;
break;
}
int have = buffer.size() - strm.avail_out;
if (have > 0)
decompressedData.append(buffer.constData(), have);
} while (ret != Z_STREAM_END);
inflateEnd(&strm);
finalZone.append(decompressedData);
// Update IV table for next block
Encryption::UpdateIVTable(ivTable, chunkIndex % 4, sha1Hash);
chunkIndex++;
}
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");
if(testFile.open(QIODevice::WriteOnly)) {
testFile.write(decompressedData);
testFile.close();
}
// Export decompressed zone
Utils::ExportData(GetBaseStem() + ".zone", finalZone);
// Load the zone file with the decompressed data (using an Xbox platform flag).
// Load zone file
ZoneFile_COD9_PC* zoneFile = new ZoneFile_COD9_PC();
zoneFile->SetStem(GetBaseStem() + ".zone");
if (!zoneFile->Load(decompressedData)) {
if (!zoneFile->Load(finalZone)) {
qWarning() << "Failed to load ZoneFile!";
return false;
}
SetZoneFile(zoneFile);
return true;
return true;
}

View File

@ -110,15 +110,7 @@ bool FastFile_COD10_PS3::Load(const QByteArray aData) {
QByteArray fileName(32, Qt::Uninitialized);
fastFileStream.readRawData(fileName.data(), 32);
// Skip the RSA signature (256 bytes).
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
}
//decompressedData = Encryption::decryptFastFile_BO2(aData);
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");

View File

@ -1,6 +1,7 @@
#include "fastfile_cod11_ps3.h"
#include "zonefile_cod11_ps3.h"
#include "encryption.h"
#include "compression.h"
#include <QFile>
#include <QDebug>
@ -66,68 +67,77 @@ bool FastFile_COD11_PS3::Load(const QString aFilePath) {
return true;
}
enum DB_CompressorType : qint32
{
DB_COMPRESSOR_INVALID = 0x0,
DB_COMPRESSOR_ZLIB = 0x1,
DB_COMPRESSOR_LZX = 0x2,
DB_COMPRESSOR_PASSTHROUGH = 0x3,
};
bool FastFile_COD11_PS3::Load(const QByteArray aData) {
QByteArray decompressedData;
// Create a XDataStream on the input data.
// Prepare data stream for parsing
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
// Parse header values.
SetCompany(pParseFFCompany(&fastFileStream));
SetType(pParseFFFileType(&fastFileStream));
SetSignage(pParseFFSignage(&fastFileStream));
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
SetPlatform(pCalculateFFPlatform(version));
SetGame("COD9");
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
if (GetPlatform() == "PC") {
fastFileStream.setByteOrder(XDataStream::LittleEndian);
}
// Select key based on game.
QByteArray key;
if (GetPlatform() == "360") {
key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3");
} else if (GetPlatform() == "PC") {
key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE");
}
// Read the 8-byte magic.
// Verify magic header
QByteArray fileMagic(8, Qt::Uninitialized);
fastFileStream.readRawData(fileMagic.data(), 8);
if (fileMagic != "PHEEBs71") {
qWarning() << "Invalid fast file magic!";
quint32 version = fastFileStream.ParseUInt32();
fastFileStream.skipRawData(1);
DB_CompressorType compressorType = (DB_CompressorType)fastFileStream.ParseInt8();
fastFileStream.skipRawData(10);
qint32 blockCount = fastFileStream.ParseInt32();
if (version != 1838)
{
qWarning() << "Invalid fast file version:" << version << "!";
return false;
}
fastFileStream.skipRawData(4);
// Read IV table name (32 bytes).
QByteArray fileName(32, Qt::Uninitialized);
fastFileStream.readRawData(fileName.data(), 32);
// Skip the RSA signature (256 bytes).
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
if (blockCount > 17280)
{
qWarning() << "Fast file has too many blocks:" << blockCount << "> 17280!";
return false;
}
fastFileStream.skipRawData(12 * blockCount);
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");
if(testFile.open(QIODevice::WriteOnly)) {
testFile.write(decompressedData);
testFile.close();
qint32 startPos = fastFileStream.ParseInt32();
Q_UNUSED(startPos);
qint32 endPos = fastFileStream.ParseInt32();
Q_UNUSED(endPos);
if (fileMagic == "S1ffu100")
{
QByteArray compressedData = aData.mid(fastFileStream.device()->pos());
if (compressorType == DB_COMPRESSOR_ZLIB)
{
decompressedData = Compression::DecompressZLIB(compressedData);
}
else if (compressorType == DB_COMPRESSOR_LZX)
{
decompressedData = Compression::DecompressXMem(compressedData, 0, 0x80000, 0);
}
}
else if (fileMagic == "S1ff0100")
{
// Load the zone file with the decompressed data (using an Xbox platform flag).
}
else
{
qWarning() << "Invalid fast file magic:" << fileMagic << "!";
return false;
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
// Load the zone file with decompressed data
ZoneFile_COD11_PS3* zoneFile = new ZoneFile_COD11_PS3();
zoneFile->SetStem(GetBaseStem() + ".zone");
if (!zoneFile->Load(decompressedData)) {

View File

@ -110,15 +110,7 @@ bool FastFile_COD12_PS3::Load(const QByteArray aData) {
QByteArray fileName(32, Qt::Uninitialized);
fastFileStream.readRawData(fileName.data(), 32);
// Skip the RSA signature (256 bytes).
QByteArray rsaSignature(256, Qt::Uninitialized);
fastFileStream.readRawData(rsaSignature.data(), 256);
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
}
decompressedData = Encryption::decryptFastFile_BO3(aData);
// For COD9, write out the complete decompressed zone for testing.
QFile testFile("exports/" + GetBaseStem() + ".zone");

View File

@ -86,29 +86,24 @@ bool FastFile_COD4_PS3::Load(const QByteArray aData) {
SetMagic(pParseFFMagic(&fastFileStream));
SetVersion(pParseFFVersion(&fastFileStream));
int pos = 12;
// Loop until EOF or invalid chunk
while (pos <= aData.size()) {
fastFileStream.setByteOrder(XDataStream::BigEndian);
while (!fastFileStream.atEnd()) {
// Read 2-byte BIG-ENDIAN chunk size
quint32 chunkSize;
XDataStream chunkStream(aData.mid(pos, 2));
chunkStream.setByteOrder(XDataStream::BigEndian);
chunkStream >> chunkSize;
quint16 chunkSize;
fastFileStream >> chunkSize;
pos += 2;
if (chunkSize == 0 || pos + chunkSize > aData.size()) {
if (chunkSize == 0 || fastFileStream.device()->pos() + chunkSize > aData.size()) {
qWarning() << "Invalid or incomplete chunk detected, stopping.";
break;
}
const QByteArray compressedChunk = aData.mid(pos, chunkSize);
QByteArray compressedChunk(chunkSize, Qt::Uninitialized);
fastFileStream.readRawData(compressedChunk.data(), chunkSize);
decompressedData.append(Compression::DecompressDeflate(compressedChunk));
pos += chunkSize;
QByteArray decompressedChunk = Compression::DecompressDeflate(compressedChunk);
decompressedData.append(decompressedChunk);
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
ZoneFile_COD4_PS3* zoneFile = new ZoneFile_COD4_PS3();

View File

@ -86,29 +86,24 @@ bool FastFile_COD5_PS3::Load(const QByteArray aData) {
SetMagic(pParseFFMagic(&fastFileStream));
SetVersion(pParseFFVersion(&fastFileStream));
int pos = 12;
// Loop until EOF or invalid chunk
while (pos <= aData.size()) {
fastFileStream.setByteOrder(XDataStream::BigEndian);
while (!fastFileStream.atEnd()) {
// Read 2-byte BIG-ENDIAN chunk size
quint32 chunkSize;
XDataStream chunkStream(aData.mid(pos, 2));
chunkStream.setByteOrder(XDataStream::BigEndian);
chunkStream >> chunkSize;
quint16 chunkSize;
fastFileStream >> chunkSize;
pos += 2;
if (chunkSize == 0 || pos + chunkSize > aData.size()) {
if (chunkSize == 0 || fastFileStream.device()->pos() + chunkSize > aData.size()) {
qWarning() << "Invalid or incomplete chunk detected, stopping.";
break;
}
const QByteArray compressedChunk = aData.mid(pos, chunkSize);
QByteArray compressedChunk(chunkSize, Qt::Uninitialized);
fastFileStream.readRawData(compressedChunk.data(), chunkSize);
decompressedData.append(Compression::DecompressDeflate(compressedChunk));
pos += chunkSize;
QByteArray decompressedChunk = Compression::DecompressDeflate(compressedChunk);
decompressedData.append(decompressedChunk);
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
ZoneFile_COD5_PS3* zoneFile = new ZoneFile_COD5_PS3();

View File

@ -72,26 +72,30 @@ bool FastFile_COD6_PS3::Load(const QString aFilePath) {
}
bool FastFile_COD6_PS3::Load(const QByteArray aData) {
StatusBarManager::instance().updateStatus("Loading COD5 Fast File w/data", 1000);
QByteArray decompressedData;
const qint64 zlibOffset = Compression::FindZlibOffset(aData);
qDebug() << "ZLib Offset: " << zlibOffset;
if (zlibOffset == -1)
{
qWarning() << "Z-Lib stream not found";
return false;
}
QByteArray compressed = aData.mid(zlibOffset);
// Create a XDataStream on the input data.
XDataStream fastFileStream(aData);
fastFileStream.setByteOrder(XDataStream::LittleEndian);
QByteArray decompressedData = Compression::DecompressZLIB(compressed);
// Parse header values.
SetCompany(pParseFFCompany(&fastFileStream));
SetType(pParseFFFileType(&fastFileStream));
SetSignage(pParseFFSignage(&fastFileStream));
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
const QString platformStr = pCalculateFFPlatform(version);
SetPlatform(platformStr);
SetGame("COD5");
if (decompressedData.isEmpty() || decompressedData.size() < 1024)
{
QByteArray stripped = Compression::StripHashBlocks(compressed);
QByteArray retry = Compression::DecompressZLIB(stripped);
if (!retry.isEmpty())
decompressedData.swap(retry);
}
// For COD5, simply decompress from offset 12.
decompressedData = Compression::DecompressZLIB(aData.mid(12));
if (decompressedData.isEmpty())
{
qWarning() << "Unable to decompress fast-file";
return false;
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);

View File

@ -62,10 +62,8 @@ bool FastFile_COD7_PS3::Load(const QString aFilePath) {
qDebug() << "Error: Failed to load fastfile: " << fastFileStem;
return false;
}
file->close();
// Open zone file after decompressing ff and writing
return true;
}
@ -81,10 +79,7 @@ bool FastFile_COD7_PS3::Load(const QByteArray aData) {
SetType(pParseFFFileType(&fastFileStream));
SetSignage(pParseFFSignage(&fastFileStream));
SetMagic(pParseFFMagic(&fastFileStream));
quint32 version = pParseFFVersion(&fastFileStream);
SetVersion(version);
SetPlatform(pCalculateFFPlatform(version));
SetGame("COD7");
SetVersion(pParseFFVersion(&fastFileStream));
// Load the zone file with the decompressed data (using an Xbox platform flag).
ZoneFile_COD7_PS3* zoneFile = new ZoneFile_COD7_PS3();
@ -92,82 +87,73 @@ bool FastFile_COD7_PS3::Load(const QByteArray aData) {
// For COD7/COD9, use BigEndian.
fastFileStream.setByteOrder(XDataStream::BigEndian);
if (GetPlatform() == "PC") {
fastFileStream.setByteOrder(XDataStream::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");
}
// Select key based on game.
QByteArray key = "46D3F997F29C9ACE175B0DAE3AB8C0C1B8E423E2E3BF7E3C311EA35245BF193A";
fastFileStream.skipRawData(4);
// 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 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);
// 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);
// 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);
// 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;
// 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);
// 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);
// Compute the IV for this section.
QByteArray iv = Encryption::GetIV(ivTable, sectionIndex);
// Decrypt the section using Salsa20.
QByteArray decData = Encryption::salsa20DecryptSection(sectionData, key, iv);
// 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);
// 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);
// 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);
// 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));
decompressedData.append(Compression::DecompressZLIB(compressedData));
sectionIndex++;
}
sectionIndex++;
}
Utils::ExportData(GetBaseStem() + ".zone", decompressedData);
if (!zoneFile->Load(decompressedData)) {
qWarning() << "Failed to load ZoneFile!";
return false;
}
if (!zoneFile->Load(decompressedData)) {
qWarning() << "Failed to load ZoneFile!";
return false;
}
SetZoneFile(zoneFile);

View File

@ -118,51 +118,6 @@ bool FastFile_COD8_PS3::Load(const QByteArray aData) {
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++;
}
if (!zoneFile->Load(decompressedData)) {
qWarning() << "Failed to load ZoneFile!";
return false;

View File

@ -117,7 +117,7 @@ bool FastFile_COD9_PS3::Load(const QByteArray aData) {
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
//decompressedData = Encryption::decryptFastFile_BO2(aData);
}
// For COD9, write out the complete decompressed zone for testing.

View File

@ -115,7 +115,7 @@ bool FastFile_COD10_WiiU::Load(const QByteArray aData) {
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
//decompressedData = Encryption::decryptFastFile_BO2(aData);
}
// For COD9, write out the complete decompressed zone for testing.

View File

@ -115,7 +115,7 @@ bool FastFile_COD9_WiiU::Load(const QByteArray aData) {
if (GetPlatform() == "360") {
//decompressedData = Compressor::cod9_decryptFastFile(aData);
} else if (GetPlatform() == "PC") {
decompressedData = Encryption::decryptFastFile_BO2(aData);
//decompressedData = Encryption::decryptFastFile_BO2(aData);
}
// For COD9, write out the complete decompressed zone for testing.

View File

@ -267,7 +267,7 @@ FastFile* FastFile::Open(const QString &aFilePath) {
const QString fastFileStem = aFilePath.section("/", -1, -1);
LogManager::instance().addEntry(QString("Stem: %1").arg(fastFileStem));
FastFile* fastFile = FastFileFactory::Create(data);
FastFile* fastFile = FastFileFactory::Create(data, fastFileStem);
fastFile->SetStem(fastFileStem);
return fastFile;

View File

@ -7,6 +7,7 @@
#include "360/fastfile_cod5_360.h"
#include "360/fastfile_cod6_360.h"
#include "360/fastfile_cod7_360.h"
#include "360/fastfile_cod7_5_360.h"
#include "360/fastfile_cod8_360.h"
#include "360/fastfile_cod9_360.h"
#include "360/fastfile_cod10_360.h"
@ -60,17 +61,18 @@ enum FastFile_Platform {
};
enum FastFile_Game {
GAME_NONE = 0x00,
GAME_COD2 = 0x01,
GAME_COD4 = 0x02,
GAME_COD5 = 0x03,
GAME_COD6 = 0x04,
GAME_COD7 = 0x05,
GAME_COD8 = 0x06,
GAME_COD9 = 0x07,
GAME_COD10 = 0x08,
GAME_COD11 = 0x09,
GAME_COD12 = 0x010
GAME_NONE = 0x00,
GAME_COD2 = 0x01,
GAME_COD4 = 0x02,
GAME_COD5 = 0x03,
GAME_COD6 = 0x04,
GAME_COD7 = 0x05,
GAME_COD7_5 = 0x06,
GAME_COD8 = 0x07,
GAME_COD9 = 0x08,
GAME_COD10 = 0x09,
GAME_COD11 = 0x10,
GAME_COD12 = 0x11
};
class FastFileFactory {
@ -185,83 +187,86 @@ public:
if (aPlatform == PLATFORM_360) {
if (aGame == GAME_COD2) {
resultFF = new FastFile_COD2_360(aData);
resultFF = new FastFile_COD2_360();
} else if (aGame == GAME_COD4) {
resultFF = new FastFile_COD4_360(aData);
resultFF = new FastFile_COD4_360();
} else if (aGame == GAME_COD5) {
resultFF = new FastFile_COD5_360(aData);
resultFF = new FastFile_COD5_360();
} else if (aGame == GAME_COD6) {
resultFF = new FastFile_COD6_360(aData);
resultFF = new FastFile_COD6_360();
} else if (aGame == GAME_COD7) {
resultFF = new FastFile_COD7_360(aData);
resultFF = new FastFile_COD7_360();
} else if (aGame == GAME_COD7_5) {
resultFF = new FastFile_COD7_5_360();
} else if (aGame == GAME_COD8) {
resultFF = new FastFile_COD8_360(aData);
resultFF = new FastFile_COD8_360();
} else if (aGame == GAME_COD9) {
resultFF = new FastFile_COD9_360(aData);
resultFF = new FastFile_COD9_360();
} else if (aGame == GAME_COD10) {
resultFF = new FastFile_COD10_360(aData);
resultFF = new FastFile_COD10_360();
} else if (aGame == GAME_COD11) {
resultFF = new FastFile_COD11_360(aData);
resultFF = new FastFile_COD11_360();
} else if (aGame == GAME_COD12) {
resultFF = new FastFile_COD12_360(aData);
resultFF = new FastFile_COD12_360();
}
} else if (aPlatform == PLATFORM_PC) {
if (aGame == GAME_COD4) {
resultFF = new FastFile_COD4_PC(aData);
resultFF = new FastFile_COD4_PC();
} else if (aGame == GAME_COD5) {
resultFF = new FastFile_COD5_PC(aData);
resultFF = new FastFile_COD5_PC();
} else if (aGame == GAME_COD6) {
resultFF = new FastFile_COD6_PC(aData);
resultFF = new FastFile_COD6_PC();
} else if (aGame == GAME_COD7) {
resultFF = new FastFile_COD7_PC(aData);
resultFF = new FastFile_COD7_PC();
} else if (aGame == GAME_COD8) {
resultFF = new FastFile_COD8_PC(aData);
resultFF = new FastFile_COD8_PC();
} else if (aGame == GAME_COD9) {
resultFF = new FastFile_COD9_PC(aData);
resultFF = new FastFile_COD9_PC();
} else if (aGame == GAME_COD10) {
resultFF = new FastFile_COD10_PC(aData);
resultFF = new FastFile_COD10_PC();
} else if (aGame == GAME_COD11) {
resultFF = new FastFile_COD11_PC(aData);
resultFF = new FastFile_COD11_PC();
} else if (aGame == GAME_COD12) {
resultFF = new FastFile_COD12_PC(aData);
resultFF = new FastFile_COD12_PC();
}
} else if (aPlatform == PLATFORM_PS3) {
if (aGame == GAME_COD4) {
resultFF = new FastFile_COD4_PS3(aData);
resultFF = new FastFile_COD4_PS3();
} else if (aGame == GAME_COD5) {
resultFF = new FastFile_COD5_PS3(aData);
resultFF = new FastFile_COD5_PS3();
} else if (aGame == GAME_COD6) {
resultFF = new FastFile_COD6_PS3(aData);
resultFF = new FastFile_COD6_PS3();
} else if (aGame == GAME_COD7) {
resultFF = new FastFile_COD7_PS3(aData);
resultFF = new FastFile_COD7_PS3();
} else if (aGame == GAME_COD8) {
resultFF = new FastFile_COD8_PS3(aData);
resultFF = new FastFile_COD8_PS3();
} else if (aGame == GAME_COD9) {
resultFF = new FastFile_COD9_PS3(aData);
resultFF = new FastFile_COD9_PS3();
} else if (aGame == GAME_COD10) {
resultFF = new FastFile_COD10_PS3(aData);
resultFF = new FastFile_COD10_PS3();
} else if (aGame == GAME_COD11) {
resultFF = new FastFile_COD11_PS3(aData);
resultFF = new FastFile_COD11_PS3();
} else if (aGame == GAME_COD12) {
resultFF = new FastFile_COD12_PS3(aData);
resultFF = new FastFile_COD12_PS3();
}
} else if (aPlatform == PLATFORM_WII) {
if (aGame == GAME_COD4) {
resultFF = new FastFile_COD4_Wii(aData);
resultFF = new FastFile_COD4_Wii();
} else if (aGame == GAME_COD7) {
resultFF = new FastFile_COD7_Wii(aData);
resultFF = new FastFile_COD7_Wii();
} else if (aGame == GAME_COD8) {
resultFF = new FastFile_COD8_Wii(aData);
resultFF = new FastFile_COD8_Wii();
}
} else if (aPlatform == PLATFORM_WIIU) {
if (aGame == GAME_COD9) {
resultFF = new FastFile_COD9_WiiU(aData);
resultFF = new FastFile_COD9_WiiU();
} else if (aGame == GAME_COD10) {
resultFF = new FastFile_COD10_WiiU(aData);
resultFF = new FastFile_COD10_WiiU();
}
}
if (resultFF) {
resultFF->SetStem(aStem);
resultFF->Load(aData);
}
return resultFF;
}
@ -283,7 +288,7 @@ private:
static FastFile_Platform pGetPlatform(const QByteArray& aData) {
const QStringList sections = pGetDataSections(aData);
if (sections[0] == "0000") {
if (sections[0] == "0000" || sections[0] == "4E58") {
return PLATFORM_360;
} else if (sections[4] == "0000") {
if (sections[5] == "0001" && sections[6] == "78DA") {
@ -292,6 +297,8 @@ private:
return PLATFORM_360;
} else if (sections[5] == "0183" && sections[6] == "7801") {
return PLATFORM_360;
} else if (sections[5] == "01D9" && sections[6] == "0000") {
return PLATFORM_360;
} else if (sections[6] == "0101" && sections[7] == "CA3E") {
return PLATFORM_360;
} else if (sections[6] == "0000" && sections[7] == "0001") {
@ -333,6 +340,8 @@ private:
return GAME_COD6;
} else if (sections[4] == "D901" || sections[5] == "01DD" || sections[5] == "01D9") {
return GAME_COD7;
} else if (sections[0] == "4E58") {
return GAME_COD7_5;
} else if (sections[4] == "0100" || sections[5] == "006B" || sections[5] == "0070") {
return GAME_COD8;
} else if (sections[4] == "9300" || sections[5] == "0092"

View File

@ -6,6 +6,6 @@ SUBDIRS += core \
fastfile \
xassets \
zonefile \
ddsfile \
iwifile \
ipakfile
#ddsfile \
#iwifile \
#ipakfile

View File

@ -1,7 +1,9 @@
#ifndef XD3DENUMS_H
#define XD3DENUMS_H
enum XD3DResourceType
#include <QtTypes>
enum XD3DResourceType : qint32
{
D3DRTYPE_NONE = 0x0,
D3DRTYPE_VERTEXBUFFER = 0x1,
@ -21,7 +23,7 @@ enum XD3DResourceType
D3DRTYPE_FORCE_DWORD = 0x7FFFFFFF,
};
enum XD3DFormat
enum XD3DFormat : qint32
{
D3DFMT_DXT1 = 0x1A200152,
D3DFMT_LIN_DXT1 = 0x1A200052,
@ -177,7 +179,7 @@ enum XD3DFormat
D3DFMT_FORCE_DWORD = 0x7FFFFFFF,
};
enum XD3DMultiSampleType
enum XD3DMultiSampleType : qint32
{
D3DMULTISAMPLE_NONE = 0x0,
D3DMULTISAMPLE_2_SAMPLES = 0x1,

View File

@ -16,9 +16,7 @@ XOperandInternalDataUnion::~XOperandInternalDataUnion()
void XOperandInternalDataUnion::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XOperandInternalDataUnion::ParseData
aStream->ParseSingle(QString("%1 center of mass x").arg(GetName()));
}
void XOperandInternalDataUnion::Clear()

View File

@ -9,16 +9,19 @@ XPhysMass::XPhysMass()
SetName("Phys Mass");
}
XPhysMass::~XPhysMass()
{
}
void XPhysMass::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
mCenterOfMass.setX(aStream->ParseSingle(QString("%1 center of mass x").arg(GetName())));
mCenterOfMass.setY(aStream->ParseSingle(QString("%1 center of mass y").arg(GetName())));
mCenterOfMass.setZ(aStream->ParseSingle(QString("%1 center of mass z").arg(GetName())));
// TODO: Fill in XPhysMass::ParseData
mMomentsOfInertia.setX(aStream->ParseSingle(QString("%1 moments of inertia x").arg(GetName())));
mMomentsOfInertia.setY(aStream->ParseSingle(QString("%1 moments of inertia y").arg(GetName())));
mMomentsOfInertia.setZ(aStream->ParseSingle(QString("%1 moments of inertia y").arg(GetName())));
mProductsOfInertia.setX(aStream->ParseSingle(QString("%1 products of inertia x").arg(GetName())));
mProductsOfInertia.setY(aStream->ParseSingle(QString("%1 products of inertia y").arg(GetName())));
mProductsOfInertia.setZ(aStream->ParseSingle(QString("%1 products of inertia z").arg(GetName())));
}
void XPhysMass::Clear()

View File

@ -8,8 +8,8 @@
class XPhysMass : public XAsset
{
public:
XPhysMass();
~XPhysMass();
explicit XPhysMass();
~XPhysMass() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;

View File

@ -4,32 +4,30 @@ XRectDef::XRectDef()
: XAsset()
, mX(0)
, mY(0)
, mW(0)
, mH(0)
, mWidth(0)
, mHeight(0)
, mHorzAlign(0)
, mVertAlign(0)
{
SetName("Rectangle Definition");
}
XRectDef::~XRectDef()
{
}
void XRectDef::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XRectDef::ParseData
mX = aStream->ParseSingle(QString("%1 x").arg(GetName()));
mY = aStream->ParseSingle(QString("%1 y").arg(GetName()));
mWidth = aStream->ParseSingle(QString("%1 width").arg(GetName()));
mHeight = aStream->ParseSingle(QString("%1 height").arg(GetName()));
mHorzAlign = aStream->ParseInt32(QString("%1 horizontal align").arg(GetName()));
mVertAlign = aStream->ParseInt32(QString("%1 vertical align").arg(GetName()));
}
void XRectDef::Clear()
{
mX = 0;
mY = 0;
mW = 0;
mH = 0;
mWidth = 0;
mHeight = 0;
mHorzAlign = 0;
mVertAlign = 0;
}

View File

@ -7,7 +7,7 @@ class XRectDef : public XAsset
{
public:
explicit XRectDef();
~XRectDef();
~XRectDef() = default;
void ParseData(XDataStream *aStream) override;
void Clear() override;
@ -15,10 +15,10 @@ public:
private:
float mX;
float mY;
float mW;
float mH;
int mHorzAlign;
int mVertAlign;
float mWidth;
float mHeight;
qint32 mHorzAlign;
qint32 mVertAlign;
};
#endif // XRECTDEF_H

View File

@ -13,16 +13,21 @@ XSrfTriangles::XSrfTriangles()
SetName("Surface Triangles");
}
XSrfTriangles::~XSrfTriangles()
{
}
void XSrfTriangles::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
mVertexLayerData = aStream->ParseInt32(QString("%1 vertex layer data").arg(GetName()));
mFirstVertex = aStream->ParseInt32(QString("%1 first vertex").arg(GetName()));
mVertexCount = aStream->ParseUInt32(QString("%1 vertex count").arg(GetName()));
mTriCount = aStream->ParseUInt32(QString("%1 tri count").arg(GetName()));
mBaseIndex = aStream->ParseInt32(QString("%1 base index").arg(GetName()));
// TODO: Fill in XSrfTriangles::ParseData
mTopMipMins.setX(aStream->ParseSingle(QString("%1 top mip min x").arg(GetName())));
mTopMipMins.setY(aStream->ParseSingle(QString("%1 top mip min y").arg(GetName())));
mTopMipMins.setZ(aStream->ParseSingle(QString("%1 top mip min z").arg(GetName())));
mTopMipMins.setX(aStream->ParseSingle(QString("%1 top mip max x").arg(GetName())));
mTopMipMins.setY(aStream->ParseSingle(QString("%1 top mip max y").arg(GetName())));
mTopMipMins.setZ(aStream->ParseSingle(QString("%1 top mip max z").arg(GetName())));
}
void XSrfTriangles::Clear()

View File

@ -9,17 +9,17 @@ class XSrfTriangles : public XAsset
{
public:
explicit XSrfTriangles();
~XSrfTriangles();
~XSrfTriangles() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;
private:
int mVertexLayerData;
int mFirstVertex;
qint32 mVertexLayerData;
qint32 mFirstVertex;
quint32 mVertexCount;
quint32 mTriCount;
int mBaseIndex;
qint32 mBaseIndex;
QVector3D mTopMipMins;
QVector3D mTopMipMaxs;
};

View File

@ -8,16 +8,21 @@ XStatement::XStatement()
SetName("Statement");
}
XStatement::~XStatement()
{
}
void XStatement::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
mNumEntries = aStream->ParseInt32(QString("%1 # entries").arg(GetName()));
// TODO: Fill in XStatement::ParseData
qint32 entriesPtr;
entriesPtr = aStream->ParseInt32(QString("%1 entries ptr").arg(GetName()));
if (entriesPtr)
{
for (int i = 0; i < mNumEntries; i++)
{
XExpressionEntry newEntry;
newEntry.ParsePtr(aStream);
mEntries.append(newEntry);
}
}
}
void XStatement::Clear()

View File

@ -10,14 +10,14 @@ class XStatement : public XAsset
{
public:
explicit XStatement();
~XStatement();
~XStatement() = default;
void ParseData(XDataStream *aStream) override;
void Clear() override;
private:
int mNumEntries;
QVector<XExpressionEntry*> mEntries;
qint32 mNumEntries;
QVector<XExpressionEntry> mEntries;
};
#endif // XSTATEMENT_H

View File

@ -2,26 +2,18 @@
XStreamDelayInfo::XStreamDelayInfo()
: XAsset()
, mPtr(0)
, mSize(0)
{
SetName("Stream Delay Info");
}
XStreamDelayInfo::~XStreamDelayInfo()
{
}
void XStreamDelayInfo::Clear()
{
mPtr = nullptr;
//mPtr = nullptr;
mSize = 0;
}
void XStreamDelayInfo::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XStreamDelayInfo::ParseData
mSize = aStream->ParseInt32(QString("%1 size").arg(GetName()));
}

View File

@ -7,14 +7,14 @@ class XStreamDelayInfo : public XAsset
{
public:
XStreamDelayInfo();
~XStreamDelayInfo();
~XStreamDelayInfo() = default;
virtual void Clear() override;
virtual void ParseData(XDataStream* aStream) override;
private:
const void *mPtr;
int mSize;
//const void *mPtr;
qint32 mSize;
};
#endif // XSTREAMDELAYINFO_H

View File

@ -2,23 +2,17 @@
XStreamedSound::XStreamedSound()
: XAsset()
, mFileName()
{
SetName("Streamed Sound");
}
XStreamedSound::~XStreamedSound()
{
}
void XStreamedSound::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XStreamedSound::ParseData
mFileName.ParseData(aStream);
}
void XStreamedSound::Clear()
{
mFileName.Clear();
}

View File

@ -2,20 +2,19 @@
#define XSTREAMEDSOUND_H
#include "xasset.h"
class XStreamedFileName;
#include "xstreamfilename.h"
class XStreamedSound : public XAsset
{
public:
XStreamedSound();
~XStreamedSound();
~XStreamedSound() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;
private:
XStreamedFileName* mStreamed;
XStreamFileName mFileName;
};
#endif // XSTREAMEDSOUND_H

View File

@ -1,24 +1,31 @@
#include "xstreamfileinfo.h"
#include "xstreamfilename.h"
XStreamFileInfo::XStreamFileInfo()
: XAsset()
, mParent(nullptr)
, mRaw()
{
SetName("Stream File Info");
}
XStreamFileInfo::~XStreamFileInfo()
XStreamFileInfo::XStreamFileInfo(XStreamFileName &aParent)
: XAsset()
, mParent(&aParent)
, mRaw()
{
SetName("Stream File Info");
}
void XStreamFileInfo::Clear()
{
mRaw.Clear();
}
void XStreamFileInfo::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XStreamFileInfo::ParseData
if (mParent && !mParent->GetFileIndex())
{
mRaw.ParseData(aStream);
}
}

View File

@ -2,21 +2,23 @@
#define XSTREAMFILEINFO_H
#include "xasset.h"
#include "xstreamfilenamepacked.h"
#include "xstreamfilenameraw.h"
class XStreamFileName;
class XStreamFileInfo : public XAsset
{
public:
XStreamFileInfo();
~XStreamFileInfo();
XStreamFileInfo(XStreamFileName& aParent);
~XStreamFileInfo() = default;
virtual void Clear() override;
virtual void ParseData(XDataStream* aStream) override;
private:
XStreamFileName* mParent;
XStreamFileNameRaw mRaw;
XStreamFileNamePacked mPacked;
};
#endif // XSTREAMFILEINFO_H

View File

@ -8,16 +8,11 @@ XStreamFileName::XStreamFileName()
SetName("Stream File Name");
}
XStreamFileName::~XStreamFileName()
{
}
void XStreamFileName::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
mFileIndex = aStream->ParseUInt32(QString("%1 file index").arg(GetName()));
// TODO: Fill in XStreamFileName::ParseData
mInfo.ParseData(aStream);
}
void XStreamFileName::Clear()
@ -25,3 +20,8 @@ void XStreamFileName::Clear()
mFileIndex = 0;
mInfo.Clear();
}
quint32 XStreamFileName::GetFileIndex() const
{
return mFileIndex;
}

View File

@ -8,11 +8,13 @@ class XStreamFileName : public XAsset
{
public:
explicit XStreamFileName();
~XStreamFileName();
~XStreamFileName() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;
quint32 GetFileIndex() const;
private:
quint32 mFileIndex;
XStreamFileInfo mInfo;

View File

@ -2,23 +2,20 @@
XStreamFileNamePacked::XStreamFileNamePacked()
: XAsset()
, mOffset(0)
, mLength(0)
{
SetName("Stream File Name Packed");
}
XStreamFileNamePacked::~XStreamFileNamePacked()
{
}
void XStreamFileNamePacked::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XStreamFileNamePacked::ParseData
mOffset = aStream->ParseUInt32(QString("%1 offset").arg(GetName()));
mLength = aStream->ParseUInt32(QString("%1 length").arg(GetName()));
}
void XStreamFileNamePacked::Clear()
{
mOffset = 0;
mLength = 0;
}

View File

@ -9,7 +9,7 @@ class XStreamFileNamePacked : public XAsset
{
public:
XStreamFileNamePacked();
~XStreamFileNamePacked();
~XStreamFileNamePacked() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;

View File

@ -2,23 +2,23 @@
XStreamFileNameRaw::XStreamFileNameRaw()
: XAsset()
, mDir()
, mName()
{
SetName("Stream File Name Raw");
}
XStreamFileNameRaw::~XStreamFileNameRaw()
{
}
void XStreamFileNameRaw::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
mDir.ParsePtr(aStream, false);
mName.ParsePtr(aStream, false);
// TODO: Fill in XStreamFileNameRaw::ParseData
mDir.ParseData(aStream);
mName.ParseData(aStream);
}
void XStreamFileNameRaw::Clear()
{
mDir.Clear();
mName.Clear();
}

View File

@ -8,14 +8,14 @@ class XStreamFileNameRaw : public XAsset
{
public:
XStreamFileNameRaw();
~XStreamFileNameRaw();
~XStreamFileNameRaw() = default;
virtual void ParseData(XDataStream* aStream) override;
virtual void Clear() override;
private:
XString* mDir;
XString* mName;
XString mDir;
XString mName;
};
#endif // XSTREAMFILENAMERAW_H

View File

@ -9,11 +9,6 @@ XStreamSourceInfo::XStreamSourceInfo()
SetName("Stream Source Info");
}
XStreamSourceInfo::~XStreamSourceInfo()
{
}
void XStreamSourceInfo::Clear()
{
mStream = 0;
@ -23,7 +18,7 @@ void XStreamSourceInfo::Clear()
void XStreamSourceInfo::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XStreamSourceInfo::ParseData
mStream = aStream->ParseUInt16(QString("%1 stream").arg(GetName()));
mOffset = aStream->ParseUInt16(QString("%1 offset").arg(GetName()));
mType = aStream->ParseUInt32(QString("%1 type").arg(GetName()));
}

View File

@ -7,14 +7,14 @@ class XStreamSourceInfo : public XAsset
{
public:
XStreamSourceInfo();
~XStreamSourceInfo();
~XStreamSourceInfo() = default;
virtual void Clear() override;
virtual void ParseData(XDataStream* aStream) override;
private:
quint32 mStream;
quint32 mOffset;
quint16 mStream;
quint16 mOffset;
quint32 mType;
};

View File

@ -35,5 +35,19 @@ void XTextureDesc::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XTextureDesc::ParseData
mResourceType = (XD3DResourceType)aStream->ParseInt32(QString("%1 resource type").arg(GetName()));
mWidth = aStream->ParseUInt32(QString("%1 width").arg(GetName()));
mHeight = aStream->ParseUInt32(QString("%1 height").arg(GetName()));
mDepth = aStream->ParseUInt32(QString("%1 depth").arg(GetName()));
mFormat = (XD3DFormat)aStream->ParseInt32(QString("%1 format").arg(GetName()));
mRowPitch = aStream->ParseUInt32(QString("%1 row pitch").arg(GetName()));
mSlicePitch = aStream->ParseUInt32(QString("%1 slice pitch").arg(GetName()));
mBitsPerPixel = aStream->ParseUInt32(QString("%1 bits per pixel").arg(GetName()));
mWidthInBlocks = aStream->ParseUInt32(QString("%1 width in blocks").arg(GetName()));
mHeightInBlocks = aStream->ParseUInt32(QString("%1 height in blocks").arg(GetName()));
mDepthInBlocks = aStream->ParseUInt32(QString("%1 depth in blocks").arg(GetName()));
mBytesPerBlock = aStream->ParseUInt32(QString("%1 bytes per block").arg(GetName()));
mExpBias = aStream->ParseInt32(QString("%1 exp bias").arg(GetName()));
mFlags = aStream->ParseUInt32(QString("%1 flags").arg(GetName()));
mMultiSampleType = (XD3DMultiSampleType)aStream->ParseInt32(QString("%1 multi-sample type").arg(GetName()));
}

View File

@ -16,7 +16,7 @@ void XWaterWritable::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XWaterWritable::ParseData
mFloatTime = aStream->ParseSingle(QString("%1 float time").arg(GetName()));
}
void XWaterWritable::Clear()

View File

@ -2,10 +2,10 @@
XWindowDef::XWindowDef()
: XAsset()
, mName("")
, mName()
, mRect()
, mRectClient()
, mGroup("")
, mGroup()
, mStyle(0)
, mBorder(0)
, mOwnerDraw(0)
@ -18,7 +18,7 @@ XWindowDef::XWindowDef()
, mBackColor()
, mBorderColor()
, mOutlineColor()
, mBackground(new XMaterial())
, mBackground()
{
SetName("Window Definition");
}
@ -32,15 +32,66 @@ void XWindowDef::ParseData(XDataStream *aStream)
{
Q_UNUSED(aStream);
// TODO: Fill in XWindowDef::ParseData
mName.ParsePtr(aStream, false);
mRect.ParseData(aStream);
mRectClient.ParseData(aStream);
mGroup.ParsePtr(aStream, false);
mStyle = aStream->ParseInt32(QString("%1 style").arg(GetName()));
mBorder = aStream->ParseInt32(QString("%1 border").arg(GetName()));
mOwnerDraw = aStream->ParseInt32(QString("%1 owner draw").arg(GetName()));
mOwnerDrawFlags = aStream->ParseInt32(QString("%1 owner draw flags").arg(GetName()));
mBorderSize = aStream->ParseSingle(QString("%1 border size").arg(GetName()));
mStaticFlags = aStream->ParseInt32(QString("%1 static flags").arg(GetName()));
for (int i = 0; i < 4; i++)
{
mDynamicFlags[i] = aStream->ParseInt32(QString("%1 dynamic flag %2").arg(GetName()).arg(i));
}
mNextTime = aStream->ParseInt32(QString("%1 next time").arg(GetName()));
float r, g, b, a;
r = aStream->ParseSingle(QString("%1 foreground red").arg(GetName()));
g = aStream->ParseSingle(QString("%1 foreground green").arg(GetName()));
b = aStream->ParseSingle(QString("%1 foreground blue").arg(GetName()));
a = aStream->ParseSingle(QString("%1 foreground alpha").arg(GetName()));
mForeColor = QColor(r, g, b, a);
r = aStream->ParseSingle(QString("%1 background red").arg(GetName()));
g = aStream->ParseSingle(QString("%1 background green").arg(GetName()));
b = aStream->ParseSingle(QString("%1 background blue").arg(GetName()));
a = aStream->ParseSingle(QString("%1 background alpha").arg(GetName()));
mBackColor = QColor(r, g, b, a);
r = aStream->ParseSingle(QString("%1 border red").arg(GetName()));
g = aStream->ParseSingle(QString("%1 border green").arg(GetName()));
b = aStream->ParseSingle(QString("%1 border blue").arg(GetName()));
a = aStream->ParseSingle(QString("%1 border alpha").arg(GetName()));
mBorderColor = QColor(r, g, b, a);
r = aStream->ParseSingle(QString("%1 outline red").arg(GetName()));
g = aStream->ParseSingle(QString("%1 outline green").arg(GetName()));
b = aStream->ParseSingle(QString("%1 outline blue").arg(GetName()));
a = aStream->ParseSingle(QString("%1 outline alpha").arg(GetName()));
mOutlineColor = QColor(r, g, b, a);
mBackground.ParsePtr(aStream, false);
mName.ParseData(aStream);
mGroup.ParseData(aStream);
mBackground.ParseData(aStream);
}
void XWindowDef::Clear()
{
mName.clear();
mName.Clear();
mRect.Clear();
mRectClient.Clear();
mGroup.clear();
mGroup.Clear();
mStyle = 0;
mBorder = 0;
mOwnerDraw = 0;
@ -53,5 +104,5 @@ void XWindowDef::Clear()
mBackColor = QColor();
mBorderColor = QColor();
mOutlineColor = QColor();
mBackground->Clear();
mBackground.Clear();
}

View File

@ -17,10 +17,10 @@ public:
void Clear() override;
private:
QString mName;
XString mName;
XRectDef mRect;
XRectDef mRectClient;
QString mGroup;
XString mGroup;
int mStyle;
int mBorder;
int mOwnerDraw;
@ -33,7 +33,7 @@ private:
QColor mBackColor;
QColor mBorderColor;
QColor mOutlineColor;
XMaterial *mBackground;
XMaterial mBackground;
};
#endif // XWINDOWDEF_H

View File

@ -120,7 +120,7 @@ void AutoTest_COD10_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD10";

View File

@ -120,7 +120,7 @@ void AutoTest_COD11_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD11";

View File

@ -238,7 +238,7 @@ void AutoTest_COD12_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD12";

View File

@ -143,7 +143,7 @@ void AutoTest_COD2_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD2";

View File

@ -126,7 +126,7 @@ void AutoTest_COD5_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD5";

View File

@ -146,7 +146,7 @@ void AutoTest_COD6_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD6";

View File

@ -67,45 +67,6 @@ void AutoTest_COD7_360::testDecompression() {
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;
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.
decompressedData.append(Compression::DecompressDeflate(decData));
sectionIndex++;
}
const QByteArray testZoneData = decompressedData;
// Verify the decompressed data via its embedded zone size.
@ -149,7 +110,7 @@ void AutoTest_COD7_360::testCompression() {
zoneFile.close();
QByteArray compressedData;
QByteArray key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d");
//QByteArray key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d");
// Read the original fastfile header to recover metadata (filename, IV table, etc.)
QString ffPath = zoneFilePath;
@ -163,40 +124,17 @@ void AutoTest_COD7_360::testCompression() {
originalFF.read(fileName.data(), 32);
originalFF.close();
QByteArray ivTable = Encryption::InitIVTable(fileName);
// Rebuild sections from zone data
QDataStream zoneStream(zoneData);
zoneStream.setByteOrder(QDataStream::BigEndian);
quint32 zoneSize;
zoneStream >> zoneSize;
QByteArray remainingData = zoneData.mid(4); // exclude size field
const int chunkSize = 0x40000; // 256KB max section size
//QByteArray remainingData = zoneData.mid(4); // exclude size field
QDataStream fastFileStreamOut(&compressedData, QIODevice::WriteOnly);
fastFileStreamOut.setByteOrder(QDataStream::BigEndian);
int sectionIndex = 0;
int offset = 0;
while (offset < remainingData.size()) {
int sectionLen = qMin(chunkSize, remainingData.size() - offset);
QByteArray chunk = remainingData.mid(offset, sectionLen);
QByteArray deflated = Compression::CompressDeflate(chunk);
QByteArray iv = Encryption::GetIV(ivTable, sectionIndex);
QByteArray encrypted = Encryption::salsa20DecryptSection(deflated, key, iv);
QByteArray sha1 = QCryptographicHash::hash(chunk, QCryptographicHash::Sha1);
Encryption::UpdateIVTable(ivTable, sectionIndex, sha1);
fastFileStreamOut << static_cast<qint32>(encrypted.size());
fastFileStreamOut.writeRawData(encrypted.constData(), encrypted.size());
offset += sectionLen;
sectionIndex++;
}
// Write 0 section size terminator
fastFileStreamOut << static_cast<qint32>(0);

View File

@ -139,7 +139,7 @@ void AutoTest_COD8_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD8";

View File

@ -120,7 +120,7 @@ void AutoTest_COD9_360::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD9";

View File

@ -116,7 +116,7 @@ void AutoTest_COD10_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD10";

View File

@ -116,7 +116,7 @@ void AutoTest_COD11_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD11";

View File

@ -209,7 +209,7 @@ void AutoTest_COD12_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD12";

View File

@ -113,7 +113,7 @@ void AutoTest_COD4_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD4";

View File

@ -117,7 +117,7 @@ void AutoTest_COD5_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD5";

View File

@ -109,7 +109,7 @@ void AutoTest_COD6_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD6";

View File

@ -116,7 +116,7 @@ void AutoTest_COD7_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD7";

View File

@ -116,7 +116,7 @@ void AutoTest_COD8_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD8";

View File

@ -116,7 +116,7 @@ void AutoTest_COD9_PC::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD9";

View File

@ -118,7 +118,7 @@ void AutoTest_COD10_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD10";

View File

@ -118,7 +118,7 @@ void AutoTest_COD11_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD11";

View File

@ -211,7 +211,7 @@ void AutoTest_COD12_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD12";

View File

@ -157,7 +157,7 @@ void AutoTest_COD4_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD4";

View File

@ -155,7 +155,7 @@ void AutoTest_COD5_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD5";

View File

@ -118,7 +118,7 @@ void AutoTest_COD6_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD6";

View File

@ -5,6 +5,8 @@
#include "autotest_cod.h"
#include "compression.h"
#include "encryption.h"
#include "fastfile.h"
#include "xdatastream.h"
class AutoTest_COD7_PS3 : public AutoTest_COD {
Q_OBJECT
@ -66,44 +68,6 @@ void AutoTest_COD7_PS3::testDecompression() {
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;
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.
decompressedData.append(Compression::DecompressDeflate(decData));
sectionIndex++;
}
const QByteArray testZoneData = decompressedData;
// Verify the decompressed data via its embedded zone size.
@ -181,7 +145,7 @@ void AutoTest_COD7_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD7";

View File

@ -118,7 +118,7 @@ void AutoTest_COD8_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD8";

View File

@ -118,7 +118,7 @@ void AutoTest_COD9_PS3::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD9";

View File

@ -117,7 +117,7 @@ void AutoTest_COD4_Wii::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD4";

View File

@ -117,7 +117,7 @@ void AutoTest_COD7_Wii::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD7";

View File

@ -109,7 +109,7 @@ void AutoTest_COD8_Wii::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD8";

View File

@ -118,7 +118,7 @@ void AutoTest_COD10_WiiU::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD10";

View File

@ -118,7 +118,7 @@ void AutoTest_COD9_WiiU::testFactory() {
const QString testName = "Factory ingest: " + fastFilePath;
FastFile* fastFile = FastFileFactory::Create(fastFilePath);
FastFile* fastFile = FastFile::Open(fastFilePath);
const QString game = fastFile->GetGame();
bool correctGame = game == "COD9";