#include "fastfile_cod21_pc.h" #include "zonefile_cod21_pc.h" #include "utils.h" #include "compression.h" #include "encryption.h" #include #include FastFile_COD21_PC::FastFile_COD21_PC() : FastFile() { SetCompany(COMPANY_INFINITY_WARD); SetType(FILETYPE_FAST_FILE); SetSignage(SIGNAGE_SIGNED); SetMagic(0); SetVersion(0); } FastFile_COD21_PC::FastFile_COD21_PC(const QByteArray& aData) : FastFile_COD21_PC() { if (!aData.isEmpty()) { Load(aData); } } FastFile_COD21_PC::FastFile_COD21_PC(const QString aFilePath) : FastFile_COD21_PC() { if (!aFilePath.isEmpty()) { Load(aFilePath); } } FastFile_COD21_PC::~FastFile_COD21_PC() { } QByteArray FastFile_COD21_PC::GetBinaryData() const { return QByteArray(); } bool FastFile_COD21_PC::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).split('.').first(); 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; } bool FastFile_COD21_PC::Load(const QByteArray aData) { QByteArray decompressedData; // Create a XDataStream on the input data. XDataStream fastFileStream(aData); fastFileStream.setByteOrder(XDataStream::LittleEndian); auto dev = fastFileStream.device(); if (dev->bytesAvailable() < 12) { qWarning() << "Not enough data for block header"; return false; } // Skip to start of fastfile quint64 startIndex = aData.indexOf("IWCIWffs100"); if (startIndex == -1) { qDebug() << "Failed to find data start!"; return true; } fastFileStream.skipRawData(startIndex + 11); qDebug() << "Start index: " << startIndex + 11; // Skip RSA hashes fastFileStream.skipRawData(32760); qint32 decompressedDataLen = fastFileStream.ParseInt32(); qDebug() << "Decompressed Data Len: " << decompressedDataLen; fastFileStream.skipRawData(3); qint8 compressionType = fastFileStream.ParseInt8(); qDebug() << "Compression Type: " << compressionType; long decompressedDataCount = 0; int loopCount = 1; while (decompressedDataCount < decompressedDataLen) { qDebug() << QString("Block Offset: {0:%1}, total: {1:%2}").arg(fastFileStream.device()->pos()).arg(decompressedDataCount); if (loopCount == 512) { // Skip an RSA block fastFileStream.skipRawData(0x4000); loopCount = 0; } qint32 compLenRaw = fastFileStream.ParseInt32(); qDebug() << "Test: " << fastFileStream.device()->pos(); qint32 compressedLen = (compLenRaw + 3) & ~3; qint32 decompressedLen = fastFileStream.ParseInt32(); if (dev->bytesAvailable() < compressedLen) { qDebug() << "Too Much at: " << fastFileStream.device()->pos(); qWarning() << "Not enough data for compressed block. Want" << compressedLen << "have" << dev->bytesAvailable(); break; } fastFileStream.skipRawData(4); // padding QByteArray compressedAligned(compressedLen, Qt::Uninitialized); // Read the full aligned block if (fastFileStream.readRawData(compressedAligned.data(), compressedLen) != compressedLen) { qWarning() << "Unexpected EOF while reading aligned compressed block"; //return false; } // Only first compLenRaw bytes are real data QByteArray compressedChunk = compressedAligned.left(compLenRaw); QByteArray decompressedChunk; switch (compressionType) { // Decompress None case 1: decompressedChunk = compressedChunk; break; // unknown case 4: case 5: qDebug() << "unimplemented compression type!"; return false; // Decompress Oodle case 6: case 7: case 12: case 13: case 14: case 15: case 16: case 17: qDebug() << "Calling Oodle with compLenRaw=" << compLenRaw << "aligned=" << compressedLen << "decompressedLen=" << decompressedLen; decompressedChunk = Compression::DecompressOodle(compressedChunk, decompressedLen); break; default: qDebug() << "Unknown type of compression!"; return false; } if (decompressedChunk.isNull()) { qDebug() << "Decompressor returned null!"; continue; } if (decompressedDataCount + decompressedLen > decompressedDataLen) { qWarning() << "Decompressed overrun:" << decompressedDataCount + decompressedLen << ">" << decompressedDataLen; decompressedLen = decompressedDataLen - decompressedDataCount; decompressedChunk.truncate(decompressedLen); } decompressedData.append(decompressedChunk); decompressedDataCount += decompressedLen; loopCount++; } Utils::ExportData(GetBaseStem() + ".zone", decompressedData); // // Load the zone file with the decompressed data (using an Xbox platform flag). ZoneFile_COD21_PC* zoneFile = new ZoneFile_COD21_PC(); zoneFile->SetStem(GetBaseStem() + ".zone"); zoneFile->SetCommonInfo(&mCommonInfo); if (!zoneFile->Load(decompressedData)) { qWarning() << "Failed to load ZoneFile!"; return false; } SetZoneFile(zoneFile); return true; }