Compare commits
11 Commits
ba83aa5247
...
91a79f78cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91a79f78cc | ||
|
|
8d5e5812ec | ||
|
|
622323117a | ||
|
|
2fdbe74a4a | ||
|
|
13af32ed2d | ||
|
|
dd00cee809 | ||
|
|
34cafac121 | ||
|
|
4bf4c12159 | ||
|
|
754c563515 | ||
|
|
ea1a829957 | ||
|
|
ccb956a834 |
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 §ionHash) {
|
||||
void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
148
libs/fastfile/360/fastfile_cod7_5_360.cpp
Normal file
148
libs/fastfile/360/fastfile_cod7_5_360.cpp
Normal 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;
|
||||
}
|
||||
20
libs/fastfile/360/fastfile_cod7_5_360.h
Normal file
20
libs/fastfile/360/fastfile_cod7_5_360.h
Normal 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
|
||||
@ -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");
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -6,6 +6,6 @@ SUBDIRS += core \
|
||||
fastfile \
|
||||
xassets \
|
||||
zonefile \
|
||||
ddsfile \
|
||||
iwifile \
|
||||
ipakfile
|
||||
#ddsfile \
|
||||
#iwifile \
|
||||
#ipakfile
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ class XStreamFileNamePacked : public XAsset
|
||||
{
|
||||
public:
|
||||
XStreamFileNamePacked();
|
||||
~XStreamFileNamePacked();
|
||||
~XStreamFileNamePacked() = default;
|
||||
|
||||
virtual void ParseData(XDataStream* aStream) override;
|
||||
virtual void Clear() override;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user