diff --git a/DevILSDK/lib/x64/unicode/Release/DevIL.dll b/DevILSDK/lib/x64/unicode/Release/DevIL.dll deleted file mode 100644 index f37e16d..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/DevIL.dll and /dev/null differ diff --git a/DevILSDK/lib/x64/unicode/Release/DevIL.lib b/DevILSDK/lib/x64/unicode/Release/DevIL.lib deleted file mode 100644 index 1402f93..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/DevIL.lib and /dev/null differ diff --git a/DevILSDK/lib/x64/unicode/Release/ILU.dll b/DevILSDK/lib/x64/unicode/Release/ILU.dll deleted file mode 100644 index 6bc6413..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/ILU.dll and /dev/null differ diff --git a/DevILSDK/lib/x64/unicode/Release/ILU.lib b/DevILSDK/lib/x64/unicode/Release/ILU.lib deleted file mode 100644 index a44cbd1..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/ILU.lib and /dev/null differ diff --git a/DevILSDK/lib/x64/unicode/Release/ILUT.dll b/DevILSDK/lib/x64/unicode/Release/ILUT.dll deleted file mode 100644 index e560746..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/ILUT.dll and /dev/null differ diff --git a/DevILSDK/lib/x64/unicode/Release/ILUT.lib b/DevILSDK/lib/x64/unicode/Release/ILUT.lib deleted file mode 100644 index 49a0775..0000000 Binary files a/DevILSDK/lib/x64/unicode/Release/ILUT.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/DevIL.dll b/DevILSDK/lib/x86/Release/DevIL.dll deleted file mode 100644 index 1bd3822..0000000 Binary files a/DevILSDK/lib/x86/Release/DevIL.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/DevIL.lib b/DevILSDK/lib/x86/Release/DevIL.lib deleted file mode 100644 index cb7de1e..0000000 Binary files a/DevILSDK/lib/x86/Release/DevIL.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/ILU.dll b/DevILSDK/lib/x86/Release/ILU.dll deleted file mode 100644 index 2966069..0000000 Binary files a/DevILSDK/lib/x86/Release/ILU.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/ILU.lib b/DevILSDK/lib/x86/Release/ILU.lib deleted file mode 100644 index 5ea04cb..0000000 Binary files a/DevILSDK/lib/x86/Release/ILU.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/ILUT.dll b/DevILSDK/lib/x86/Release/ILUT.dll deleted file mode 100644 index 0fa9deb..0000000 Binary files a/DevILSDK/lib/x86/Release/ILUT.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/Release/ILUT.lib b/DevILSDK/lib/x86/Release/ILUT.lib deleted file mode 100644 index c78c6a4..0000000 Binary files a/DevILSDK/lib/x86/Release/ILUT.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/DevIL.dll b/DevILSDK/lib/x86/unicode/Release/DevIL.dll deleted file mode 100644 index 38b2017..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/DevIL.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/DevIL.lib b/DevILSDK/lib/x86/unicode/Release/DevIL.lib deleted file mode 100644 index 8669310..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/DevIL.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/ILU.dll b/DevILSDK/lib/x86/unicode/Release/ILU.dll deleted file mode 100644 index 7228e66..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/ILU.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/ILU.lib b/DevILSDK/lib/x86/unicode/Release/ILU.lib deleted file mode 100644 index ab2231f..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/ILU.lib and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/ILUT.dll b/DevILSDK/lib/x86/unicode/Release/ILUT.dll deleted file mode 100644 index c329aaf..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/ILUT.dll and /dev/null differ diff --git a/DevILSDK/lib/x86/unicode/Release/ILUT.lib b/DevILSDK/lib/x86/unicode/Release/ILUT.lib deleted file mode 100644 index 0219ac0..0000000 Binary files a/DevILSDK/lib/x86/unicode/Release/ILUT.lib and /dev/null differ diff --git a/XPlor.pro b/XPlor.pro index 0d07980..c64d696 100644 --- a/XPlor.pro +++ b/XPlor.pro @@ -1,72 +1,12 @@ -QT += core gui 3dcore 3drender 3dinput 3dextras +TEMPLATE = subdirs -RC_ICONS = XPlor.ico - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -CONFIG += c++17 - -SOURCES += \ - aboutdialog.cpp \ - ddsfile.cpp \ - ddsviewer.cpp \ - fastfile.cpp \ - fastfileviewer.cpp \ - imagewidget.cpp \ - iwifile.cpp \ - iwiviewer.cpp \ - localstringviewer.cpp \ - lzokay.cpp \ - main.cpp \ - mainwindow.cpp \ - modelviewer.cpp \ - iwifile.cpp \ - techsetviewer.cpp \ - xtreewidget.cpp \ - zonefile.cpp \ - zonefileviewer.cpp - -HEADERS += \ - aboutdialog.h \ - asset_structs.h \ - compressor.h \ - d3dbsp_structs.h \ - dds_structs.h \ - ddsfile.h \ - ddsviewer.h \ - enums.h \ - fastfile.h \ - fastfileviewer.h \ - imagewidget.h \ - ipak_structs.h \ - iwifile.h \ - iwiviewer.h \ - localstringviewer.h \ - lzokay.hpp \ - lzx.h \ - mainwindow.h \ - modelviewer.h \ - techsetviewer.h \ - utils.h \ - xtreewidget.h \ - iwifile.h \ - zonefile.h \ - zonefileviewer.h - -FORMS += \ - aboutdialog.ui \ - ddsviewer.ui \ - fastfileviewer.ui \ - imagewidget.ui \ - iwiviewer.ui \ - localstringviewer.ui \ - mainwindow.ui \ - techsetviewer.ui \ - zonefileviewer.ui - -RESOURCES += \ - data/Data.qrc - -LIBS += -L$$PWD/DevILSDK/lib/x64/Unicode/Release -lDevIL -LIBS += -L$$PWD/DevILSDK/lib/x64/Unicode/Release -lILU -LIBS += -L$$PWD/DevILSDK/lib/x64/Unicode/Release -lILUT +SUBDIRS += libs/core \ + libs/compression \ + libs/encryption \ + libs/fastfile \ + libs/zonefile \ + libs/ddsfile \ + libs/iwifile \ + libs/ipakfile \ + app \ + tests diff --git a/LICENSE b/app/LICENSE similarity index 100% rename from LICENSE rename to app/LICENSE diff --git a/aboutdialog.cpp b/app/aboutdialog.cpp similarity index 100% rename from aboutdialog.cpp rename to app/aboutdialog.cpp diff --git a/aboutdialog.h b/app/aboutdialog.h similarity index 100% rename from aboutdialog.h rename to app/aboutdialog.h diff --git a/aboutdialog.ui b/app/aboutdialog.ui similarity index 100% rename from aboutdialog.ui rename to app/aboutdialog.ui diff --git a/XPlor.ico b/app/app.ico similarity index 100% rename from XPlor.ico rename to app/app.ico diff --git a/app/app.pro b/app/app.pro new file mode 100644 index 0000000..6f6b789 --- /dev/null +++ b/app/app.pro @@ -0,0 +1,113 @@ +QT += core widgets gui multimedia + +RC_ICONS = app.ico + +SUBDIRS += app + +CONFIG += c++17 + +SOURCES += \ + aboutdialog.cpp \ + ddsviewer.cpp \ + fastfileviewer.cpp \ + imagewidget.cpp \ + iwiviewer.cpp \ + localstringviewer.cpp \ + main.cpp \ + mainwindow.cpp \ + materialviewer.cpp \ + preferenceeditor.cpp \ + soundviewer.cpp \ + stringtableviewer.cpp \ + techsetviewer.cpp \ + xtreewidget.cpp \ + xtreewidgetitem.cpp \ + zonefileviewer.cpp + +HEADERS += \ + aboutdialog.h \ + d3dbsp_structs.h \ + ddsviewer.h \ + fastfileviewer.h \ + imagewidget.h \ + iwiviewer.h \ + localstringviewer.h \ + mainwindow.h \ + materialviewer.h \ + preferenceeditor.h \ + soundviewer.h \ + stringtableviewer.h \ + techsetviewer.h \ + xtreewidget.h \ + xtreewidgetitem.h \ + zonefileviewer.h + +FORMS += \ + aboutdialog.ui \ + ddsviewer.ui \ + fastfileviewer.ui \ + imagewidget.ui \ + iwiviewer.ui \ + localstringviewer.ui \ + mainwindow.ui \ + materialviewer.ui \ + modelviewer.ui \ + preferenceeditor.ui \ + soundviewer.ui \ + stringtableviewer.ui \ + techsetviewer.ui \ + zonefileviewer.ui + +RESOURCES += ../data/data.qrc + +app.depends += \ + libs/core \ + libs/compression \ + libs/encryption \ + libs/fastfile \ + libs/ddsfile \ + libs/ipakfile \ + libs/iwifile \ + libs/zonefile + +LIBS += \ + -L$$PWD/../third_party/devil_sdk/lib/ -lDevIL -lILU -lILUT \ + -L$$PWD/../third_party/zlib/lib/ -lzlib \ + -L$$OUT_PWD/../libs/ -lcore \ + -L$$OUT_PWD/../libs/ -lcompression \ + -L$$OUT_PWD/../libs/ -lencryption \ + -L$$OUT_PWD/../libs/ -lfastfile \ + -L$$OUT_PWD/../libs/ -lddsfile \ + -L$$OUT_PWD/../libs/ -lipakfile \ + -L$$OUT_PWD/../libs/ -liwifile \ + -L$$OUT_PWD/../libs/ -lzonefile + +INCLUDEPATH += \ + $$PWD/../third_party/devil_sdk/include/ \ + $$PWD/../third_party/zlib/include \ + $$PWD/../libs/core \ + $$PWD/../libs/compression \ + $$PWD/../libs/encryption \ + $$PWD/../libs/fastfile \ + $$PWD/../libs/ddsfile \ + $$PWD/../libs/ipakfile \ + $$PWD/../libs/iwifile \ + $$PWD/../libs/zonefile + +DEPENDPATH += \ + $$PWD/../third_party/devil_sdk/include/ \ + $$PWD/../third_party/zlib/include \ + $$PWD/../libs/core \ + $$PWD/../libs/compression \ + $$PWD/../libs/encryption \ + $$PWD/../libs/fastfile \ + $$PWD/../libs/ddsfile \ + $$PWD/../libs/ipakfile \ + $$PWD/../libs/iwifile \ + $$PWD/../libs/zonefile + +# Copy DLLs to Debug folder +QMAKE_POST_LINK += xcopy /Y /E /I \"G:/Projects/Qt/XPlor/third_party/devil_sdk/lib\\*.dll\" \"$$OUT_PWD/debug/\" $$escape_expand(\\n\\t) + +# Copy DLLs to Release folder +QMAKE_POST_LINK += xcopy /Y /E /I \"G:/Projects/Qt/XPlor/third_party/devil_sdk/lib\\*.dll\" \"$$OUT_PWD/release/\" $$escape_expand(\\n\\t) diff --git a/d3dbsp_structs.h b/app/d3dbsp_structs.h similarity index 100% rename from d3dbsp_structs.h rename to app/d3dbsp_structs.h diff --git a/ddsviewer.cpp b/app/ddsviewer.cpp similarity index 100% rename from ddsviewer.cpp rename to app/ddsviewer.cpp diff --git a/ddsviewer.h b/app/ddsviewer.h similarity index 100% rename from ddsviewer.h rename to app/ddsviewer.h diff --git a/ddsviewer.ui b/app/ddsviewer.ui similarity index 100% rename from ddsviewer.ui rename to app/ddsviewer.ui diff --git a/fastfileviewer.cpp b/app/fastfileviewer.cpp similarity index 83% rename from fastfileviewer.cpp rename to app/fastfileviewer.cpp index 8b883e8..2fb29bb 100644 --- a/fastfileviewer.cpp +++ b/app/fastfileviewer.cpp @@ -18,9 +18,9 @@ FastFileViewer::~FastFileViewer() void FastFileViewer::SetFastFile(std::shared_ptr aFastFile) { mFastFile.swap(aFastFile); - ui->label_Title->setText(mFastFile->GetFileStem()); + ui->label_Title->setText(mFastFile->GetStem() + ".ff"); ui->comboBox_Company->setCurrentIndex(mFastFile->GetCompany()); - ui->comboBox_FileType->setCurrentIndex(mFastFile->GetFileType()); + ui->comboBox_FileType->setCurrentIndex(mFastFile->GetType()); ui->checkBox_Signed->setChecked(mFastFile->GetSignage() == SIGNAGE_SIGNED); ui->lineEdit_Magic->setText(mFastFile->GetMagic()); ui->spinBox_Version->setValue(mFastFile->GetVersion()); diff --git a/fastfileviewer.h b/app/fastfileviewer.h similarity index 100% rename from fastfileviewer.h rename to app/fastfileviewer.h diff --git a/fastfileviewer.ui b/app/fastfileviewer.ui similarity index 100% rename from fastfileviewer.ui rename to app/fastfileviewer.ui diff --git a/imagewidget.cpp b/app/imagewidget.cpp similarity index 100% rename from imagewidget.cpp rename to app/imagewidget.cpp diff --git a/imagewidget.h b/app/imagewidget.h similarity index 100% rename from imagewidget.h rename to app/imagewidget.h diff --git a/imagewidget.ui b/app/imagewidget.ui similarity index 100% rename from imagewidget.ui rename to app/imagewidget.ui diff --git a/iwiviewer.cpp b/app/iwiviewer.cpp similarity index 100% rename from iwiviewer.cpp rename to app/iwiviewer.cpp diff --git a/iwiviewer.h b/app/iwiviewer.h similarity index 100% rename from iwiviewer.h rename to app/iwiviewer.h diff --git a/iwiviewer.ui b/app/iwiviewer.ui similarity index 100% rename from iwiviewer.ui rename to app/iwiviewer.ui diff --git a/localstringviewer.cpp b/app/localstringviewer.cpp similarity index 95% rename from localstringviewer.cpp rename to app/localstringviewer.cpp index 8874da3..6550668 100644 --- a/localstringviewer.cpp +++ b/app/localstringviewer.cpp @@ -52,7 +52,7 @@ void LocalStringViewer::SetZoneFile(std::shared_ptr aZoneFile) { mLocalStrings.clear(); ui->tableWidget_Strings->clear(); - ui->label_Title->setText(aZoneFile->GetFileStem().section('.', 0, 0) + ".str"); + ui->label_Title->setText(aZoneFile->GetStem().section('.', 0, 0) + ".str"); for (LocalString localStr : aZoneFile->GetAssetMap().localStrings) { AddLocalString(localStr); } diff --git a/localstringviewer.h b/app/localstringviewer.h similarity index 100% rename from localstringviewer.h rename to app/localstringviewer.h diff --git a/localstringviewer.ui b/app/localstringviewer.ui similarity index 100% rename from localstringviewer.ui rename to app/localstringviewer.ui diff --git a/main.cpp b/app/main.cpp similarity index 100% rename from main.cpp rename to app/main.cpp diff --git a/mainwindow.cpp b/app/mainwindow.cpp similarity index 70% rename from mainwindow.cpp rename to app/mainwindow.cpp index 8715004..94d4b79 100644 --- a/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -1,18 +1,29 @@ #include "mainwindow.h" #include "aboutdialog.h" #include "fastfile.h" -#include "qheaderview.h" +#include "materialviewer.h" +#include "preferenceeditor.h" +#include "soundviewer.h" +#include "stringtableviewer.h" #include "techsetviewer.h" #include "ui_mainwindow.h" -#include "compressor.h" -#include "dds_structs.h" +#include "compression.h" #include "iwifile.h" #include "ddsfile.h" +#include "statusbarmanager.h" +#include "ddsviewer.h" +#include "fastfileviewer.h" +#include "ipak_structs.h" +#include "iwiviewer.h" +#include "localstringviewer.h" +#include "imagewidget.h" +#include "xtreewidget.h" +#include "zonefileviewer.h" +#include "techsetviewer.h" +#include "logmanager.h" #include -#include "DevILSDK/include/IL/il.h" - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -30,10 +41,33 @@ MainWindow::MainWindow(QWidget *parent) mDiskLumpOrder = QVector(); mLumps = QMap(); mTreeWidget = new XTreeWidget(this); + mLogWidget = new QPlainTextEdit(this); //ModelViewer *mModelViewer = new ModelViewer(container); //mModelViewer->setAcceptDrops(false); + mProgressBar = new QProgressBar(this); + mProgressBar->setMaximum(100); // Default max value + mProgressBar->setVisible(false); // Initially hidden + + connect(&StatusBarManager::instance(), &StatusBarManager::statusUpdated, + this, &MainWindow::HandleStatusUpdate); + + connect(&StatusBarManager::instance(), &StatusBarManager::progressUpdated, + this, &MainWindow::HandleProgressUpdate); + + connect(&LogManager::instance(), &LogManager::entryAdded, + this, &MainWindow::HandleLogEntry); + + statusBar()->addPermanentWidget(mProgressBar); + + connect(ui->actionPreferences, &QAction::triggered, this, [this](bool checked) { + Q_UNUSED(checked); + + PreferenceEditor *prefEditor = new PreferenceEditor(this); + prefEditor->exec(); + }); + ui->tabWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tabWidget, &QTabWidget::customContextMenuRequested, this, [this](const QPoint &pos) { if (pos.isNull()) @@ -104,9 +138,14 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->removeTab(index); }); - connect(mTreeWidget, &XTreeWidget::RawFileSelected, this, [this](std::shared_ptr rawFile) { + connect(mTreeWidget, &XTreeWidget::Cleared, this, [this]() { + ui->tabWidget->clear(); + }); + + connect(mTreeWidget, &XTreeWidget::RawFileSelected, this, [this](std::shared_ptr rawFile, const QString aParentName) { QPlainTextEdit *scriptEditor = new QPlainTextEdit(this); scriptEditor->setAcceptDrops(false); + scriptEditor->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); if (rawFile->contents.isEmpty()) { scriptEditor->setPlainText("EMPTY"); @@ -121,15 +160,19 @@ MainWindow::MainWindow(QWidget *parent) } } - ui->tabWidget->addTab(scriptEditor, fileStem); + QScrollArea *scrollArea = new QScrollArea(ui->tabWidget); + scrollArea->layout()->addWidget(scriptEditor); + + ui->tabWidget->addTab(scrollArea, fileStem); ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_GSCFile.png")); ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::ImageSelected, this, [this](std::shared_ptr image) { + connect(mTreeWidget, &XTreeWidget::ImageSelected, this, [this](std::shared_ptr image, const QString aParentName) { ImageWidget *mImageWidget = new ImageWidget(this); mImageWidget->setAcceptDrops(false); mImageWidget->SetImage(image); + mImageWidget->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); QString fileStem = image->materialName; for (int i = 0; i < ui->tabWidget->count(); i++) { @@ -143,14 +186,15 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::MenuSelected, this, [this](std::shared_ptr menu) { + connect(mTreeWidget, &XTreeWidget::MenuSelected, this, [](std::shared_ptr menu, const QString aParentName) { Q_UNUSED(menu); }); - connect(mTreeWidget, &XTreeWidget::DDSFileSelected, this, [this](std::shared_ptr ddsFile) { + connect(mTreeWidget, &XTreeWidget::DDSFileSelected, this, [this](std::shared_ptr ddsFile, const QString aParentName) { DDSViewer *ddsViewer = new DDSViewer(this); ddsViewer->setAcceptDrops(false); ddsViewer->SetDDSFile(ddsFile); + ddsViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); QString fileStem = ddsFile->fileStem + ".dds"; for (int i = 0; i < ui->tabWidget->count(); i++) { @@ -164,10 +208,47 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::IWIFileSelected, this, [this](std::shared_ptr iwiFile) { + connect(mTreeWidget, &XTreeWidget::MaterialSelected, this, [this](std::shared_ptr material, const QString aParentName) { + MaterialViewer *matViewer = new MaterialViewer(this); + matViewer->setAcceptDrops(false); + matViewer->SetMaterial(material); + matViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); + + QString fileStem = material->name; + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == fileStem) { + return; + } + } + + ui->tabWidget->addTab(matViewer, fileStem); + ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_Material.png")); + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); + }); + + connect(mTreeWidget, &XTreeWidget::DDSFileSelected, this, [this](std::shared_ptr ddsFile, const QString aParentName) { + DDSViewer *ddsViewer = new DDSViewer(this); + ddsViewer->setAcceptDrops(false); + ddsViewer->SetDDSFile(ddsFile); + ddsViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); + + QString fileStem = ddsFile->fileStem + ".dds"; + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == fileStem) { + return; + } + } + + ui->tabWidget->addTab(ddsViewer, fileStem); + ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_DDSFile.png")); + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); + }); + + connect(mTreeWidget, &XTreeWidget::IWIFileSelected, this, [this](std::shared_ptr iwiFile, const QString aParentName) { IWIViewer *iwiViewer = new IWIViewer(this); iwiViewer->setAcceptDrops(false); iwiViewer->SetIWIFile(iwiFile); + iwiViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); QString fileStem = iwiFile->fileStem + ".iwi"; for (int i = 0; i < ui->tabWidget->count(); i++) { @@ -181,12 +262,13 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::FastFileSelected, this, [this](std::shared_ptr aFastFile) { + connect(mTreeWidget, &XTreeWidget::FastFileSelected, this, [this](std::shared_ptr aFastFile, const QString aParentName) { FastFileViewer *fastFileViewer = new FastFileViewer(this); fastFileViewer->setAcceptDrops(false); fastFileViewer->SetFastFile(aFastFile); + fastFileViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); - QString fileStem = aFastFile->GetFileStem(); + QString fileStem = aFastFile->GetStem() + ".ff"; for (int i = 0; i < ui->tabWidget->count(); i++) { if (ui->tabWidget->tabText(i) == fileStem) { return; @@ -198,29 +280,43 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::ZoneFileSelected, this, [this](std::shared_ptr aZoneFile) { + connect(mTreeWidget, &XTreeWidget::ZoneFileSelected, this, [this](std::shared_ptr aZoneFile, const QString aParentName) { ZoneFileViewer *zoneFileViewer = new ZoneFileViewer(this); zoneFileViewer->setAcceptDrops(false); zoneFileViewer->SetZoneFile(aZoneFile); + zoneFileViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); - QString fileStem = aZoneFile->GetFileStem(); + QString fileStem = aZoneFile->GetStem() + ".zone"; for (int i = 0; i < ui->tabWidget->count(); i++) { if (ui->tabWidget->tabText(i) == fileStem) { return; } } - ui->tabWidget->addTab(zoneFileViewer, fileStem); + QWidget *containerWidget = new QWidget(); + QVBoxLayout *layout = new QVBoxLayout(containerWidget); + layout->addWidget(zoneFileViewer); + containerWidget->setLayout(layout); + + // Create a scroll area and set its properties + QScrollArea *scrollArea = new QScrollArea(ui->tabWidget); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea->setWidgetResizable(true); // Important to allow resizing + scrollArea->setWidget(containerWidget); + + ui->tabWidget->addTab(scrollArea, fileStem); ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_ZoneFile.png")); ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::LocalStringSelected, this, [this](std::shared_ptr aZoneFile) { + connect(mTreeWidget, &XTreeWidget::LocalStringSelected, this, [this](std::shared_ptr aZoneFile, const QString aParentName) { LocalStringViewer *localStrViewer = new LocalStringViewer(this); localStrViewer->setAcceptDrops(false); localStrViewer->SetZoneFile(aZoneFile); + localStrViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); - QString fileStem = aZoneFile->GetFileStem() + ".str"; + QString fileStem = aZoneFile->GetStem() + ".str"; for (int i = 0; i < ui->tabWidget->count(); i++) { if (ui->tabWidget->tabText(i) == fileStem) { return; @@ -232,11 +328,11 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); - connect(mTreeWidget, &XTreeWidget::TechSetSelected, this, [this](std::shared_ptr aTechSet) { - + connect(mTreeWidget, &XTreeWidget::TechSetSelected, this, [this](std::shared_ptr aTechSet, const QString aParentName) { TechSetViewer *techSetViewer = new TechSetViewer(this); techSetViewer->setAcceptDrops(false); techSetViewer->SetTechSet(aTechSet); + techSetViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); QString fileStem = aTechSet->name; for (int i = 0; i < ui->tabWidget->count(); i++) { @@ -250,6 +346,61 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); + connect(mTreeWidget, &XTreeWidget::StrTableSelected, this, [this](std::shared_ptr aStrTable, const QString aParentName) { + StringTableViewer *strTableViewer = new StringTableViewer(this); + strTableViewer->setAcceptDrops(false); + strTableViewer->SetStringTable(aStrTable); + strTableViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); + + QString fileStem = aStrTable->name; + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == fileStem) { + return; + } + } + + ui->tabWidget->addTab(strTableViewer, fileStem); + ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_StringTable.png")); + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); + }); + + connect(mTreeWidget, &XTreeWidget::SoundSelected, this, [this](std::shared_ptr aSound, const QString aParentName) { + SoundViewer *soundViewer = new SoundViewer(this); + soundViewer->setAcceptDrops(false); + soundViewer->SetSound(aSound); + soundViewer->setProperty("PARENT_NAME", QVariant::fromValue(aParentName)); + + QString fileStem = aSound->path.split('/').last(); + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == fileStem) { + return; + } + } + + ui->tabWidget->addTab(soundViewer, fileStem); + ui->tabWidget->setTabIcon(ui->tabWidget->count() - 1, QIcon(":/icons/icons/Icon_Sound.png")); + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); + }); + + connect(mTreeWidget, &XTreeWidget::ItemSelected, this, [this](const QString itemText) { + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == itemText) { + ui->tabWidget->setCurrentIndex(i); + break; + } + } + }); + + connect(mTreeWidget, &XTreeWidget::ItemClosed, this, [this](const QString itemText) { + for (int i = 0; i < ui->tabWidget->count(); i++) { + const QString parentName = ui->tabWidget->widget(i)->property("PARENT_NAME").toString(); + if (parentName == itemText) { + ui->tabWidget->removeTab(i); + break; + } + } + }); + // Connect Help > About dialog connect(ui->actionAbout, &QAction::triggered, this, [this](bool checked) { Q_UNUSED(checked); @@ -273,8 +424,14 @@ MainWindow::MainWindow(QWidget *parent) QDockWidget *treeDockWidget = new QDockWidget(this); treeDockWidget->setWidget(mTreeWidget); + treeDockWidget->setWindowTitle("Tree Browser"); addDockWidget(Qt::LeftDockWidgetArea, treeDockWidget); + QDockWidget *logDockWidget = new QDockWidget(this); + logDockWidget->setWidget(mLogWidget); + logDockWidget->setWindowTitle("Logs"); + addDockWidget(Qt::RightDockWidgetArea, logDockWidget); + ui->toolBar->addAction(ui->actionNew_Fast_File); ui->toolBar->addAction(ui->actionNew_Zone_File); ui->toolBar->addAction(ui->actionOpen_Fast_File); @@ -318,15 +475,18 @@ void MainWindow::Reset() { and opens the selected file. */ bool MainWindow::OpenFastFile(const QString aFastFilePath) { - FastFile fastFile; - if (!fastFile.Load(aFastFilePath)) { - qDebug() << "Error: Failed to load fast file!"; + const QString fastFileStem = aFastFilePath.section("/", -1, -1).section('.', 0, 0); + if (mTreeWidget->HasFastFile(fastFileStem)) { + LogManager::instance().addError("Can't add duplicate file!"); return false; } - mTreeWidget->AddFastFile(std::make_shared(fastFile)); + + std::shared_ptr fastFile = FastFile::Open(aFastFilePath); + fastFile->SetStem(fastFileStem); + mTreeWidget->AddFastFile(fastFile); // Open zone file after decompressing ff and writing - return false; + return true; } /* @@ -351,14 +511,15 @@ bool MainWindow::OpenFastFile() { and opens the selected file. */ bool MainWindow::OpenZoneFile(const QString aZoneFilePath, bool fromFF) { + Q_UNUSED(aZoneFilePath); Q_UNUSED(fromFF); - ZoneFile zoneFile; - if (!zoneFile.Load(aZoneFilePath)) { - qDebug() << "Error: Failed to load zone file!"; - return false; - } - mTreeWidget->AddZoneFile(std::make_shared(zoneFile)); + //ZoneFile zoneFile; + //if (!zoneFile.Load(aZoneFilePath)) { + // qDebug() << "Error: Failed to load zone file!"; + // return false; + //} + //mTreeWidget->AddZoneFile(std::make_shared(zoneFile)); return true; } @@ -453,6 +614,28 @@ int MainWindow::LoadFile_DDSFiles(const QStringList aFilePaths) { return 0; } +void MainWindow::HandleLogEntry(const QString &entry) { + QString logContents = mLogWidget->toPlainText() + "\n" + entry; + if (mLogWidget->toPlainText().isEmpty()) { + logContents = entry; + } + mLogWidget->setPlainText(logContents); +} + +void MainWindow::HandleStatusUpdate(const QString &message, int timeout) { + statusBar()->showMessage(message, timeout); + mProgressBar->setVisible(false); // Hide progress bar if just a message +} + +void MainWindow::HandleProgressUpdate(const QString &message, int progress, int max) { + mProgressBar->setMaximum(max); + mProgressBar->setValue(progress); + mProgressBar->setVisible(true); + + QString progressText = QString("%1 (%2/%3)").arg(message).arg(progress).arg(max); + statusBar()->showMessage(progressText); +} + int MainWindow::LoadFile_DDS(const QString aFilePath) { if (!aFilePath.endsWith(".dds", Qt::CaseInsensitive)) { qDebug() << "Error: Invalid filename " << aFilePath; @@ -572,7 +755,7 @@ int MainWindow::LoadFile_IPAK(const QString aFilePath) { QString outputFilePath = outputFolder.filePath(QString("%1.iwi").arg(j)); if (command.compressed) { - data = Compressor::DecompressLZO(data); + data = Compression::DecompressLZO(data); } QFile outputFile(outputFilePath); if (!outputFile.open(QIODevice::WriteOnly)) { @@ -666,9 +849,9 @@ void MainWindow::dropEvent(QDropEvent *event) { foreach (const QUrl url, mimeData->urls()) { const QString urlStr = url.toLocalFile(); if (urlStr.contains(".zone")) { - OpenZoneFile(urlStr); + qDebug() << "OpenZoneFile Returned: " << OpenZoneFile(urlStr); } else if (urlStr.contains(".ff")) { - OpenFastFile(urlStr); + qDebug() << "OpenFastFile Returned: " << OpenFastFile(urlStr); } else if (urlStr.contains(".ipak")) { qDebug() << "LoadFile_IPAK Returned: " << LoadFile_IPAK(urlStr); } else if (urlStr.contains(".xsub")) { @@ -689,11 +872,11 @@ void MainWindow::dropEvent(QDropEvent *event) { qDebug() << "LZO: Failed to read file!"; continue; } - QByteArray data = Compressor::DecompressLZO(lzoFile.readAll()); + QByteArray data = Compression::DecompressLZO(lzoFile.readAll()); lzoFile.close(); if (data.isEmpty()) { - qDebug() << "LZO: Decompressor gave empty result!"; + qDebug() << "LZO: Decompression gave empty result!"; continue; } diff --git a/mainwindow.h b/app/mainwindow.h similarity index 86% rename from mainwindow.h rename to app/mainwindow.h index d4ad802..daa90dc 100644 --- a/mainwindow.h +++ b/app/mainwindow.h @@ -3,16 +3,7 @@ #include "d3dbsp_structs.h" #include "asset_structs.h" -#include "ddsviewer.h" -#include "fastfileviewer.h" -#include "ipak_structs.h" -#include "iwiviewer.h" -#include "localstringviewer.h" -#include "modelviewer.h" -#include "imagewidget.h" #include "xtreewidget.h" -#include "zonefileviewer.h" -#include "techsetviewer.h" #include #include @@ -24,6 +15,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -55,6 +47,10 @@ private slots: int LoadFile_DDS(const QString aFilePath); int LoadFile_DDSFiles(const QStringList aFilePaths); + void HandleLogEntry(const QString &entry); + void HandleStatusUpdate(const QString &message, int timeout); + void HandleProgressUpdate(const QString &message, int progress, int max); + protected: void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; @@ -72,6 +68,8 @@ private: QMap mTreeMap; QMap>> mStrTableMap; XTreeWidget *mTreeWidget; + QPlainTextEdit *mLogWidget; + QProgressBar *mProgressBar; quint32 mBSPVersion; quint32 mDiskLumpCount; diff --git a/mainwindow.ui b/app/mainwindow.ui similarity index 81% rename from mainwindow.ui rename to app/mainwindow.ui index 9ed5eb0..1e1b164 100644 --- a/mainwindow.ui +++ b/app/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 986 - 692 + 1579 + 857 @@ -46,7 +46,7 @@ 0 0 - 986 + 1579 21 @@ -58,17 +58,11 @@ Recent... - - - Import... - - - @@ -114,6 +108,8 @@ + + @@ -140,7 +136,8 @@ - + + :/icons/icons/Icon_NewFile.png:/icons/icons/Icon_NewFile.png New @@ -148,7 +145,8 @@ - + + :/icons/icons/Icon_NewFile.png:/icons/icons/Icon_NewFile.png New Fast File @@ -156,7 +154,8 @@ - + + :/icons/icons/Icon_NewFile.png:/icons/icons/Icon_NewFile.png New Zone File @@ -164,7 +163,8 @@ - + + :/icons/icons/Icon_OpenFile.png:/icons/icons/Icon_OpenFile.png Open Fast File @@ -172,7 +172,8 @@ - + + :/icons/icons/Icon_OpenFile.png:/icons/icons/Icon_OpenFile.png Open Zone File @@ -180,7 +181,8 @@ - + + :/icons/icons/Icon_OpenFile.png:/icons/icons/Icon_OpenFile.png Open Folder @@ -188,16 +190,14 @@ - + + :/icons/icons/Icon_Save.png:/icons/icons/Icon_Save.png Save - - - Save As @@ -248,24 +248,19 @@ - - - Undo - - - Redo - + + :/icons/icons/Icon_Cut.png:/icons/icons/Icon_Cut.png Cut @@ -273,7 +268,8 @@ - + + :/icons/icons/Icon_Copy.png:/icons/icons/Icon_Copy.png Copy @@ -281,16 +277,14 @@ - + + :/icons/icons/Icon_Paste.png:/icons/icons/Icon_Paste.png Paste - - - Rename @@ -306,9 +300,6 @@ - - - Delete @@ -334,9 +325,6 @@ - - - About @@ -347,22 +335,27 @@ - - - Check for Updates - + + :/icons/icons/Icon_Find.png:/icons/icons/Icon_Find.png Find + + + Preferences... + + - + + + diff --git a/app/materialviewer.cpp b/app/materialviewer.cpp new file mode 100644 index 0000000..1a97977 --- /dev/null +++ b/app/materialviewer.cpp @@ -0,0 +1,42 @@ +#include "materialviewer.h" +#include "ui_materialviewer.h" + +MaterialViewer::MaterialViewer(QWidget *parent) + : QWidget(parent) + , ui(new Ui::MaterialViewer) { + ui->setupUi(this); +} + +MaterialViewer::~MaterialViewer() { + delete ui; +} + +QString ToHexStr(quint32 in) { + return QString("%1").arg(in, 8, 16, QChar('0')).toUpper(); +} + +void MaterialViewer::SetMaterial(std::shared_ptr aMaterial) { + ui->lineEdit_NamePtr->setText(ToHexStr(aMaterial->namePtr)); + ui->lineEdit_Name->setText(aMaterial->name); + ui->lineEdit_RefPtr->setText(ToHexStr(aMaterial->refNamePtr)); + ui->lineEdit_RefName->setText(aMaterial->refName); + ui->lineEdit_Unknowns->setText(ToHexStr(aMaterial->unknownA[0]) + + ToHexStr(aMaterial->unknownA[1]) + + ToHexStr(aMaterial->unknownA[2]) + + ToHexStr(aMaterial->unknownA[3]) + + ToHexStr(aMaterial->unknownA[4]) + + ToHexStr(aMaterial->unknownA[5]) + + ToHexStr(aMaterial->unknownA[6]) + + ToHexStr(aMaterial->unknownA[7]) + + ToHexStr(aMaterial->unknownA[8]) + + ToHexStr(aMaterial->unknownA[9]) + + ToHexStr(aMaterial->unknownA[10]) + + ToHexStr(aMaterial->unknownA[11])); + ui->lineEdit_StateA->setText(ToHexStr(aMaterial->stateBits[0])); + ui->lineEdit_StateA->setText(ToHexStr(aMaterial->stateBits[1])); + ui->spinBox_TextureCount->setValue(aMaterial->textureCount); + ui->spinBox_ConstCount->setValue(aMaterial->constCount); + ui->lineEdit_TechSetPtr->setText(ToHexStr(aMaterial->techSetPtr)); + ui->lineEdit_TexturePtr->setText(ToHexStr(aMaterial->texturePtr)); + ui->lineEdit_ConstantPtr->setText(ToHexStr(aMaterial->constPtr)); +} diff --git a/app/materialviewer.h b/app/materialviewer.h new file mode 100644 index 0000000..7b24deb --- /dev/null +++ b/app/materialviewer.h @@ -0,0 +1,27 @@ +#ifndef MATERIALVIEWER_H +#define MATERIALVIEWER_H + +#include "asset_structs.h" + +#include +#include + +namespace Ui { +class MaterialViewer; +} + +class MaterialViewer : public QWidget +{ + Q_OBJECT + +public: + explicit MaterialViewer(QWidget *parent = nullptr); + ~MaterialViewer(); + + void SetMaterial(std::shared_ptr aMaterial); + +private: + Ui::MaterialViewer *ui; +}; + +#endif // MATERIALVIEWER_H diff --git a/app/materialviewer.ui b/app/materialviewer.ui new file mode 100644 index 0000000..f7aa9d8 --- /dev/null +++ b/app/materialviewer.ui @@ -0,0 +1,236 @@ + + + MaterialViewer + + + + 0 + 0 + 1001 + 650 + + + + Form + + + + + + + Roboto + 16 + true + + + + Material 0 + + + + + + + + + + + + 325 + 398 + + + + + 325 + 16777215 + + + + + Roboto + 9 + + + + Header + + + + + + Name Ptr: + + + + + + + + + + Name: + + + + + + + Ref Ptr: + + + + + + + Ref Name: + + + + + + + Unknowns: + + + + + + + State A: + + + + + + + State B: + + + + + + + Texture Count: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Constant Count: + + + + + + + Tech Set Ptr: + + + + + + + Texture Ptr: + + + + + + + Constant Ptr: + + + + + + + + + + + + + + + + + + + 400 + 400 + + + + + Roboto + 9 + + + + Data + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 143 + + + + + + + + + diff --git a/app/modelviewer.cpp b/app/modelviewer.cpp new file mode 100644 index 0000000..a461223 --- /dev/null +++ b/app/modelviewer.cpp @@ -0,0 +1,14 @@ +#include "modelviewer.h" +#include "ui_modelviewer.h" + +ModelViewer::ModelViewer(QWidget *parent) + : QWidget(parent) + , ui(new Ui::ModelViewer) +{ + ui->setupUi(this); +} + +ModelViewer::~ModelViewer() +{ + delete ui; +} diff --git a/app/modelviewer.h b/app/modelviewer.h new file mode 100644 index 0000000..1fa493f --- /dev/null +++ b/app/modelviewer.h @@ -0,0 +1,22 @@ +#ifndef MODELVIEWER_H +#define MODELVIEWER_H + +#include + +namespace Ui { +class ModelViewer; +} + +class ModelViewer : public QWidget +{ + Q_OBJECT + +public: + explicit ModelViewer(QWidget *parent = nullptr); + ~ModelViewer(); + +private: + Ui::ModelViewer *ui; +}; + +#endif // MODELVIEWER_H diff --git a/app/modelviewer.ui b/app/modelviewer.ui new file mode 100644 index 0000000..d321671 --- /dev/null +++ b/app/modelviewer.ui @@ -0,0 +1,624 @@ + + + ModelViewer + + + + 0 + 0 + 1001 + 897 + + + + Form + + + + + + Properties + + + + + + + + Name Pointer: + + + + + + + 1000000000 + + + + + + + Model Name: + + + + + + + + + + Tag Count: + + + + + + + tags + + + 1000000000 + + + + + + + Root Tag Count: + + + + + + + root tags + + + 1000000000 + + + + + + + Surface Count: + + + + + + + surfaces + + + 1000000000 + + + + + + + Unknown A: + + + + + + + 1000000000 + + + + + + + Bone Name Pointer: + + + + + + + 1000000000 + + + + + + + Parent List Pointer: + + + + + + + 1000000000 + + + + + + + Quats Pointer: + + + + + + + 1000000000 + + + + + + + Transformation Pointer: + + + + + + + 1000000000 + + + + + + + Classification Pointer: + + + + + + + 1000000000 + + + + + + + Base Material Pointer: + + + + + + + 1000000000 + + + + + + + Surfaces Pointer; + + + + + + + 1000000000 + + + + + + + Material Handlers Pointer: + + + + + + + 1000000000 + + + + + + + Coll Surf Pointer: + + + + + + + 1000000000 + + + + + + + Coll Surface Count: + + + + + + + 1000000000 + + + + + + + Contents: + + + + + + + 1000000000 + + + + + + + Bone Info Pointer: + + + + + + + 1000000000 + + + + + + + Radius: + + + + + + + + + + Min X: + + + + + + + + + + Min Y: + + + + + + + + + + Min Z: + + + + + + + + + + Max X: + + + + + + + + + + Max Y: + + + + + + + + + + Max Z: + + + + + + + + + + Lod Count: + + + + + + + 1000000000 + + + + + + + Coll Lod: + + + + + + + 1000000000 + + + + + + + Stream Info Pointer: + + + + + + + 1000000000 + + + + + + + Memory Usage: + + + + + + + 1000000000 + + + + + + + Flags: + + + + + + + 1000000000 + + + + + + + Phys Preset Pointer: + + + + + + + 1000000000 + + + + + + + Phys Geometry Pointer: + + + + + + + 1000000000 + + + + + + + + + + + Lod Info + + + + + + Lod Info Index: + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + Distance: + + + + + + + + + + Surface Count: + + + + + + + 1000000000 + + + + + + + Surface Index: + + + + + + + 1000000000 + + + + + + + Part Bit 1: + + + + + + + 1000000000 + + + + + + + Part Bit 2: + + + + + + + 1000000000 + + + + + + + Part Bit 3: + + + + + + + 1000000000 + + + + + + + Part Bit 4: + + + + + + + 1000000000 + + + + + + + Part Bit 5: + + + + + + + 1000000000 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + + + + 3D Window + + + + + + + + diff --git a/app/preferenceeditor.cpp b/app/preferenceeditor.cpp new file mode 100644 index 0000000..44b0ae1 --- /dev/null +++ b/app/preferenceeditor.cpp @@ -0,0 +1,35 @@ +#include "preferenceeditor.h" +#include "ui_preferenceeditor.h" + +PreferenceEditor::PreferenceEditor(QWidget *parent) + : QDialog(parent) + , ui(new Ui::PreferenceEditor) +{ + ui->setupUi(this); + + ui->frame_View->show(); + ui->frame_TreeWidget->hide(); + ui->frame_FileEditors->hide(); + + connect(ui->listWidget_Categories, &QListWidget::itemSelectionChanged, this, [this]() { + const QString itemText = ui->listWidget_Categories->selectedItems().first()->text(); + if (itemText == "View") { + ui->frame_View->show(); + ui->frame_TreeWidget->hide(); + ui->frame_FileEditors->hide(); + } else if (itemText == "Tree Widget") { + ui->frame_View->hide(); + ui->frame_TreeWidget->show(); + ui->frame_FileEditors->hide(); + } else if (itemText == "File Editors") { + ui->frame_View->hide(); + ui->frame_TreeWidget->hide(); + ui->frame_FileEditors->show(); + } + }); +} + +PreferenceEditor::~PreferenceEditor() +{ + delete ui; +} diff --git a/app/preferenceeditor.h b/app/preferenceeditor.h new file mode 100644 index 0000000..60ed8c0 --- /dev/null +++ b/app/preferenceeditor.h @@ -0,0 +1,22 @@ +#ifndef PREFERENCEEDITOR_H +#define PREFERENCEEDITOR_H + +#include + +namespace Ui { +class PreferenceEditor; +} + +class PreferenceEditor : public QDialog +{ + Q_OBJECT + +public: + explicit PreferenceEditor(QWidget *parent = nullptr); + ~PreferenceEditor(); + +private: + Ui::PreferenceEditor *ui; +}; + +#endif // PREFERENCEEDITOR_H diff --git a/app/preferenceeditor.ui b/app/preferenceeditor.ui new file mode 100644 index 0000000..ab80157 --- /dev/null +++ b/app/preferenceeditor.ui @@ -0,0 +1,528 @@ + + + PreferenceEditor + + + + 0 + 0 + 1118 + 861 + + + + + 703 + 512 + + + + Dialog + + + + + + + + + + false + + + + 0 + 0 + + + + + 150 + 16777215 + + + + + Roboto + 10 + + + + Filter + + + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + + Roboto + 10 + + + + 0 + + + + View + + + + :/icons/icons/Icon_Views.png:/icons/icons/Icon_Views.png + + + + + Tree Widget + + + + :/icons/icons/Icon_Tree.png:/icons/icons/Icon_Tree.png + + + + + File Editors + + + + :/icons/icons/Icon_Editor.png:/icons/icons/Icon_Editor.png + + + + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + :/icons/icons/Icon_Views.png + + + true + + + + + + + + Roboto + 12 + true + + + + View + + + + + + + + + + Roboto + 10 + + + + 0 + + + + Font && Colors + + + + + + Font + + + + + + Family: + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Size: + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Zoom: + + + + + + + % + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 588 + + + + + + + + + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + :/icons/icons/Icon_Tree.png + + + true + + + + + + + + Roboto + 12 + true + + + + Tree Widget + + + + + + + + + + Roboto + 10 + + + + 0 + + + + Tab 1 + + + + + Tab 2 + + + + + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + + + 30 + 30 + + + + + 30 + 30 + + + + + + + :/icons/icons/Icon_Editor.png + + + true + + + + + + + + Roboto + 12 + true + + + + File Editors + + + + + + + + + + Roboto + 10 + + + + 7 + + + + Fast File + + + + + Zone File + + + + + Images + + + + + Local Strings + + + + + String Table + + + + + Sounds + + + + + Tech Set + + + + + Model + + + + + Page + + + + + + + + + + + + + + + + + + + + + + + + + + + + Roboto + 10 + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + + + + + buttonBox + accepted() + PreferenceEditor + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PreferenceEditor + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/app/soundviewer.cpp b/app/soundviewer.cpp new file mode 100644 index 0000000..9007e9f --- /dev/null +++ b/app/soundviewer.cpp @@ -0,0 +1,80 @@ +#include "soundviewer.h" +#include "ui_soundviewer.h" + +SoundViewer::SoundViewer(QWidget *parent) + : QWidget(parent) + , ui(new Ui::SoundViewer) + , player(new QMediaPlayer()) + , buffer(new QBuffer()) +{ + ui->setupUi(this); + + connect(ui->pushButton_Play, &QPushButton::clicked, player, &QMediaPlayer::play); + connect(ui->pushButton_Pause, &QPushButton::clicked, player, &QMediaPlayer::pause); + connect(ui->pushButton_Stop, &QPushButton::clicked, this, [this]() { + if (player->isPlaying()) { + player->stop(); + } + }); + connect(ui->pushButton_SkipForward, &QPushButton::clicked, this, [this]() { + player->setPosition(player->position() + 30); + }); + connect(ui->pushButton_SkipBack, &QPushButton::clicked, this, [this]() { + player->setPosition(player->position() - 30); + }); + connect(player, &QMediaPlayer::positionChanged, player, [this](qint64 position) { + ui->horizontalSlider->setSliderPosition(position); + ui->label_Time->setText(QString("%1:%2:%3") + .arg(position / 60000) + .arg((position % 60000) / 1000) + .arg(position % 1000)); + }); + connect(player, &QMediaPlayer::durationChanged, player, [this](qint64 duration) { + ui->horizontalSlider->setMaximum(duration); + ui->label_TimeMax->setText(QString("%1:%2:%3") + .arg(duration / 60000) + .arg((duration % 60000) / 1000) + .arg(duration % 1000)); + }); + connect(ui->horizontalSlider, &QSlider::sliderMoved, this, [this](int position) { + player->setPosition(position); + }); + + for (auto outputDevice : QMediaDevices::audioOutputs()) { + ui->comboBox_Output->addItem(outputDevice.description()); + } + connect(ui->comboBox_Output, &QComboBox::currentIndexChanged, this, [this](int index) { + auto outputDevice = QMediaDevices::audioOutputs()[index]; + QAudioOutput *audioOutput = new QAudioOutput(outputDevice); + player->setAudioOutput(audioOutput); + }); + + auto outputDevice = QMediaDevices::defaultAudioOutput(); + QAudioOutput *audioOutput = new QAudioOutput(outputDevice); + player->setAudioOutput(audioOutput); +} + +SoundViewer::~SoundViewer() +{ + delete buffer; + delete player; + delete ui; +} + +void SoundViewer::SetSound(std::shared_ptr aSound) +{ + buffer->setData(aSound->data); + if (!buffer->open(QIODevice::ReadOnly)) { + qWarning() << "Failed to open QBuffer."; + return; + } + + ui->groupBox->setTitle(aSound->path); + player->setSourceDevice(buffer); +} + +void SoundViewer::SetOutput(QAudioOutput *aOutput) { + if (!aOutput) { return; } + + player->setAudioOutput(aOutput); +} diff --git a/app/soundviewer.h b/app/soundviewer.h new file mode 100644 index 0000000..825c28c --- /dev/null +++ b/app/soundviewer.h @@ -0,0 +1,34 @@ +#ifndef SOUNDVIEWER_H +#define SOUNDVIEWER_H + +#include "asset_structs.h" + +#include +#include +#include +#include +#include +#include + +namespace Ui { +class SoundViewer; +} + +class SoundViewer : public QWidget +{ + Q_OBJECT + +public: + explicit SoundViewer(QWidget *parent = nullptr); + ~SoundViewer(); + + void SetSound(std::shared_ptr aSound); + + void SetOutput(QAudioOutput *aOutput); +private: + Ui::SoundViewer *ui; + QMediaPlayer *player; + QBuffer *buffer; +}; + +#endif // SOUNDVIEWER_H diff --git a/app/soundviewer.ui b/app/soundviewer.ui new file mode 100644 index 0000000..e6b290a --- /dev/null +++ b/app/soundviewer.ui @@ -0,0 +1,2573 @@ + + + SoundViewer + + + + 0 + 0 + 294 + 198 + + + + Form + + + + + + + 0 + 160 + + + + + 16777215 + 131 + + + + Player + + + + + + + + Output Device: + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 40 + 40 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + :/icons/icons/Icon_SkipBack.png:/icons/icons/Icon_SkipBack.png + + + + 40 + 40 + + + + + + + + + 40 + 40 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + :/icons/icons/Icon_Stop.png:/icons/icons/Icon_Stop.png + + + + 40 + 40 + + + + + + + + + 50 + 50 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + :/icons/icons/Icon_Play.png:/icons/icons/Icon_Play.png + + + + 50 + 50 + + + + + + + + + 40 + 40 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + :/icons/icons/Icon_Pause.png:/icons/icons/Icon_Pause.png + + + + 40 + 40 + + + + + + + + + 40 + 40 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 247 + 247 + 247 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 247 + 247 + 247 + + + + + + + 120 + 120 + 120 + + + + + + + 160 + 160 + 160 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + 120 + 120 + 120 + + + + + + + 240 + 240 + 240 + + + + + + + 240 + 240 + 240 + + + + + + + 0 + 0 + 0 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + :/icons/icons/Icon_SkipForward.png:/icons/icons/Icon_SkipForward.png + + + + 40 + 40 + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 00:00:00 + + + + + + + Qt::Orientation::Horizontal + + + + + + + 00:00:00 + + + + + + + + + + + + + + + diff --git a/app/stringtableviewer.cpp b/app/stringtableviewer.cpp new file mode 100644 index 0000000..b71b8e5 --- /dev/null +++ b/app/stringtableviewer.cpp @@ -0,0 +1,36 @@ +#include "stringtableviewer.h" +#include "ui_stringtableviewer.h" + +StringTableViewer::StringTableViewer(QWidget *parent) + : QWidget(parent) + , ui(new Ui::StringTableViewer) +{ + ui->setupUi(this); +} + +StringTableViewer::~StringTableViewer() +{ + delete ui; +} + +void StringTableViewer::SetStringTable(std::shared_ptr aStringTable) { + ui->tableWidget_Strings->clear(); + + ui->tableWidget_Strings->setRowCount(aStringTable->rowCount); + ui->tableWidget_Strings->setColumnCount(aStringTable->columnCount); + + int currentIndex = 0; + for (const QString &key : aStringTable->content.keys()) { + const QString value = aStringTable->content[key]; + + QTableWidgetItem *tableKeyItem = new QTableWidgetItem(); + tableKeyItem->setText(key); + ui->tableWidget_Strings->setItem(currentIndex, 0, tableKeyItem); + + QTableWidgetItem *tableValItem = new QTableWidgetItem(); + tableValItem->setText(value); + ui->tableWidget_Strings->setItem(currentIndex, 1, tableValItem); + + currentIndex++; + } +} diff --git a/app/stringtableviewer.h b/app/stringtableviewer.h new file mode 100644 index 0000000..29dc08a --- /dev/null +++ b/app/stringtableviewer.h @@ -0,0 +1,25 @@ +#ifndef STRINGTABLEVIEWER_H +#define STRINGTABLEVIEWER_H + +#include "asset_structs.h" +#include + +namespace Ui { +class StringTableViewer; +} + +class StringTableViewer : public QWidget +{ + Q_OBJECT + +public: + explicit StringTableViewer(QWidget *parent = nullptr); + ~StringTableViewer(); + + void SetStringTable(std::shared_ptr aStringTable); + +private: + Ui::StringTableViewer *ui; +}; + +#endif // STRINGTABLEVIEWER_H diff --git a/app/stringtableviewer.ui b/app/stringtableviewer.ui new file mode 100644 index 0000000..55ce6e0 --- /dev/null +++ b/app/stringtableviewer.ui @@ -0,0 +1,24 @@ + + + StringTableViewer + + + + 0 + 0 + 525 + 752 + + + + Form + + + + + + + + + + diff --git a/techsetviewer.cpp b/app/techsetviewer.cpp similarity index 100% rename from techsetviewer.cpp rename to app/techsetviewer.cpp diff --git a/techsetviewer.h b/app/techsetviewer.h similarity index 100% rename from techsetviewer.h rename to app/techsetviewer.h diff --git a/techsetviewer.ui b/app/techsetviewer.ui similarity index 100% rename from techsetviewer.ui rename to app/techsetviewer.ui diff --git a/app/xtreewidget.cpp b/app/xtreewidget.cpp new file mode 100644 index 0000000..043f5e4 --- /dev/null +++ b/app/xtreewidget.cpp @@ -0,0 +1,800 @@ +#include "xtreewidget.h" +#include "qheaderview.h" +#include "qmenu.h" +#include "logmanager.h" + +XTreeWidget::XTreeWidget(QWidget *parent) + : QTreeWidget(parent) { + mFastFiles = QMap>(); + mZoneFiles = QMap>(); + mDDSFiles = QMap>(); + mIWIFiles = QMap>(); + + setContextMenuPolicy(Qt::CustomContextMenu); + setSelectionMode(QTreeWidget::SingleSelection); + setColumnCount(3); + header()->hide(); + setMinimumWidth(350); + setSortingEnabled(true); + + header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + + // Set the last two columns to a fixed width + //header()->setSectionResizeMode(1, QHeaderView::Fixed); + //header()->setSectionResizeMode(2, QHeaderView::Fixed); + + // Adjust the fixed widths to suit your icon size (e.g., 32 pixels) + //header()->resizeSection(0, 275); + //header()->resizeSection(1, 32); + //header()->resizeSection(2, 32); + + connect(this, &XTreeWidget::itemSelectionChanged, this, &XTreeWidget::ItemSelectionChanged); + connect(this, &XTreeWidget::customContextMenuRequested, this, &XTreeWidget::PrepareContextMenu); +} + +XTreeWidget::~XTreeWidget() { + +} + +void XTreeWidget::AddFastFile(std::shared_ptr aFastFile) { + XTreeWidgetItem *fastFileItem = new XTreeWidgetItem(this); + fastFileItem->setText(0, aFastFile->GetStem() + ".ff"); + fastFileItem->setIcon(0, QIcon(":/icons/icons/Icon_FastFile.png")); + if (aFastFile->GetPlatform() == "PC") { + fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_PC.png")); + } else if (aFastFile->GetPlatform() == "360") { + fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_Xbox.png")); + } else if (aFastFile->GetPlatform() == "Wii") { + fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_Wii.png")); + } else if (aFastFile->GetPlatform() == "WiiU") { + fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_WiiU.png")); + } + if (aFastFile->GetGame() == "COD2") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD2.png")); + } if (aFastFile->GetGame() == "COD4") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD4.png")); + } else if (aFastFile->GetGame() == "COD5") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD5.png")); + } else if (aFastFile->GetGame() == "COD6") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD6.png")); + } else if (aFastFile->GetGame() == "COD7") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD7.png")); + } else if (aFastFile->GetGame() == "COD8") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD8.png")); + } else if (aFastFile->GetGame() == "COD9") { + fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD9.png")); + } + + AddZoneFile(aFastFile->GetZoneFile(), fastFileItem); + + mFastFiles[aFastFile->GetStem().section(".", 0, 0)] = aFastFile; + + resizeColumnToContents(1); + setSortingEnabled(true); + sortByColumn(0, Qt::AscendingOrder); +} + +void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, XTreeWidgetItem *aParentItem) { + XTreeWidgetItem *zoneItem; + if (aParentItem != nullptr) { + zoneItem = new XTreeWidgetItem(aParentItem); + } else { + zoneItem = new XTreeWidgetItem(this); + } + zoneItem->setIcon(0, QIcon(":/icons/icons/Icon_ZoneFile.png")); + zoneItem->setText(0, aZoneFile->GetStem() + ".zone"); + + auto assetMap = aZoneFile->GetAssetMap(); + + if (!assetMap.localStrings.isEmpty()) { + XTreeWidgetItem *localStrRoot = new XTreeWidgetItem(zoneItem); + localStrRoot->setText(0, "String Files"); + localStrRoot->setIcon(0, QIcon(":/icons/icons/Icon_StringFile.png")); + + XTreeWidgetItem *localStrItem = new XTreeWidgetItem(localStrRoot); + localStrItem->setText(0, aZoneFile->GetStem().section('.', 0, 0) + ".str"); + localStrItem->setIcon(0, QIcon(":/icons/icons/Icon_StringFile.png")); + } + + if (!assetMap.techSets.isEmpty()) { + XTreeWidgetItem *techSetRoot = new XTreeWidgetItem(zoneItem); + techSetRoot->setText(0, "Tech Sets"); + techSetRoot->setIcon(0, QIcon(":/icons/icons/Icon_TechSetFile.png")); + + for (TechSet techSet : assetMap.techSets) { + XTreeWidgetItem *techSetItem = new XTreeWidgetItem(techSetRoot); + techSetItem->setText(0, techSet.name); + techSetItem->setIcon(0, QIcon(":/icons/icons/Icon_TechSetFile.png")); + } + } + + if (!assetMap.rawFiles.isEmpty()) { + XTreeWidgetItem *rawFileRoot = new XTreeWidgetItem(zoneItem); + rawFileRoot->setText(0, "Raw Files"); + rawFileRoot->setIcon(0, QIcon(":/icons/icons/Icon_GSCFile.png")); + for (RawFile rawFile : assetMap.rawFiles) { + if (!rawFile.length) { continue; } + + XTreeWidgetItem *tempItem = rawFileRoot; + for (const QString &pathPart : rawFile.path.split('/')) { + bool childFound = false; + for (int i = 0; i < tempItem->childCount(); i++) { + QTreeWidgetItem *rawChildItem = tempItem->child(i); + XTreeWidgetItem *childItem = dynamic_cast(rawChildItem); + if (childItem->text(0) == pathPart) { + tempItem = childItem; + + childFound = true; + break; + } + } + + if (pathPart.contains(".gsc")) { + XTreeWidgetItem *rawFileItem = new XTreeWidgetItem(tempItem); + rawFileItem->setText(0, pathPart); + + tempItem = rawFileItem; + } else if (!childFound) { + tempItem = new XTreeWidgetItem(tempItem); + tempItem->setText(0, pathPart); + } + + } + tempItem->setIcon(0, QIcon(":/icons/icons/Icon_GSCFile.png")); + } + } + + if (!assetMap.menuFiles.isEmpty()) { + XTreeWidgetItem *menuRoot = new XTreeWidgetItem(zoneItem); + menuRoot->setText(0, "Menu Files"); + menuRoot->setIcon(0, QIcon(":/icons/icons/Icon_MenuFile.png")); + + int menuIndex = 1; + for (MenuFile menuFile : assetMap.menuFiles) { + XTreeWidgetItem *menuFileRoot = new XTreeWidgetItem(menuRoot); + menuFileRoot->setText(0, QString("Menu %1").arg(menuIndex)); + for (Menu menu : menuFile.menuDefs) { + XTreeWidgetItem *menuItem = new XTreeWidgetItem(menuFileRoot); + menuItem->setText(0, menu.name); + menuItem->setIcon(0, QIcon(":/icons/icons/Icon_MenuFile.png")); + } + menuIndex++; + } + } + + if (!assetMap.images.isEmpty()) { + XTreeWidgetItem *imageRoot = new XTreeWidgetItem(zoneItem); + imageRoot->setText(0, "Images"); + imageRoot->setIcon(0, QIcon(":/icons/icons/Icon_Image.png")); + + for (Image image : assetMap.images) { + XTreeWidgetItem *imageItem = new XTreeWidgetItem(imageRoot); + imageItem->setText(0, image.materialName); + imageItem->setIcon(0, QIcon(":/icons/icons/Icon_Image.png")); + } + } + + if (!assetMap.models.isEmpty()) { + XTreeWidgetItem *modelsRoot = new XTreeWidgetItem(zoneItem); + modelsRoot->setText(0, "Models"); + modelsRoot->setIcon(0, QIcon(":/icons/icons/Icon_Model.png")); + + for (Model model: assetMap.models) { + XTreeWidgetItem *modelItem = new XTreeWidgetItem(modelsRoot); + modelItem->setText(0, model.modelName); + modelItem->setIcon(0, QIcon(":/icons/icons/Icon_Model.png")); + } + } + + if (!assetMap.materials.isEmpty()) { + XTreeWidgetItem *materialsRoot = new XTreeWidgetItem(zoneItem); + materialsRoot->setText(0, "Materials"); + materialsRoot->setIcon(0, QIcon(":/icons/icons/Icon_Material.png")); + + for (Material material: assetMap.materials) { + XTreeWidgetItem *materialItem = new XTreeWidgetItem(materialsRoot); + materialItem->setText(0, material.name); + materialItem->setIcon(0, QIcon(":/icons/icons/Icon_Material.png")); + } + } + + if (!assetMap.stringTables.isEmpty()) { + XTreeWidgetItem *strTableRoot = new XTreeWidgetItem(zoneItem); + strTableRoot->setText(0, "String Tables"); + strTableRoot->setIcon(0, QIcon(":/icons/icons/Icon_StringTable.png")); + + for (StringTable strTable: assetMap.stringTables) { + XTreeWidgetItem *modelItem = new XTreeWidgetItem(strTableRoot); + modelItem->setText(0, strTable.name); + modelItem->setIcon(0, QIcon(":/icons/icons/Icon_StringTable.png")); + } + } + + if (!assetMap.sounds.isEmpty()) { + XTreeWidgetItem *soundsRoot = new XTreeWidgetItem(zoneItem); + soundsRoot->setText(0, "Sounds"); + soundsRoot->setIcon(0, QIcon(":/icons/icons/Icon_Sound.png")); + for (SoundAsset soundAsset : assetMap.sounds) { + for (Sound sound : soundAsset.sounds) { + XTreeWidgetItem *tempItem = soundsRoot; + + if (!sound.dataLength) { continue; } + + for (const QString &pathPart : sound.path.split('/')) { + if (pathPart.isEmpty()) { continue; } + + bool childFound = false; + for (int i = 0; i < tempItem->childCount(); i++) { + XTreeWidgetItem *childItem = dynamic_cast(tempItem->child(i)); + if (childItem->text(0) == pathPart) { + tempItem = childItem; + + childFound = true; + break; + } + } + + if (pathPart.contains(".wav")) { + XTreeWidgetItem *soundItem = new XTreeWidgetItem(tempItem); + soundItem->setText(0, pathPart); + + tempItem = soundItem; + } else if (!childFound) { + tempItem = new XTreeWidgetItem(tempItem); + tempItem->setText(0, pathPart); + } + + } + tempItem->setIcon(0, QIcon(":/icons/icons/Icon_Sound.png")); + } + } + } + + mZoneFiles[aZoneFile->GetStem().section(".", 0, 0)] = aZoneFile; +} + +void XTreeWidget::CloseFastFile(const QString aFFName) { + const QString fileStem = aFFName.section(".", 0, 0); + emit ItemClosed(fileStem); +} + +void XTreeWidget::PrepareContextMenu(const QPoint &pos) { + auto activeItem = itemAt(pos); + if (!activeItem) { return; } + if (activeItem->text(0).isEmpty()) { return; } + + QString activeText = activeItem->text(0); + QMenu *contextMenu = new QMenu(this); + + if (activeText.contains(".dds")) { + const QString fileStem = activeText.replace(".dds", ""); + if (!mDDSFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in DDS map!"; + return; + } + + QAction *closeAction = new QAction("Close File"); + contextMenu->addAction(closeAction); + connect(closeAction, &QAction::triggered, this, [this, &fileStem, &activeItem](bool checked) { + Q_UNUSED(checked); + + mDDSFiles.remove(fileStem); + invisibleRootItem()->removeChild(activeItem); + }); + + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + std::shared_ptr ddsFile = mDDSFiles[fileStem]; + + QAction *exportIWIAction = new QAction("Export as IWI"); + exportSubmenu->addAction(exportIWIAction); + connect(exportIWIAction, &QAction::triggered, this, [ddsFile](bool checked) { + Q_UNUSED(checked); + + ddsFile->SaveIWI(); + }); + + QAction *exportPNGAction = new QAction("Export as PNG"); + exportSubmenu->addAction(exportPNGAction); + connect(exportPNGAction, &QAction::triggered, this, [ddsFile](bool checked) { + Q_UNUSED(checked); + + ddsFile->SavePNG(); + }); + + QAction *exportJPGAction = new QAction("Export as JPG"); + exportSubmenu->addAction(exportJPGAction); + connect(exportJPGAction, &QAction::triggered, this, [ddsFile](bool checked) { + Q_UNUSED(checked); + + ddsFile->SaveJPG(); + }); + } else if (activeText.contains(".iwi")) { + const QString fileStem = activeText.replace(".iwi", ""); + if (!mIWIFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in IWI map!"; + return; + } + + QAction *closeAction = new QAction("Close File"); + contextMenu->addAction(closeAction); + connect(closeAction, &QAction::triggered, this, [this, &fileStem, &activeItem](bool checked) { + Q_UNUSED(checked); + + mIWIFiles.remove(fileStem); + invisibleRootItem()->removeChild(activeItem); + }); + + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + std::shared_ptr iwiFile = mIWIFiles[fileStem]; + + QAction *exportDDSAction = new QAction("Export as DDS"); + exportSubmenu->addAction(exportDDSAction); + connect(exportDDSAction, &QAction::triggered, this, [iwiFile](bool checked) { + Q_UNUSED(checked); + + iwiFile->SaveDDS(); + }); + + QAction *exportPNGAction = new QAction("Export as PNG"); + exportSubmenu->addAction(exportPNGAction); + connect(exportPNGAction, &QAction::triggered, this, [iwiFile](bool checked) { + Q_UNUSED(checked); + + iwiFile->SavePNG(); + }); + + QAction *exportJPGAction = new QAction("Export as JPG"); + exportSubmenu->addAction(exportJPGAction); + connect(exportJPGAction, &QAction::triggered, this, [iwiFile](bool checked) { + Q_UNUSED(checked); + + iwiFile->SaveJPG(); + }); + } else if (activeText.contains(".ff")) { + const QString fileStem = activeText.replace(".zone", ""); + + QMenu *closeMultipleAction = new QMenu("Close Multiple Tabs"); + + QAction *closeAllAction = new QAction("Close All"); + closeMultipleAction->addAction(closeAllAction); + connect(closeAllAction, &QAction::triggered, this, [this](bool checked) { + Q_UNUSED(checked); + + mFastFiles.clear(); + clear(); + + emit Cleared(); + }); + + QAction *closeAllButAction = new QAction("Close All BUT This"); + closeMultipleAction->addAction(closeAllButAction); + connect(closeAllButAction, &QAction::triggered, this, [this, &activeItem](bool checked) { + Q_UNUSED(checked); + + for (int i = 0; i < invisibleRootItem()->childCount(); i++) { + auto childItem = invisibleRootItem()->child(i); + if (childItem == activeItem) { continue; } + + const QString fileStem = childItem->text(0).replace(".ff", ""); + if (!mFastFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in Fast File map!"; + return; + } + + mFastFiles.remove(fileStem); + CloseFastFile(fileStem); + invisibleRootItem()->removeChild(childItem); + i--; + } + }); + + QAction *closeAboveAction = new QAction("Close All Above"); + closeMultipleAction->addAction(closeAboveAction); + connect(closeAboveAction, &QAction::triggered, this, [this, &activeItem](bool checked) { + Q_UNUSED(checked); + + for (int i = 0; i < invisibleRootItem()->childCount(); i++) { + auto childItem = invisibleRootItem()->child(i); + if (childItem == activeItem) { return; } + + const QString fileStem = childItem->text(0).replace(".ff", ""); + if (!mFastFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in Fast File map!"; + return; + } + + mFastFiles.remove(fileStem); + CloseFastFile(fileStem); + invisibleRootItem()->removeChild(childItem); + i--; + } + }); + + QAction *closeBelowAction = new QAction("Close All Below"); + closeMultipleAction->addAction(closeBelowAction); + connect(closeBelowAction, &QAction::triggered, this, [this, &activeItem](bool checked) { + Q_UNUSED(checked); + + bool ready = false; + for (int i = 0; i < invisibleRootItem()->childCount(); i++) { + auto childItem = invisibleRootItem()->child(i); + if (!ready && (childItem != activeItem)) { continue; } + if (childItem == activeItem) { + ready = true; + continue; + } + + const QString fileStem = childItem->text(0).replace(".ff", ""); + if (!mFastFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in Fast File map!"; + return; + } + + mFastFiles.remove(fileStem); + CloseFastFile(fileStem); + invisibleRootItem()->removeChild(childItem); + i--; + } + }); + + contextMenu->addMenu(closeMultipleAction); + + QAction *closeAction = new QAction("Close File"); + contextMenu->addAction(closeAction); + connect(closeAction, &QAction::triggered, this, [this, &activeItem, &activeText](bool checked) { + Q_UNUSED(checked); + + const QString fileStem = activeItem->text(0).replace(".ff", ""); + + mFastFiles.remove(fileStem); + CloseFastFile(activeText); + invisibleRootItem()->removeChild(activeItem); + }); + + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + std::shared_ptr fastFile = mFastFiles[fileStem]; + + QAction *exportFastFileAction = new QAction("Export Fast File"); + exportSubmenu->addAction(exportFastFileAction); + connect(exportFastFileAction, &QAction::triggered, this, [fastFile](bool checked) { + Q_UNUSED(checked); + + const QString fastFilePath = QFileDialog::getSaveFileName( + nullptr, "Export Fast File...", QDir::currentPath(), + "Fast File (*.ff);;All Files(*.*)"); + + fastFile->ExportFastFile(fastFilePath); + }); + QAction *exportZoneFileAction = new QAction("Export Zone File"); + exportSubmenu->addAction(exportZoneFileAction); + connect(exportZoneFileAction, &QAction::triggered, this, [fastFile](bool checked) { + Q_UNUSED(checked); + + const QString zoneFilePath = QFileDialog::getSaveFileName( + nullptr, "Export Zone File...", QDir::currentPath(), + "Zone File (*.zone);;All Files(*.*)"); + fastFile->GetZoneFile()->SaveZoneFile(zoneFilePath); + }); + } else if (activeText.contains(".zone")) { + const QString fileStem = activeText.replace(".zone", ""); + if (!mZoneFiles.contains(fileStem)) { + qDebug() << "Error: Could not find " << fileStem << " in Zone File map!"; + return; + } + + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + std::shared_ptr zoneFile = mZoneFiles[fileStem]; + + QAction *exportZoneFileAction = new QAction("Export Zone File"); + exportSubmenu->addAction(exportZoneFileAction); + connect(exportZoneFileAction, &QAction::triggered, this, [](bool checked) { + Q_UNUSED(checked); + }); + QAction *exportFastFileAction = new QAction("Export Fast File"); + exportSubmenu->addAction(exportFastFileAction); + connect(exportFastFileAction, &QAction::triggered, this, [](bool checked) { + Q_UNUSED(checked); + }); + } else if (activeItem && activeText.contains(".wav")) { + XTreeWidgetItem *parentItem = dynamic_cast(activeItem->parent()); + while (parentItem && !parentItem->text(0).contains(".zone")) { + parentItem = dynamic_cast(parentItem->parent()); + + if (parentItem == invisibleRootItem()) { + break; + } + } + if (parentItem && parentItem != invisibleRootItem() && parentItem->text(0).contains(".zone")) { + const QString fileStem = parentItem->text(0).section('.', 0, 0); + QVector soundAssets = mZoneFiles[fileStem]->GetAssetMap().sounds; + for (SoundAsset soundAsset : soundAssets) { + for (Sound sound : soundAsset.sounds) { + if (sound.path.contains(activeText)) { + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + QAction *exportWAVAction = new QAction("Export as WAV File"); + exportSubmenu->addAction(exportWAVAction); + connect(exportWAVAction, &QAction::triggered, this, [sound](bool checked) { + Q_UNUSED(checked); + + QDir dir = QDir::currentPath(); + if (!dir.exists("exports/")) { + dir.mkdir("exports/"); + } + + if (!dir.exists("exports/sounds/")) { + dir.mkdir("exports/sounds/"); + } + + const QString fileName = "exports/sounds/" + sound.path.split('/').last(); + QFile wavFile(fileName); + if (!wavFile.open(QIODevice::WriteOnly)) { + qDebug() << "Failed to write wav file!"; + return; + } + wavFile.write(sound.data); + wavFile.close(); + }); + break; + } + } + } + } + } else if (activeItem && activeText == "Sounds") { + XTreeWidgetItem *parentItem = dynamic_cast(activeItem->parent()); + while (parentItem && !parentItem->text(0).contains(".zone")) { + parentItem = dynamic_cast(parentItem->parent()); + + if (parentItem == invisibleRootItem()) { + break; + } + } + if (parentItem && parentItem != invisibleRootItem() && parentItem->text(0).contains(".zone")) { + const QString fileStem = parentItem->text(0).section('.', 0, 0); + auto zoneFile = mZoneFiles[fileStem]; + + QMenu *exportSubmenu = new QMenu("Export...", this); + contextMenu->addMenu(exportSubmenu); + + QAction *exportAllWAVAction = new QAction("Export ALL as WAV Files"); + exportSubmenu->addAction(exportAllWAVAction); + connect(exportAllWAVAction, &QAction::triggered, this, [zoneFile](bool checked) { + Q_UNUSED(checked); + + for (SoundAsset soundAsset : zoneFile->GetAssetMap().sounds) { + for (Sound sound : soundAsset.sounds) { + if (!sound.dataLength) { continue; } + + QDir dir = QDir::currentPath(); + if (!dir.exists("exports/")) { + dir.mkdir("exports/"); + } + + if (!dir.exists("exports/sounds/")) { + dir.mkdir("exports/sounds/"); + } + + const QString fileName = "exports/sounds/" + sound.path.split('/').last(); + QFile wavFile(fileName); + if (!wavFile.open(QIODevice::WriteOnly)) { + qDebug() << "Failed to write wav file!"; + return; + } + wavFile.write(sound.data); + wavFile.close(); + } + } + }); + } + } + + QPoint pt(pos); + contextMenu->exec(mapToGlobal(pt)); + + delete contextMenu; +} + +void XTreeWidget::ItemSelectionChanged() { + if (selectedItems().isEmpty()) { return; } + + XTreeWidgetItem *selectedItem = dynamic_cast(selectedItems().first()); + if (!selectedItem) { return; } + if (selectedItem->text(0).isEmpty()) { return; } + QString selectedText = selectedItem->text(0); + emit ItemSelected(selectedText); + + const QString fileStem = selectedText.section(".", 0, 0); + + XTreeWidgetItem *parentItem = dynamic_cast(selectedItem->parent()); + + if (selectedText.contains(".dds")) { + if (!mDDSFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in DDS map!"); + return; + } + std::shared_ptr ddsFile = mDDSFiles[fileStem]; + emit DDSFileSelected(ddsFile, fileStem); + } else if (selectedText.contains(".iwi")) { + if (!mIWIFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in IWI map!"); + return; + } + emit IWIFileSelected(mIWIFiles[fileStem], fileStem); + } else if (selectedText.contains(".ff")) { + if (!mFastFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in Fast File map!"); + return; + } + emit FastFileSelected(mFastFiles[fileStem], fileStem); + } else if (selectedText.contains(".zone")) { + if (!mZoneFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in Zone File map!"); + return; + } + emit ZoneFileSelected(mZoneFiles[fileStem], fileStem); + } else if (selectedText.contains(".str")) { + if (!mZoneFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in Zone File map!"); + return; + } + emit LocalStringSelected(mZoneFiles[fileStem], fileStem); + } else if (selectedText.contains(".gsc")) { + XTreeWidgetItem *zoneRoot = selectedItem; + if (!zoneRoot) { return; } + + while (!zoneRoot->text(0).contains(".zone")) { + zoneRoot = dynamic_cast(zoneRoot->parent()); + if (!zoneRoot) { return; } + } + + const QString fileStem = zoneRoot->text(0).section('.', 0, 0); + if (!mZoneFiles.contains(fileStem)) { + LogManager::instance().addError("Could not find " + fileStem + " in Zone File map!"); + return; + } + + QVector rawFiles = mZoneFiles[fileStem]->GetAssetMap().rawFiles; + for (RawFile rawFile : rawFiles) { + if (rawFile.path.contains(selectedText)) { + emit RawFileSelected(std::make_shared(rawFile), fileStem); + return; + } + } + } else if (parentItem && (parentItem->text(0) == "Images")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + if (grandpaItem && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + QVector images = mZoneFiles[fileStem]->GetAssetMap().images; + for (Image image : images) { + if (image.materialName == selectedText) { + emit ImageSelected(std::make_shared(image), fileStem); + break; + } + } + } + } else if (parentItem && (parentItem->text(0) == "Tech Sets")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + if (grandpaItem && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + auto techsets = mZoneFiles[fileStem]->GetAssetMap().techSets; + for (auto techset : techsets) { + if (techset.name == selectedText) { + emit TechSetSelected(std::make_shared(techset), fileStem); + break; + } + } + } + } else if (parentItem && (parentItem->text(0) == "Tech Sets")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + if (grandpaItem && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + auto techsets = mZoneFiles[fileStem]->GetAssetMap().techSets; + for (auto techset : techsets) { + if (techset.name == selectedText) { + emit TechSetSelected(std::make_shared(techset), fileStem); + break; + } + } + } + } else if (parentItem && (parentItem->text(0) == "Materials")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + if (grandpaItem && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + QVector materials = mZoneFiles[fileStem]->GetAssetMap().materials; + for (Material material : materials) { + if (material.name == selectedText) { + emit MaterialSelected(std::make_shared(material), fileStem); + break; + } + } + } + } else if (parentItem && selectedText.contains(".wav")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + while (grandpaItem && !grandpaItem->text(0).contains(".zone")) { + grandpaItem = dynamic_cast(grandpaItem->parent()); + + if (grandpaItem == invisibleRootItem()) { + break; + } + } + if (grandpaItem && grandpaItem != invisibleRootItem() && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + QVector soundAssets = mZoneFiles[fileStem]->GetAssetMap().sounds; + for (SoundAsset soundAsset : soundAssets) { + for (Sound sound : soundAsset.sounds) { + if (sound.path.contains(selectedText)) { + emit SoundSelected(std::make_shared(sound), fileStem); + break; + } + } + } + } + } +} + +std::shared_ptr XTreeWidget::FindZoneFile(const QString aStem) { + foreach (auto zoneFile, mZoneFiles) { + if (zoneFile->GetStem() == aStem) { + return zoneFile; + } + } + return nullptr; +} + +std::shared_ptr XTreeWidget::FindFastFile(const QString aStem) { + foreach (auto fastFile, mFastFiles) { + if (fastFile->GetStem() == aStem) { + return fastFile; + } + } + return nullptr; +} + +bool XTreeWidget::HasZoneFile(const QString aStem) { + return FindZoneFile(aStem) != nullptr; +} + +bool XTreeWidget::HasFastFile(const QString aStem) { + return FindFastFile(aStem) != nullptr; +} + +void XTreeWidget::AddIWIFile(std::shared_ptr aIWIFile) { + const QString iwiFileName = QString(aIWIFile->fileStem + ".iwi"); + + for (int i = 0; i < invisibleRootItem()->childCount(); i++) { + auto iwiFileItem = invisibleRootItem()->child(i); + if (iwiFileItem->text(0) == iwiFileName) { + delete iwiFileItem; + } + } + + XTreeWidgetItem *iwiItem = new XTreeWidgetItem(this); + iwiItem->setIcon(0, QIcon(":/icons/icons/Icon_IWIFile.png")); + iwiItem->setText(0, iwiFileName); + mIWIFiles[aIWIFile->fileStem.section(".", 0, 0)] = aIWIFile; +} + +void XTreeWidget::AddDDSFile(std::shared_ptr aDDSFile) { + const QString ddsFileName = QString(aDDSFile->fileStem + ".dds"); + + for (int i = 0; i < invisibleRootItem()->childCount(); i++) { + auto ddsFileItem = invisibleRootItem()->child(i); + if (ddsFileItem->text(0) == ddsFileName) { + delete ddsFileItem; + } + } + + XTreeWidgetItem *ddsItem = new XTreeWidgetItem(this); + ddsItem->setIcon(0, QIcon(":/icons/icons/Icon_DDSFile.png")); + ddsItem->setText(0, ddsFileName); + mDDSFiles[aDDSFile->fileStem.section(".", 0, 0)] = aDDSFile; +} diff --git a/app/xtreewidget.h b/app/xtreewidget.h new file mode 100644 index 0000000..37e3761 --- /dev/null +++ b/app/xtreewidget.h @@ -0,0 +1,62 @@ +#ifndef XTREEWIDGET_H +#define XTREEWIDGET_H + +#include "d3dbsp_structs.h" +#include "asset_structs.h" +#include "ddsfile.h" +#include "iwifile.h" +#include "fastfile.h" +#include "xtreewidgetitem.h" +#include "zonefile.h" + +#include + +class XTreeWidget : public QTreeWidget +{ + Q_OBJECT +public: + explicit XTreeWidget(QWidget *parent = nullptr); + ~XTreeWidget(); + + void AddFastFile(std::shared_ptr aFastFile); + void AddZoneFile(std::shared_ptr aZoneFile, XTreeWidgetItem *aParentItem = nullptr); + void AddIWIFile(std::shared_ptr aIWIFile); + void AddDDSFile(std::shared_ptr aDDSFile); + + std::shared_ptr FindZoneFile(const QString aStem); + std::shared_ptr FindFastFile(const QString aStem); + + bool HasZoneFile(const QString aStem); + bool HasFastFile(const QString aStem); + + void CloseFastFile(const QString aFFName); +signals: + void DDSFileSelected(std::shared_ptr aDDSFile, const QString aParentName); + void IWIFileSelected(std::shared_ptr aIWIFile, const QString aParentName); + void FastFileSelected(std::shared_ptr aFastFile, const QString aParentName); + void ZoneFileSelected(std::shared_ptr aZoneFile, const QString aParentName); + void LocalStringSelected(std::shared_ptr aZoneFile, const QString aParentName); + void RawFileSelected(std::shared_ptr aRawFile, const QString aParentName); + void ImageSelected(std::shared_ptr aImage, const QString aParentName); + void TechSetSelected(std::shared_ptr aZoneFile, const QString aParentName); + void StrTableSelected(std::shared_ptr aStrTable, const QString aParentName); + void MenuSelected(std::shared_ptr aMenu, const QString aParentName); + void SoundSelected(std::shared_ptr aSound, const QString aParentName); + void MaterialSelected(std::shared_ptr aMaterial, const QString aParentName); + void ItemSelected(const QString itemText); + + void ItemClosed(const QString itemText); + void Cleared(); + +protected: + void ItemSelectionChanged(); + void PrepareContextMenu(const QPoint &pos); + +private: + QMap> mFastFiles; + QMap> mZoneFiles; + QMap> mDDSFiles; + QMap> mIWIFiles; +}; + +#endif // XTREEWIDGET_H diff --git a/app/xtreewidgetitem.cpp b/app/xtreewidgetitem.cpp new file mode 100644 index 0000000..eae4ee9 --- /dev/null +++ b/app/xtreewidgetitem.cpp @@ -0,0 +1,42 @@ +#include "xtreewidgetitem.h" + +XTreeWidgetItem::XTreeWidgetItem(QTreeWidget *parent, bool group) + : QTreeWidgetItem(parent), isGroup(group) { + +} + +XTreeWidgetItem::XTreeWidgetItem(QTreeWidgetItem *parent, bool group) + : QTreeWidgetItem(parent), isGroup(group) { + +} + +bool XTreeWidgetItem::operator<(const QTreeWidgetItem &other) const { + // Attempt to cast the other item to our custom type. + const XTreeWidgetItem* otherItem = dynamic_cast(&other); + if (otherItem) { + bool thisIsGroup = this->childCount() > 0; + bool otherIsGroup = otherItem->childCount() > 0; + + if (thisIsGroup != otherIsGroup) { + return otherIsGroup; // Groups should come before non-groups + } + } + // Fallback to the default string comparison on the current sort column. + return QTreeWidgetItem::operator<(other); +} + + +XTreeWidgetItem& XTreeWidgetItem::operator=(const XTreeWidgetItem &other) +{ + if (this != &other) { + // Copy text and icon for each column. + const int colCount = other.columnCount(); + for (int i = 0; i < colCount; ++i) { + setText(i, other.text(i)); + setIcon(i, other.icon(i)); + } + // Copy custom members. + this->isGroup = other.isGroup; + } + return *this; +} diff --git a/app/xtreewidgetitem.h b/app/xtreewidgetitem.h new file mode 100644 index 0000000..e189d04 --- /dev/null +++ b/app/xtreewidgetitem.h @@ -0,0 +1,24 @@ +#ifndef XTREEWIDGETITEM_H +#define XTREEWIDGETITEM_H + +#include +#include + +// Custom item class +class XTreeWidgetItem : public QTreeWidgetItem +{ +public: + // Flag to indicate if the item is a collapsible group/header. + bool isGroup; + + // Constructors: default to non-group unless specified. + XTreeWidgetItem(QTreeWidget *parent, bool group = false); + XTreeWidgetItem(QTreeWidgetItem *parent, bool group = false); + + // Override the less-than operator to customize sorting. + bool operator<(const QTreeWidgetItem &other) const override; + XTreeWidgetItem &operator =(const XTreeWidgetItem &other); +}; + + +#endif // XTREEWIDGETITEM_H diff --git a/zonefileviewer.cpp b/app/zonefileviewer.cpp similarity index 52% rename from zonefileviewer.cpp rename to app/zonefileviewer.cpp index c02a10c..12c48a3 100644 --- a/zonefileviewer.cpp +++ b/app/zonefileviewer.cpp @@ -3,33 +3,32 @@ ZoneFileViewer::ZoneFileViewer(QWidget *parent) : QWidget(parent) - , ui(new Ui::ZoneFileViewer) -{ + , ui(new Ui::ZoneFileViewer) { ui->setupUi(this); aZoneFile = nullptr; - ui->tableWidget_RecordCounts->setColumnCount(2); - ui->tableWidget_RecordCounts->setHorizontalHeaderLabels({ "Asset", "Count" }); + ui->tableWidget_RecordCounts->setColumnCount(4); + ui->tableWidget_RecordCounts->setHorizontalHeaderLabels({ "Identifier", "Asset", "Count", "Icon" }); ui->tableWidget_RecordCounts->horizontalHeader()->setStretchLastSection(true); - ui->tableWidget_RecordOrder->setColumnCount(2); - ui->tableWidget_RecordOrder->setHorizontalHeaderLabels({ "Asset", "Count" }); + ui->tableWidget_RecordOrder->setColumnCount(4); + ui->tableWidget_RecordOrder->setHorizontalHeaderLabels({ "Identifier", "Asset", "Count", "Icon" }); ui->tableWidget_RecordOrder->horizontalHeader()->setStretchLastSection(true); } -ZoneFileViewer::~ZoneFileViewer() -{ +ZoneFileViewer::~ZoneFileViewer() { delete ui; } void ZoneFileViewer::SetZoneFile(std::shared_ptr aZoneFile) { - ui->tableWidget_RecordCounts->clear(); + ui->tableWidget_RecordCounts->clearContents(); + ui->tableWidget_RecordOrder->clearContents(); ui->listWidget_Tags->clear(); const QStringList tags = aZoneFile->GetTags(); ui->listWidget_Tags->addItems(tags); - ui->label_Title->setText(aZoneFile->GetFileStem()); + ui->label_Title->setText(aZoneFile->GetStem() + ".zone"); if (tags.isEmpty()) { ui->groupBox_Tags->hide(); @@ -38,74 +37,91 @@ void ZoneFileViewer::SetZoneFile(std::shared_ptr aZoneFile) { } QString lastAsset = ""; + QString lastRecord = ""; + QIcon assetIcon; int consecutiveCount = 1; int consecutiveIndex = 0; const QStringList records = aZoneFile->GetRecords(); QMap recordCounts = QMap(); for (const QString &record : records) { + lastRecord = record; if (record == "ffffffff") { break; } if (!recordCounts.contains(record)) { recordCounts[record] = 0; } recordCounts[record]++; - QString assetType = Utils::AssetTypeToString(record); + QString assetType = aZoneFile->AssetTypeToString(record); + if (assetIcon.isNull()) { + qDebug() << "Icon is null for record: " << record; + } + if (lastAsset.isEmpty()) { lastAsset = assetType; + lastRecord = record; } else if (lastAsset == assetType) { consecutiveCount++; } else { ui->tableWidget_RecordOrder->setRowCount(consecutiveIndex + 1); + QTableWidgetItem *recordItem = new QTableWidgetItem(lastRecord.toUpper()); QTableWidgetItem *recordStrItem = new QTableWidgetItem(lastAsset); QTableWidgetItem *recordCountItem = new QTableWidgetItem(QString::number(consecutiveCount)); + QTableWidgetItem *recordIconItem = new QTableWidgetItem(); + assetIcon = aZoneFile->AssetStrToIcon(lastAsset); + recordIconItem->setIcon(assetIcon); - ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 0, recordStrItem); - ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 1, recordCountItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 0, recordItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 1, recordStrItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 2, recordCountItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 3, recordIconItem); consecutiveCount = 1; consecutiveIndex++; lastAsset = assetType; + lastRecord = record; } } ui->tableWidget_RecordOrder->setRowCount(consecutiveIndex + 1); + QTableWidgetItem *recordItem = new QTableWidgetItem(lastRecord.toUpper()); QTableWidgetItem *recordStrItem = new QTableWidgetItem(lastAsset); QTableWidgetItem *recordCountItem = new QTableWidgetItem(QString::number(consecutiveCount)); + QTableWidgetItem *recordIconItem = new QTableWidgetItem(); + assetIcon = aZoneFile->AssetStrToIcon(lastAsset); + recordIconItem->setIcon(assetIcon); - ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 0, recordStrItem); - ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 1, recordCountItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 0, recordItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 1, recordStrItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 2, recordCountItem); + ui->tableWidget_RecordOrder->setItem(consecutiveIndex, 3, recordIconItem); int recordIndex = 0; for (const QString &record : recordCounts.keys()) { int recordCount = recordCounts[record]; - QString assetType = Utils::AssetTypeToString(record); + + QString assetType = aZoneFile->AssetTypeToString(record); + assetIcon = aZoneFile->AssetStrToIcon(assetType); + if (assetIcon.isNull()) { + qDebug() << "Icon is null for record: " << record; + } ui->tableWidget_RecordCounts->setRowCount(recordIndex + 1); QTableWidgetItem *recordCountStrItem = new QTableWidgetItem(assetType); + QTableWidgetItem *recordItem = new QTableWidgetItem(record.toUpper()); QTableWidgetItem *recordCountItem = new QTableWidgetItem(QString::number(recordCount)); + QTableWidgetItem *recordIconItem = new QTableWidgetItem(); + recordIconItem->setIcon(assetIcon); - ui->tableWidget_RecordCounts->setItem(recordIndex, 0, recordCountStrItem); - ui->tableWidget_RecordCounts->setItem(recordIndex, 1, recordCountItem); + ui->tableWidget_RecordCounts->setItem(recordIndex, 0, recordItem); + ui->tableWidget_RecordCounts->setItem(recordIndex, 1, recordCountStrItem); + ui->tableWidget_RecordCounts->setItem(recordIndex, 2, recordCountItem); + ui->tableWidget_RecordCounts->setItem(recordIndex, 3, recordIconItem); recordIndex++; } - auto assetMap = aZoneFile->GetAssetMap(); - for (LocalString localString : assetMap.localStrings) { - } - for (RawFile rawFile : assetMap.rawFiles) { - } - for (TechSet techSet : assetMap.techSets) { - } - for (Image image : assetMap.images) { - } - for (Animation animation : assetMap.animations) { - } - for (StringTable stringTable : assetMap.stringTables) { - } - for (MenuFile menuFile : assetMap.menuFiles) { - } - + ui->tableWidget_RecordOrder->resizeColumnsToContents(); + ui->tableWidget_RecordCounts->resizeColumnsToContents(); } diff --git a/zonefileviewer.h b/app/zonefileviewer.h similarity index 100% rename from zonefileviewer.h rename to app/zonefileviewer.h diff --git a/app/zonefileviewer.ui b/app/zonefileviewer.ui new file mode 100644 index 0000000..7b0755a --- /dev/null +++ b/app/zonefileviewer.ui @@ -0,0 +1,99 @@ + + + ZoneFileViewer + + + + 0 + 0 + 556 + 428 + + + + Form + + + + + + + Roboto + 16 + true + + + + ZoneFile 0 + + + + + + + + + + Roboto + 9 + + + + Tags + + + + + + + + Search: + + + + + + + Search tags... + + + + + + + + + + + + + + + Record Counts + + + + + + + + + + + + Record Order + + + + + + + + + + + + + + + diff --git a/compressor.h b/compressor.h deleted file mode 100644 index 459cbf3..0000000 --- a/compressor.h +++ /dev/null @@ -1,410 +0,0 @@ -#ifndef COMPRESSOR_H -#define COMPRESSOR_H - -#include "utils.h" -#include "QtZlib/zlib.h" -#include "lzokay.hpp" -#include "lzx.h" - -#include -#include -#include -#include -#include -#include -#include - -typedef enum { - EResult_LookbehindOverrun = -4, - EResult_OutputOverrun = -3, - EResult_InputOverrun = -2, - EResult_Error = -1, - EResult_Success = 0, - EResult_InputNotConsumed = 1, -} lzokay_EResult; - -static_assert(EResult_LookbehindOverrun == lzokay_EResult(lzokay::EResult::LookbehindOverrun), "LookbehindOverrun mismatch"); -static_assert(EResult_OutputOverrun == lzokay_EResult(lzokay::EResult::OutputOverrun), "OutputOverrun mismatch"); -static_assert(EResult_InputOverrun == lzokay_EResult(lzokay::EResult::InputOverrun), "InputOverrun mismatch"); -static_assert(EResult_Error == lzokay_EResult(lzokay::EResult::Error), "Error mismatch"); -static_assert(EResult_Success == lzokay_EResult(lzokay::EResult::Success), "Success mismatch"); -static_assert(EResult_InputNotConsumed == lzokay_EResult(lzokay::EResult::InputNotConsumed), "InputNotConsumed mismatch"); - -class Compressor { -public: - static QByteArray DecompressZLIB(const QByteArray &compressedData) { - if (compressedData.isEmpty()) - return QByteArray(); - - // Set up the inflate stream. - z_stream strm; - memset(&strm, 0, sizeof(strm)); - // The inflate() function needs a non-const pointer; this is safe as we never modify the input. - strm.next_in = reinterpret_cast(const_cast(compressedData.data())); - strm.avail_in = static_cast(compressedData.size()); - - // Use inflateInit(); if you want to support gzip streams, see note below. - int ret = inflateInit(&strm); - if (ret != Z_OK) { - qWarning() << "inflateInit failed:" << zError(ret); - return QByteArray(); - } - - QByteArray outArray; - char buffer[4096]; - - // Decompress until we reach the stream end. - do { - strm.next_out = reinterpret_cast(buffer); - strm.avail_out = sizeof(buffer); - - ret = inflate(&strm, Z_NO_FLUSH); - // Handle a special case: if inflate() returns Z_BUF_ERROR without - // having produced any output and with no further input, then we break out. - if (ret == Z_BUF_ERROR && strm.avail_in == 0) { - break; - } - if (ret != Z_OK && ret != Z_STREAM_END) { - qWarning() << "Error: ZLib inflate failed:" << zError(ret); - inflateEnd(&strm); - return QByteArray(); - } - - // Calculate number of bytes produced in this iteration. - int bytesProduced = sizeof(buffer) - strm.avail_out; - if (bytesProduced > 0) - outArray.append(buffer, bytesProduced); - - } while (ret != Z_STREAM_END); - - inflateEnd(&strm); - return outArray; - } - - static QByteArray DecompressLZO(const QByteArray& input) { - lzokay::EResult error; - - // Ensure the input QByteArray is valid - if (input.isEmpty()) { - qDebug() << "Input QByteArray is empty."; - return QByteArray(); - } - - // Step 1: Cast QByteArray to uint8_t* - const uint8_t *compressedData = reinterpret_cast(input.constData()); - std::size_t compressedSize = static_cast(input.size()); - - // Step 2: Allocate a sufficiently large decompression buffer - // Use a large initial estimate if the decompressed size is unknown - std::size_t initialBufferSize = compressedSize * 20; // Arbitrary multiplier for decompression - std::unique_ptr decompressed(new uint8_t[initialBufferSize]); - - // Step 3: Attempt decompression - std::size_t decompressedSize = 0; - error = lzokay::decompress( - compressedData, compressedSize, // Input data and size - decompressed.get(), initialBufferSize, // Output buffer and initial size - decompressedSize // Actual decompressed size - ); - - // Step 4: Handle decompression errors - if (error != lzokay::EResult::Success) { - qDebug() << "Decompression failed with error code:" << static_cast(error); - return QByteArray(); - } - - // Step 5: Return the decompressed data as a QByteArray - return QByteArray(reinterpret_cast(decompressed.get()), decompressedSize); - } - - static const int VECTOR_SIZE = 16; // 16 32-bit words - static const int NUM_OF_BLOCKS_PER_CHUNK = 8192; - - //-------------------------------------------------------------------- - // Helper functions (assuming little–endian order) - - static void Convert32BitTo8Bit(quint32 value, quint8* array) { - array[0] = static_cast(value >> 0); - array[1] = static_cast(value >> 8); - array[2] = static_cast(value >> 16); - array[3] = static_cast(value >> 24); - } - - static quint32 ConvertArrayTo32Bit(const QByteArray &array) { - return ((static_cast(static_cast(array[0])) << 0) | - (static_cast(static_cast(array[1])) << 8) | - (static_cast(static_cast(array[2])) << 16) | - (static_cast(static_cast(array[3])) << 24)); - } - - static quint32 Rotate(quint32 value, quint32 numBits) { - return (value << numBits) | (value >> (32 - numBits)); - } - - // Build the IV table from a 0x20–byte feed. The table is 0xFB0 bytes. - static QByteArray InitIVTable(const QByteArray &feed) { - const int tableSize = 0xFB0; - QByteArray table; - table.resize(tableSize); - int ptr = 0; - for (int i = 0; i < 200; ++i) { - for (int x = 0; x < 5; ++x) { - if (static_cast(feed.at(ptr)) == 0x00) - ptr = 0; - int base = i * 20 + x * 4; - table[base] = feed.at(ptr); - table[base + 1] = feed.at(ptr); - table[base + 2] = feed.at(ptr); - table[base + 3] = feed.at(ptr); - ++ptr; - } - } - // Copy block numbers [1,0,0,0] into the last 16 bytes - QByteArray oneBlock; - oneBlock.append(char(1)); oneBlock.append(char(0)); oneBlock.append(char(0)); oneBlock.append(char(0)); - table.replace(0xFA0, 4, oneBlock); - table.replace(0xFA4, 4, oneBlock); - table.replace(0xFA8, 4, oneBlock); - table.replace(0xFAC, 4, oneBlock); - return table; - } - - // "unk" function as in the C# code. - static int unk(quint64 arg1, quint8 arg2) { - if (arg2 >= 0x40) - return 0; - return static_cast(arg1 >> arg2); - } - - // Compute the IV for a given section index using the IV table. - static QByteArray GetIV(const QByteArray &table, int index) { - int num1 = 0xFA0 + index; - int num2 = unk(0x51EB851FLL * num1, 0x20); - int adjust = ((num2 >> 6) + (num2 >> 31)); - int startIndex = 20 * (num1 - 200 * adjust); - // Return 8 bytes from that location. - return table.mid(startIndex, 8); - } - - // Update the IV table given the section's SHA1 hash. - static void UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash) { - int blockNumIndex = index % 4; - int baseOffset = 0xFA0 + blockNumIndex * 4; - quint32 blockNumVal = (static_cast(table.at(baseOffset)) ) | - (static_cast(table.at(baseOffset + 1)) << 8 ) | - (static_cast(table.at(baseOffset + 2)) << 16) | - (static_cast(table.at(baseOffset + 3)) << 24); - int blockNum = blockNumVal * 4 + index; - int num2 = unk(0x51EB851FLL * blockNum, 0x20); - int adjust = ((num2 >> 6) + (num2 >> 31)); - int startIndex = 20 * (blockNum - 200 * adjust) + 1; - int hashIndex = 0; - for (int x = 0; x < 4; ++x) { - table[startIndex - 1] = table.at(startIndex - 1) ^ sectionHash.at(hashIndex); - table[startIndex] = table.at(startIndex) ^ sectionHash.at(hashIndex + 1); - table[startIndex + 1] = table.at(startIndex + 1) ^ sectionHash.at(hashIndex + 2); - table[startIndex + 2] = table.at(startIndex + 2) ^ sectionHash.at(hashIndex + 3); - table[startIndex + 3] = table.at(startIndex + 3) ^ sectionHash.at(hashIndex + 4); - startIndex += 5; - hashIndex += 5; - } - } - - static quint32 ToUInt32(const QByteArray &data, int offset) { - // Converts 4 bytes (starting at offset) from data into a 32-bit unsigned integer (little-endian) - return ((static_cast(static_cast(data[offset])) ) | - (static_cast(static_cast(data[offset+1])) << 8 ) | - (static_cast(static_cast(data[offset+2])) << 16) | - (static_cast(static_cast(data[offset+3])) << 24)); - } - - //-------------------------------------------------------------------- - // Salsa20 decryption for one section. - // This function resets the counter for each section. - static QByteArray salsa20DecryptSection(const QByteArray §ionData, const QByteArray &key, const QByteArray &iv, int blockSize = 64) - { - // Choose the appropriate constant based on key length. - QByteArray constants; - if (key.size() == 32) - constants = "expand 32-byte k"; - else if (key.size() == 16) - constants = "expand 16-byte k"; - else { - qWarning() << "Invalid key size:" << key.size() << "; expected 16 or 32 bytes."; - return QByteArray(); - } - - QVector state(VECTOR_SIZE); - - // Set state[0] using the first 4 bytes of the constant. - state[0] = ConvertArrayTo32Bit(constants.mid(0, 4)); - - // state[1] through state[4] come from the first 16 bytes of the key. - state[1] = ToUInt32(key, 0); - state[2] = ToUInt32(key, 4); - state[3] = ToUInt32(key, 8); - state[4] = ToUInt32(key, 12); - - // state[5] comes from the next 4 bytes of the constant. - state[5] = ConvertArrayTo32Bit(constants.mid(4, 4)); - - // state[6] and state[7] come from the IV (which must be 8 bytes). - state[6] = ConvertArrayTo32Bit(iv.mid(0, 4)); - state[7] = ConvertArrayTo32Bit(iv.mid(4, 4)); - - // state[8] and state[9] are the 64-bit block counter (start at 0). - state[8] = 0; - state[9] = 0; - - // state[10] comes from the next 4 bytes of the constant. - state[10] = ConvertArrayTo32Bit(constants.mid(8, 4)); - - // For state[11] through state[14]: - // If the key is 32 bytes, use bytes 16..31; if 16 bytes, reuse the first 16 bytes. - if (key.size() == 32) { - state[11] = ToUInt32(key, 16); - state[12] = ToUInt32(key, 20); - state[13] = ToUInt32(key, 24); - state[14] = ToUInt32(key, 28); - } else { // key.size() == 16 - state[11] = ToUInt32(key, 0); - state[12] = ToUInt32(key, 4); - state[13] = ToUInt32(key, 8); - state[14] = ToUInt32(key, 12); - } - - // state[15] comes from the last 4 bytes of the constant. - state[15] = ConvertArrayTo32Bit(constants.mid(12, 4)); - - // Prepare the output buffer. - QByteArray output(sectionData.size(), Qt::Uninitialized); - int numBlocks = sectionData.size() / blockSize; - int remainder = sectionData.size() % blockSize; - - // Process each full block. - for (int blockIndex = 0; blockIndex < numBlocks; ++blockIndex) { - QVector x = state; // make a copy of the current state for this block - - // Run 20 rounds (10 iterations) of Salsa20. - for (int round = 20; round > 0; round -= 2) { - x[4] ^= Rotate(x[0] + x[12], 7); - x[8] ^= Rotate(x[4] + x[0], 9); - x[12] ^= Rotate(x[8] + x[4], 13); - x[0] ^= Rotate(x[12] + x[8], 18); - - x[9] ^= Rotate(x[5] + x[1], 7); - x[13] ^= Rotate(x[9] + x[5], 9); - x[1] ^= Rotate(x[13] + x[9], 13); - x[5] ^= Rotate(x[1] + x[13], 18); - - x[14] ^= Rotate(x[10] + x[6], 7); - x[2] ^= Rotate(x[14] + x[10], 9); - x[6] ^= Rotate(x[2] + x[14], 13); - x[10] ^= Rotate(x[6] + x[2], 18); - - x[3] ^= Rotate(x[15] + x[11], 7); - x[7] ^= Rotate(x[3] + x[15], 9); - x[11] ^= Rotate(x[7] + x[3], 13); - x[15] ^= Rotate(x[11] + x[7], 18); - - x[1] ^= Rotate(x[0] + x[3], 7); - x[2] ^= Rotate(x[1] + x[0], 9); - x[3] ^= Rotate(x[2] + x[1], 13); - x[0] ^= Rotate(x[3] + x[2], 18); - - x[6] ^= Rotate(x[5] + x[4], 7); - x[7] ^= Rotate(x[6] + x[5], 9); - x[4] ^= Rotate(x[7] + x[6], 13); - x[5] ^= Rotate(x[4] + x[7], 18); - - x[11] ^= Rotate(x[10] + x[9], 7); - x[8] ^= Rotate(x[11] + x[10], 9); - x[9] ^= Rotate(x[8] + x[11], 13); - x[10] ^= Rotate(x[9] + x[8], 18); - - x[12] ^= Rotate(x[15] + x[14], 7); - x[13] ^= Rotate(x[12] + x[15], 9); - x[14] ^= Rotate(x[13] + x[12], 13); - x[15] ^= Rotate(x[14] + x[13], 18); - } - - // Produce the 64-byte keystream block by adding the original state. - QVector keyStreamBlock(blockSize); - for (int i = 0; i < VECTOR_SIZE; ++i) { - x[i] += state[i]; - Convert32BitTo8Bit(x[i], keyStreamBlock.data() + 4 * i); - } - - // XOR the keystream block with the corresponding block of sectionData. - const uchar* inBlock = reinterpret_cast(sectionData.constData()) + blockIndex * blockSize; - uchar* outBlock = reinterpret_cast(output.data()) + blockIndex * blockSize; - for (int j = 0; j < blockSize; ++j) { - outBlock[j] = inBlock[j] ^ keyStreamBlock[j]; - } - // Increment the 64-bit block counter. - state[8]++; - if (state[8] == 0) - state[9]++; - } - - // Process any remaining bytes. - if (remainder > 0) { - QVector x = state; - for (int round = 20; round > 0; round -= 2) { - x[4] ^= Rotate(x[0] + x[12], 7); - x[8] ^= Rotate(x[4] + x[0], 9); - x[12] ^= Rotate(x[8] + x[4], 13); - x[0] ^= Rotate(x[12] + x[8], 18); - - x[9] ^= Rotate(x[5] + x[1], 7); - x[13] ^= Rotate(x[9] + x[5], 9); - x[1] ^= Rotate(x[13] + x[9], 13); - x[5] ^= Rotate(x[1] + x[13], 18); - - x[14] ^= Rotate(x[10] + x[6], 7); - x[2] ^= Rotate(x[14] + x[10], 9); - x[6] ^= Rotate(x[2] + x[14], 13); - x[10] ^= Rotate(x[6] + x[2], 18); - - x[3] ^= Rotate(x[15] + x[11], 7); - x[7] ^= Rotate(x[3] + x[15], 9); - x[11] ^= Rotate(x[7] + x[3], 13); - x[15] ^= Rotate(x[11] + x[7], 18); - - x[1] ^= Rotate(x[0] + x[3], 7); - x[2] ^= Rotate(x[1] + x[0], 9); - x[3] ^= Rotate(x[2] + x[1], 13); - x[0] ^= Rotate(x[3] + x[2], 18); - - x[6] ^= Rotate(x[5] + x[4], 7); - x[7] ^= Rotate(x[6] + x[5], 9); - x[4] ^= Rotate(x[7] + x[6], 13); - x[5] ^= Rotate(x[4] + x[7], 18); - - x[11] ^= Rotate(x[10] + x[9], 7); - x[8] ^= Rotate(x[11] + x[10], 9); - x[9] ^= Rotate(x[8] + x[11], 13); - x[10] ^= Rotate(x[9] + x[8], 18); - - x[12] ^= Rotate(x[15] + x[14], 7); - x[13] ^= Rotate(x[12] + x[15], 9); - x[14] ^= Rotate(x[13] + x[12], 13); - x[15] ^= Rotate(x[14] + x[13], 18); - } - QVector keyStreamBlock(blockSize); - for (int i = 0; i < VECTOR_SIZE; ++i) { - x[i] += state[i]; - Convert32BitTo8Bit(x[i], keyStreamBlock.data() + 4 * i); - } - const uchar* inBlock = reinterpret_cast(sectionData.constData()) + numBlocks * blockSize; - uchar* outBlock = reinterpret_cast(output.data()) + numBlocks * blockSize; - for (int j = 0; j < remainder; ++j) - outBlock[j] = inBlock[j] ^ keyStreamBlock[j]; - } - - return output; - } -}; - - -#endif // COMPRESSOR_H diff --git a/data/Data.qrc b/data/Data.qrc index 44abea1..e1c377a 100644 --- a/data/Data.qrc +++ b/data/Data.qrc @@ -53,5 +53,39 @@ icons/Icon_WAVFile.png icons/Icon_MenuFile.png icons/Icon_Image.png + icons/Icon_Model.png + icons/Icon_StringTable.png + icons/Icon_Sound.png + icons/Icon_Pause.png + icons/Icon_Play.png + icons/Icon_SkipBack.png + icons/Icon_SkipForward.png + icons/Icon_Stop.png + icons/Icon_Editor.png + icons/Icon_Views.png + icons/Icon_Tree.png + icons/Icon_Copy.png + icons/Icon_Cut.png + icons/Icon_Find.png + icons/Icon_NewFile.png + icons/Icon_Paste.png + icons/Icon_Save.png + icons/Icon_OpenFile.png + icons/Icon_COD2.png + icons/Icon_Material.png + icons/Icon_Animation.png + icons/Icon_BSP.png + icons/Icon_ColMapSp.png + icons/Icon_Effect.png + icons/Icon_GameMapSp.png + icons/Icon_Font.png + icons/Icon_FXMap.png + icons/Icon_LightDef.png + icons/Icon_Weapon.png + icons/Icon_RawFile.png + icons/Icon_Destructible.png + icons/Icon_PhysPreset.png + icons/Icon_Wii.png + icons/Icon_WiiU.png diff --git a/data/icons/Icon_Animation.png b/data/icons/Icon_Animation.png new file mode 100644 index 0000000..0dc0e31 Binary files /dev/null and b/data/icons/Icon_Animation.png differ diff --git a/data/icons/Icon_BSP.png b/data/icons/Icon_BSP.png new file mode 100644 index 0000000..4a3ab1d Binary files /dev/null and b/data/icons/Icon_BSP.png differ diff --git a/data/icons/Icon_COD2.png b/data/icons/Icon_COD2.png new file mode 100644 index 0000000..470c389 Binary files /dev/null and b/data/icons/Icon_COD2.png differ diff --git a/data/icons/Icon_ColMapSp.png b/data/icons/Icon_ColMapSp.png new file mode 100644 index 0000000..085e84d Binary files /dev/null and b/data/icons/Icon_ColMapSp.png differ diff --git a/data/icons/Icon_Copy.png b/data/icons/Icon_Copy.png new file mode 100644 index 0000000..2246199 Binary files /dev/null and b/data/icons/Icon_Copy.png differ diff --git a/data/icons/Icon_Cut.png b/data/icons/Icon_Cut.png new file mode 100644 index 0000000..3ff11cc Binary files /dev/null and b/data/icons/Icon_Cut.png differ diff --git a/data/icons/Icon_Destructible.png b/data/icons/Icon_Destructible.png new file mode 100644 index 0000000..06f5eb1 Binary files /dev/null and b/data/icons/Icon_Destructible.png differ diff --git a/data/icons/Icon_Editor.png b/data/icons/Icon_Editor.png new file mode 100644 index 0000000..f3b72af Binary files /dev/null and b/data/icons/Icon_Editor.png differ diff --git a/data/icons/Icon_Effect.png b/data/icons/Icon_Effect.png new file mode 100644 index 0000000..ba41f66 Binary files /dev/null and b/data/icons/Icon_Effect.png differ diff --git a/data/icons/Icon_FXMap.png b/data/icons/Icon_FXMap.png new file mode 100644 index 0000000..f434048 Binary files /dev/null and b/data/icons/Icon_FXMap.png differ diff --git a/data/icons/Icon_Find.png b/data/icons/Icon_Find.png new file mode 100644 index 0000000..936c431 Binary files /dev/null and b/data/icons/Icon_Find.png differ diff --git a/data/icons/Icon_Font.png b/data/icons/Icon_Font.png new file mode 100644 index 0000000..1af47b2 Binary files /dev/null and b/data/icons/Icon_Font.png differ diff --git a/data/icons/Icon_GameMapSp.png b/data/icons/Icon_GameMapSp.png new file mode 100644 index 0000000..6b72e9a Binary files /dev/null and b/data/icons/Icon_GameMapSp.png differ diff --git a/data/icons/Icon_LightDef.png b/data/icons/Icon_LightDef.png new file mode 100644 index 0000000..20689ba Binary files /dev/null and b/data/icons/Icon_LightDef.png differ diff --git a/data/icons/Icon_Material.png b/data/icons/Icon_Material.png new file mode 100644 index 0000000..2e9d2e8 Binary files /dev/null and b/data/icons/Icon_Material.png differ diff --git a/data/icons/Icon_Model.png b/data/icons/Icon_Model.png new file mode 100644 index 0000000..c5e0355 Binary files /dev/null and b/data/icons/Icon_Model.png differ diff --git a/data/icons/Icon_NewFile.png b/data/icons/Icon_NewFile.png new file mode 100644 index 0000000..0e9d05c Binary files /dev/null and b/data/icons/Icon_NewFile.png differ diff --git a/data/icons/Icon_OpenFile.png b/data/icons/Icon_OpenFile.png new file mode 100644 index 0000000..a7166b5 Binary files /dev/null and b/data/icons/Icon_OpenFile.png differ diff --git a/data/icons/Icon_Paste.png b/data/icons/Icon_Paste.png new file mode 100644 index 0000000..65d704c Binary files /dev/null and b/data/icons/Icon_Paste.png differ diff --git a/data/icons/Icon_Pause.png b/data/icons/Icon_Pause.png new file mode 100644 index 0000000..8d46f91 Binary files /dev/null and b/data/icons/Icon_Pause.png differ diff --git a/data/icons/Icon_PhysPreset.png b/data/icons/Icon_PhysPreset.png new file mode 100644 index 0000000..5e4339f Binary files /dev/null and b/data/icons/Icon_PhysPreset.png differ diff --git a/data/icons/Icon_Play.png b/data/icons/Icon_Play.png new file mode 100644 index 0000000..47d198f Binary files /dev/null and b/data/icons/Icon_Play.png differ diff --git a/data/icons/Icon_RawFile.png b/data/icons/Icon_RawFile.png new file mode 100644 index 0000000..1281b71 Binary files /dev/null and b/data/icons/Icon_RawFile.png differ diff --git a/data/icons/Icon_Save.png b/data/icons/Icon_Save.png new file mode 100644 index 0000000..587ea2e Binary files /dev/null and b/data/icons/Icon_Save.png differ diff --git a/data/icons/Icon_SkipBack.png b/data/icons/Icon_SkipBack.png new file mode 100644 index 0000000..fd217d8 Binary files /dev/null and b/data/icons/Icon_SkipBack.png differ diff --git a/data/icons/Icon_SkipForward.png b/data/icons/Icon_SkipForward.png new file mode 100644 index 0000000..d41874f Binary files /dev/null and b/data/icons/Icon_SkipForward.png differ diff --git a/data/icons/Icon_Sound.png b/data/icons/Icon_Sound.png new file mode 100644 index 0000000..f55c471 Binary files /dev/null and b/data/icons/Icon_Sound.png differ diff --git a/data/icons/Icon_Stop.png b/data/icons/Icon_Stop.png new file mode 100644 index 0000000..fb18f60 Binary files /dev/null and b/data/icons/Icon_Stop.png differ diff --git a/data/icons/Icon_StringTable.png b/data/icons/Icon_StringTable.png new file mode 100644 index 0000000..9ca4722 Binary files /dev/null and b/data/icons/Icon_StringTable.png differ diff --git a/data/icons/Icon_Tree.png b/data/icons/Icon_Tree.png new file mode 100644 index 0000000..d3d29b1 Binary files /dev/null and b/data/icons/Icon_Tree.png differ diff --git a/data/icons/Icon_Views.png b/data/icons/Icon_Views.png new file mode 100644 index 0000000..7b0e437 Binary files /dev/null and b/data/icons/Icon_Views.png differ diff --git a/data/icons/Icon_Weapon.png b/data/icons/Icon_Weapon.png new file mode 100644 index 0000000..76e98e7 Binary files /dev/null and b/data/icons/Icon_Weapon.png differ diff --git a/data/icons/Icon_Wii.png b/data/icons/Icon_Wii.png new file mode 100644 index 0000000..efed38a Binary files /dev/null and b/data/icons/Icon_Wii.png differ diff --git a/data/icons/Icon_WiiU.png b/data/icons/Icon_WiiU.png new file mode 100644 index 0000000..a827c6a Binary files /dev/null and b/data/icons/Icon_WiiU.png differ diff --git a/fastfile.cpp b/fastfile.cpp deleted file mode 100644 index 4e57009..0000000 --- a/fastfile.cpp +++ /dev/null @@ -1,374 +0,0 @@ -#include "fastfile.h" -#include "compressor.h" - -#include -#include - -FastFile::FastFile() : - fileStem(), - company(), - fileType(), - signage(), - magic(), - version() { -} - -FastFile::~FastFile() { - -} - -FastFile::FastFile(const FastFile &aFastFile) { - fileStem = aFastFile.GetFileStem(); - company = aFastFile.GetCompany(); - fileType = aFastFile.GetFileType(); - signage = aFastFile.GetSignage(); - magic = aFastFile.GetMagic(); - version = aFastFile.GetVersion(); - zoneFile = aFastFile.zoneFile; - game = aFastFile.GetGame(); - platform = aFastFile.GetPlatform(); -} - -FastFile &FastFile::operator=(const FastFile &other) { - if (this != &other) { - fileStem = other.GetFileStem(); - company = other.GetCompany(); - fileType = other.GetFileType(); - signage = other.GetSignage(); - magic = other.GetMagic(); - version = other.GetVersion(); - zoneFile = other.zoneFile; - game = other.GetGame(); - platform = other.GetPlatform(); - } - return *this; -} - -bool FastFile::Load(const QByteArray aData) { - QByteArray decompressedData; - - // Create a QDataStream on the input data. - QDataStream fastFileStream(aData); - fastFileStream.setByteOrder(QDataStream::LittleEndian); - - // Parse header values. - company = pParseFFCompany(&fastFileStream); - fileType = pParseFFFileType(&fastFileStream); - signage = pParseFFSignage(&fastFileStream); - magic = pParseFFMagic(&fastFileStream); - version = pParseFFVersion(&fastFileStream); - platform = pCalculateFFPlatform(); - game = pCalculateFFGame(); - - if (game == "COD5") { - // For COD5, simply decompress from offset 12. - decompressedData = Compressor::DecompressZLIB(aData.mid(12)); - - QFile testFile("exports/" + fileStem.section('.', 0, 0) + ".zone"); - if(testFile.open(QIODevice::WriteOnly)) { - testFile.write(decompressedData); - testFile.close(); - } - - zoneFile.Load(decompressedData, fileStem.section('.', 0, 0) + ".zone"); - } - else if (game == "COD7" || game == "COD9") { - // For COD7/COD9, use BigEndian. - fastFileStream.setByteOrder(QDataStream::BigEndian); - if (platform == "PC") { - fastFileStream.setByteOrder(QDataStream::LittleEndian); - } - - // Select key based on game. - QByteArray key; - if (game == "COD7") { - fastFileStream.skipRawData(4); - if (platform == "360") { - key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d"); - } else if (platform == "PS3") { - key = QByteArray::fromHex("46D3F997F29C9ACE175B0DAE3AB8C0C1B8E423E2E3BF7E3C311EA35245BF193A"); - // or - // key = QByteArray::fromHex("0C99B3DDB8D6D0845D1147E470F28A8BF2AE69A8A9F534767B54E9180FF55370"); - } - } else if (game == "COD9") { - if (platform == "360") { - key = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3"); - } else if (platform == "PC") { - key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE"); - } - } - - // 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 = Compressor::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 = Compressor::GetIV(ivTable, sectionIndex); - - // Decrypt the section using Salsa20. - QByteArray decData = Compressor::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. - Compressor::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); - - // For COD7, always decompress. - // For COD9, conditionally use DEFLATE (set useDeflateForCOD9 as needed). - if (game == "COD7") { - decompressedData.append(Compressor::DecompressZLIB(compressedData)); - } else if (game == "COD9") { - if (platform == "PC") { - decompressedData.append(Compressor::DecompressZLIB(compressedData)); - } else if (platform == "360") { - decompressedData.append(LZX::DecompressLZX(compressedData, compressedData.size())); - } - } else { - // If not decompressing, append the compressed buffer as-is. - decompressedData.append(compressedData); - } - - // Optionally write out test files for COD9. - if (game == "COD9") { - QFile testFile("exports/" + QString("%1.out").arg(sectionIndex)); - if(testFile.open(QIODevice::WriteOnly)) { - testFile.write(decompressedData); - testFile.close(); - } - } - - sectionIndex++; - } - - // For COD9, write out the complete decompressed zone for testing. - if (false && game == "COD9") { - QFile testFile("exports/test.zone"); - if(testFile.open(QIODevice::WriteOnly)) { - testFile.write(decompressedData); - testFile.close(); - } - } - - // Load the zone file with the decompressed data (using an Xbox platform flag). - zoneFile.Load(decompressedData, fileStem.section('.', 0, 0) + ".zone", FF_PLATFORM_XBOX); - } - - return true; -} - - - -bool FastFile::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); - fileStem = 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; -} - -QString FastFile::GetFileStem() const { - return fileStem; -} - -FF_COMPANY FastFile::GetCompany() const { - return company; -} - -FF_FILETYPE FastFile::GetFileType() const { - return fileType; -} - -FF_SIGNAGE FastFile::GetSignage() const { - return signage; -} - -QString FastFile::GetMagic() const { - return magic; -} - -quint32 FastFile::GetVersion() const { - return version; -} - -ZoneFile FastFile::GetZoneFile() const { - return zoneFile; -} - -QString FastFile::GetGame() const { - return game; -} - -QString FastFile::GetPlatform() const { - return platform; -} - -FF_COMPANY FastFile::pParseFFCompany(QDataStream *afastFileStream) { - // Check for null datastream ptr - if (!afastFileStream) { return COMPANY_NONE; } - // Parse company - QByteArray companyData(2, Qt::Uninitialized); - afastFileStream->readRawData(companyData.data(), 2); - if (companyData == "IW") { - qDebug() << "Company found: 'INFINITY_WARD'"; - return COMPANY_INFINITY_WARD; - } else if (companyData == "TA") { - qDebug() << "Company found: 'TREYARCH'"; - return COMPANY_TREYARCH; - } else if (companyData == "Sl") { - qDebug() << "Company found: 'SLEDGEHAMMER'"; - return COMPANY_SLEDGEHAMMER; - } else if (companyData == "NX") { - qDebug() << "Company found: 'NEVERSOFT'"; - return COMPANY_NEVERSOFT; - } else { - qDebug() << QString("Failed to find company, found '%1'!").arg(companyData); - } - return COMPANY_NONE; -} - -FF_FILETYPE FastFile::pParseFFFileType(QDataStream *afastFileStream) { - // Parse filetype - QByteArray fileTypeData(2, Qt::Uninitialized); - afastFileStream->readRawData(fileTypeData.data(), 2); - if (fileTypeData == "ff") { - qDebug() << "File type found: 'FAST_FILE'"; - return FILETYPE_FAST_FILE; - } else { - qDebug() << "Failed to find file type!"; - } - return FILETYPE_NONE; -} - -FF_SIGNAGE FastFile::pParseFFSignage(QDataStream *afastFileStream) { - // Parse filetype - QByteArray signedData(1, Qt::Uninitialized); - afastFileStream->readRawData(signedData.data(), 1); - if (signedData == "u") { - qDebug() << "Found valid signage: Unsigned"; - return SIGNAGE_UNSIGNED; - } else if (signedData == "0" || signedData == "x") { - qDebug() << "Found valid signage: Signed"; - return SIGNAGE_SIGNED; - } else { - qDebug() << "Failed to determine signage of fastfile!"; - } - return SIGNAGE_NONE; -} - -QString FastFile::pParseFFMagic(QDataStream *afastFileStream) { - // Parse magic - QByteArray magicData(3, Qt::Uninitialized); - afastFileStream->readRawData(magicData.data(), 3); - if (magicData == "100") { - qDebug() << QString("Found valid magic: '%1'").arg(magicData); - return magicData; - } else { - qDebug() << "Magic invalid!"; - } - return ""; -} - -quint32 FastFile::pParseFFVersion(QDataStream *afastFileStream) { - // Parse version - quint32 version; - *afastFileStream >> version; - qDebug() << QString("Found version: '%1'").arg(version); - return version; -} - -QString FastFile::pCalculateFFPlatform() { - QString result = "NONE"; - switch (version) { - case 387: // PC World at War - case 473: // PC Black Ops 1 - case 1: // PC Modern Warfare 3 - case 147: // PC Black Ops 2 - result = "PC"; - break; - case 3640721408: // Xbox 360 Black Ops 1 - case 2449473536: // Xbox 360 Black Ops 2 - result = "360"; - break; - } - qDebug() << QString("Found platform: '%1'").arg(result); - return result; -} - -QString FastFile::pCalculateFFGame() { - QString result = "NONE"; - switch (version) { - case 387: // PC World at War - result = "COD5"; - break; - case 473: // PC Black Ops 1 - break; - case 3640721408: // Xbox 360 Black Ops 1 - result = "COD7"; - break; - case 1: // PC Modern Warfare 3 - result = "COD8"; - break; - case 147: // PC Black Ops 2 - case 2449473536: // Xbox 360 Black Ops 2 - result = "COD9"; - break; - } - qDebug() << QString("Found game: '%1'").arg(result); - return result; -} diff --git a/fastfile.h b/fastfile.h deleted file mode 100644 index 0277d1b..0000000 --- a/fastfile.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef FASTFILE_H -#define FASTFILE_H - -#include "enums.h" -#include "zonefile.h" - -#include -#include - -class FastFile -{ -public: - FastFile(); - ~FastFile(); - FastFile(const FastFile &aFastFile); - FastFile &operator=(const FastFile &other); - - bool Load(const QString aFilePath); - bool Load(const QByteArray aData); - - QString GetFileStem() const; - FF_COMPANY GetCompany() const; - FF_FILETYPE GetFileType() const; - FF_SIGNAGE GetSignage() const; - QString GetMagic() const; - quint32 GetVersion() const; - ZoneFile GetZoneFile() const; - QString GetGame() const; - QString GetPlatform() const; - -private: - QString fileStem; - FF_COMPANY company; - FF_FILETYPE fileType; - FF_SIGNAGE signage; - QString magic; - quint32 version; - ZoneFile zoneFile; - QString game; - QString platform; - - QString pCalculateFFGame(); - QString pCalculateFFPlatform(); - QString pParseFFMagic(QDataStream *afastFileStream); - FF_SIGNAGE pParseFFSignage(QDataStream *afastFileStream); - FF_FILETYPE pParseFFFileType(QDataStream *afastFileStream); - FF_COMPANY pParseFFCompany(QDataStream *afastFileStream); - quint32 pParseFFVersion(QDataStream *afastFileStream); -}; - -#endif // FASTFILE_H diff --git a/libs/compression/compression.cpp b/libs/compression/compression.cpp new file mode 100644 index 0000000..a9b424c --- /dev/null +++ b/libs/compression/compression.cpp @@ -0,0 +1,392 @@ +#include "compression.h" +#include "lzokay.h" +#include "qlibrary.h" + +QByteArray Compression::DecompressZLIB(const QByteArray &aCompressedData) { + if (aCompressedData.isEmpty()) + return {}; + + z_stream strm{}; + strm.next_in = reinterpret_cast(const_cast(aCompressedData.data())); + strm.avail_in = static_cast(aCompressedData.size()); + + if (inflateInit2(&strm, MAX_WBITS) != Z_OK) { + qWarning() << "Failed to initialize zlib for decompression."; + return {}; + } + + QByteArray decompressed; + char buffer[4096]; + + int ret; + do { + strm.next_out = reinterpret_cast(buffer); + strm.avail_out = sizeof(buffer); + + ret = inflate(&strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + qWarning() << "Zlib decompression error:" << zError(ret); + inflateEnd(&strm); + return {}; + } + + decompressed.append(buffer, sizeof(buffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + inflateEnd(&strm); + return decompressed; +} + +QByteArray Compression::CompressZLIB(const QByteArray &aData) { + return CompressZLIBWithSettings(aData); +} + +QByteArray Compression::CompressZLIBWithSettings(const QByteArray &aData, int aCompressionLevel, int aWindowBits, int aMemLevel, int aStrategy, const QByteArray &aDictionary) { + if (aData.isEmpty()) + return {}; + + z_stream strm{}; + if (deflateInit2(&strm, aCompressionLevel, Z_DEFLATED, aWindowBits, aMemLevel, aStrategy) != Z_OK) { + qWarning() << "Failed to initialize compression with custom settings."; + return {}; + } + + if (!aDictionary.isEmpty()) { + deflateSetDictionary(&strm, reinterpret_cast(aDictionary.constData()), aDictionary.size()); + } + + strm.next_in = reinterpret_cast(const_cast(aData.data())); + strm.avail_in = aData.size(); + + QByteArray compressed; + char buffer[4096]; + + int ret; + do { + strm.next_out = reinterpret_cast(buffer); + strm.avail_out = sizeof(buffer); + + ret = deflate(&strm, strm.avail_in ? Z_NO_FLUSH : Z_FINISH); + if (ret != Z_OK && ret != Z_STREAM_END) { + qWarning() << "Compression error:" << zError(ret); + deflateEnd(&strm); + return {}; + } + + compressed.append(buffer, sizeof(buffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + deflateEnd(&strm); + return compressed; +} + +QByteArray Compression::DecompressDeflate(const QByteArray &aCompressedData) { + if (aCompressedData.isEmpty()) + return {}; + + z_stream strm{}; + strm.next_in = reinterpret_cast(const_cast(aCompressedData.data())); + strm.avail_in = static_cast(aCompressedData.size()); + + // Negative window bits (-MAX_WBITS) indicate raw DEFLATE data. + if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) { + qWarning() << "Failed to initialize DEFLATE for decompression."; + return QByteArray(); + } + + QByteArray decompressed; + char buffer[4096]; + + int ret; + do { + strm.next_out = reinterpret_cast(buffer); + strm.avail_out = sizeof(buffer); + + ret = inflate(&strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + qWarning() << "DEFLATE decompression error:" << zError(ret); + inflateEnd(&strm); + return QByteArray(); + } + + decompressed.append(buffer, sizeof(buffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + inflateEnd(&strm); + return decompressed; +} + +QByteArray Compression::CompressDeflate(const QByteArray &aData) { + return CompressDeflateWithSettings(aData); +} + +QByteArray Compression::CompressDeflateWithSettings(const QByteArray &aData, int aCompressionLevel, int aWindowBits, int aMemLevel, int aStrategy, const QByteArray &aDictionary) { + if (aData.isEmpty()) + return QByteArray(); + + z_stream strm{}; + + // Negative window bits (-MAX_WBITS) indicate raw DEFLATE data. + if (deflateInit2(&strm, aCompressionLevel, Z_DEFLATED, -aWindowBits, aMemLevel, aStrategy) != Z_OK) { + qWarning() << "Failed to initialize DEFLATE for compression."; + return QByteArray(); + } + + strm.next_in = reinterpret_cast(const_cast(aData.data())); + strm.avail_in = static_cast(aData.size()); + + QByteArray compressed; + char buffer[4096]; + + int ret; + do { + strm.next_out = reinterpret_cast(buffer); + strm.avail_out = sizeof(buffer); + + ret = deflate(&strm, strm.avail_in ? Z_NO_FLUSH : Z_FINISH); + + if (ret != Z_OK && ret != Z_STREAM_END) { + qWarning() << "DEFLATE compression error:" << zError(ret); + deflateEnd(&strm); + return {}; + } + + compressed.append(buffer, sizeof(buffer) - strm.avail_out); + } while (ret != Z_STREAM_END); + + deflateEnd(&strm); + return compressed; + +} + +QByteArray Compression::DecompressOodle(const QByteArray &aCompressedData, quint32 aDecompressedSize) { + return pDecompressOodle(aCompressedData, aCompressedData.length(), aDecompressedSize); +} + +QByteArray Compression::CompressOodle(const QByteArray &aData) { + + quint32 maxSize = pGetOodleCompressedBounds(aData.length()); + QByteArray compressedData = pCompressOodle(aData, aData.length(), + maxSize, OodleFormat::Kraken, OodleCompressionLevel::Optimal5); + + return compressedData.mid(0, maxSize); +} + +quint32 Compression::pGetOodleCompressedBounds(quint32 aBufferSize) { + return aBufferSize + 274 * ((aBufferSize + 0x3FFFF) / 0x400000); +} + +QByteArray Compression::pCompressOodle(QByteArray aBuffer, quint32 aBufferSize, quint32 aOutputBufferSize, OodleFormat aformat, OodleCompressionLevel alevel) { + QLibrary oodleLib("oo2core_8_win64"); + + if (!oodleLib.load()) { + qDebug() << "Failed to load DLL:" << oodleLib.errorString(); + return QByteArray(); + } + + OodleLZ_CompressFunc OodleLZ_Compress = + (OodleLZ_CompressFunc)oodleLib.resolve("OodleLZ_Compress"); + + if (!OodleLZ_Compress) { + qDebug() << "Failed to resolve function:" << oodleLib.errorString(); + return QByteArray(); + } + + std::byte *outputBuffer = new std::byte[aOutputBufferSize]; + + if (aBuffer.length() > 0 && aBufferSize > 0 && aOutputBufferSize > 0) + OodleLZ_Compress(aformat, reinterpret_cast(aBuffer.data()), aBufferSize, outputBuffer, alevel, 0, 0, 0); + + return QByteArray(reinterpret_cast(outputBuffer), aOutputBufferSize); +} + +QByteArray Compression::pDecompressOodle(QByteArray aBuffer, quint32 aBufferSize, quint32 aOutputBufferSize) { + QLibrary oodleLib("oo2core_8_win64"); + + if (!oodleLib.load()) { + qDebug() << "Failed to load DLL:" << oodleLib.errorString(); + return QByteArray(); + } + + OodleLZ_DecompressFunc OodleLZ_Decompress = + (OodleLZ_DecompressFunc)oodleLib.resolve("OodleLZ_Decompress"); + + if (!OodleLZ_Decompress) { + qDebug() << "Failed to resolve function:" << oodleLib.errorString(); + return QByteArray(); + } + + std::byte *outputBuffer = new std::byte[aOutputBufferSize]; + + if (aBuffer.length() > 0 && aBufferSize > 0 && aOutputBufferSize > 0) + OodleLZ_Decompress(reinterpret_cast(aBuffer.data()), aBufferSize, outputBuffer, aOutputBufferSize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + return QByteArray(reinterpret_cast(outputBuffer), aOutputBufferSize); +} + +QByteArray Compression::DecompressLZO(const QByteArray &aCompressedData) { + lzokay::EResult error; + + // Ensure the input QByteArray is valid + if (aCompressedData.isEmpty()) { + qDebug() << "Input QByteArray is empty."; + return QByteArray(); + } + + // Step 1: Cast QByteArray to uint8_t* + const uint8_t *compressedData = reinterpret_cast(aCompressedData.constData()); + std::size_t compressedSize = static_cast(aCompressedData.size()); + + // Step 2: Allocate a sufficiently large decompression buffer + // Use a large initial estimate if the decompressed size is unknown + std::size_t initialBufferSize = compressedSize * 20; // Arbitrary multiplier for decompression + std::unique_ptr decompressed(new uint8_t[initialBufferSize]); + + // Step 3: Attempt decompression + std::size_t decompressedSize = 0; + error = lzokay::decompress( + compressedData, compressedSize, // Input data and size + decompressed.get(), initialBufferSize, // Output buffer and initial size + decompressedSize // Actual decompressed size + ); + + // Step 4: Handle decompression errors + if (error != lzokay::EResult::Success) { + qDebug() << "Decompression failed with error code:" << static_cast(error); + return QByteArray(); + } + + // Step 5: Return the decompressed data as a QByteArray + return QByteArray(reinterpret_cast(decompressed.get()), decompressedSize); +} + +QByteArray Compression::CompressLZO(const QByteArray &aData) { + lzokay::EResult error; + + // Check input validity + if (aData.isEmpty()) { + qDebug() << "Input QByteArray is empty."; + return QByteArray(); + } + + // Step 1: Cast QByteArray to uint8_t* + const uint8_t *uncompressedData = reinterpret_cast(aData.constData()); + std::size_t uncompressedSize = static_cast(aData.size()); + + // Step 2: Allocate output buffer with sufficient size (compressBound-like estimation) + std::size_t maxCompressedSize = uncompressedSize + uncompressedSize / 16 + 64 + 3; // Safe estimation + std::unique_ptr compressed(new uint8_t[maxCompressedSize]); + + // Step 3: Compress data + std::size_t compressedSize = 0; + error = lzokay::compress( + uncompressedData, uncompressedSize, // Input data + compressed.get(), maxCompressedSize, // Output buffer + compressedSize // Actual compressed size + ); + + // Step 4: Handle compression errors + if (error != lzokay::EResult::Success) { + qDebug() << "Compression failed with error code:" << static_cast(error); + return QByteArray(); + } + + // Step 5: Return compressed data as QByteArray + return QByteArray(reinterpret_cast(compressed.get()), compressedSize); +} + +QByteArray Compression::DecompressLZX(const QByteArray &compressedData, uint32_t windowBits) +{ + if (compressedData.isEmpty()) + return QByteArray(); + + // Calculate sliding window size. + const uint32_t windowSize = 1u << windowBits; + std::vector window(windowSize, 0); + uint32_t windowPos = 0; + + // Use a dynamic output buffer. + QByteArray outArray; + // Reserve an initial capacity. + outArray.reserve(1024); + + // --- Bitstream state --- + const uint8_t *inData = reinterpret_cast(compressedData.constData()); + size_t inSize = compressedData.size(); + size_t inPos = 0; + uint32_t bitBuffer = 0; + int bitsInBuffer = 0; + + // Lambda: Ensure at least 'count' bits are available. + auto ensureBits = [&](int count) -> bool { + while (bitsInBuffer < count) { + if (inPos < inSize) { + bitBuffer = (bitBuffer << 8) | inData[inPos++]; + bitsInBuffer += 8; + } else { + return false; + } + } + return true; + }; + + // Lambda: Get (and remove) 'count' bits from the bit buffer. + auto getBits = [&](int count) -> uint32_t { + if (!ensureBits(count)) + return 0; + uint32_t result = (bitBuffer >> (bitsInBuffer - count)) & ((1u << count) - 1); + bitsInBuffer -= count; + return result; + }; + + // --- Main decompression loop --- + // In this simplified placeholder format: + // - A flag bit of 1 means a literal byte follows (8 bits). + // - A flag bit of 0 means a match follows: first 4 bits for match length (plus base 2) + // then windowBits bits for the match offset (relative to the current sliding window). + while (true) { + // Try to read a flag bit; if not available, we assume the stream is complete. + if (!ensureBits(1)) + break; + uint32_t flag = getBits(1); + if (flag == 1) { + // Literal: next 8 bits form a literal byte. + if (!ensureBits(8)) { + qWarning() << "Unexpected end of input while reading literal."; + break; + } + uint8_t literal = static_cast(getBits(8)); + outArray.append(static_cast(literal)); + // Update the sliding window. + window[windowPos] = literal; + windowPos = (windowPos + 1) % windowSize; + } else { + // Match: first read a 4-bit match length (with a base of 2). + if (!ensureBits(4)) { + qWarning() << "Unexpected end of input while reading match length."; + break; + } + uint32_t matchLength = getBits(4) + 2; + // Then read the match offset (fixed number of bits equals windowBits). + if (!ensureBits(windowBits)) { + qWarning() << "Unexpected end of input while reading match offset."; + break; + } + uint32_t matchOffset = getBits(windowBits); + // Compute the source position in the sliding window. + uint32_t copyPos = (windowPos + windowSize - matchOffset) % windowSize; + // Copy matchLength bytes from the sliding window. + for (uint32_t i = 0; i < matchLength; i++) { + uint8_t byte = window[(copyPos + i) % windowSize]; + outArray.append(static_cast(byte)); + // Update the sliding window with the decompressed byte. + window[windowPos] = byte; + windowPos = (windowPos + 1) % windowSize; + } + } + } + + return outArray; +} diff --git a/libs/compression/compression.h b/libs/compression/compression.h new file mode 100644 index 0000000..83c96c1 --- /dev/null +++ b/libs/compression/compression.h @@ -0,0 +1,83 @@ +#ifndef COMPRESSION_H +#define COMPRESSION_H + +#include "QtZlib/zlib.h" + +#include +#include +#include +#include +#include +#include +#include + +enum OodleFormat { + LZH = 0, + LZHLW = 1, + LZNIB = 2, + FormatNone = 3, + LZB16 = 4, + LZBLW = 5, + LZA = 6, + LZNA = 7, + Kraken = 8, + Mermaid = 9, + BitKnit = 10, + Selkie = 11, + Hydra = 12, + Leviathan = 13 +}; + +enum OodleCompressionLevel { + LevelNone = 0, + SuperFast = 1, + VeryFast = 2, + Fast = 3, + Normal = 4, + Optimal1 = 5, + Optimal2 = 6, + Optimal3 = 7, + Optimal4 = 8, + Optimal5 = 9 +}; + +typedef int (*OodleLZ_CompressFunc)(OodleFormat Format, std::byte *Buffer, long BufferSize, std::byte *OutputBuffer, OodleCompressionLevel Level, uint a, uint b, uint c); +typedef int (*OodleLZ_DecompressFunc)(std::byte* Buffer, long BufferSize, std::byte* OutputBuffer, long OutputBufferSize, uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h, uint i, int ThreadModule); + +class Compression { +public: + static QByteArray DecompressZLIB(const QByteArray &aCompressedData); + static QByteArray CompressZLIB(const QByteArray &aData); + static QByteArray CompressZLIBWithSettings(const QByteArray &aData, + int aCompressionLevel = Z_BEST_COMPRESSION, + int aWindowBits = MAX_WBITS, + int aMemLevel = 8, + int aStrategy = Z_DEFAULT_STRATEGY, + const QByteArray &aDictionary = {}); + + static QByteArray DecompressDeflate(const QByteArray &aCompressedData); + static QByteArray CompressDeflate(const QByteArray &aData); + static QByteArray CompressDeflateWithSettings(const QByteArray &aData, + int aCompressionLevel = Z_BEST_COMPRESSION, + int aWindowBits = MAX_WBITS, + int aMemLevel = 8, + int aStrategy = Z_DEFAULT_STRATEGY, + const QByteArray &aDictionary = {}); + + static QByteArray DecompressOodle(const QByteArray &aCompressedData, quint32 aDecompressedSize); + static QByteArray CompressOodle(const QByteArray &aData); + + static QByteArray DecompressLZO(const QByteArray& aCompressedData); + static QByteArray CompressLZO(const QByteArray& aData); + + static QByteArray DecompressLZX(const QByteArray &compressedData, uint32_t windowBits = 15); + +private: + static quint32 pGetOodleCompressedBounds(quint32 aBufferSize); + static QByteArray pCompressOodle(QByteArray aBuffer, quint32 aBufferSize, quint32 aOutputBufferSize, + OodleFormat aformat, OodleCompressionLevel alevel); + static QByteArray pDecompressOodle(QByteArray aBuffer, quint32 aBufferSize, quint32 aOutputBufferSize); +}; + + +#endif // COMPRESSION_H diff --git a/libs/compression/compression.pro b/libs/compression/compression.pro new file mode 100644 index 0000000..cbec4a7 --- /dev/null +++ b/libs/compression/compression.pro @@ -0,0 +1,25 @@ +QT += core +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + compression.cpp \ + lzokay.cpp \ + +HEADERS += \ + compression.h \ + lzokay.h + +LIBS += \ + -L$$OUT_PWD/../libs/core -lcore \ + -L$$OUT_PWD/../libs/encryption -lencryption + +INCLUDEPATH += \ + $$PWD/../core \ + $$PWD/../encryption + +DEPENDPATH += \ + $$PWD/../core \ + $$PWD/../encryption + +DESTDIR = $$OUT_PWD/../ diff --git a/lzokay.cpp b/libs/compression/lzokay.cpp similarity index 99% rename from lzokay.cpp rename to libs/compression/lzokay.cpp index e2808b9..ad7b39c 100644 --- a/lzokay.cpp +++ b/libs/compression/lzokay.cpp @@ -1,4 +1,4 @@ -#include "lzokay.hpp" +#include "lzokay.h" #include #include #include diff --git a/lzokay.hpp b/libs/compression/lzokay.h similarity index 97% rename from lzokay.hpp rename to libs/compression/lzokay.h index 522edd8..f152727 100644 --- a/lzokay.hpp +++ b/libs/compression/lzokay.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef LZOKAY_H +#define LZOKAY_H + #include #include #include @@ -77,3 +79,5 @@ constexpr std::size_t compress_worst_size(std::size_t s) { } } + +#endif // LZOKAY_H diff --git a/libs/core/core.pro b/libs/core/core.pro new file mode 100644 index 0000000..8cb5e5d --- /dev/null +++ b/libs/core/core.pro @@ -0,0 +1,16 @@ +QT += core widgets +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + logmanager.cpp \ + statusbarmanager.cpp + +HEADERS += \ + enums.h \ + logmanager.h \ + stringutils.h \ + utils.h \ + statusbarmanager.h + +DESTDIR = $$OUT_PWD/../ diff --git a/libs/core/enums.h b/libs/core/enums.h new file mode 100644 index 0000000..92a9739 --- /dev/null +++ b/libs/core/enums.h @@ -0,0 +1,878 @@ +#ifndef ENUMS_H +#define ENUMS_H + +#include + +enum FF_PLATFORM { + FF_PLATFORM_NONE = 0x00, // No platform + FF_PLATFORM_XBOX = 0x01, // Xbox 360 + FF_PLATFORM_PS3 = 0x02, // Playstation 3 + FF_PLATFORM_PC = 0x03, // PC + FF_PLATFORM_WII = 0x04, // WII + FF_PLATFORM_WIIU = 0x05 // WII U +}; + +enum FF_GAME { + FF_GAME_NONE = 0x00, // No game + FF_GAME_COD1 = 0x01, // Call of Duty + FF_GAME_COD2 = 0x02, // Call of Duty 2 + FF_GAME_COD3 = 0x03, // Call of Duty 3 + FF_GAME_COD4 = 0x04, // Modern Warware 1 + FF_GAME_COD5 = 0x05, // World at War + FF_GAME_COD6 = 0x06, // Modern Warfare 2 + FF_GAME_COD7 = 0x07, // Black Ops 1 + FF_GAME_COD8 = 0x08, // Modern Warfare 3 + FF_GAME_COD9 = 0x09, // Black Ops 2 +}; + +enum IWI_VERSION { + IWI_VERSION_COD2 = 0x05, // 05 CoD2 + IWI_VERSION_COD4 = 0x06, // 06 CoD4 + IWI_VERSION_COD5 = 0x06, // 06 CoD5 + IWI_VERSION_CODMW2 = 0x08, // 08 CoDMW2 + IWI_VERSION_CODMW3 = 0x08, // 08 CoDMW3 + IWI_VERSION_CODBO1 = 0x0D, // 13 CoDBO1 + IWI_VERSION_CODBO2 = 0x1B, // 27 CoDBO2 +}; + +enum IWI_FORMAT { + // IWI Format + IWI_FORMAT_ARGB32 = 0x01, // 01 ARGB32 + IWI_FORMAT_RGB24 = 0x02, // 02 RGB24 + IWI_FORMAT_GA16 = 0x03, // 03 GA16 + IWI_FORMAT_A8 = 0x04, // 04 A8 + IWI_FORMAT_DXT1 = 0x0B, // 11 DXT1 + IWI_FORMAT_DXT3 = 0x0C, // 12 DXT3 + IWI_FORMAT_DXT5 = 0x0D // 13 DXT5 +}; + +enum DDS_FLAGS { + DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000 +}; + +enum DDS_PIXELFORMAT_FLAGS { + DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000 +}; + +enum DDS_CAPS_FLAGS { + DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000 +}; + +enum DDS_CAPS2_FLAGS { + DDSCAPS2_CUBEMAP = 0x200, // Indicates a cubemap. + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000 // Indicates that the texture is a volume texture. +}; + +enum LUMP_TYPE +{ + LUMP_MATERIALS = 0x0, + LUMP_LIGHTBYTES = 0x1, + LUMP_LIGHTGRIDENTRIES = 0x2, + LUMP_LIGHTGRIDCOLORS = 0x3, + LUMP_PLANES = 0x4, + LUMP_BRUSHSIDES = 0x5, + LUMP_BRUSHSIDEEDGECOUNTS = 0x6, + LUMP_BRUSHEDGES = 0x7, + LUMP_BRUSHES = 0x8, + LUMP_TRIANGLES = 0x9, + LUMP_DRAWVERTS = 0xA, + LUMP_DRAWINDICES = 0xB, + LUMP_CULLGROUPS = 0xC, + LUMP_CULLGROUPINDICES = 0xD, + LUMP_OBSOLETE_1 = 0xE, + LUMP_OBSOLETE_2 = 0xF, + LUMP_OBSOLETE_3 = 0x10, + LUMP_OBSOLETE_4 = 0x11, + LUMP_OBSOLETE_5 = 0x12, + LUMP_PORTALVERTS = 0x13, + LUMP_OBSOLETE_6 = 0x14, + LUMP_UINDS = 0x15, + LUMP_BRUSHVERTSCOUNTS = 0x16, + LUMP_BRUSHVERTS = 0x17, + LUMP_AABBTREES = 0x18, + LUMP_CELLS = 0x19, + LUMP_PORTALS = 0x1A, + LUMP_NODES = 0x1B, + LUMP_LEAFS = 0x1C, + LUMP_LEAFBRUSHES = 0x1D, + LUMP_LEAFSURFACES = 0x1E, + LUMP_COLLISIONVERTS = 0x1F, + LUMP_COLLISIONTRIS = 0x20, + LUMP_COLLISIONEDGEWALKABLE = 0x21, + LUMP_COLLISIONBORDERS = 0x22, + LUMP_COLLISIONPARTITIONS = 0x23, + LUMP_COLLISIONAABBS = 0x24, + LUMP_MODELS = 0x25, + LUMP_VISIBILITY = 0x26, + LUMP_ENTITIES = 0x27, + LUMP_PATHCONNECTIONS = 0x28, + LUMP_REFLECTION_PROBES = 0x29, + LUMP_VERTEX_LAYER_DATA = 0x2A, + LUMP_PRIMARY_LIGHTS = 0x2B, + LUMP_LIGHTGRIDHEADER = 0x2C, + LUMP_LIGHTGRIDROWS = 0x2D, + LUMP_OBSOLETE_10 = 0x2E, + + //Obsolete in BO1///////////////// + LUMP_UNLAYERED_TRIANGLES = 0x2F, + LUMP_UNLAYERED_DRAWVERTS = 0x30, + LUMP_UNLAYERED_DRAWINDICES = 0x31, + LUMP_UNLAYERED_CULLGROUPS = 0x32, + LUMP_UNLAYERED_AABBTREES = 0x33, + ////////////////////////////////// + + LUMP_WATERHEADER = 0x34, + LUMP_WATERCELLS = 0x35, + LUMP_WATERCELLDATA = 0x36, + LUMP_BURNABLEHEADER = 0x37, + LUMP_BURNABLECELLS = 0x38, + LUMP_BURNABLECELLDATA = 0x39, + LUMP_SIMPLELIGHTMAPBYTES = 0x3A, + LUMP_LODCHAINS = 0x3B, + LUMP_LODINFOS = 0x3C, + LUMP_LODSURFACES = 0x3D, + LUMP_LIGHTREGIONS = 0x3E, + LUMP_LIGHTREGION_HULLS = 0x3F, + LUMP_LIGHTREGION_AXES = 0x40, + LUMP_WIILIGHTGRID = 0x41, + LUMP_LIGHTGRID2D_LIGHTS = 0x42, + LUMP_LIGHTGRID2D_INDICES = 0x43, + LUMP_LIGHTGRID2D_POINTS = 0x44, + LUMP_LIGHTGRID2D_CELLS = 0x45, + LUMP_LIGHT_CORONAS = 0x46, + + //BO Specific///////////////////////// + LUMP_SHADOWMAP_VOLUMES = 0x47, + LUMP_SHADOWMAP_VOLUME_PLANES = 0x48, + LUMP_EXPOSURE_VOLUMES = 0x49, + LUMP_EXPOSURE_VOLUME_PLANES = 0x4A, + LUMP_OCCLUDERS = 0x4B, + LUMP_OUTDOORBOUNDS = 0x4C, + LUMP_HERO_ONLY_LIGHTS = 0x4D, + ////////////////////////////////////// + + INFO_LUMP_TYPE_COUNT +}; + +enum BSPVERSION_TYPE +{ + BSPVERSION_COD_WAW = 31, + BSPVERSION_COD_BO = 45 +}; + +enum FF_COMPANY { + COMPANY_NONE = 0x00, + COMPANY_INFINITY_WARD = 0x01, + COMPANY_TREYARCH = 0x02, + COMPANY_SLEDGEHAMMER = 0x03, + COMPANY_NEVERSOFT = 0x04 +}; + +enum FF_FILETYPE { + FILETYPE_NONE = 0x00, + FILETYPE_FAST_FILE = 0x01 +}; + +enum FF_SIGNAGE { + SIGNAGE_NONE = 0x00, + SIGNAGE_SIGNED = 0x01, + SIGNAGE_UNSIGNED = 0x02 +}; + +enum IMAGE_COMPRESSION { + COMPRESSION_NONE = 0x00, + COMPRESSION_DXT1 = 0x01, + COMPRESSION_DXT3 = 0x02, + COMPRESSION_DXT5 = 0x03 +}; +enum MENU_ITEM_TYPE { + ITEM_TYPE_TEXT = 0, // simple text + ITEM_TYPE_BUTTON = 1, // button, basically text with a border + ITEM_TYPE_RADIOBUTTON = 2, // toggle button, may be grouped + ITEM_TYPE_CHECKBOX = 3, // check box + ITEM_TYPE_EDITFIELD = 4, // editable text, associated with a dvar + ITEM_TYPE_COMBO = 5, // drop down list + ITEM_TYPE_LISTBOX = 6, // scrollable list + ITEM_TYPE_MODEL = 7, // model + ITEM_TYPE_OWNERDRAW = 8, // owner draw, name specs what it is + ITEM_TYPE_NUMERICFIELD = 9, // editable text, associated with a dvar + ITEM_TYPE_SLIDER = 10, // mouse speed, volume, etc. + ITEM_TYPE_YESNO = 11, // yes no dvar setting + ITEM_TYPE_MULTI = 12, // multiple list setting, enumerated + ITEM_TYPE_DVARENUM = 13, // multiple list setting, enumerated from a dvar + ITEM_TYPE_BIND = 14, // bind + ITEM_TYPE_MENUMODEL = 15, // special menu model + ITEM_TYPE_VALIDFILEFIELD = 16, // text must be valid for use in a dos filename + ITEM_TYPE_DECIMALFIELD = 17, // editable text, associated with a dvar, which allows decimal input + ITEM_TYPE_UPREDITFIELD = 18, // editable text, associated with a dvar + ITEM_TYPE_GAME_MESSAGE_WINDOW = 19, // game message window + ITEM_TYPE_SCALEFORM = 20, // Flash movie for Scaleform GFx + ITEM_TYPE_BIND2 = 21, // bind2 +}; + +enum MENU_ITEM_H_ALIGN { + ITEM_ALIGN_LEFT = 0, // aligns left of text to left of containing rectangle + ITEM_ALIGN_CENTER = 1, // aligns center of text to center of containing rectangle + ITEM_ALIGN_RIGHT = 2, // aligns right of text to right of containing rectangle + ITEM_ALIGN_X_MASK = 3, +}; + +enum MENU_ITEM_V_ALIGN { + ITEM_ALIGN_LEGACY = 0, // aligns bottom of text to top of containing rectangle + ITEM_ALIGN_TOP = 4, // aligns top of text to top of containing rectangle + ITEM_ALIGN_MIDDLE = 8, // aligns middle of text to middle of containing rectangle + ITEM_ALIGN_BOTTOM = 12, // aligns bottom of text to bottom of containing rectangle + ITEM_ALIGN_Y_MASK = 12, +}; + +enum MENU_ITEM_ALIGN { + ITEM_ALIGN_LEGACY_LEFT = 0, + ITEM_ALIGN_LEGACY_CENTER = 1, + ITEM_ALIGN_LEGACY_RIGHT = 2, + ITEM_ALIGN_TOP_LEFT = 4, + ITEM_ALIGN_TOP_CENTER = 5, + ITEM_ALIGN_TOP_RIGHT = 6, + ITEM_ALIGN_MIDDLE_LEFT = 8, + ITEM_ALIGN_MIDDLE_CENTER = 9, + ITEM_ALIGN_MIDDLE_RIGHT = 10, + ITEM_ALIGN_BOTTOM_LEFT = 12, + ITEM_ALIGN_BOTTOM_CENTER = 13, + ITEM_ALIGN_BOTTOM_RIGHT = 14 +}; + +enum MENU_ITEM_TEXTSTYLE { + ITEM_TEXTSTYLE_NORMAL = 0, // normal text + ITEM_TEXTSTYLE_BLINK = 1, // fast blinking + ITEM_TEXTSTYLE_SHADOWED = 3, // drop shadow ( need a color for this ) + ITEM_TEXTSTYLE_SHADOWEDMORE = 6, // drop shadow ( need a color for this ) + ITEM_TEXTSTYLE_MONOSPACE = 128 +}; + +enum MENU_WINDOW_BORDER { + WINDOW_BORDER_NONE = 0, // no border + WINDOW_BORDER_FULL = 1, // full border based on border color ( single pixel ) + WINDOW_BORDER_HORZ = 2, // horizontal borders only + WINDOW_BORDER_VERT = 3, // vertical borders only + WINDOW_BORDER_KCGRADIENT = 4, // horizontal border using the gradient bars + WINDOW_BORDER_RAISED = 5, // darken the bottom and right sides of the border + WINDOW_BORDER_SUNKEN = 6 // darken the top and left sides of the border +}; + +enum MENU_WINDOW_STYLE { + WINDOW_STYLE_EMPTY = 0, // no background + WINDOW_STYLE_FILLED = 1, // filled with background color + WINDOW_STYLE_GRADIENT = 2, // gradient bar based on background color + WINDOW_STYLE_SHADER = 3, // shader based on background color + WINDOW_STYLE_TEAMCOLOR = 4, // team color + WINDOW_STYLE_DVAR_SHADER = 5, // draws the shader specified by the dvar + WINDOW_STYLE_LOADBAR = 6, // shader based on background color +}; + +enum MENU_MODE { + MODE_BOTTOMUP_ALIGN_TOP = 0, // text appears on bottom of list and moves up to specified Y coordinate as old text fades out + MODE_BOTTOMUP_ALIGN_BOTTOM = 1, // text appears on bottom of list and moves away from specified Y coordinate as new text pushes it up + MODE_TOPDOWN_ALIGN_TOP = 2, // text appears on top of list and moves away from specified Y coordinate as new text pushes it down + MODE_TOPDOWN_ALIGN_BOTTOM = 3 // text appears on top of list and moves down to specified Y coordinate as old text fades out +}; + +enum MENU_BOOL { + MENU_TRUE = 1, + MENU_FALSE = 0 +}; + +enum MENU_ORIENTATION { + HUD_VERTICAL = 0x00, + HUD_HORIZONTAL = 0x01 +}; + +enum MENU_RANGETYPE { + RANGETYPE_ABSOLUTE = 0, + RANGETYPE_RELATIVE = 1 +}; + +// list box element types +enum MENU_LIST_BOX { + LISTBOX_TEXT = 0x00, + LISTBOX_IMAGE = 0x01 +}; + +// list feeders +enum MENU_FEEDERS { + FEEDER_HEADS = 0x00, // model heads + FEEDER_MAPS = 0x01, // text maps based on game type + FEEDER_SERVERS = 0x02, // servers + FEEDER_CLAN_MEMBERS = 0x03, // clan names + FEEDER_ALLMAPS = 0x04, // all maps available, in graphic format + FEEDER_REDTEAM_LIST = 0x05, // red team members + FEEDER_BLUETEAM_LIST = 0x06, // blue team members + FEEDER_PLAYER_LIST = 0x07, // players + FEEDER_TEAM_LIST = 0x08, // team members for team voting + FEEDER_MODS = 0x09, // team members for team voting + FEEDER_DEMOS = 0x0a, // team members for team voting + FEEDER_SCOREBOARD = 0x0b, // team members for team voting + FEEDER_Q3HEADS = 0x0c, // model heads + FEEDER_SERVERSTATUS = 0x0d, // server status + FEEDER_FINDPLAYER = 0x0e, // find player + FEEDER_CINEMATICS = 0x0f, // cinematics + FEEDER_SAVEGAMES = 0x10, // savegames + FEEDER_PICKSPAWN = 0x11, // pickspawn + FEEDER_LOBBY_MEMBERS = 0x12, // list of players in your party + FEEDER_LOBBY_MEMBERS_TALK = 0x13, // icon for whether they are speaking or not + FEEDER_MUTELIST = 0x14, // list of musted players + FEEDER_PLAYERSTALKING = 0x15, // list of players who are currently talking + FEEDER_SPLITSCREENPLAYERS = 0x16, // list of all players who are playing splitscreen + FEEDER_LOBBY_MEMBERS_READY = 0x17, // icon for whether they are ready or not + FEEDER_PLAYER_PROFILES = 0x18, // player profiles + FEEDER_PARTY_MEMBERS = 0x19, // list of players in your party + FEEDER_PARTY_MEMBERS_TALK = 0x1a, // icon for whether they are speaking or not + FEEDER_PARTY_MEMBERS_READY = 0x1b, // icon for whether they are ready or not + FEEDER_PLAYLISTS = 0x1c, // list of all playlists + FEEDER_GAMEMODES = 0x1d, // list of all game type modes, including any player custom modes + FEEDER_CATEGORIES = 0x1e, // list of all categories + FEEDER_LEADERBOARD = 0x1f, // list of rows for a leaderboard + FEEDER_MYTEAM_MEMBERS = 0x20, // list of marine team members + FEEDER_MYTEAM_MEMBERS_TALK = 0x21, // icon for whether they are speaking + FEEDER_ENEMY_MEMBERS = 0x22, // list of opfor team members + FEEDER_ENEMY_MEMBERS_TALK = 0x23, // icon for whether they are speaking + FEEDER_LOBBY_MEMBERS_STAT = 0x24, // last round stats for lobby members + FEEDER_MYTEAM_MEMBERS_STAT = 0x25, // last round stats for marine team members + FEEDER_ENEMY_MEMBERS_STAT = 0x26, // last round stats for opfor team members + FEEDER_ONLINEFRIENDS = 0x27, // list of your online friends + FEEDER_LOBBY_MEMBERS_RANK = 0x28, // rank icon + FEEDER_PARTY_MEMBERS_RANK = 0x29, // rank icon + FEEDER_ENEMY_MEMBERS_RANK = 0x2a, // rank icon + FEEDER_MYTEAM_MEMBERS_RANK = 0x2b, // rank icon + FEEDER_TESTMAPS = 0x2c, // department test maps + FEEDER_SYSTEMLINK_LOBBY_MEMBERS = 0x2d, // list of players in a system link lobby + FEEDER_LOBBY_MEMBERS_CONTROLLER = 0x2e, // icon for controller quadrant for splitscreen + FEEDER_PARTY_MEMBERS_CONTROLLER = 0x2f, // icon for controller quadrant for splitscreen + FEEDER_MYTEAM_MEMBERS_SQUAD = 0x30, // squad icon + FEEDER_ENEMY_MEMBERS_SQUAD = 0x31, // squad icon + FEEDER_INGAME_SQUAD_MEMBERS = 0x32, // Squad members displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_MEMBERS_FULL = 0x33, // Squad members' name, rank, talk, leader displayed in PC MP 'Pause' menu + FEEDER_INGAME_SQUAD_NAMES = 0x34, // Squad names displayed in MP 'Join Squad' menu + FEEDER_INGAME_SQUAD_NAMES2 = 0x35, // Squad names displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_NAMES_FULL = 0x36, // Squad names, lock, invite displayed in PC MP 'Pause' menu + FEEDER_CUSTOM_GAMETYPES = 0x37, // list of all the gametypes for custom matches + FEEDER_INGAME_SQUAD_MEMBER_RANK = 0x38, // Squad members' rank displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_MEMBER_TALK = 0x39, // Squad members' talk icon displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_LOCKED = 0x3a, // Squad lock icon displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_MEMBER_INVITED = 0x3b, // Squad invite icon displayed in MP 'Pause' menu + FEEDER_INGAME_SQUAD_INVITE = 0x3c, // Squad invite icon displayed in MP 'Join Squad' menu + FEEDER_INGAME_SQUAD_LEADER = 0x3d, // Squad leader icon displayled in MP 'Pause' menu + FEEDER_FRIENDS = 0x3e, // list of your friends + FEEDER_PENDINGFRIENDS = 0x3f, // list of your pending friends + FEEDER_INVITES = 0x40, // list of the game invites from your friends +}; + +// display flags +enum MENU_DISPLAY_FLAG { + CG_SHOW_BLUE_TEAM_HAS_REDFLAG = 0x00000001, + CG_SHOW_RED_TEAM_HAS_BLUEFLAG = 0x00000002, + CG_SHOW_ANYTEAMGAME = 0x00000004, + CG_SHOW_CTF = 0x00000020, + CG_SHOW_OBELISK = 0x00000040, + CG_SHOW_HEALTHCRITICAL = 0x00000080, + CG_SHOW_SINGLEPLAYER = 0x00000100, + CG_SHOW_TOURNAMENT = 0x00000200, + CG_SHOW_DURINGINCOMINGVOICE = 0x00000400, + CG_SHOW_IF_PLAYER_HAS_FLAG = 0x00000800, + CG_SHOW_LANPLAYONLY = 0x00001000, + CG_SHOW_MINED = 0x00002000, + CG_SHOW_HEALTHOK = 0x00004000, + CG_SHOW_TEAMINFO = 0x00008000, + CG_SHOW_NOTEAMINFO = 0x00010000, + CG_SHOW_OTHERTEAMHASFLAG = 0x00020000, + CG_SHOW_YOURTEAMHASENEMYFLAG = 0x00040000, + CG_SHOW_ANYNONTEAMGAME = 0x00080000, + CG_SHOW_TEXTASINT = 0x00200000, + CG_SHOW_HIGHLIGHTED = 0x00100000, + CG_SHOW_NOT_V_CLEAR = 0x02000000, + CG_SHOW_2DONLY = 0x10000000 +}; + +enum MENU_UI_FLAG{ + UI_SHOW_LEADER = 0x00000001, + UI_SHOW_NOTLEADER = 0x00000002, + UI_SHOW_FAVORITESERVERS = 0x00000004, + UI_SHOW_ANYNONTEAMGAME = 0x00000008, + UI_SHOW_ANYTEAMGAME = 0x00000010, + UI_SHOW_NEWHIGHSCORE = 0x00000020, + UI_SHOW_DEMOAVAILABLE = 0x00000040, + UI_SHOW_NEWBESTTIME = 0x00000080, + UI_SHOW_FFA = 0x00000100, + UI_SHOW_NOTFFA = 0x00000200, + UI_SHOW_NETANYNONTEAMGAME = 0x00000400, + UI_SHOW_NETANYTEAMGAME = 0x00000800, + UI_SHOW_NOTFAVORITESERVERS = 0x00001000 +}; + +// font types +enum MENU_FONT_TYPE{ + UI_FONT_DEFAULT = 0, // auto-chose betwen big/reg/small + UI_FONT_NORMAL = 1, + UI_FONT_BIG = 2, + UI_FONT_SMALL = 3, + UI_FONT_BOLD = 4, + UI_FONT_CONSOLE = 5, + UI_FONT_OBJECTIVE = 6, + UI_FONT_MAX = 6, +}; + +// owner draw types +// ideally these should be done outside of this file but +// this makes it much easier for the macro expansion to +// convert them for the designers ( from the .menu files ) +enum MENU_OWNER_DRAW_TYPE { + CG_OWNERDRAW_BASE = 1, + CG_PLAYER_AMMO_VALUE = 5, + CG_PLAYER_AMMO_BACKDROP = 6, + CG_PLAYER_HEAT_VALUE = 7, + CG_PLAYER_STANCE = 20, + CG_SPECTATORS = 60, + CG_HOLD_BREATH_HINT = 71, + CG_CURSORHINT = 72, + CG_PLAYER_POWERUP = 73, + CG_PLAYER_HOLDABLE = 74, + CG_PLAYER_INVENTORY = 75, + CG_CURSORHINT_STATUS = 78, // like 'health' bar when pointing at a func_explosive + CG_PLAYER_BAR_HEALTH = 79, + CG_MANTLE_HINT = 80, + CG_PLAYER_WEAPON_NAME = 81, + CG_PLAYER_WEAPON_NAME_BACK = 82, + CG_CENTER_MESSAGE = 90, // for things like "You were killed by ..." + CG_TANK_BODY_DIR = 95, + CG_TANK_BARREL_DIR = 96, + CG_DEADQUOTE = 97, + CG_PLAYER_BAR_HEALTH_BACK = 98, + CG_MISSION_OBJECTIVE_HEADER = 99, + CG_MISSION_OBJECTIVE_LIST = 100, + CG_MISSION_OBJECTIVE_BACKDROP = 101, + CG_PAUSED_MENU_LINE = 102, + CG_OFFHAND_WEAPON_ICON_FRAG = 103, + CG_OFFHAND_WEAPON_ICON_SMOKEFLASH = 104, + CG_OFFHAND_WEAPON_AMMO_FRAG = 105, + CG_OFFHAND_WEAPON_AMMO_SMOKEFLASH = 106, + CG_OFFHAND_WEAPON_NAME_FRAG = 107, + CG_OFFHAND_WEAPON_NAME_SMOKEFLASH = 108, + CG_OFFHAND_WEAPON_SELECT_FRAG = 109, + CG_OFFHAND_WEAPON_SELECT_SMOKEFLASH = 110, + CG_SAVING = 111, + CG_PLAYER_LOW_HEALTH_OVERLAY = 112, + CG_INVALID_CMD_HINT = 113, + CG_PLAYER_SPRINT_METER = 114, + CG_PLAYER_SPRINT_BACK = 115, + CG_PLAYER_WEAPON_BACKGROUND = 116, + CG_PLAYER_WEAPON_AMMO_CLIP_GRAPHIC = 117, + CG_PLAYER_WEAPON_PRIMARY_ICON = 118, + CG_PLAYER_WEAPON_AMMO_STOCK = 119, + CG_PLAYER_WEAPON_LOW_AMMO_WARNING = 120, + CG_BATTLE_COMPASS_MARKERS = 122, + CG_BATTLE_FULLMAP_MARKERS = 123, + CG_SUCCESSFUL_CMD_HINT = 130, + CG_WAR_TEXT = 135, + CG_PLAYER_COMPASS_TICKERTAPE = 145, + CG_PLAYER_COMPASS_TICKERTAPE_NO_OBJ = 146, + CG_PLAYER_COMPASS_DOGS = 147, + CG_PLAYER_COMPASS_ARTILLERY_ICON = 148, + CG_PLAYER_COMPASS_SQUAD_OBJECTIVE = 149, + CG_PLAYER_COMPASS_PLAYER = 150, + CG_PLAYER_COMPASS_BACK = 151, + CG_PLAYER_COMPASS_POINTERS = 152, + CG_PLAYER_COMPASS_ACTORS = 153, + CG_PLAYER_COMPASS_TANKS = 154, + CG_PLAYER_COMPASS_HELICOPTERS = 155, + CG_PLAYER_COMPASS_PLANES = 156, + CG_PLAYER_COMPASS_AUTOMOBILES = 157, + CG_PLAYER_COMPASS_FRIENDS = 158, + CG_PLAYER_COMPASS_MAP = 159, + CG_PLAYER_COMPASS_NORTHCOORD = 160, + CG_PLAYER_COMPASS_EASTCOORD = 161, + CG_PLAYER_COMPASS_NCOORD_SCROLL = 162, + CG_PLAYER_COMPASS_ECOORD_SCROLL = 163, + CG_PLAYER_COMPASS_GOALDISTANCE = 164, + CG_PLAYER_ACTIONSLOT_DPAD = 165, + CG_PLAYER_ACTIONSLOT_1 = 166, + CG_PLAYER_ACTIONSLOT_2 = 167, + CG_PLAYER_ACTIONSLOT_3 = 168, + CG_PLAYER_ACTIONSLOT_4 = 169, + CG_PLAYER_COMPASS_ENEMIES = 170, + CG_PLAYER_FULLMAP_DOGS = 176, + CG_PLAYER_FULLMAP_VEHICLES = 177, + CG_PLAYER_FULLMAP_ARTILLERY_ICON = 178, + CG_PLAYER_FULLMAP_SQUAD_OBJECTIVE = 179, + CG_PLAYER_FULLMAP_BACK = 180, + CG_PLAYER_FULLMAP_MAP = 181, + CG_PLAYER_FULLMAP_POINTERS = 182, + CG_PLAYER_FULLMAP_PLAYER = 183, + CG_PLAYER_FULLMAP_ACTORS = 184, + CG_PLAYER_FULLMAP_FRIENDS = 185, + CG_PLAYER_FULLMAP_LOCATION_SELECTOR = 186, + CG_PLAYER_FULLMAP_BORDER = 187, + CG_PLAYER_FULLMAP_ENEMIES = 188, + CG_PLAYER_COMPASS = 189, + CG_VEHICLE_RETICLE = 190, + CG_HUD_TARGETS_VEHICLE = 191, + CG_HUD_TARGETS_JAVELIN = 192, + CG_TALKER1 = 193, + CG_TALKER2 = 194, + CG_TALKER3 = 195, + CG_TALKER4 = 196, + CG_FRIENDLYARROWS = 197, + CG_FRIENDLYNAMES = 198, + UI_OWNERDRAW_BASE = 200, + UI_HANDICAP = 200, + UI_EFFECTS = 201, + UI_PLAYERMODEL = 202, + UI_GAMETYPE = 205, + UI_SKILL = 207, + UI_NETSOURCE = 220, + UI_NETFILTER = 222, + UI_VOTE_KICK = 238, + UI_NETGAMETYPE = 245, + UI_SERVERREFRESHDATE = 247, + UI_SERVERMOTD = 248, + UI_GLINFO = 249, + UI_KEYBINDSTATUS = 250, + UI_JOINGAMETYPE = 253, + UI_MAPPREVIEW = 254, + UI_MENUMODEL = 257, + UI_SAVEGAME_SHOT = 258, + UI_SAVEGAMENAME = 262, + UI_SAVEGAMEINFO = 263, + UI_LOADPROFILING = 264, + UI_RECORDLEVEL = 265, + UI_AMITALKING = 266, + UI_TALKER1 = 267, + UI_TALKER2 = 268, + UI_TALKER3 = 269, + UI_TALKER4 = 270, + UI_PARTYSTATUS = 271, + UI_LOGGEDINUSER = 272, + UI_RESERVEDSLOTS = 273, + UI_PLAYLISTNAME = 274, + UI_PLAYLISTDESCRIPTION = 275, + UI_USERNAME = 276, + UI_CINEMATIC = 277, + UI_TOTALONLINE = 278, + UI_CATEGORYNAME = 279, + UI_CATEGORYDESCRIPTION = 280, + UI_PLAYLISTICON = 281, + UI_CATEGORYICON = 282, + UI_GAMETYPE_MAPNAME = 283, + CG_HUD_WAR_MOMENTUM_PROGRESS = 284, + CG_HUD_WAR_MOMENTUM_MULTIPLIER = 285, + CG_HUD_WAR_MOMENTUM_MULTIPLIER_DETAIL = 286, + CG_HUD_WAR_MOMENTUM_MULTIPLIER_BLITZKRIEG = 287, + CG_COMPETITIVE_MODE_SCORES = 288, + UI_LOAD_STATUS_SCREEN = 289, + UI_LEADERBOARD_GAMEMODE = 290, + CG_PLAYER_ACTIONSLOT_BACK_1 = 290, + CG_PLAYER_ACTIONSLOT_BACK_2 = 291, + CG_PLAYER_ACTIONSLOT_BACK_3 = 292, + CG_PLAYER_ACTIONSLOT_BACK_4 = 293, + CG_PLAYER_ACTIONSLOT_ARROW_1 = 294, + CG_PLAYER_ACTIONSLOT_ARROW_2 = 295, + CG_PLAYER_ACTIONSLOT_ARROW_3 = 296, + CG_PLAYER_ACTIONSLOT_ARROW_4 = 297, + UI_DIFFICULTY_INFO = 298, + UI_DIFFICULTY_ICON = 299, + UI_LOBBY_CHAT = 300 +}; + +// Edge relative placement values for rect->h_align and rect->v_align +enum MENU_H_ALIGNMENT { + HORIZONTAL_ALIGN_SUBLEFT = 0, // left edge of a 4:3 screen (safe area not included) + HORIZONTAL_ALIGN_LEFT = 1, // left viewable (safe area) edge + HORIZONTAL_ALIGN_CENTER = 2, // center of the screen (reticle) + HORIZONTAL_ALIGN_RIGHT = 3, // right viewable (safe area) edge + HORIZONTAL_ALIGN_FULLSCREEN = 4, // disregards safe area + HORIZONTAL_ALIGN_NOSCALE = 5, // uses exact parameters - neither adjusts for safe area nor scales for screen size + HORIZONTAL_ALIGN_TO640 = 6, // scales a real-screen resolution x down into the 0 - 640 range + HORIZONTAL_ALIGN_CENTER_SAFEAREA = 7, // center of the safearea + HORIZONTAL_ALIGN_MAX = HORIZONTAL_ALIGN_CENTER_SAFEAREA, + HORIZONTAL_ALIGN_DEFAULT = HORIZONTAL_ALIGN_SUBLEFT +}; + +enum MENU_V_ALIGNMENT { + VERTICAL_ALIGN_SUBTOP = 0, // top edge of the 4:3 screen (safe area not included) + VERTICAL_ALIGN_TOP = 1, // top viewable (safe area) edge + VERTICAL_ALIGN_CENTER = 2, // center of the screen (reticle) + VERTICAL_ALIGN_BOTTOM = 3, // bottom viewable (safe area) edge + VERTICAL_ALIGN_FULLSCREEN = 4, // disregards safe area + VERTICAL_ALIGN_NOSCALE = 5, // uses exact parameters - neither adjusts for safe area nor scales for screen size + VERTICAL_ALIGN_TO480 = 6, // scales a real-screen resolution y down into the 0 - 480 range + VERTICAL_ALIGN_CENTER_SAFEAREA = 7, // center of the save area + VERTICAL_ALIGN_MAX = VERTICAL_ALIGN_CENTER_SAFEAREA, + VERTICAL_ALIGN_DEFAULT = VERTICAL_ALIGN_SUBTOP +}; + +enum MENU_BUTTON { + BUTTON_A = 1, + BUTTON_B = 2, + BUTTON_X = 3, + BUTTON_Y = 4, + BUTTON_LSHLDR = 5, + BUTTON_RSHLDR = 6, + BUTTON_START = 14, + BUTTON_BACK = 15, + BUTTON_LSTICK = 16, + BUTTON_RSTICK = 17, + BUTTON_LTRIG = 18, + BUTTON_RTRIG = 19, + DPAD_UP = 20, + DPAD_DOWN = 21, + DPAD_LEFT = 22, + DPAD_RIGHT = 23, + APAD_UP = 28, + APAD_DOWN = 29, + APAD_LEFT = 30, + APAD_RIGHT = 31 +}; + +enum ASSET_TYPE { + ASSET_UNKNOWN = 0, + ASSET_ANIMATION = 4, // x_anim PARTIALLY VERIFIED + ASSET_MODEL = 5, // xmodel PARTIALLY VERIFIED + ASSET_MATERIAL = 6, // material VERIFIED + ASSET_BIK_FILE = 7, // .bik file PARTIALLY VERIFIED + ASSET_SOUND = 9, // loaded_sound VERIFIED + ASSET_COLLISION_MAP = 12, // collision_map PARTIALLY VERIFIED + ASSET_SHADER = 13, // shader PARTIALLY VERIFIED + ASSET_D3DBSP_DUMP = 17, // d3dbsp dump VERIFIED + ASSET_FONT = 20, // font PARTIALLY VERIFIED + ASSET_MENU = 21, // menu_file VERIFIED + ASSET_LOCAL_STRING = 23, // localized string VERIFIED + ASSET_WEAPON = 24, // weapon VERIFIED + ASSET_EFFECT = 26, // fx VERIFIED + ASSET_RAW_FILE = 32, // raw_file VERIFIED + ASSET_STRING_TABLE = 33 // string_table PARTIALLY VERIFIED +}; + +enum SHADER_TYPE { + SHADER_NONE = 0x00, + SHADER_PIXEL = 0x01, + SHADER_VERTEX = 0x02 +}; + +enum SHADER_OPCODE { + OPCODE_Nop, + OPCODE_Mov, + OPCODE_Add, + OPCODE_Sub, + OPCODE_Mad, + OPCODE_Mul, + OPCODE_Rcp, + OPCODE_Rsq, + OPCODE_Dp3, + OPCODE_Dp4, + OPCODE_Min, + OPCODE_Max, + OPCODE_Slt, + OPCODE_Sge, + OPCODE_Exp, + OPCODE_Log, + OPCODE_Lit, + OPCODE_Dst, + OPCODE_Lrp, + OPCODE_Frc, + OPCODE_M4x4, + OPCODE_M4x3, + OPCODE_M3x4, + OPCODE_M3x3, + OPCODE_M3x2, + OPCODE_Call, + OPCODE_CallNZ, + OPCODE_Loop, + OPCODE_Ret, + OPCODE_EndLoop, + OPCODE_Label, + OPCODE_Dcl, + OPCODE_Pow, + OPCODE_Crs, + OPCODE_Sgn, + OPCODE_Abs, + OPCODE_Nrm, + OPCODE_SinCos, + OPCODE_Rep, + OPCODE_EndRep, + OPCODE_If, + OPCODE_IfC, + OPCODE_Else, + OPCODE_Endif, + OPCODE_Break, + OPCODE_BreakC, + OPCODE_MovA, + OPCODE_DefB, + OPCODE_DefI, + OPCODE_TexCoord = 64, + OPCODE_TexKill, + OPCODE_Tex, + OPCODE_TexBem, + OPCODE_TexBeml, + OPCODE_TexReg2AR, + OPCODE_TexReg2GB, + OPCODE_TeXM3x2Pad, + OPCODE_TexM3x2Tex, + OPCODE_TeXM3x3Pad, + OPCODE_TexM3x3Tex, + OPCODE_TexM3x3Diff, + OPCODE_TexM3x3Spec, + OPCODE_TexM3x3VSpec, + OPCODE_ExpP, + OPCODE_LogP, + OPCODE_Cnd, + OPCODE_Def, + OPCODE_TexReg2RGB, + OPCODE_TexDP3Tex, + OPCODE_TexM3x2Depth, + OPCODE_TexDP3, + OPCODE_TexM3x3, + OPCODE_TexDepth, + OPCODE_Cmp, + OPCODE_Bem, + OPCODE_DP2Add, + OPCODE_DSX, + OPCODE_DSY, + OPCODE_TexLDD, + OPCODE_SetP, + OPCODE_TexLDL, + OPCODE_Breakp, + OPCODE_Phase = 0xFFFD, + OPCODE_Comment = 0xFFFE, + OPCODE_End = 0xFFFF +}; + +#define SECTION_TYPE_INFO 1 +#define SECTION_TYPE_DATA 2 + +#define IPAK_SECTION_ENTRY 1 +#define IPAK_SECTION_DATA 2 +#define IPAK_SECTION_METADATA 3 + +#define FORMAT_DXT1 0 +#define FORMAT_DXT3 1 +#define FORMAT_DXT5 2 +#define FORMAT_A8R8G8B8 3 + +// Change this depending on the platform +// PC +#define DEVMAP_LEVEL_FIRST "devmap intro_pac" +#define DEVMAP "devmap" +// Not PC +// #define DEVMAP_LEVEL_FIRST "map intro_pac" +// #define DEVMAP "map" + +// Remove this to restore full frontend instead of limited EPD frontend +#define COOP_EPD 0 + +// LDS - This enables a German SKU with Nazi Zombies enabled *SHOULD BE SET TO 0 IF NOT APPROVED* +#define GERMAN_ZOMBIE_BUILD 0 + +#define DEVMAP_LEVEL_TRAINING "devmap training" +#define LEVEL_FIRST "intro_pac" +#define LEVEL_TRAINING "training" +#define FIRST_PLAYABLE_CAMPAIGN_LEVEL "mak" +#define FIRST_PLAYABLE_ZOMBIE_LEVEL "nazi_zombie_prototype" + +// Size define for the hud compass +// These are used for both the dynamic & non-dynamic compass drawing +// If these are changed, the cgame should be recompiled +#define COMPASS_SIZE 160 +#define MINIMAP_X 11.5 +#define MINIMAP_Y 5 +#define MINIMAP_W 89.5 +#define MINIMAP_H 89.5 + +#define COMPASS_SIZE_MP 125 +#define MINIMAP_X_MP 0 +#define MINIMAP_Y_MP 12 +#define MINIMAP_W_MP 102 +#define MINIMAP_H_MP 102 + +#define FULLSCREEN 0 0 640 480 +#define FULLSCREEN_WIDE -107 0 854 480 + +// PC +#define ORIGIN_TITLE 30 34 +// Not PC +// #define ORIGIN_TITLE 0 0 + +#define ORIGIN_TITLE_SS 104 120 + +#define FONTSCALE_SMALL 0.3095 //0.3750 // <-- COD4 // COD5 --> 0.30952//0.35897//0.24138 //14 pt //0.2900 //0.2750 // 18 +#define FONTSCALE_LOBBY 0.26 // <--Slate // 0.3010 <-- Slate Compressed // 0.3750 // <-- COD4 CONDUIT ITC small +#define FONTSCALE_NORMAL 0.3810 //0.35897//0.4583 +#define FONTSCALE_BOLD 0.5476 //0.4583 +#define FONTSCALE_BIG 0.5476 //0.5833 +#define FONTSCALE_EXTRABIG 1 //1.0000 + +// new settings +#define TEXTSIZE_SMALL FONTSCALE_SMALL +#define TEXTSIZE_SMALL_SS (FONTSCALE_SMALL*2) +#define TEXTSIZE_DEFAULT FONTSCALE_NORMAL +#define TEXTSIZE_DEFAULT_SS (FONTSCALE_NORMAL*2) +#define TEXTSIZE_TITLE FONTSCALE_BIG +#define TEXTSIZE_TITLE_SS 1 + +#define TEXTSIZE_BOLD TEXTSIZE_DEFAULT +#define TEXTSIZE_BIG TEXTSIZE_TITLE + +//#define COLOR_TITLE 1 0.8 0.4 1 +#define COLOR_TITLE 1 1 1 1 +#define COLOR_HEADER 0.69 0.69 0.69 1 +#define COLOR_FOCUSED 0.95294 0.72156 0.21176 1 //1 0.788 0.129 1 +//#define COLOR_FOCUS_YELLOW 0.95294 0.72156 0.21176 1 +#define COLOR_UNFOCUSED 0.4823 0.4823 0.4823 1 +//#define COLOR_DISABLED 0.35 0.35 0.35 1 +#define COLOR_SAFEAREA 0 0 1 1 + +#define COLOR_INFO_YELLOW COLOR_FOCUSED//1 0.84706 0 1 +#define COLOR_TEXT 0.84313 0.84313 0.84313 1 +#define COLOR_DISABLED 0.34118 0.36863 0.37647 1 +#define COLOR_TITLEBAR 0.14510 0.16078 0.16862 0.3//1 +#define COLOR_RED_TEXT 0.69020 0.00784 0.00784 1 + +#define COLOR_FADEOUT 0.09412 0.09412 0.04912 0.65 + +#define COLOR_BODY_TEXT 0.62745 0.66667 0.67451 1 + +#define COLOR_USMC 0 0.0196 0.41 +#define COLOR_JPN 0.53 0.027 0.027 +#define COLOR_USSR 0.368 0.035 0.035 +#define COLOR_GER 0.937 0.9 0.607 + +#define DEFAULT_MP_CFG "default_mp.cfg" +#define SPLITSCREEN_MP_CFG "default_splitscreen.cfg" +#define SYSTEMLINK_MP_CFG "default_systemlink.cfg" +#define XBOXLIVE_MP_CFG "default_xboxlive.cfg" + +#define MAX_RANK int(tableLookup( "mp/rankTable.csv", 0, "maxrank", 1)) +#define MAX_PRESTIGE int(tableLookup( "mp/rankIconTable.csv", 0, "maxprestige", 1)) + +#define PRESTIGE_AVAIL (stat(2326) < MAX_PRESTIGE && stat(2301) == int(tableLookup("mp/rankTable.csv",0,MAX_RANK,7))) +#define PRESTIGE_NEXT (stat(2326) < MAX_PRESTIGE && stat(252) == MAX_RANK) +#define PRESTIGE_FINISH (stat(2326) == MAX_PRESTIGE) + +#define CAN_RANK_UP (stat(252) < MAX_RANK || stat(2326) < MAX_PRESTIGE) +#endif // ENUMS_H diff --git a/libs/core/logmanager.cpp b/libs/core/logmanager.cpp new file mode 100644 index 0000000..a0ab70e --- /dev/null +++ b/libs/core/logmanager.cpp @@ -0,0 +1,15 @@ +#include "logmanager.h" +#include "qdebug.h" + +void LogManager::addEntry(const QString &entry) { + qDebug() << entry; + emit entryAdded(entry); +} + +void LogManager::addError(const QString &error) { + emit entryAdded(QString("ERROR: " + error)); +} + +void LogManager::addLine() { + emit entryAdded(""); +} diff --git a/libs/core/logmanager.h b/libs/core/logmanager.h new file mode 100644 index 0000000..d3161d5 --- /dev/null +++ b/libs/core/logmanager.h @@ -0,0 +1,31 @@ +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include +#include + +class LogManager : public QObject +{ + Q_OBJECT + +public: + static LogManager &instance() { + static LogManager instance; + return instance; + } + + void addEntry(const QString &entry); + void addError(const QString &error); + void addLine(); + +signals: + void entryAdded(const QString &entry); + +private: + LogManager() {} // Private constructor for singleton + ~LogManager() {} + + Q_DISABLE_COPY(LogManager) +}; + +#endif // LOGMANAGER_H diff --git a/libs/core/statusbarmanager.cpp b/libs/core/statusbarmanager.cpp new file mode 100644 index 0000000..d72c664 --- /dev/null +++ b/libs/core/statusbarmanager.cpp @@ -0,0 +1,9 @@ +#include "statusbarmanager.h" + +void StatusBarManager::updateStatus(const QString &message, int timeout) { + emit statusUpdated(message, timeout); +} + +void StatusBarManager::updateProgressStatus(const QString &message, int progress, int max) { + emit progressUpdated(message, progress, max); +} diff --git a/libs/core/statusbarmanager.h b/libs/core/statusbarmanager.h new file mode 100644 index 0000000..ed7cae4 --- /dev/null +++ b/libs/core/statusbarmanager.h @@ -0,0 +1,31 @@ +#ifndef STATUSBARMANAGER_H +#define STATUSBARMANAGER_H + +#include +#include + +class StatusBarManager : public QObject +{ + Q_OBJECT + +public: + static StatusBarManager &instance() { + static StatusBarManager instance; + return instance; + } + + void updateStatus(const QString &message, int timeout = 5000); + void updateProgressStatus(const QString &message, int progress, int max); + +signals: + void statusUpdated(const QString &message, int timeout); + void progressUpdated(const QString &message, int progress, int max); + +private: + StatusBarManager() {} // Private constructor for singleton + ~StatusBarManager() {} + + Q_DISABLE_COPY(StatusBarManager) +}; + +#endif // STATUSBARMANAGER_H diff --git a/libs/core/stringutils.h b/libs/core/stringutils.h new file mode 100644 index 0000000..e69de29 diff --git a/utils.h b/libs/core/utils.h similarity index 83% rename from utils.h rename to libs/core/utils.h index 49c21dd..9ebf74c 100644 --- a/utils.h +++ b/libs/core/utils.h @@ -10,6 +10,86 @@ class Utils { public: + static bool ExportData(const QString aFileName, const QByteArray aData) { + QDir workingDir = QDir::currentPath(); + workingDir.mkdir("exports"); + + QFile testFile("exports/" + aFileName); + if(!testFile.open(QIODevice::WriteOnly)) { + return false; + } + testFile.write(aData); + testFile.close(); + return true; + } + static bool ReadUntilString(QDataStream* stream, const QString& targetString) { + if (!stream || targetString.isEmpty()) { + return false; // Invalid input + } + + QByteArray buffer; + QByteArray targetBytes = targetString.toUtf8(); // Handle multibyte characters + const int targetLength = targetBytes.size(); + qDebug() << targetBytes << targetLength; + + // Read as unsigned bytes to handle all possible values (0-255) + unsigned char byte; + while (!stream->atEnd()) { + // Read one byte at a time + *stream >> byte; + buffer.append(static_cast(byte)); // Append as char for QByteArray + + // Keep buffer size limited to the target length + if (buffer.size() > targetLength) { + buffer.remove(0, 1); + } + + // Check if the buffer matches the target string in raw bytes + if (buffer == targetBytes) { + // Backup to the start of the matched string + stream->device()->seek(stream->device()->pos() - targetLength); + return true; + } + } + + // Target string not found + return false; + } + + static bool ReadUntilHex(QDataStream* stream, const QString& hexString) { + if (!stream || hexString.isEmpty() || hexString.size() % 2 != 0) { + return false; // Invalid input + } + + // Convert hex string to byte array + QByteArray targetBytes = QByteArray::fromHex(hexString.toUtf8()); + const int targetLength = targetBytes.size(); + + QByteArray buffer; + unsigned char byte; + + while (!stream->atEnd()) { + // Read one byte at a time + *stream >> byte; + buffer.append(static_cast(byte)); // Append as char for QByteArray + + // Keep buffer size limited to the target length + if (buffer.size() > targetLength) { + buffer.remove(0, 1); + } + + // Check if the buffer matches the target byte sequence + if (buffer == targetBytes) { + // Backup to the start of the matched sequence + stream->device()->seek(stream->device()->pos() - targetLength); + return true; + } + } + + // Target sequence not found + return false; + } + /* AssetTypeToString() diff --git a/dds_structs.h b/libs/ddsfile/dds_structs.h similarity index 100% rename from dds_structs.h rename to libs/ddsfile/dds_structs.h diff --git a/ddsfile.cpp b/libs/ddsfile/ddsfile.cpp similarity index 99% rename from ddsfile.cpp rename to libs/ddsfile/ddsfile.cpp index bae7cec..bc99b47 100644 --- a/ddsfile.cpp +++ b/libs/ddsfile/ddsfile.cpp @@ -1,6 +1,6 @@ #include "ddsfile.h" -#include "enums.h" #include "iwifile.h" +#include "qdir.h" DDSPixelFormat DDSFile::CalculatePixelFormat(quint8 aIWIFormat) { DDSPixelFormat ddsPixelFormat = {}; diff --git a/ddsfile.h b/libs/ddsfile/ddsfile.h similarity index 97% rename from ddsfile.h rename to libs/ddsfile/ddsfile.h index 5ccf0c7..a807e62 100644 --- a/ddsfile.h +++ b/libs/ddsfile/ddsfile.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include struct DDSPixelFormat { quint32 size; diff --git a/libs/ddsfile/ddsfile.pro b/libs/ddsfile/ddsfile.pro new file mode 100644 index 0000000..10fbb3c --- /dev/null +++ b/libs/ddsfile/ddsfile.pro @@ -0,0 +1,27 @@ +QT += core +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + ddsfile.cpp + +HEADERS += \ + dds_structs.h \ + ddsfile.h \ + enums.h + +LIBS += \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lDevIL \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lILU \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lILUT \ + -L$$OUT_PWD/../libs/iwifile -liwifile + +INCLUDEPATH += \ + $$PWD/../iwifile/ \ + $$PWD/../../third_party/devil_sdk/include/ + +DEPENDPATH += \ + $$PWD/../iwifile/ \ + $$PWD/../../third_party/devil_sdk/include/ + +DESTDIR = $$OUT_PWD/../ diff --git a/enums.h b/libs/ddsfile/enums.h similarity index 98% rename from enums.h rename to libs/ddsfile/enums.h index 8818afa..b5f8204 100644 --- a/enums.h +++ b/libs/ddsfile/enums.h @@ -12,12 +12,15 @@ enum FF_PLATFORM { enum FF_GAME { FF_GAME_NONE = 0x00, // No game - FF_GAME_COD4 = 0x01, // Modern Warware 1 - FF_GAME_COD5 = 0x02, // World at War - FF_GAME_COD6 = 0x03, // Modern Warfare 2 - FF_GAME_COD7 = 0x04, // Black Ops 1 - FF_GAME_COD8 = 0x05, // Modern Warfare 3 - FF_GAME_COD9 = 0x06, // Black Ops 2 + FF_GAME_COD1 = 0x01, // Call of Duty + FF_GAME_COD2 = 0x02, // Call of Duty 2 + FF_GAME_COD3 = 0x03, // Call of Duty 3 + FF_GAME_COD4 = 0x04, // Modern Warware 1 + FF_GAME_COD5 = 0x05, // World at War + FF_GAME_COD6 = 0x06, // Modern Warfare 2 + FF_GAME_COD7 = 0x07, // Black Ops 1 + FF_GAME_COD8 = 0x08, // Modern Warfare 3 + FF_GAME_COD9 = 0x09, // Black Ops 2 }; enum IWI_VERSION { diff --git a/libs/encryption/config_win32.h b/libs/encryption/config_win32.h new file mode 100644 index 0000000..29e4784 --- /dev/null +++ b/libs/encryption/config_win32.h @@ -0,0 +1,28 @@ +/* configuration header file for compiling under Microsoft Windows */ + +/* update package version here */ + +#pragma once + +#define PACKAGE "jbig2dec" +#define VERSION "0.2" + +/* define this iff you are linking to/compiling in libpng */ +#define HAVE_LIBPNG + +#ifdef _MSC_VER /* Microsoft Visual C+*/ + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef __int64 int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +/* no uint64_t */ + +# define vsnprintf _vsnprintf +# define snprintf _snprintf + +#endif /* _MSC_VER */ diff --git a/libs/encryption/ecrypt-config.h b/libs/encryption/ecrypt-config.h new file mode 100644 index 0000000..f388224 --- /dev/null +++ b/libs/encryption/ecrypt-config.h @@ -0,0 +1,272 @@ +/* ecrypt-config.h */ + +/* *** Normally, it should not be necessary to edit this file. *** */ + +#ifndef ECRYPT_CONFIG +#define ECRYPT_CONFIG + +/* ------------------------------------------------------------------------- */ + +/* Guess the endianness of the target architecture. */ + +/* + * The LITTLE endian machines: + */ +#if defined(__ultrix) /* Older MIPS */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__alpha) /* Alpha */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_M_IX86) /* x86 (MSC, Borland) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_MSC_VER) /* x86 (surely MSC) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__INTEL_COMPILER) /* x86 (surely Intel compiler icl.exe) */ +#define ECRYPT_LITTLE_ENDIAN + +/* + * The BIG endian machines: + */ +#elif defined(sun) /* Newer Sparc's */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__ppc__) /* PowerPC */ +#define ECRYPT_BIG_ENDIAN + +/* + * Finally machines with UNKNOWN endianness: + */ +#elif defined (_AIX) /* RS6000 */ +#define ECRYPT_UNKNOWN +#elif defined(__hpux) /* HP-PA */ +#define ECRYPT_UNKNOWN +#elif defined(__aux) /* 68K */ +#define ECRYPT_UNKNOWN +#elif defined(__dgux) /* 88K (but P6 in latest boxes) */ +#define ECRYPT_UNKNOWN +#elif defined(__sgi) /* Newer MIPS */ +#define ECRYPT_UNKNOWN +#else /* Any other processor */ +#define ECRYPT_UNKNOWN +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * Find minimal-width types to store 8-bit, 16-bit, 32-bit, and 64-bit + * integers. + * + * Note: to enable 64-bit types on 32-bit compilers, it might be + * necessary to switch from ISO C90 mode to ISO C99 mode (e.g., gcc + * -std=c99). + */ + +#include + +/* --- check char --- */ + +#if (UCHAR_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T char +#define U8C(v) (v##U) + +#if (UCHAR_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if (UCHAR_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T char +#define U16C(v) (v##U) +#endif + +#if (UCHAR_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T char +#define U32C(v) (v##U) +#endif + +#if (UCHAR_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T char +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check short --- */ + +#if (USHRT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T short +#define U8C(v) (v##U) + +#if (USHRT_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if (USHRT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T short +#define U16C(v) (v##U) +#endif + +#if (USHRT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T short +#define U32C(v) (v##U) +#endif + +#if (USHRT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T short +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check int --- */ + +#if (UINT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T int +#define U8C(v) (v##U) + +#if (ULONG_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if (UINT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T int +#define U16C(v) (v##U) +#endif + +#if (UINT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T int +#define U32C(v) (v##U) +#endif + +#if (UINT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T int +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long --- */ + +#if (ULONG_MAX / 0xFUL > 0xFUL) +#ifndef I8T +#define I8T long +#define U8C(v) (v##UL) + +#if (ULONG_MAX == 0xFFUL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if (ULONG_MAX / 0xFFUL > 0xFFUL) +#ifndef I16T +#define I16T long +#define U16C(v) (v##UL) +#endif + +#if (ULONG_MAX / 0xFFFFUL > 0xFFFFUL) +#ifndef I32T +#define I32T long +#define U32C(v) (v##UL) +#endif + +#if (ULONG_MAX / 0xFFFFFFFFUL > 0xFFFFFFFFUL) +#ifndef I64T +#define I64T long +#define U64C(v) (v##UL) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long long --- */ + +#ifdef ULLONG_MAX + +#if (ULLONG_MAX / 0xFULL > 0xFULL) +#ifndef I8T +#define I8T long long +#define U8C(v) (v##ULL) + +#if (ULLONG_MAX == 0xFFULL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if (ULLONG_MAX / 0xFFULL > 0xFFULL) +#ifndef I16T +#define I16T long long +#define U16C(v) (v##ULL) +#endif + +#if (ULLONG_MAX / 0xFFFFULL > 0xFFFFULL) +#ifndef I32T +#define I32T long long +#define U32C(v) (v##ULL) +#endif + +#if (ULLONG_MAX / 0xFFFFFFFFULL > 0xFFFFFFFFULL) +#ifndef I64T +#define I64T long long +#define U64C(v) (v##ULL) +#endif + +#endif +#endif +#endif +#endif + +#endif + +/* --- check __int64 --- */ + +#ifdef _UI64_MAX + +#if (_UI64_MAX / 0xFFFFFFFFuLL > 0xFFFFFFFFuLL) +#ifndef I64T +#define I64T __int64 +#define U64C(v) (v##ui64) +#endif + +#endif + +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/libs/encryption/ecrypt-machine.h b/libs/encryption/ecrypt-machine.h new file mode 100644 index 0000000..3e550d0 --- /dev/null +++ b/libs/encryption/ecrypt-machine.h @@ -0,0 +1,46 @@ +/* ecrypt-machine.h */ + +/* + * This file is included by 'ecrypt-portable.h'. It allows to override + * the default macros for specific platforms. Please carefully check + * the machine code generated by your compiler (with optimisations + * turned on) before deciding to edit this file. + */ + +/* ------------------------------------------------------------------------- */ + +#if (defined(ECRYPT_DEFAULT_ROT) && !defined(ECRYPT_MACHINE_ROT)) + +#define ECRYPT_MACHINE_ROT + +#if (defined(WIN32) && defined(_MSC_VER)) + +#undef ROTL32 +#undef ROTR32 +#undef ROTL64 +#undef ROTR64 + +#include + +#define ROTL32(v, n) _lrotl(v, n) +#define ROTR32(v, n) _lrotr(v, n) +#define ROTL64(v, n) _rotl64(v, n) +#define ROTR64(v, n) _rotr64(v, n) + +#endif + +#endif + +/* ------------------------------------------------------------------------- */ + +#if (defined(ECRYPT_DEFAULT_SWAP) && !defined(ECRYPT_MACHINE_SWAP)) + +#define ECRYPT_MACHINE_SWAP + +/* + * If you want to overwrite the default swap macros, put it here. And so on. + */ + +#endif + +/* ------------------------------------------------------------------------- */ diff --git a/libs/encryption/ecrypt-portable.h b/libs/encryption/ecrypt-portable.h new file mode 100644 index 0000000..972e7e9 --- /dev/null +++ b/libs/encryption/ecrypt-portable.h @@ -0,0 +1,303 @@ +/* ecrypt-portable.h */ + +/* + * WARNING: the conversions defined below are implemented as macros, + * and should be used carefully. They should NOT be used with + * parameters which perform some action. E.g., the following two lines + * are not equivalent: + * + * 1) ++x; y = ROTL32(x, n); + * 2) y = ROTL32(++x, n); + */ + +/* + * *** Please do not edit this file. *** + * + * The default macros can be overridden for specific architectures by + * editing 'ecrypt-machine.h'. + */ + +#ifndef ECRYPT_PORTABLE +#define ECRYPT_PORTABLE + +#include "ecrypt-config.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following types are defined (if available): + * + * u8: unsigned integer type, at least 8 bits + * u16: unsigned integer type, at least 16 bits + * u32: unsigned integer type, at least 32 bits + * u64: unsigned integer type, at least 64 bits + * + * s8, s16, s32, s64 -> signed counterparts of u8, u16, u32, u64 + * + * The selection of minimum-width integer types is taken care of by + * 'ecrypt-config.h'. Note: to enable 64-bit types on 32-bit + * compilers, it might be necessary to switch from ISO C90 mode to ISO + * C99 mode (e.g., gcc -std=c99). + */ + +#ifdef I8T +typedef signed I8T s8; +typedef unsigned I8T u8; +#endif + +#ifdef I16T +typedef signed I16T s16; +typedef unsigned I16T u16; +#endif + +#ifdef I32T +typedef signed I32T s32; +typedef unsigned I32T u32; +#endif + +#ifdef I64T +typedef signed I64T s64; +typedef unsigned I64T u64; +#endif + +/* + * The following macros are used to obtain exact-width results. + */ + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U16V(v) ((u16)(v) & U16C(0xFFFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) +#define U64V(v) ((u64)(v) & U64C(0xFFFFFFFFFFFFFFFF)) + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return words with their bits rotated over n + * positions to the left/right. + */ + +#define ECRYPT_DEFAULT_ROT + +#define ROTL8(v, n) \ +(U8V((v) << (n)) | ((v) >> (8 - (n)))) + +#define ROTL16(v, n) \ + (U16V((v) << (n)) | ((v) >> (16 - (n)))) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define ROTL64(v, n) \ + (U64V((v) << (n)) | ((v) >> (64 - (n)))) + +#define ROTR8(v, n) ROTL8(v, 8 - (n)) +#define ROTR16(v, n) ROTL16(v, 16 - (n)) +#define ROTR32(v, n) ROTL32(v, 32 - (n)) +#define ROTR64(v, n) ROTL64(v, 64 - (n)) + +#include "ecrypt-machine.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return a word with bytes in reverse order. + */ + +#define ECRYPT_DEFAULT_SWAP + +#define SWAP16(v) \ + ROTL16(v, 8) + +#define SWAP32(v) \ + ((ROTL32(v, 8) & U32C(0x00FF00FF)) | \ + (ROTL32(v, 24) & U32C(0xFF00FF00))) + +#ifdef ECRYPT_NATIVE64 +#define SWAP64(v) \ + ((ROTL64(v, 8) & U64C(0x000000FF000000FF)) | \ + (ROTL64(v, 24) & U64C(0x0000FF000000FF00)) | \ + (ROTL64(v, 40) & U64C(0x00FF000000FF0000)) | \ + (ROTL64(v, 56) & U64C(0xFF000000FF000000))) +#else +#define SWAP64(v) \ + (((u64)SWAP32(U32V(v)) << 32) | (u64)SWAP32(U32V(v >> 32))) +#endif + +#include "ecrypt-machine.h" + +#define ECRYPT_DEFAULT_WTOW + +#ifdef ECRYPT_LITTLE_ENDIAN +#define U16TO16_LITTLE(v) (v) +#define U32TO32_LITTLE(v) (v) +#define U64TO64_LITTLE(v) (v) + +#define U16TO16_BIG(v) SWAP16(v) +#define U32TO32_BIG(v) SWAP32(v) +#define U64TO64_BIG(v) SWAP64(v) +#endif + +#ifdef ECRYPT_BIG_ENDIAN +#define U16TO16_LITTLE(v) SWAP16(v) +#define U32TO32_LITTLE(v) SWAP32(v) +#define U64TO64_LITTLE(v) SWAP64(v) + +#define U16TO16_BIG(v) (v) +#define U32TO32_BIG(v) (v) +#define U64TO64_BIG(v) (v) +#endif + +#include "ecrypt-machine.h" + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ + +#define ECRYPT_DEFAULT_BTOW + +#if (!defined(ECRYPT_UNKNOWN) && defined(ECRYPT_I8T_IS_BYTE)) + +#define U8TO16_LITTLE(p) U16TO16_LITTLE(((u16*)(p))[0]) +#define U8TO32_LITTLE(p) U32TO32_LITTLE(((u32*)(p))[0]) +#define U8TO64_LITTLE(p) U64TO64_LITTLE(((u64*)(p))[0]) + +#define U8TO16_BIG(p) U16TO16_BIG(((u16*)(p))[0]) +#define U8TO32_BIG(p) U32TO32_BIG(((u32*)(p))[0]) +#define U8TO64_BIG(p) U64TO64_BIG(((u64*)(p))[0]) + +#define U16TO8_LITTLE(p, v) (((u16*)(p))[0] = U16TO16_LITTLE(v)) +#define U32TO8_LITTLE(p, v) (((u32*)(p))[0] = U32TO32_LITTLE(v)) +#define U64TO8_LITTLE(p, v) (((u64*)(p))[0] = U64TO64_LITTLE(v)) + +#define U16TO8_BIG(p, v) (((u16*)(p))[0] = U16TO16_BIG(v)) +#define U32TO8_BIG(p, v) (((u32*)(p))[0] = U32TO32_BIG(v)) +#define U64TO8_BIG(p, v) (((u64*)(p))[0] = U64TO64_BIG(v)) + +#else + +#define U8TO16_LITTLE(p) \ +(((u16)((p)[0]) ) | \ + ((u16)((p)[1]) << 8)) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_LITTLE(p) \ + (((u64)((p)[0]) ) | \ + ((u64)((p)[1]) << 8) | \ + ((u64)((p)[2]) << 16) | \ + ((u64)((p)[3]) << 24) | \ + ((u64)((p)[4]) << 32) | \ + ((u64)((p)[5]) << 40) | \ + ((u64)((p)[6]) << 48) | \ + ((u64)((p)[7]) << 56)) +#else +#define U8TO64_LITTLE(p) \ + ((u64)U8TO32_LITTLE(p) | ((u64)U8TO32_LITTLE((p) + 4) << 32)) +#endif + +#define U8TO16_BIG(p) \ + (((u16)((p)[0]) << 8) | \ + ((u16)((p)[1]) )) + +#define U8TO32_BIG(p) \ + (((u32)((p)[0]) << 24) | \ + ((u32)((p)[1]) << 16) | \ + ((u32)((p)[2]) << 8) | \ + ((u32)((p)[3]) )) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_BIG(p) \ + (((u64)((p)[0]) << 56) | \ + ((u64)((p)[1]) << 48) | \ + ((u64)((p)[2]) << 40) | \ + ((u64)((p)[3]) << 32) | \ + ((u64)((p)[4]) << 24) | \ + ((u64)((p)[5]) << 16) | \ + ((u64)((p)[6]) << 8) | \ + ((u64)((p)[7]) )) +#else +#define U8TO64_BIG(p) \ + (((u64)U8TO32_BIG(p) << 32) | (u64)U8TO32_BIG((p) + 4)) +#endif + +#define U16TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ +} while (0) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + (p)[4] = U8V((v) >> 32); \ + (p)[5] = U8V((v) >> 40); \ + (p)[6] = U8V((v) >> 48); \ + (p)[7] = U8V((v) >> 56); \ + } while (0) +#else +#define U64TO8_LITTLE(p, v) \ + do { \ + U32TO8_LITTLE((p), U32V((v) )); \ + U32TO8_LITTLE((p) + 4, U32V((v) >> 32)); \ + } while (0) +#endif + +#define U16TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + } while (0) + +#define U32TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 24); \ + (p)[1] = U8V((v) >> 16); \ + (p)[2] = U8V((v) >> 8); \ + (p)[3] = U8V((v) ); \ + } while (0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 56); \ + (p)[1] = U8V((v) >> 48); \ + (p)[2] = U8V((v) >> 40); \ + (p)[3] = U8V((v) >> 32); \ + (p)[4] = U8V((v) >> 24); \ + (p)[5] = U8V((v) >> 16); \ + (p)[6] = U8V((v) >> 8); \ + (p)[7] = U8V((v) ); \ +} while (0) +#else +#define U64TO8_BIG(p, v) \ + do { \ + U32TO8_BIG((p), U32V((v) >> 32)); \ + U32TO8_BIG((p) + 4, U32V((v) )); \ +} while (0) +#endif + +#endif + +#include "ecrypt-machine.h" + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/libs/encryption/ecrypt-sync.h b/libs/encryption/ecrypt-sync.h new file mode 100644 index 0000000..9027877 --- /dev/null +++ b/libs/encryption/ecrypt-sync.h @@ -0,0 +1,279 @@ +/* ecrypt-sync.h */ + +/* + * Header file for synchronous stream ciphers without authentication + * mechanism. + * + * *** Please only edit parts marked with "[edit]". *** + */ + +#ifndef ECRYPT_SYNC +#define ECRYPT_SYNC + +#include "ecrypt-portable.h" + +/* ------------------------------------------------------------------------- */ + +/* Cipher parameters */ + +/* + * The name of your cipher. + */ +#define ECRYPT_NAME "Salsa20" /* [edit] */ +#define ECRYPT_PROFILE "S!_H." + +/* + * Specify which key and IV sizes are supported by your cipher. A user + * should be able to enumerate the supported sizes by running the + * following code: + * + * for (i = 0; ECRYPT_KEYSIZE(i) <= ECRYPT_MAXKEYSIZE; ++i) + * { + * keysize = ECRYPT_KEYSIZE(i); + * + * ... + * } + * + * All sizes are in bits. + */ + +#define ECRYPT_MAXKEYSIZE 256 /* [edit] */ +#define ECRYPT_KEYSIZE(i) (128 + (i)*128) /* [edit] */ + +#define ECRYPT_MAXIVSIZE 64 /* [edit] */ +#define ECRYPT_IVSIZE(i) (64 + (i)*64) /* [edit] */ + +/* ------------------------------------------------------------------------- */ + +/* Data structures */ + +/* + * ECRYPT_ctx is the structure containing the representation of the + * internal state of your cipher. + */ + +typedef struct +{ + u32 input[16]; /* could be compressed */ + /* + * [edit] + * + * Put here all state variable needed during the encryption process. + */ +} ECRYPT_ctx; + +/* ------------------------------------------------------------------------- */ + +/* Mandatory functions */ + +/* + * Key and message independent initialization. This function will be + * called once when the program starts (e.g., to build expanded S-box + * tables). + */ +void ECRYPT_init(); + +/* + * Key setup. It is the user's responsibility to select the values of + * keysize and ivsize from the set of supported values specified + * above. + */ +void ECRYPT_keysetup( + ECRYPT_ctx* ctx, + const u8* key, + u32 keysize, /* Key size in bits. */ + u32 ivsize); /* IV size in bits. */ + +/* + * IV setup. After having called ECRYPT_keysetup(), the user is + * allowed to call ECRYPT_ivsetup() different times in order to + * encrypt/decrypt different messages with the same key but different + * IV's. + */ +void ECRYPT_ivsetup( + ECRYPT_ctx* ctx, + const u8* iv); + +/* + * Encryption/decryption of arbitrary length messages. + * + * For efficiency reasons, the API provides two types of + * encrypt/decrypt functions. The ECRYPT_encrypt_bytes() function + * (declared here) encrypts byte strings of arbitrary length, while + * the ECRYPT_encrypt_blocks() function (defined later) only accepts + * lengths which are multiples of ECRYPT_BLOCKLENGTH. + * + * The user is allowed to make multiple calls to + * ECRYPT_encrypt_blocks() to incrementally encrypt a long message, + * but he is NOT allowed to make additional encryption calls once he + * has called ECRYPT_encrypt_bytes() (unless he starts a new message + * of course). For example, this sequence of calls is acceptable: + * + * ECRYPT_keysetup(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_bytes(); + * + * The following sequence is not: + * + * ECRYPT_keysetup(); + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * ECRYPT_encrypt_blocks(); + */ + +void ECRYPT_encrypt_bytes( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 msglen); /* Message length in bytes. */ + +void ECRYPT_decrypt_bytes( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 msglen); /* Message length in bytes. */ + +/* ------------------------------------------------------------------------- */ + +/* Optional features */ + +/* + * For testing purposes it can sometimes be useful to have a function + * which immediately generates keystream without having to provide it + * with a zero plaintext. If your cipher cannot provide this function + * (e.g., because it is not strictly a synchronous cipher), please + * reset the ECRYPT_GENERATES_KEYSTREAM flag. + */ + +#define ECRYPT_GENERATES_KEYSTREAM +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_bytes( + ECRYPT_ctx* ctx, + u8* keystream, + u32 length); /* Length of keystream in bytes. */ + +#endif + +/* ------------------------------------------------------------------------- */ + +/* Optional optimizations */ + +/* + * By default, the functions in this section are implemented using + * calls to functions declared above. However, you might want to + * implement them differently for performance reasons. + */ + +/* + * All-in-one encryption/decryption of (short) packets. + * + * The default definitions of these functions can be found in + * "ecrypt-sync.c". If you want to implement them differently, please + * undef the ECRYPT_USES_DEFAULT_ALL_IN_ONE flag. + */ +#define ECRYPT_USES_DEFAULT_ALL_IN_ONE /* [edit] */ + +void ECRYPT_encrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* plaintext, + u8* ciphertext, + u32 msglen); + +void ECRYPT_decrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* ciphertext, + u8* plaintext, + u32 msglen); + +/* + * Encryption/decryption of blocks. + * + * By default, these functions are defined as macros. If you want to + * provide a different implementation, please undef the + * ECRYPT_USES_DEFAULT_BLOCK_MACROS flag and implement the functions + * declared below. + */ + +#define ECRYPT_BLOCKLENGTH 64 /* [edit] */ + +#define ECRYPT_USES_DEFAULT_BLOCK_MACROS /* [edit] */ +#ifdef ECRYPT_USES_DEFAULT_BLOCK_MACROS + +#define ECRYPT_encrypt_blocks(ctx, plaintext, ciphertext, blocks) \ +ECRYPT_encrypt_bytes(ctx, plaintext, ciphertext, \ + (blocks) * ECRYPT_BLOCKLENGTH) + +#define ECRYPT_decrypt_blocks(ctx, ciphertext, plaintext, blocks) \ + ECRYPT_decrypt_bytes(ctx, ciphertext, plaintext, \ + (blocks) * ECRYPT_BLOCKLENGTH) + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +#define ECRYPT_keystream_blocks(ctx, keystream, blocks) \ + ECRYPT_keystream_bytes(ctx, keystream, \ + (blocks) * ECRYPT_BLOCKLENGTH) + +#endif + +#else + +void ECRYPT_encrypt_blocks( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 blocks); /* Message length in blocks. */ + +void ECRYPT_decrypt_blocks( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 blocks); /* Message length in blocks. */ + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_blocks( + ECRYPT_ctx* ctx, + const u8* keystream, + u32 blocks); /* Keystream length in blocks. */ + +#endif + +#endif + +/* + * If your cipher can be implemented in different ways, you can use + * the ECRYPT_VARIANT parameter to allow the user to choose between + * them at compile time (e.g., gcc -DECRYPT_VARIANT=3 ...). Please + * only use this possibility if you really think it could make a + * significant difference and keep the number of variants + * (ECRYPT_MAXVARIANT) as small as possible (definitely not more than + * 10). Note also that all variants should have exactly the same + * external interface (i.e., the same ECRYPT_BLOCKLENGTH, etc.). + */ +#define ECRYPT_MAXVARIANT 1 /* [edit] */ + +#ifndef ECRYPT_VARIANT +#define ECRYPT_VARIANT 1 +#endif + +#if (ECRYPT_VARIANT > ECRYPT_MAXVARIANT) +#error this variant does not exist +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/libs/encryption/encryption.cpp b/libs/encryption/encryption.cpp new file mode 100644 index 0000000..613ea2e --- /dev/null +++ b/libs/encryption/encryption.cpp @@ -0,0 +1,440 @@ +#include "encryption.h" +#include "QtZlib/zlib.h" +#include "ecrypt-sync.h" + +void Encryption::Convert32BitTo8Bit(quint32 value, quint8 *array) { + array[0] = static_cast(value >> 0); + array[1] = static_cast(value >> 8); + array[2] = static_cast(value >> 16); + array[3] = static_cast(value >> 24); +} + +quint32 Encryption::ConvertArrayTo32Bit(const QByteArray &array) { + return ((static_cast(static_cast(array[0])) << 0) | + (static_cast(static_cast(array[1])) << 8) | + (static_cast(static_cast(array[2])) << 16) | + (static_cast(static_cast(array[3])) << 24)); +} + +quint32 Encryption::Rotate(quint32 value, quint32 numBits) { + return (value << numBits) | (value >> (32 - numBits)); +} + +QByteArray Encryption::InitIVTable(const QByteArray &feed) { + const int tableSize = 0xFB0; + QByteArray table; + table.resize(tableSize); + int ptr = 0; + for (int i = 0; i < 200; ++i) { + for (int x = 0; x < 5; ++x) { + if (static_cast(feed.at(ptr)) == 0x00) + ptr = 0; + int base = i * 20 + x * 4; + table[base] = feed.at(ptr); + table[base + 1] = feed.at(ptr); + table[base + 2] = feed.at(ptr); + table[base + 3] = feed.at(ptr); + ++ptr; + } + } + // Copy block numbers [1,0,0,0] into the last 16 bytes + QByteArray oneBlock; + oneBlock.append(char(1)); oneBlock.append(char(0)); oneBlock.append(char(0)); oneBlock.append(char(0)); + table.replace(0xFA0, 4, oneBlock); + table.replace(0xFA4, 4, oneBlock); + table.replace(0xFA8, 4, oneBlock); + table.replace(0xFAC, 4, oneBlock); + return table; +} + +int Encryption::unk(quint64 arg1, quint8 arg2) { + if (arg2 >= 0x40) + return 0; + return static_cast(arg1 >> arg2); +} + +QByteArray Encryption::GetIV(const QByteArray &table, int index) { + int num1 = 0xFA0 + index; + int num2 = unk(0x51EB851FLL * num1, 0x20); + int adjust = ((num2 >> 6) + (num2 >> 31)); + int startIndex = 20 * (num1 - 200 * adjust); + // Return 8 bytes from that location. + return table.mid(startIndex, 8); +} + +void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash) { + int blockNumIndex = index % 4; + int baseOffset = 0xFA0 + blockNumIndex * 4; + quint32 blockNumVal = (static_cast(table.at(baseOffset)) ) | + (static_cast(table.at(baseOffset + 1)) << 8 ) | + (static_cast(table.at(baseOffset + 2)) << 16) | + (static_cast(table.at(baseOffset + 3)) << 24); + int blockNum = blockNumVal * 4 + index; + int num2 = unk(0x51EB851FLL * blockNum, 0x20); + int adjust = ((num2 >> 6) + (num2 >> 31)); + int startIndex = 20 * (blockNum - 200 * adjust) + 1; + int hashIndex = 0; + for (int x = 0; x < 4; ++x) { + table[startIndex - 1] = table.at(startIndex - 1) ^ sectionHash.at(hashIndex); + table[startIndex] = table.at(startIndex) ^ sectionHash.at(hashIndex + 1); + table[startIndex + 1] = table.at(startIndex + 1) ^ sectionHash.at(hashIndex + 2); + table[startIndex + 2] = table.at(startIndex + 2) ^ sectionHash.at(hashIndex + 3); + table[startIndex + 3] = table.at(startIndex + 3) ^ sectionHash.at(hashIndex + 4); + startIndex += 5; + hashIndex += 5; + } +} + +quint32 Encryption::ToUInt32(const QByteArray &data, int offset) { + // Converts 4 bytes (starting at offset) from data into a 32-bit unsigned integer (little-endian) + return ((static_cast(static_cast(data[offset])) ) | + (static_cast(static_cast(data[offset+1])) << 8 ) | + (static_cast(static_cast(data[offset+2])) << 16) | + (static_cast(static_cast(data[offset+3])) << 24)); +} + +QByteArray Encryption::salsa20DecryptSection(const QByteArray §ionData, const QByteArray &key, const QByteArray &iv, int blockSize) +{ + // Choose the appropriate constant based on key length. + QByteArray constants; + if (key.size() == 32) + constants = "expand 32-byte k"; + else if (key.size() == 16) + constants = "expand 16-byte k"; + else { + qWarning() << "Invalid key size:" << key.size() << "; expected 16 or 32 bytes."; + return QByteArray(); + } + + QVector state(VECTOR_SIZE); + + // Set state[0] using the first 4 bytes of the constant. + state[0] = ConvertArrayTo32Bit(constants.mid(0, 4)); + + // state[1] through state[4] come from the first 16 bytes of the key. + state[1] = ToUInt32(key, 0); + state[2] = ToUInt32(key, 4); + state[3] = ToUInt32(key, 8); + state[4] = ToUInt32(key, 12); + + // state[5] comes from the next 4 bytes of the constant. + state[5] = ConvertArrayTo32Bit(constants.mid(4, 4)); + + // state[6] and state[7] come from the IV (which must be 8 bytes). + state[6] = ConvertArrayTo32Bit(iv.mid(0, 4)); + state[7] = ConvertArrayTo32Bit(iv.mid(4, 4)); + + // state[8] and state[9] are the 64-bit block counter (start at 0). + state[8] = 0; + state[9] = 0; + + // state[10] comes from the next 4 bytes of the constant. + state[10] = ConvertArrayTo32Bit(constants.mid(8, 4)); + + // For state[11] through state[14]: + // If the key is 32 bytes, use bytes 16..31; if 16 bytes, reuse the first 16 bytes. + if (key.size() == 32) { + state[11] = ToUInt32(key, 16); + state[12] = ToUInt32(key, 20); + state[13] = ToUInt32(key, 24); + state[14] = ToUInt32(key, 28); + } else { // key.size() == 16 + state[11] = ToUInt32(key, 0); + state[12] = ToUInt32(key, 4); + state[13] = ToUInt32(key, 8); + state[14] = ToUInt32(key, 12); + } + + // state[15] comes from the last 4 bytes of the constant. + state[15] = ConvertArrayTo32Bit(constants.mid(12, 4)); + + // Prepare the output buffer. + QByteArray output(sectionData.size(), Qt::Uninitialized); + int numBlocks = sectionData.size() / blockSize; + int remainder = sectionData.size() % blockSize; + + // Process each full block. + for (int blockIndex = 0; blockIndex < numBlocks; ++blockIndex) { + QVector x = state; // make a copy of the current state for this block + + // Run 20 rounds (10 iterations) of Salsa20. + for (int round = 20; round > 0; round -= 2) { + x[4] ^= Rotate(x[0] + x[12], 7); + x[8] ^= Rotate(x[4] + x[0], 9); + x[12] ^= Rotate(x[8] + x[4], 13); + x[0] ^= Rotate(x[12] + x[8], 18); + + x[9] ^= Rotate(x[5] + x[1], 7); + x[13] ^= Rotate(x[9] + x[5], 9); + x[1] ^= Rotate(x[13] + x[9], 13); + x[5] ^= Rotate(x[1] + x[13], 18); + + x[14] ^= Rotate(x[10] + x[6], 7); + x[2] ^= Rotate(x[14] + x[10], 9); + x[6] ^= Rotate(x[2] + x[14], 13); + x[10] ^= Rotate(x[6] + x[2], 18); + + x[3] ^= Rotate(x[15] + x[11], 7); + x[7] ^= Rotate(x[3] + x[15], 9); + x[11] ^= Rotate(x[7] + x[3], 13); + x[15] ^= Rotate(x[11] + x[7], 18); + + x[1] ^= Rotate(x[0] + x[3], 7); + x[2] ^= Rotate(x[1] + x[0], 9); + x[3] ^= Rotate(x[2] + x[1], 13); + x[0] ^= Rotate(x[3] + x[2], 18); + + x[6] ^= Rotate(x[5] + x[4], 7); + x[7] ^= Rotate(x[6] + x[5], 9); + x[4] ^= Rotate(x[7] + x[6], 13); + x[5] ^= Rotate(x[4] + x[7], 18); + + x[11] ^= Rotate(x[10] + x[9], 7); + x[8] ^= Rotate(x[11] + x[10], 9); + x[9] ^= Rotate(x[8] + x[11], 13); + x[10] ^= Rotate(x[9] + x[8], 18); + + x[12] ^= Rotate(x[15] + x[14], 7); + x[13] ^= Rotate(x[12] + x[15], 9); + x[14] ^= Rotate(x[13] + x[12], 13); + x[15] ^= Rotate(x[14] + x[13], 18); + } + + // Produce the 64-byte keystream block by adding the original state. + QVector keyStreamBlock(blockSize); + for (int i = 0; i < VECTOR_SIZE; ++i) { + x[i] += state[i]; + Convert32BitTo8Bit(x[i], keyStreamBlock.data() + 4 * i); + } + + // XOR the keystream block with the corresponding block of sectionData. + const uchar* inBlock = reinterpret_cast(sectionData.constData()) + blockIndex * blockSize; + uchar* outBlock = reinterpret_cast(output.data()) + blockIndex * blockSize; + for (int j = 0; j < blockSize; ++j) { + outBlock[j] = inBlock[j] ^ keyStreamBlock[j]; + } + // Increment the 64-bit block counter. + state[8]++; + if (state[8] == 0) + state[9]++; + } + + // Process any remaining bytes. + if (remainder > 0) { + QVector x = state; + for (int round = 20; round > 0; round -= 2) { + x[4] ^= Rotate(x[0] + x[12], 7); + x[8] ^= Rotate(x[4] + x[0], 9); + x[12] ^= Rotate(x[8] + x[4], 13); + x[0] ^= Rotate(x[12] + x[8], 18); + + x[9] ^= Rotate(x[5] + x[1], 7); + x[13] ^= Rotate(x[9] + x[5], 9); + x[1] ^= Rotate(x[13] + x[9], 13); + x[5] ^= Rotate(x[1] + x[13], 18); + + x[14] ^= Rotate(x[10] + x[6], 7); + x[2] ^= Rotate(x[14] + x[10], 9); + x[6] ^= Rotate(x[2] + x[14], 13); + x[10] ^= Rotate(x[6] + x[2], 18); + + x[3] ^= Rotate(x[15] + x[11], 7); + x[7] ^= Rotate(x[3] + x[15], 9); + x[11] ^= Rotate(x[7] + x[3], 13); + x[15] ^= Rotate(x[11] + x[7], 18); + + x[1] ^= Rotate(x[0] + x[3], 7); + x[2] ^= Rotate(x[1] + x[0], 9); + x[3] ^= Rotate(x[2] + x[1], 13); + x[0] ^= Rotate(x[3] + x[2], 18); + + x[6] ^= Rotate(x[5] + x[4], 7); + x[7] ^= Rotate(x[6] + x[5], 9); + x[4] ^= Rotate(x[7] + x[6], 13); + x[5] ^= Rotate(x[4] + x[7], 18); + + x[11] ^= Rotate(x[10] + x[9], 7); + x[8] ^= Rotate(x[11] + x[10], 9); + x[9] ^= Rotate(x[8] + x[11], 13); + x[10] ^= Rotate(x[9] + x[8], 18); + + x[12] ^= Rotate(x[15] + x[14], 7); + x[13] ^= Rotate(x[12] + x[15], 9); + x[14] ^= Rotate(x[13] + x[12], 13); + x[15] ^= Rotate(x[14] + x[13], 18); + } + QVector keyStreamBlock(blockSize); + for (int i = 0; i < VECTOR_SIZE; ++i) { + x[i] += state[i]; + Convert32BitTo8Bit(x[i], keyStreamBlock.data() + 4 * i); + } + const uchar* inBlock = reinterpret_cast(sectionData.constData()) + numBlocks * blockSize; + uchar* outBlock = reinterpret_cast(output.data()) + numBlocks * blockSize; + for (int j = 0; j < remainder; ++j) + outBlock[j] = inBlock[j] ^ keyStreamBlock[j]; + } + + return output; +} + +void Encryption::fillIVTable(QByteArray fastFileData, QByteArray &ivTable, quint32 ivTableLength) +{ + QDataStream stream(fastFileData); + stream.skipRawData(24); + + quint32 nameKeyLength = 0; + for (int i = 0; i < 32 && !stream.atEnd(); i++) { + if (!stream.atEnd() && stream.device()->peek(1).toHex() != "00") { + nameKeyLength++; + stream.skipRawData(1); + } else { + break; + } + } + + if (nameKeyLength < 32) { + stream.skipRawData(32 - nameKeyLength); + } + + if (ivTableLength < 16) { + qWarning() << "IV table length too small!"; + return; + } + + for (quint32 i = 0; i < ivTableLength - 16; i++) { + if (stream.atEnd()) { + qWarning() << "Stream ended while filling IV table!"; + return; + } + quint8 ivVal; + stream >> ivVal; + ivTable[i] = ivVal; + } +} + +void Encryption::fillIV(int index, QByteArray &ivPtr, const QByteArray &ivTable, const QVector &ivCounter) +{ + if (index < 0 || index >= ivCounter.size()) { + qWarning() << "Invalid IV index: " << index; + return; + } + + int ivOffset = ((index + 4 * (ivCounter[index] - 1)) % 800) * 20; + + if (ivOffset + 8 > ivTable.size()) { + qWarning() << "IV offset out of bounds! Offset: " << ivOffset; + return; + } + + ivPtr = ivTable.mid(ivOffset, 8); +} + +void Encryption::generateNewIV(int index, const QByteArray &hash, QByteArray &ivTable, QVector &ivCounter) +{ + if (index < 0 || index >= ivCounter.size()) { + qWarning() << "Invalid index: " << index; + return; + } + + quint32 safeCounter = fmin(ivCounter[index], 800u - 1); + int ivOffset = (index + 4 * safeCounter) % 800 * 5; + + for (int i = 0; i < 20; i++) { + if (ivOffset + i >= ivTable.size()) { + qWarning() << "Index out of bounds for IV table!"; + return; + } + ivTable[ivOffset + i] ^= hash[i]; + } + + ivCounter[index]++; +} + +QByteArray Encryption::decryptFastFile(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 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(bo2_salsa20_key.constData()), 256, 0); + ECRYPT_ivsetup(&x, reinterpret_cast(ivPtr.constData())); + + QByteArray encryptedBlock = fileData.mid(stream.device()->pos(), dataLength); + QByteArray decryptedBlock; + decryptedBlock.resize(dataLength); + + ECRYPT_decrypt_bytes(&x, reinterpret_cast(encryptedBlock.constData()), + reinterpret_cast(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(decryptedBlock.size()); + strm.next_in = reinterpret_cast(decryptedBlock.data()); + + QByteArray decompressedData; + decompressedData.resize(fmax(dataLength * 2, 4096)); + strm.avail_out = decompressedData.size(); + strm.next_out = reinterpret_cast(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(dataLength) > fileData.size()) { + qWarning() << "Skipping past file size!"; + break; + } + + stream.skipRawData(dataLength); + chunkIndex++; + } + + return finalFastFile; +} diff --git a/libs/encryption/encryption.h b/libs/encryption/encryption.h new file mode 100644 index 0000000..6b36041 --- /dev/null +++ b/libs/encryption/encryption.h @@ -0,0 +1,52 @@ +#ifndef ENCRYPTION_H +#define ENCRYPTION_H + +#include +#include +#include +#include + +class Encryption { +public: + static const int VECTOR_SIZE = 16; // 16 32-bit words + static const int NUM_OF_BLOCKS_PER_CHUNK = 8192; + + //-------------------------------------------------------------------- + // Helper functions (assuming little–endian order) + + static void Convert32BitTo8Bit(quint32 value, quint8* array); + + static quint32 ConvertArrayTo32Bit(const QByteArray &array); + + static quint32 Rotate(quint32 value, quint32 numBits); + + // Build the IV table from a 0x20–byte feed. The table is 0xFB0 bytes. + static QByteArray InitIVTable(const QByteArray &feed); + + // "unk" function as in the C# code. + static int unk(quint64 arg1, quint8 arg2); + + // Compute the IV for a given section index using the IV table. + static QByteArray GetIV(const QByteArray &table, int index); + + // Update the IV table given the section's SHA1 hash. + static void UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash); + + static quint32 ToUInt32(const QByteArray &data, int offset); + + //-------------------------------------------------------------------- + // Salsa20 decryption for one section. + // This function resets the counter for each section. + static QByteArray salsa20DecryptSection(const QByteArray §ionData, const QByteArray &key, const QByteArray &iv, int blockSize = 64); + + static void fillIVTable(QByteArray fastFileData, QByteArray& ivTable, quint32 ivTableLength); + + static void fillIV(int index, QByteArray& ivPtr, const QByteArray& ivTable, const QVector& ivCounter); + + + static void generateNewIV(int index, const QByteArray& hash, QByteArray& ivTable, QVector& ivCounter); + + static QByteArray decryptFastFile(const QByteArray& fastFileData); +}; + +#endif // ENCRYPTION_H diff --git a/libs/encryption/encryption.pro b/libs/encryption/encryption.pro new file mode 100644 index 0000000..28d2cfb --- /dev/null +++ b/libs/encryption/encryption.pro @@ -0,0 +1,20 @@ +QT += core +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + salsa20.cpp \ + sha1.cpp \ + encryption.cpp + +HEADERS += \ + ecrypt-config.h \ + ecrypt-machine.h \ + ecrypt-portable.h \ + ecrypt-sync.h \ + encryption.h \ + os_types.h \ + config_win32.h \ + sha1.h + +DESTDIR = $$OUT_PWD/../ diff --git a/libs/encryption/os_types.h b/libs/encryption/os_types.h new file mode 100644 index 0000000..2006a21 --- /dev/null +++ b/libs/encryption/os_types.h @@ -0,0 +1,30 @@ +/* + jbig2dec + + Copyright (c) 2002 artofcode LLC. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + $Id: os_types.h,v 1.1 2002/07/20 17:23:15 giles Exp $ +*/ + +/* + indirection layer for build and platform-specific definitions + + in general, this header should insure that the stdint types are + available, and that any optional compile flags are defined if + the build system doesn't pass them directly. +*/ + +#ifdef HAVE_CONFIG_H +#include "config_types.h" +#elif defined(_WIN32) +#include "config_win32.h" +#endif + +#if defined(HAVE_STDINT_H) || defined(__MACOS__) +#include +#endif diff --git a/libs/encryption/salsa20.cpp b/libs/encryption/salsa20.cpp new file mode 100644 index 0000000..39349f3 --- /dev/null +++ b/libs/encryption/salsa20.cpp @@ -0,0 +1,219 @@ +/* +salsa20-merged.c version 20051118 +D. J. Bernstein +Public domain. +*/ + +#include "ecrypt-sync.h" + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +void ECRYPT_init(void) +{ + return; +} + +static const char sigma[17] = "expand 32-byte k"; +static const char tau[17] = "expand 16-byte k"; + +void ECRYPT_keysetup(ECRYPT_ctx *x,const u8 *k,u32 kbits,u32 ivbits) +{ + const char *constants; + + x->input[1] = U8TO32_LITTLE(k + 0); + x->input[2] = U8TO32_LITTLE(k + 4); + x->input[3] = U8TO32_LITTLE(k + 8); + x->input[4] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[11] = U8TO32_LITTLE(k + 0); + x->input[12] = U8TO32_LITTLE(k + 4); + x->input[13] = U8TO32_LITTLE(k + 8); + x->input[14] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[5] = U8TO32_LITTLE(constants + 4); + x->input[10] = U8TO32_LITTLE(constants + 8); + x->input[15] = U8TO32_LITTLE(constants + 12); +} + +void ECRYPT_ivsetup(ECRYPT_ctx *x,const u8 *iv) +{ + x->input[6] = U8TO32_LITTLE(iv + 0); + x->input[7] = U8TO32_LITTLE(iv + 4); + x->input[8] = 0; + x->input[9] = 0; +} + +void ECRYPT_encrypt_bytes(ECRYPT_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget; + u8 tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); + + j8 = PLUSONE(j8); + if (!j8) { + j9 = PLUSONE(j9); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[8] = j8; + x->input[9] = j9; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +void ECRYPT_decrypt_bytes(ECRYPT_ctx *x,const u8 *c,u8 *m,u32 bytes) +{ + ECRYPT_encrypt_bytes(x,c,m,bytes); +} + +void ECRYPT_keystream_bytes(ECRYPT_ctx *x,u8 *stream,u32 bytes) +{ + u32 i; + for (i = 0;i < bytes;++i) stream[i] = 0; + ECRYPT_encrypt_bytes(x,stream,stream,bytes); +} diff --git a/libs/encryption/sha1.cpp b/libs/encryption/sha1.cpp new file mode 100644 index 0000000..e2f9109 --- /dev/null +++ b/libs/encryption/sha1.cpp @@ -0,0 +1,259 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "os_types.h" +#include "sha1.h" + +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifdef WORDS_BIGENDIAN +#define blk0(i) block->l[i] +#else +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg){ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif /* VERBOSE */ + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ +void SHA1_Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1_Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (uint8_t *)"\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} diff --git a/libs/encryption/sha1.h b/libs/encryption/sha1.h new file mode 100644 index 0000000..fd308b3 --- /dev/null +++ b/libs/encryption/sha1.h @@ -0,0 +1,30 @@ +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + +#include "config_win32.h" +#include + +#ifndef __SHA1_H +#define __SHA1_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; + +#define SHA1_DIGEST_SIZE 20 + +void SHA1_Init(SHA1_CTX* context); +void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len); +void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]); + +#ifdef __cplusplus +} +#endif + +#endif /* __SHA1_H */ diff --git a/libs/fastfile/fastfile.cpp b/libs/fastfile/fastfile.cpp new file mode 100644 index 0000000..6dcdcca --- /dev/null +++ b/libs/fastfile/fastfile.cpp @@ -0,0 +1,338 @@ +#include "fastfile.h" + +#include "fastfile_cod2.h" +#include "fastfile_cod5.h" +#include "fastfile_cod7.h" +#include "fastfile_cod9.h" +#include "logmanager.h" + +#include +#include + +FastFile::FastFile() + : mStem(""), + mType(FILETYPE_NONE), + mCompany(COMPANY_NONE), + mSignage(SIGNAGE_NONE), + mMagic(""), + mVersion(0), + mZoneFile(nullptr), + mGame(""), + mPlatform("") { + +} + +FastFile::FastFile(FastFile &fastFile) + : mStem(fastFile.GetStem()), + mType(fastFile.GetType()), + mCompany(fastFile.GetCompany()), + mSignage(fastFile.GetSignage()), + mMagic(fastFile.GetMagic()), + mVersion(fastFile.GetVersion()), + mZoneFile(fastFile.GetZoneFile()), + mGame(fastFile.GetGame()), + mPlatform(fastFile.GetPlatform()) { + +} + +FastFile::~FastFile() { + +} + +QString FastFile::GetStem() const { + return mStem; +} + +FF_FILETYPE FastFile::GetType() const { + return mType; +} + +FF_COMPANY FastFile::GetCompany() const { + return mCompany; +} + +FF_SIGNAGE FastFile::GetSignage() const { + return mSignage; +} + +QString FastFile::GetMagic() const { + return mMagic; +} + +quint32 FastFile::GetVersion() const { + return mVersion; +} + +std::shared_ptr FastFile::GetZoneFile() const { + return mZoneFile; +} + +QString FastFile::GetGame() const { + return mGame; +} + +QString FastFile::GetPlatform() const { + return mPlatform; +} + +void FastFile::SetStem(const QString aStem) { + mStem = aStem; +} + +void FastFile::SetType(const FF_FILETYPE aType) { + mType = aType; +} + +void FastFile::SetCompany(const FF_COMPANY aCompany) { + mCompany = aCompany; +} + +void FastFile::SetSignage(const FF_SIGNAGE aSignage) { + mSignage = aSignage; +} + +void FastFile::SetMagic(const QString aMagic) { + mMagic = aMagic; +} + +void FastFile::SetVersion(const quint32 aVersion) { + mVersion = aVersion; +} + +void FastFile::SetZoneFile(const std::shared_ptr aZoneFile) { + mZoneFile = aZoneFile; +} + +void FastFile::SetGame(const QString aGame) { mGame = aGame; +} + +void FastFile::SetPlatform(const QString aPlatform) { + mPlatform = aPlatform; +} + +FF_COMPANY FastFile::pParseFFCompany(QDataStream *afastFileStream, quint32 &aCompanyInt) { + LogManager::instance().addEntry("Parsing company into reference..."); + // Check for null datastream ptr + if (!afastFileStream) { return COMPANY_NONE; } + // Parse company + QByteArray companyData(2, Qt::Uninitialized); + afastFileStream->readRawData(companyData.data(), 2); + aCompanyInt = companyData.toUInt(); + + if (companyData == "IW") { + return COMPANY_INFINITY_WARD; + } else if (companyData == "TA") { + return COMPANY_TREYARCH; + } else if (companyData == "Sl") { + return COMPANY_SLEDGEHAMMER; + } else if (companyData == "NX") { + return COMPANY_NEVERSOFT; + } + LogManager::instance().addEntry(QString("Failed to find company, found '%1'!").arg(companyData)); + return COMPANY_NONE; +} + +FF_COMPANY FastFile::pParseFFCompany(QDataStream *afastFileStream) { + LogManager::instance().addEntry("Parsing company..."); + // Check for null datastream ptr + if (!afastFileStream) { return COMPANY_NONE; } + // Parse company + QByteArray companyData(2, Qt::Uninitialized); + afastFileStream->readRawData(companyData.data(), 2); + + if (companyData == "IW") { + return COMPANY_INFINITY_WARD; + } else if (companyData == "TA") { + return COMPANY_TREYARCH; + } else if (companyData == "Sl") { + } else if (companyData == "NX") { + return COMPANY_NEVERSOFT; + } + LogManager::instance().addEntry(QString("Failed to find company, found '%1'!").arg(companyData)); + return COMPANY_NONE; +} + +FF_FILETYPE FastFile::pParseFFFileType(QDataStream *afastFileStream) { + // Parse filetype + QByteArray fileTypeData(2, Qt::Uninitialized); + afastFileStream->readRawData(fileTypeData.data(), 2); + if (fileTypeData == "ff") { + return FILETYPE_FAST_FILE; + } + LogManager::instance().addEntry("Failed to find file type!"); + return FILETYPE_NONE; +} + +FF_SIGNAGE FastFile::pParseFFSignage(QDataStream *afastFileStream) { + // Parse filetype + QByteArray signedData(1, Qt::Uninitialized); + afastFileStream->readRawData(signedData.data(), 1); + if (signedData == "u") { + return SIGNAGE_UNSIGNED; + } else if (signedData == "0" || signedData == "x") { + return SIGNAGE_SIGNED; + } + LogManager::instance().addEntry("Failed to determine signage of fastfile!"); + return SIGNAGE_NONE; +} + +QString FastFile::pParseFFMagic(QDataStream *afastFileStream) { + // Parse magic + QByteArray magicData(3, Qt::Uninitialized); + afastFileStream->readRawData(magicData.data(), 3); + if (magicData == "100") { + return magicData; + } + LogManager::instance().addEntry("Magic invalid!"); + return ""; +} + +quint32 FastFile::pParseFFVersion(QDataStream *afastFileStream) { + // Parse version + quint32 version; + *afastFileStream >> version; + qDebug() << QString("Found version: '%1'").arg(version); + return version; +} + +QString FastFile::pCalculateFFPlatform(quint32 aVersion) { + switch (aVersion) { + case 387: // PC World at War + case 473: // PC Black Ops 1 + case 1: // PC Modern Warfare 3 + case 147: // PC Black Ops 2 + return "PC"; + case 3640721408: // Xbox 360 Black Ops 1 + case 2449473536: // Xbox 360 Black Ops 2 + return "360"; + case 3707830272: // Wii Black Ops 1 + return "Wii"; + } + return "NONE"; +} + +QString FastFile::pCalculateFFGame(quint32 aVersion) { + QString result = "NONE"; + switch (aVersion) { + case 387: // PC World at War + result = "COD5"; + break; + case 473: // PC Black Ops 1 + break; + case 3640721408: // Xbox 360 Black Ops 1 + case 3707830272: // Wii Black Ops 1 + result = "COD7"; + break; + case 1: // PC Modern Warfare 3 + result = "COD8"; + break; + case 147: // PC Black Ops 2 + case 2449473536: // Xbox 360 Black Ops 2 + result = "COD9"; + break; + } + qDebug() << QString("Found game: '%1'").arg(result); + return result; +} + +std::shared_ptr FastFile::Open(const QString &aFilePath) { + LogManager::instance().addEntry("Processing Fastfile..."); + + if (aFilePath.isEmpty()) { + LogManager::instance().addError("Attempted to open file w/no name!"); + return nullptr; + } + LogManager::instance().addEntry("File Path: " + aFilePath); + + // Check fastfile can be read + QFile *file = new QFile(aFilePath); + if (!file->open(QIODevice::ReadOnly)) { + LogManager::instance().addError(QString("File failed to open: %1").arg(file->errorString())); + return nullptr; + } + LogManager::instance().addEntry("File opened"); + + const QByteArray data = file->readAll(); + LogManager::instance().addEntry("Contents read in"); + LogManager::instance().addEntry(QString("- Size: %1 B").arg(data.size())); + + LogManager::instance().addEntry("File closed"); + file->close(); + + // Create a QDataStream on the input data. + QDataStream fastFileStream(data); + fastFileStream.setByteOrder(QDataStream::LittleEndian); + + quint32 companyInt; + FF_COMPANY company = pParseFFCompany(&fastFileStream, companyInt); + qDebug() << "Company: " << company; + FF_FILETYPE fileType; + FF_SIGNAGE signage; + QString magic; + quint32 version; + QString platform; + QString game; + + if ((company == COMPANY_NONE) && (companyInt == 0)) { + company = COMPANY_INFINITY_WARD; + game = "COD2"; + platform = "360"; + } else { + fileType = pParseFFFileType(&fastFileStream); + signage = pParseFFSignage(&fastFileStream); + magic = pParseFFMagic(&fastFileStream); + version = pParseFFVersion(&fastFileStream); + platform = pCalculateFFPlatform(version); + game = pCalculateFFGame(version); + + LogManager::instance().addEntry(QString("Type: %1").arg(fileType)); + LogManager::instance().addEntry(QString("Signage: %1").arg(signage)); + LogManager::instance().addEntry(QString("Magic: %1").arg(magic)); + LogManager::instance().addEntry(QString("Version: %1").arg(version)); + } + LogManager::instance().addEntry(QString("Company: %1").arg(company)); + LogManager::instance().addEntry(QString("Game: %1").arg(game)); + LogManager::instance().addEntry(QString("Platform: %1").arg(platform)); + + const QString fastFileStem = aFilePath.section("/", -1, -1).section('.', 0, 0); + LogManager::instance().addEntry(QString("Stem: %1").arg(fastFileStem)); + + FastFile *fastFile; + bool validff = true; + if (game == "COD2") { + fastFile = new FastFile_COD2(); + } else if (game == "COD5") { + fastFile = new FastFile_COD5(); + } else if (game == "COD7") { + fastFile = new FastFile_COD7(); + } else if (game == "COD9") { + fastFile = new FastFile_COD9(); + } else { + validff = false; + } + + LogManager::instance().addLine(); + + if (validff) { + fastFile->SetCompany(company); + fastFile->SetStem(fastFileStem); + fastFile->Load(data); + return std::unique_ptr(fastFile); + } + + + // Open zone file after decompressing ff and writing + return nullptr; +} +bool FastFile::ExportFastFile(const QString aFastFilePath) { + QFile fastFile(aFastFilePath); + if (!fastFile.open(QIODevice::WriteOnly)) { + LogManager::instance().addEntry("Failed to write fast file! " + + fastFile.errorString()); + return false; + } + fastFile.write(GetBinaryData()); + fastFile.close(); + + return true; +} diff --git a/libs/fastfile/fastfile.h b/libs/fastfile/fastfile.h new file mode 100644 index 0000000..a1de1a2 --- /dev/null +++ b/libs/fastfile/fastfile.h @@ -0,0 +1,68 @@ +#ifndef FASTFILE_H +#define FASTFILE_H + +#include "enums.h" +#include "zonefile.h" + +#include +#include +#include + +class FastFile +{ +public: + FastFile(); + FastFile(FastFile &fastFile); + ~FastFile(); + + virtual bool Load(const QString aFilePath) = 0; + virtual bool Load(const QByteArray aData) = 0; + + virtual bool ExportFastFile(const QString aFastFilePath); + + virtual QByteArray GetBinaryData() = 0; + + virtual QString GetStem() const; + virtual FF_FILETYPE GetType() const; + virtual FF_COMPANY GetCompany() const; + virtual FF_SIGNAGE GetSignage() const; + virtual QString GetMagic() const; + virtual quint32 GetVersion() const; + virtual std::shared_ptr GetZoneFile() const; + virtual QString GetGame() const; + virtual QString GetPlatform() const; + + virtual void SetStem(const QString aStem); + virtual void SetType(const FF_FILETYPE aType); + virtual void SetCompany(const FF_COMPANY aCompany); + virtual void SetSignage(const FF_SIGNAGE aSignage); + virtual void SetMagic(const QString aMagic); + virtual void SetVersion(const quint32 aVersion); + virtual void SetZoneFile(const std::shared_ptr aZoneFile); + virtual void SetGame(const QString aGame); + virtual void SetPlatform(const QString aPlatform); + + static FF_COMPANY pParseFFCompany(QDataStream *afastFileStream, quint32 &aCompanyInt); + static FF_COMPANY pParseFFCompany(QDataStream *afastFileStream); + static FF_FILETYPE pParseFFFileType(QDataStream *afastFileStream); + static FF_SIGNAGE pParseFFSignage(QDataStream *afastFileStream); + static QString pParseFFMagic(QDataStream *afastFileStream); + static quint32 pParseFFVersion(QDataStream *afastFileStream); + static QString pCalculateFFPlatform(quint32 aVersion); + static QString pCalculateFFGame(quint32 aVersion); + + static std::shared_ptr Open(const QString& aFilePath); + +private: + QString mStem; + FF_FILETYPE mType; + FF_COMPANY mCompany; + FF_SIGNAGE mSignage; + QString mMagic; + quint32 mVersion; + std::shared_ptr mZoneFile; + QString mGame; + QString mPlatform; +}; + +#endif // FASTFILE_H diff --git a/libs/fastfile/fastfile.pro b/libs/fastfile/fastfile.pro new file mode 100644 index 0000000..9729134 --- /dev/null +++ b/libs/fastfile/fastfile.pro @@ -0,0 +1,37 @@ +QT += core widgets +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + fastfile_cod2.cpp \ + fastfile_cod5.cpp \ + fastfile_cod7.cpp \ + fastfile_cod9.cpp \ + fastfile.cpp + +HEADERS += \ + fastfile.h \ + fastfile_cod2.h \ + fastfile_cod5.h \ + fastfile_cod7.h \ + fastfile_cod9.h + +LIBS += \ + -L$$OUT_PWD/../libs/core -lcore \ + -L$$OUT_PWD/../libs/compression -lcompression \ + -L$$OUT_PWD/../libs/encryption -lencryption \ + -L$$OUT_PWD/../libs/zonefile -lzonefile + +INCLUDEPATH += \ + $$PWD/../core \ + $$PWD/../compression \ + $$PWD/../encryption \ + $$PWD/../zonefile + +DEPENDPATH += \ + $$PWD/../core \ + $$PWD/../compression \ + $$PWD/../encryption \ + $$PWD/../zonefile + +DESTDIR = $$OUT_PWD/../ diff --git a/libs/fastfile/fastfile_cod2.cpp b/libs/fastfile/fastfile_cod2.cpp new file mode 100644 index 0000000..8c58687 --- /dev/null +++ b/libs/fastfile/fastfile_cod2.cpp @@ -0,0 +1,83 @@ +#include "fastfile_cod2.h" + +#include "utils.h" +#include "compression.h" +#include "zonefile_cod2.h" + +#include +#include + +FastFile_COD2::FastFile_COD2() { + +} + +FastFile_COD2::~FastFile_COD2() { + +} + +QByteArray FastFile_COD2::GetBinaryData() { + return QByteArray(); +} + +bool FastFile_COD2::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).section(".", 0, 0); + qDebug() << "fastFileStem: " << fastFileStem; + SetStem(fastFileStem); + if (!Load(file->readAll())) { + qDebug() << "Error: Failed to load fastfile: " << fastFileStem; + return false; + } + + file->close(); + + // Open zone file after decompressing ff and writing + return true; +} + +bool FastFile_COD2::Load(const QByteArray aData) { + // Create a QDataStream on the input data. + QDataStream fastFileStream(aData); + fastFileStream.setByteOrder(QDataStream::LittleEndian); + + // Parse header values. + SetCompany(COMPANY_INFINITY_WARD); + SetType(FILETYPE_FAST_FILE); + SetSignage(SIGNAGE_UNSIGNED); + SetMagic(0); + SetVersion(0); + SetPlatform("360"); + SetGame("COD2"); + + Utils::ReadUntilHex(&fastFileStream, "78"); + QByteArray compressedData = aData.mid(fastFileStream.device()->pos()); + QByteArray decompressedData = Compression::DecompressZLIB(compressedData); + + QDir exportsDir(QDir::currentPath()); + exportsDir.mkdir("exports"); + + QFile testFile("exports/" + GetStem() + ".zone"); + if(testFile.open(QIODevice::WriteOnly)) { + testFile.write(decompressedData); + testFile.close(); + } + + // Load the zone file with the decompressed data (using an Xbox platform flag). + ZoneFile_COD2 zoneFile; + zoneFile.SetStem(GetStem()); + zoneFile.Load(decompressedData, FF_PLATFORM_XBOX); + SetZoneFile(std::make_shared(zoneFile)); + + return true; +} diff --git a/libs/fastfile/fastfile_cod2.h b/libs/fastfile/fastfile_cod2.h new file mode 100644 index 0000000..91adfe9 --- /dev/null +++ b/libs/fastfile/fastfile_cod2.h @@ -0,0 +1,18 @@ +#ifndef FASTFILE_COD2_H +#define FASTFILE_COD2_H + +#include "fastfile.h" + +class FastFile_COD2 : public FastFile +{ +public: + FastFile_COD2(); + ~FastFile_COD2(); + + QByteArray GetBinaryData() override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD2_H diff --git a/libs/fastfile/fastfile_cod5.cpp b/libs/fastfile/fastfile_cod5.cpp new file mode 100644 index 0000000..50d11f2 --- /dev/null +++ b/libs/fastfile/fastfile_cod5.cpp @@ -0,0 +1,88 @@ +#include "fastfile_cod5.h" +#include "zonefile_cod5.h" + +#include "utils.h" +#include "compression.h" +#include "statusbarmanager.h" + +#include +#include + +FastFile_COD5::FastFile_COD5() { + +} + +FastFile_COD5::~FastFile_COD5() { + +} + +QByteArray FastFile_COD5::GetBinaryData() { + return QByteArray(); +} + +bool FastFile_COD5::Load(const QString aFilePath) { + StatusBarManager::instance().updateStatus("Loading COD5 Fast File w/path", 1000); + + 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; +} + +bool FastFile_COD5::Load(const QByteArray aData) { + StatusBarManager::instance().updateStatus("Loading COD5 Fast File w/data", 1000); + QByteArray decompressedData; + + // Create a QDataStream on the input data. + QDataStream fastFileStream(aData); + fastFileStream.setByteOrder(QDataStream::LittleEndian); + + // 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"); + + // For COD5, simply decompress from offset 12. + decompressedData = Compression::DecompressZLIB(aData.mid(12)); + + Utils::ExportData(GetStem() + ".zone", decompressedData); + + FF_PLATFORM platform = FF_PLATFORM_NONE; + if (platformStr == "PC") { + platform = FF_PLATFORM_PC; + } else if (platformStr == "360") { + platform = FF_PLATFORM_XBOX; + } + + ZoneFile_COD5 zoneFile; + zoneFile.SetStem(GetStem()); + zoneFile.Load(decompressedData, platform); + SetZoneFile(std::make_shared(zoneFile)); + + return true; +} diff --git a/libs/fastfile/fastfile_cod5.h b/libs/fastfile/fastfile_cod5.h new file mode 100644 index 0000000..0622ce3 --- /dev/null +++ b/libs/fastfile/fastfile_cod5.h @@ -0,0 +1,18 @@ +#ifndef FASTFILE_COD5_H +#define FASTFILE_COD5_H + +#include "fastfile.h" + +class FastFile_COD5 : public FastFile +{ +public: + FastFile_COD5(); + ~FastFile_COD5(); + + QByteArray GetBinaryData() override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD5_H diff --git a/libs/fastfile/fastfile_cod7.cpp b/libs/fastfile/fastfile_cod7.cpp new file mode 100644 index 0000000..92f34c2 --- /dev/null +++ b/libs/fastfile/fastfile_cod7.cpp @@ -0,0 +1,168 @@ +#include "fastfile_cod7.h" +#include "zonefile_cod7.h" + +#include "utils.h" +#include "compression.h" +#include "encryption.h" + +#include +#include + +FastFile_COD7::FastFile_COD7() { + +} + +FastFile_COD7::~FastFile_COD7() { + +} + +QByteArray FastFile_COD7::GetBinaryData() { + return QByteArray(); +} + +bool FastFile_COD7::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; +} + +bool FastFile_COD7::Load(const QByteArray aData) { + QByteArray decompressedData; + + // Create a QDataStream on the input data. + QDataStream fastFileStream(aData); + fastFileStream.setByteOrder(QDataStream::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("COD7"); + + // Load the zone file with the decompressed data (using an Xbox platform flag). + ZoneFile_COD7 zoneFile; + zoneFile.SetStem(GetStem()); + + // For COD7/COD9, use BigEndian. + fastFileStream.setByteOrder(QDataStream::BigEndian); + if (GetPlatform() == "PC") { + fastFileStream.setByteOrder(QDataStream::LittleEndian); + + // Select key based on game. + QByteArray key; + fastFileStream.skipRawData(4); + if (GetPlatform() == "360") { + key = QByteArray::fromHex("1ac1d12d527c59b40eca619120ff8217ccff09cd16896f81b829c7f52793405d"); + } else if (GetPlatform() == "PS3") { + key = QByteArray::fromHex("46D3F997F29C9ACE175B0DAE3AB8C0C1B8E423E2E3BF7E3C311EA35245BF193A"); + // or + // key = QByteArray::fromHex("0C99B3DDB8D6D0845D1147E470F28A8BF2AE69A8A9F534767B54E9180FF55370"); + } + + // Read the 8-byte magic. + QByteArray fileMagic(8, Qt::Uninitialized); + fastFileStream.readRawData(fileMagic.data(), 8); + if (fileMagic != "PHEEBs71") { + qWarning() << "Invalid fast file magic!"; + return false; + } + fastFileStream.skipRawData(4); + + // Read IV table name (32 bytes). + QByteArray fileName(32, Qt::Uninitialized); + fastFileStream.readRawData(fileName.data(), 32); + + // Build the IV table from the fileName. + QByteArray ivTable = Encryption::InitIVTable(fileName); + + // Skip the RSA signature (256 bytes). + QByteArray rsaSignature(256, Qt::Uninitialized); + fastFileStream.readRawData(rsaSignature.data(), 256); + + // Now the stream should be positioned at 0x13C, where sections begin. + int sectionIndex = 0; + while (true) { + qint32 sectionSize = 0; + fastFileStream >> sectionSize; + qDebug() << "Section index:" << sectionIndex << "Size:" << sectionSize + << "Pos:" << fastFileStream.device()->pos(); + if (sectionSize == 0) + break; + + // Read the section data. + QByteArray sectionData; + sectionData.resize(sectionSize); + fastFileStream.readRawData(sectionData.data(), sectionSize); + + // Compute the IV for this section. + QByteArray iv = Encryption::GetIV(ivTable, sectionIndex); + + // Decrypt the section using Salsa20. + QByteArray decData = Encryption::salsa20DecryptSection(sectionData, key, iv); + + // Compute SHA1 hash of the decrypted data. + QByteArray sectionHash = QCryptographicHash::hash(decData, QCryptographicHash::Sha1); + + // Update the IV table based on the section hash. + Encryption::UpdateIVTable(ivTable, sectionIndex, sectionHash); + + // Build a compressed data buffer by prepending the two-byte zlib header. + QByteArray compressedData; + compressedData.append(char(0x78)); + compressedData.append(char(0x01)); + compressedData.append(decData); + + decompressedData.append(Compression::DecompressZLIB(compressedData)); + + sectionIndex++; + } + + zoneFile.Load(decompressedData, FF_PLATFORM_XBOX); + } else if (GetPlatform() == "Wii") { + // For COD7, simply decompress from offset 12. + decompressedData = Compression::DecompressZLIB(aData.mid(12)); + + Utils::ExportData(GetStem() + ".zone", decompressedData); + + QDir workingDir = QDir::currentPath(); + workingDir.mkdir("exports"); + + QFile outputFile("exports/" + GetStem() + ".zone"); + if (!outputFile.open(QIODevice::WriteOnly)) { + qDebug() << "Failed to extract IPAK file."; + } + qDebug() << " - File Name: " << outputFile.fileName(); + outputFile.write(decompressedData); + outputFile.close(); + + zoneFile.Load(decompressedData, FF_PLATFORM_WII); + } + + SetZoneFile(std::make_shared(zoneFile)); + + return true; +} diff --git a/libs/fastfile/fastfile_cod7.h b/libs/fastfile/fastfile_cod7.h new file mode 100644 index 0000000..ae2d478 --- /dev/null +++ b/libs/fastfile/fastfile_cod7.h @@ -0,0 +1,18 @@ +#ifndef FASTFILE_COD7_H +#define FASTFILE_COD7_H + +#include "fastfile.h" + +class FastFile_COD7 : public FastFile +{ +public: + FastFile_COD7(); + ~FastFile_COD7(); + + QByteArray GetBinaryData() override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD7_H diff --git a/libs/fastfile/fastfile_cod9.cpp b/libs/fastfile/fastfile_cod9.cpp new file mode 100644 index 0000000..353b0a0 --- /dev/null +++ b/libs/fastfile/fastfile_cod9.cpp @@ -0,0 +1,114 @@ +#include "fastfile_cod9.h" +#include "zonefile_cod9.h" +#include "encryption.h" + +#include +#include + +FastFile_COD9::FastFile_COD9() { + +} + +FastFile_COD9::~FastFile_COD9() { + +} + +QByteArray FastFile_COD9::GetBinaryData() { + return QByteArray(); +} + +bool FastFile_COD9::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).section(".", 0, 0); + SetStem(fastFileStem); + if (!Load(file->readAll())) { + qDebug() << "Error: Failed to load fastfile: " << fastFileStem + ".ff"; + return false; + } + + file->close(); + + // Open zone file after decompressing ff and writing + return true; +} + +bool FastFile_COD9::Load(const QByteArray aData) { + QByteArray decompressedData; + + // Create a QDataStream on the input data. + QDataStream fastFileStream(aData); + fastFileStream.setByteOrder(QDataStream::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(QDataStream::BigEndian); + if (GetPlatform() == "PC") { + fastFileStream.setByteOrder(QDataStream::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. + 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); + + // 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(aData); + } + + // For COD9, write out the complete decompressed zone for testing. + QFile testFile("exports/" + GetStem() + ".zone"); + if(testFile.open(QIODevice::WriteOnly)) { + testFile.write(decompressedData); + testFile.close(); + } + + // Load the zone file with the decompressed data (using an Xbox platform flag). + ZoneFile_COD9 zoneFile; + zoneFile.SetStem(GetStem()); + zoneFile.Load(decompressedData, FF_PLATFORM_PC); + SetZoneFile(std::make_shared(zoneFile)); + + return true; +} diff --git a/libs/fastfile/fastfile_cod9.h b/libs/fastfile/fastfile_cod9.h new file mode 100644 index 0000000..fdc3d16 --- /dev/null +++ b/libs/fastfile/fastfile_cod9.h @@ -0,0 +1,18 @@ +#ifndef FASTFILE_COD9_H +#define FASTFILE_COD9_H + +#include "fastfile.h" + +class FastFile_COD9 : public FastFile +{ +public: + FastFile_COD9(); + ~FastFile_COD9(); + + QByteArray GetBinaryData() override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD9_H diff --git a/ipak_structs.h b/libs/ipakfile/ipak_structs.h similarity index 100% rename from ipak_structs.h rename to libs/ipakfile/ipak_structs.h diff --git a/libs/ipakfile/ipakfile.cpp b/libs/ipakfile/ipakfile.cpp new file mode 100644 index 0000000..e864f00 --- /dev/null +++ b/libs/ipakfile/ipakfile.cpp @@ -0,0 +1 @@ +#include "ipak_structs.h" diff --git a/libs/ipakfile/ipakfile.pro b/libs/ipakfile/ipakfile.pro new file mode 100644 index 0000000..73422b6 --- /dev/null +++ b/libs/ipakfile/ipakfile.pro @@ -0,0 +1,11 @@ +QT += core +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + ipakfile.cpp + +HEADERS += \ + ipak_structs.h + +DESTDIR = $$OUT_PWD/../ diff --git a/iwifile.cpp b/libs/iwifile/iwifile.cpp similarity index 100% rename from iwifile.cpp rename to libs/iwifile/iwifile.cpp diff --git a/iwifile.h b/libs/iwifile/iwifile.h similarity index 96% rename from iwifile.h rename to libs/iwifile/iwifile.h index c1c4d2d..0af67bd 100644 --- a/iwifile.h +++ b/libs/iwifile/iwifile.h @@ -1,7 +1,6 @@ #ifndef IWIFILE_H #define IWIFILE_H -#include "qimage.h" #include #include #include @@ -9,7 +8,6 @@ #include #include #include -#include // Supported versions static const QVector supportedVersions = { diff --git a/libs/iwifile/iwifile.pro b/libs/iwifile/iwifile.pro new file mode 100644 index 0000000..9c972c3 --- /dev/null +++ b/libs/iwifile/iwifile.pro @@ -0,0 +1,25 @@ +QT += core +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + iwifile.cpp + +HEADERS += \ + iwifile.h + +LIBS += \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lDevIL \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lILU \ + -L$$PWD/../../third_party/devil_sdk/lib/ -lILUT \ + -L$$OUT_PWD/../libs/ddsfile -lddsfile + +INCLUDEPATH += \ + $$PWD/../../third_party/devil_sdk/include/ \ + $$PWD/../ddsfile + +DEPENDPATH += \ + $$PWD/../../third_party/devil_sdk/include/ \ + $$PWD/../ddsfile + +DESTDIR = $$OUT_PWD/../ diff --git a/asset_structs.h b/libs/zonefile/asset_structs.h similarity index 92% rename from asset_structs.h rename to libs/zonefile/asset_structs.h index f3bb206..1ed6c16 100644 --- a/asset_structs.h +++ b/libs/zonefile/asset_structs.h @@ -6,6 +6,7 @@ #include #include #include +#include struct LocalString { QString string; @@ -142,6 +143,8 @@ struct StringTable { quint32 columnCount; quint32 rowCount; QString name; + QVector tablePointers; + QMap content; }; struct Image { @@ -174,6 +177,20 @@ struct Image { quint32 unknown9; }; +struct Material { + quint32 namePtr; + QString name; + quint32 refNamePtr; + QString refName; + quint8 unknownA[12]; + quint32 stateBits[2]; + quint16 textureCount; + quint16 constCount; + quint32 techSetPtr; + quint32 texturePtr; + quint32 constPtr; +}; + struct Menu { QString filePath; QString name; @@ -319,16 +336,30 @@ struct MenuFile { QVector menuDefs; }; +struct Sound { + QString path; + QString alias; + quint32 dataPtr; + quint32 dataLength; + QByteArray data; +}; + +struct SoundAsset { + QString name; + quint32 count; + QVector sounds; +}; + struct AssetMap { QVector localStrings; QVector rawFiles; //QVector phyPresets; QVector models; - //QVector rawFiles; + QVector materials; //QVector shaders; QVector techSets; QVector images; - //QVector sounds; + QVector sounds; //QVector collMaps; //QVector lightDefs; //QVector uiMaps; diff --git a/libs/zonefile/zonefile.cpp b/libs/zonefile/zonefile.cpp new file mode 100644 index 0000000..ac9aae8 --- /dev/null +++ b/libs/zonefile/zonefile.cpp @@ -0,0 +1,161 @@ +#include "zonefile.h" +#include "utils.h" +#include "logmanager.h" + +#include +#include +#include + +ZoneFile::ZoneFile() : + mStem(), + mSize(), + mTagCount(), + mTags(), + mRecordCount(), + mRecords(), + mAssetMap() { + +} + +ZoneFile::~ZoneFile() { + +} + +ZoneFile::ZoneFile(const ZoneFile &aZoneFile) { + mStem = aZoneFile.mStem; + mSize = aZoneFile.mSize; + mTagCount = aZoneFile.mTagCount; + mTags = aZoneFile.mTags; + mRecordCount = aZoneFile.mRecordCount; + mRecords = aZoneFile.mRecords; + mAssetMap = aZoneFile.mAssetMap; +} + +ZoneFile &ZoneFile::operator=(const ZoneFile &other) { + if (this != &other) { + mStem = other.mStem; + mSize = other.mSize; + mTagCount = other.mTagCount; + mTags = other.mTags; + mRecordCount = other.mRecordCount; + mRecords = other.mRecords; + mAssetMap = other.mAssetMap; + } + return *this; +} + +QIcon ZoneFile::AssetStrToIcon(const QString aAssetStr) { + const QString cleanedType = aAssetStr.toUpper(); + if (cleanedType == "LOCAL STRING") { // localized string PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_StringFile.png"); + } else if (cleanedType == "RAW FILE") { // raw_file PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_RawFile.png"); + } else if (cleanedType == "GSC FILE") { // raw_file PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_GSCFile.png"); + } else if (cleanedType == "EFFECT") { // fx PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Effect.png"); + } else if (cleanedType == "SOUND") { // loaded_sound PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Sound.png"); + } else if (cleanedType == "ANIMATION") { // x_anim PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Animation.png"); + } else if (cleanedType == "COLLISION MAP") { // collision_map PARTIALLY VERIFIED + //return "COLLISION MAP"; + } else if (cleanedType == "STRING TABLE") { // string_table PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_StringTable.png"); + } else if (cleanedType == "MENU") { // menu_file PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_MenuFile.png"); + } else if (cleanedType == "TECH SET") { // tech set PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_TechSetFile.png"); + } else if (cleanedType == "WEAPON") { // weapon PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Weapon.png"); + } else if (cleanedType == "GFX MAP") { // gfx map PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_FXMap.png"); + } else if (cleanedType == "LIGHT DEF") { // light_def PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_LightDef.png"); + } else if (cleanedType == "FONT") { // font PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Font.png"); + } else if (cleanedType == "MODEL") { // xmodel PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Model.png"); + } else if (cleanedType == "D3DBSP") { // d3dbsp PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_BSP.png"); + } else if (cleanedType == "IMAGE") { // image PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Image.png"); + } else if (cleanedType == "GAME MAP SP") { // game map sp PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_GameMapSp.png"); + } else if (cleanedType == "COL MAP SP") { // col map sp PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_ColMapSp.png"); + } else if (cleanedType == "PHYS PRESET") { // col map sp PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_PhysPreset.png"); + } else if (cleanedType == "DESTRUCTIBLE") { // col map sp PARTIALLY VERIFIED + return QIcon(":/icons/icons/Icon_Destructible.png"); + } + return QIcon(); +} + +bool ZoneFile::SaveZoneFile(const QString aZoneFilePath) { + QFile zoneFile(aZoneFilePath); + if (!zoneFile.open(QIODevice::WriteOnly)) { + LogManager::instance().addEntry("Failed to write zone file! " + zoneFile.errorString()); + return false; + } + zoneFile.write(GetBinaryData()); + zoneFile.close(); + return true; +} + +QString ZoneFile::GetStem() { + return mStem; +} + +quint32 ZoneFile::GetSize() { + return mSize; +} + +quint32 ZoneFile::GetTagCount() { + return mTagCount; +} + +QStringList ZoneFile::GetTags() { + return mTags; +} + +quint32 ZoneFile::GetRecordCount() { + return mRecordCount; +} + +QStringList ZoneFile::GetRecords() { + return mRecords; +} + +AssetMap ZoneFile::GetAssetMap() { + return mAssetMap; +} + +void ZoneFile::SetStem(const QString aStem) { + mStem = aStem; +} + +void ZoneFile::SetSize(quint32 aSize) { + mSize = aSize; +} + +void ZoneFile::SetTagCount(quint32 aTagCount) { + mTagCount = aTagCount; +} + +void ZoneFile::SetTags(const QStringList aTags) { + mTags = aTags; +} + +void ZoneFile::SetRecordCount(quint32 aRecordCount) { + mRecordCount = aRecordCount; +} + +void ZoneFile::SetRecords(const QStringList aRecords) { + mRecords = aRecords; +} + +void ZoneFile::SetAssetMap(const AssetMap aAssetMap) { + mAssetMap = aAssetMap; +} + diff --git a/libs/zonefile/zonefile.h b/libs/zonefile/zonefile.h new file mode 100644 index 0000000..1e08020 --- /dev/null +++ b/libs/zonefile/zonefile.h @@ -0,0 +1,87 @@ +#ifndef ZONEFILE_H +#define ZONEFILE_H + +#include "asset_structs.h" +#include "qicon.h" + +#include + +class ZoneFile +{ +public: + ZoneFile(); + ~ZoneFile(); + ZoneFile(const ZoneFile &aZoneFile); + ZoneFile &operator=(const ZoneFile &other); + + virtual bool Load(const QByteArray aFileData, FF_PLATFORM platform = FF_PLATFORM_NONE) = 0; + virtual QString AssetTypeToString(const QString aAssetType) = 0; + + QIcon AssetStrToIcon(const QString aAssetStr); + + virtual QByteArray GetBinaryData() = 0; + virtual bool SaveZoneFile(const QString aZoneFilePath); + + QString GetStem(); + quint32 GetSize(); + quint32 GetTagCount(); + QStringList GetTags(); + quint32 GetRecordCount(); + QStringList GetRecords(); + AssetMap GetAssetMap(); + + void SetStem(const QString aStem); + void SetSize(quint32 aSize); + void SetTagCount(quint32 aTagCount); + void SetTags(const QStringList aTags); + void SetRecordCount(quint32 aRecordCount); + void SetRecords(const QStringList aRecords); + void SetAssetMap(const AssetMap aAssetMap); + +private slots: + virtual void pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) = 0; + virtual quint32 pParseZoneSize(QDataStream *aZoneFileStream) = 0; + virtual void pParseZoneUnknownsA(QDataStream *aZoneFileStream) = 0; + virtual quint32 pParseZoneTagCount(QDataStream *aZoneFileStream) = 0; + virtual quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream) = 0; + virtual void pParseZoneUnknownsB(QDataStream *aZoneFileStream) = 0; + virtual void pParseZoneUnknownsC(QDataStream *aZoneFileStream) = 0; + virtual QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) = 0; + virtual QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) = 0; + virtual AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) = 0; + virtual LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream) = 0; + virtual RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_PhysPreset(QDataStream *aZoneFileStream) = 0; + virtual Model pParseAsset_Model(QDataStream *aZoneFileStream) = 0; + virtual Material pParseAsset_Material(QDataStream *aZoneFileStream) = 0; + virtual Shader pParseAsset_Shader(QDataStream *aZoneFileStream) = 0; + virtual TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream) = 0; + virtual Image pParseAsset_Image(QDataStream *aZoneFileStream) = 0; + virtual SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_ColMapMP(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_GameMapSP(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_GameMapMP(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_LightDef(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_UIMap(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_AIType(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_FX(QDataStream *aZoneFileStream) = 0; + virtual Animation pParseAsset_Animation(QDataStream *aZoneFileStream) = 0; + virtual MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_Weapon(QDataStream *aZoneFileStream) = 0; + virtual void pParseAsset_D3DBSP(QDataStream *aZoneFileStream) = 0; + virtual StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream) = 0; + +private: + QString mStem; + quint32 mSize; + quint32 mTagCount; + QStringList mTags; + quint32 mRecordCount; + QStringList mRecords; + AssetMap mAssetMap; + QString mPlatform; + QString mGame; +}; + +#endif // ZONEFILE_H diff --git a/libs/zonefile/zonefile.pro b/libs/zonefile/zonefile.pro new file mode 100644 index 0000000..ec85195 --- /dev/null +++ b/libs/zonefile/zonefile.pro @@ -0,0 +1,29 @@ +QT += core widgets +TEMPLATE = lib +CONFIG += staticlib c++17 + +SOURCES += \ + zonefile.cpp \ + zonefile_cod2.cpp \ + zonefile_cod5.cpp \ + zonefile_cod7.cpp \ + zonefile_cod9.cpp + +HEADERS += \ + asset_structs.h \ + zonefile.h \ + zonefile_cod2.h \ + zonefile_cod5.h \ + zonefile_cod7.h \ + zonefile_cod9.h + +LIBS += \ + -L$$OUT_PWD/../libs/core -lcore + +INCLUDEPATH += \ + $$PWD/../core + +DEPENDPATH += \ + $$PWD/../core + +DESTDIR = $$OUT_PWD/../ diff --git a/zonefile.cpp b/libs/zonefile/zonefile_cod2.cpp similarity index 63% rename from zonefile.cpp rename to libs/zonefile/zonefile_cod2.cpp index e423d78..d27599e 100644 --- a/zonefile.cpp +++ b/libs/zonefile/zonefile_cod2.cpp @@ -1,142 +1,53 @@ -#include "zonefile.h" -#include "utils.h" +#include "zonefile_cod2.h" -#include #include +#include #include -ZoneFile::ZoneFile() : - fileStem(), - size(), - tagCount(), - tags(), - recordCount(), - records(), - assetMap() { +ZoneFile_COD2::ZoneFile_COD2() +{ } -ZoneFile::~ZoneFile() { +ZoneFile_COD2::~ZoneFile_COD2() +{ } -ZoneFile::ZoneFile(const ZoneFile &aZoneFile) { - fileStem = aZoneFile.fileStem; - size = aZoneFile.size; - tagCount = aZoneFile.tagCount; - tags = aZoneFile.tags; - recordCount = aZoneFile.recordCount; - records = aZoneFile.records; - assetMap = aZoneFile.assetMap; -} - -ZoneFile &ZoneFile::operator=(const ZoneFile &other) { - if (this != &other) { - fileStem = other.fileStem; - size = other.size; - tagCount = other.tagCount; - tags = other.tags; - recordCount = other.recordCount; - records = other.records; - assetMap = other.assetMap; - } - return *this; -} - -bool ZoneFile::Load(const QString aFilePath, FF_PLATFORM platform) { - Q_UNUSED(platform); - - if (aFilePath.isEmpty()) { - return false; - } - - fileStem = aFilePath.split('/').last(); - - // Check zone file can be read - QFile *zoneFileObj = new QFile(aFilePath); - if (!zoneFileObj->open(QIODevice::ReadOnly)) { - qDebug() << QString("Error: %1 could not be read!.").arg(aFilePath); - return false; - } else if (!Load(zoneFileObj->readAll())) { - qDebug() << "Error: Failed to load from zone data"; - return false; - } - - // Clean up zone file - zoneFileObj->close(); - delete zoneFileObj; - - return true; -} - -bool ZoneFile::Load(const QByteArray aFileData, const QString aFileStem, FF_PLATFORM platform) { +bool ZoneFile_COD2::Load(const QByteArray aFileData, FF_PLATFORM aPlatform) { // Open zone file as little endian stream QDataStream zoneFileStream(aFileData); - if (platform == FF_PLATFORM_PC) { + if (aPlatform == FF_PLATFORM_PC) { zoneFileStream.setByteOrder(QDataStream::LittleEndian); } else { zoneFileStream.setByteOrder(QDataStream::BigEndian); } // Parse data from zone file header - pParseZoneHeader(&zoneFileStream); - fileStem = aFileStem; - records = - pParseZoneIndex(&zoneFileStream, recordCount); - assetMap = - pParseAssets(&zoneFileStream, records); + pParseZoneHeader(&zoneFileStream, aPlatform); + SetRecords(pParseZoneIndex(&zoneFileStream, GetRecordCount())); + SetAssetMap(pParseAssets(&zoneFileStream, GetRecords())); return true; } -QString ZoneFile::GetFileStem() { - return fileStem; -} - -quint32 ZoneFile::GetSize() { - return size; -} - -quint32 ZoneFile::GetTagCount() { - return tagCount; -} - -QStringList ZoneFile::GetTags() { - return tags; -} - -quint32 ZoneFile::GetRecordCount() { - return recordCount; -} - -QStringList ZoneFile::GetRecords() { - return records; -} - -AssetMap ZoneFile::GetAssetMap() { - return assetMap; -} - -void ZoneFile::pParseZoneHeader(QDataStream *aZoneFileStream) { - size = pParseZoneSize(aZoneFileStream); - pParseZoneUnknownsA(aZoneFileStream); - - tagCount = pParseZoneTagCount(aZoneFileStream); +void ZoneFile_COD2::pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) { + SetTagCount(pParseZoneTagCount(aZoneFileStream)); pParseZoneUnknownsB(aZoneFileStream); + pParseZoneUnknownsC(aZoneFileStream); + SetRecordCount(pParseZoneRecordCount(aZoneFileStream)); - recordCount = pParseZoneRecordCount(aZoneFileStream); + aZoneFileStream->skipRawData(4); + quint32 tagCount = GetTagCount(); if (tagCount) { - pParseZoneUnknownsC(aZoneFileStream); - if (tagCount > 1) { - tags = pParseZoneTags(aZoneFileStream, tagCount); - } + SetTags(pParseZoneTags(aZoneFileStream, tagCount)); } else { aZoneFileStream->skipRawData(4); } } -quint32 ZoneFile::pParseZoneSize(QDataStream *aZoneFileStream) { +quint32 ZoneFile_COD2::pParseZoneSize(QDataStream *aZoneFileStream) { quint32 zoneFileSize; *aZoneFileStream >> zoneFileSize; if (zoneFileSize <= 0) { @@ -152,7 +63,7 @@ quint32 ZoneFile::pParseZoneSize(QDataStream *aZoneFileStream) { Parses the 1st section of unknowns as hex vals and uint32s */ -void ZoneFile::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { // Byte 4-7, 8-11, 12-15: unknown QByteArray unknown1(4, Qt::Uninitialized); aZoneFileStream->readRawData(unknown1.data(), 4); @@ -187,7 +98,7 @@ void ZoneFile::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { Parses the number of string tags in the zone index */ -quint32 ZoneFile::pParseZoneTagCount(QDataStream *aZoneFileStream) { +quint32 ZoneFile_COD2::pParseZoneTagCount(QDataStream *aZoneFileStream) { quint32 tagCount; *aZoneFileStream >> tagCount; return tagCount; @@ -198,7 +109,7 @@ quint32 ZoneFile::pParseZoneTagCount(QDataStream *aZoneFileStream) { Parses the number of records in the zone index */ -quint32 ZoneFile::pParseZoneRecordCount(QDataStream *aZoneFileStream) { +quint32 ZoneFile_COD2::pParseZoneRecordCount(QDataStream *aZoneFileStream) { quint32 recordCount; *aZoneFileStream >> recordCount; return recordCount; @@ -209,7 +120,7 @@ quint32 ZoneFile::pParseZoneRecordCount(QDataStream *aZoneFileStream) { Parses the 2nd section of unknowns as hex vals and uint32s */ -void ZoneFile::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { // Byte 44-47: Unknown/empty? QByteArray unknown9(4, Qt::Uninitialized); aZoneFileStream->readRawData(unknown9.data(), 4); @@ -220,7 +131,7 @@ void ZoneFile::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { Parses the 3rd section of unknowns as hex vals and uint32s */ -void ZoneFile::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { // Byte 40-43: Unknown/empty? QByteArray unknown10(4, Qt::Uninitialized); aZoneFileStream->readRawData(unknown10.data(), 4); @@ -235,11 +146,11 @@ void ZoneFile::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { Parses the string tags ate the start of zone file */ -QStringList ZoneFile::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { +QStringList ZoneFile_COD2::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { QStringList tags; // Byte 48-51: Repeated separators? ÿÿÿÿ x i - aZoneFileStream->skipRawData(4 * (tagCount - 1)); + aZoneFileStream->skipRawData(4 * tagCount); // Parse tags/strings before index QString zoneTag; @@ -252,9 +163,6 @@ QStringList ZoneFile::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCo } tags << zoneTag; zoneTag.clear(); - - qDebug() << "Peek: " << aZoneFileStream->device()->peek(8) << aZoneFileStream->device()->peek(8).contains(QByteArray::fromHex("FFFFFFFF")); - if (aZoneFileStream->device()->peek(8).contains(QByteArray::fromHex("FFFFFFFF"))) { break; } } return tags; } @@ -264,14 +172,14 @@ QStringList ZoneFile::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCo Parse the binary zone index data and populate table */ -QStringList ZoneFile::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { +QStringList ZoneFile_COD2::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { QStringList result; // Don't parse if no records if (!recordCount) { return result; } // Parse index & map found asset types - for (quint32 i = 0; i <= recordCount; i++) { + for (quint32 i = 0; i < recordCount; i++) { // Skip record start QByteArray rawAssetType(4, Qt::Uninitialized); aZoneFileStream->readRawData(rawAssetType.data(), 4); @@ -283,14 +191,14 @@ QStringList ZoneFile::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 reco return result; } -AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { +AssetMap ZoneFile_COD2::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { AssetMap result; - aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); + //aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); for (int i = 0; i < assetOrder.size(); i++) { const QString typeHex = assetOrder[i]; - const QString typeStr = Utils::AssetTypeToString(typeHex); + const QString typeStr = AssetTypeToString(typeHex); if (typeStr == "LOCAL STRING") { // localized string asset result.localStrings << pParseAsset_LocalString(aZoneFileStream); @@ -304,7 +212,7 @@ AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetO } else if (typeStr == "MODEL") { // xmodel result.models << pParseAsset_Model(aZoneFileStream); } else if (typeStr == "MATERIAL") { // material - pParseAsset_Material(aZoneFileStream); + result.materials << pParseAsset_Material(aZoneFileStream); } else if (typeStr == "SHADER") { // pixelshader pParseAsset_Shader(aZoneFileStream); } else if (typeStr == "TECH SET") { // techset include @@ -312,7 +220,7 @@ AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetO } else if (typeStr == "IMAGE") { // image result.images << pParseAsset_Image(aZoneFileStream); } else if (typeStr == "SOUND") { // loaded_sound - pParseAsset_LoadedSound(aZoneFileStream); + result.sounds << pParseAsset_Sound(aZoneFileStream); } else if (typeStr == "COLLISION MAP") { // col_map_mp pParseAsset_ColMapMP(aZoneFileStream); } else if (typeStr == "MP MAP") { // game_map_sp @@ -346,7 +254,7 @@ AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetO return result; } -LocalString ZoneFile::pParseAsset_LocalString(QDataStream *aZoneFileStream) { +LocalString ZoneFile_COD2::pParseAsset_LocalString(QDataStream *aZoneFileStream) { LocalString result; quint32 stringPtr, aliasPtr; @@ -380,7 +288,7 @@ LocalString ZoneFile::pParseAsset_LocalString(QDataStream *aZoneFileStream) { return result; } -RawFile ZoneFile::pParseAsset_RawFile(QDataStream *aZoneFileStream) { +RawFile ZoneFile_COD2::pParseAsset_RawFile(QDataStream *aZoneFileStream) { RawFile result; // Skip start separator FF FF FF FF (pointer?) @@ -415,20 +323,20 @@ RawFile ZoneFile::pParseAsset_RawFile(QDataStream *aZoneFileStream) { return result; } -void ZoneFile::pParseAsset_PhysPreset(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_PhysPreset(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -Model ZoneFile::pParseAsset_Model(QDataStream *aZoneFileStream) { +Model ZoneFile_COD2::pParseAsset_Model(QDataStream *aZoneFileStream) { Model result; qDebug() << "Model Info:"; *aZoneFileStream >> result.namePtr >> result.tagCount >> result.rootTagCount - >> result.surfCount >> result.unknownCount >> result.boneNamePtr - >> result.parentListPtr >> result.quatsPtr >> result.transPtr - >> result.partClassPtr >> result.baseMatPtr - >> result.surfsPtr >> result.materialHandlesPtr; + >> result.surfCount >> result.unknownCount >> result.boneNamePtr + >> result.parentListPtr >> result.quatsPtr >> result.transPtr + >> result.partClassPtr >> result.baseMatPtr + >> result.surfsPtr >> result.materialHandlesPtr; // Parse XModelLodInfo for (int i = 1; i <= 4; i++) { @@ -443,16 +351,16 @@ Model ZoneFile::pParseAsset_Model(QDataStream *aZoneFileStream) { aZoneFileStream->skipRawData(4); *aZoneFileStream >> result.lodInfo[i].partBits[0] - >> result.lodInfo[i].partBits[1] - >> result.lodInfo[i].partBits[2] - >> result.lodInfo[i].partBits[3]; + >> result.lodInfo[i].partBits[1] + >> result.lodInfo[i].partBits[2] + >> result.lodInfo[i].partBits[3]; } *aZoneFileStream >> result.collSurfsPtr >> result.numCollSurfs >> result.contents >> result.boneInfoPtr; quint32 intRadius, intMins[3], intMaxs[3]; *aZoneFileStream >> intRadius >> intMins[0] >> intMins[1] - >> intMins[2] >> intMaxs[0] >> intMaxs[1] >> intMaxs[2]; + >> intMins[2] >> intMaxs[0] >> intMaxs[1] >> intMaxs[2]; std::memcpy(&result.radius, &intRadius, sizeof(result.radius)); @@ -465,7 +373,7 @@ Model ZoneFile::pParseAsset_Model(QDataStream *aZoneFileStream) { std::memcpy(&result.maxs[2], &intMaxs[2], sizeof(result.maxs[3])); *aZoneFileStream >> result.numLods >> result.collLod >> result.streamInfoPtr - >> result.memUsage >> result.flags >> result.physPresetPtr >> result.physGeomsPtr; + >> result.memUsage >> result.flags >> result.physPresetPtr >> result.physGeomsPtr; // Parse model name char modelNameChar; @@ -480,11 +388,7 @@ Model ZoneFile::pParseAsset_Model(QDataStream *aZoneFileStream) { return result; } -void ZoneFile::pParseAsset_Material(QDataStream *aZoneFileStream) { - Q_UNUSED(aZoneFileStream); -} - -Shader ZoneFile::pParseAsset_Shader(QDataStream *aZoneFileStream) { +Shader ZoneFile_COD2::pParseAsset_Shader(QDataStream *aZoneFileStream) { Shader result = Shader(); quint8 minorVersion, majorVersion; @@ -527,75 +431,7 @@ Shader ZoneFile::pParseAsset_Shader(QDataStream *aZoneFileStream) { return result; } -bool ZoneFile::pReadUntilString(QDataStream* stream, const QString& targetString) { - if (!stream || targetString.isEmpty()) { - return false; // Invalid input - } - - QByteArray buffer; - QByteArray targetBytes = targetString.toUtf8(); // Handle multibyte characters - const int targetLength = targetBytes.size(); - qDebug() << targetBytes << targetLength; - - // Read as unsigned bytes to handle all possible values (0-255) - unsigned char byte; - while (!stream->atEnd()) { - // Read one byte at a time - *stream >> byte; - buffer.append(static_cast(byte)); // Append as char for QByteArray - - // Keep buffer size limited to the target length - if (buffer.size() > targetLength) { - buffer.remove(0, 1); - } - - // Check if the buffer matches the target string in raw bytes - if (buffer == targetBytes) { - // Backup to the start of the matched string - stream->device()->seek(stream->device()->pos() - targetLength); - return true; - } - } - - // Target string not found - return false; -} - -bool ZoneFile::pReadUntilHex(QDataStream* stream, const QString& hexString) { - if (!stream || hexString.isEmpty() || hexString.size() % 2 != 0) { - return false; // Invalid input - } - - // Convert hex string to byte array - QByteArray targetBytes = QByteArray::fromHex(hexString.toUtf8()); - const int targetLength = targetBytes.size(); - - QByteArray buffer; - unsigned char byte; - - while (!stream->atEnd()) { - // Read one byte at a time - *stream >> byte; - buffer.append(static_cast(byte)); // Append as char for QByteArray - - // Keep buffer size limited to the target length - if (buffer.size() > targetLength) { - buffer.remove(0, 1); - } - - // Check if the buffer matches the target byte sequence - if (buffer == targetBytes) { - // Backup to the start of the matched sequence - stream->device()->seek(stream->device()->pos() - targetLength); - return true; - } - } - - // Target sequence not found - return false; -} - -TechSet ZoneFile::pParseAsset_TechSet(QDataStream *aZoneFileStream) { +TechSet ZoneFile_COD2::pParseAsset_TechSet(QDataStream *aZoneFileStream) { TechSet result; for (int i = 1; i <= 62; i++) { @@ -618,14 +454,14 @@ TechSet ZoneFile::pParseAsset_TechSet(QDataStream *aZoneFileStream) { return result; } -Image ZoneFile::pParseAsset_Image(QDataStream *aZoneFileStream) { +Image ZoneFile_COD2::pParseAsset_Image(QDataStream *aZoneFileStream) { Image result; aZoneFileStream->skipRawData(4); *aZoneFileStream >> result.unknowna >> result.unknownb - >> result.unknownc >> result.unknownd - >> result.unknowne >> result.unknownf - >> result.unknowng; + >> result.unknownc >> result.unknownd + >> result.unknowne >> result.unknownf + >> result.unknowng; aZoneFileStream->skipRawData(15 * 4); *aZoneFileStream >> result.unknownh >> result.unknowni; @@ -655,8 +491,8 @@ Image ZoneFile::pParseAsset_Image(QDataStream *aZoneFileStream) { aZoneFileStream->skipRawData(4); *aZoneFileStream >> result.unknown2 >> result.unknown3 - >> result.size1 >> result.size2 - >> result.unknown4 >> result.unknown5; + >> result.size1 >> result.size2 + >> result.unknown4 >> result.unknown5; aZoneFileStream->skipRawData(4); @@ -687,85 +523,225 @@ Image ZoneFile::pParseAsset_Image(QDataStream *aZoneFileStream) { return result; } -void ZoneFile::pParseAsset_LoadedSound(QDataStream *aZoneFileStream) { +Material ZoneFile_COD2::pParseAsset_Material(QDataStream *aZoneFileStream) { + Material result; + + *aZoneFileStream >> result.namePtr; + if (result.namePtr != 4294967295 && GetTagCount() > result.namePtr - 1) { + result.name = GetTags()[result.namePtr - 1]; + } + *aZoneFileStream >> result.refNamePtr; + if (result.refNamePtr != 4294967295 && GetTagCount() > result.refNamePtr - 1) { + result.refName = GetTags()[result.refNamePtr - 1]; + } + aZoneFileStream->skipRawData(12); + + *aZoneFileStream >> result.unknownA[0] >> result.unknownA[0] + >> result.unknownA[1] >> result.unknownA[2] >> result.unknownA[3] + >> result.unknownA[4] >> result.unknownA[5] >> result.unknownA[6] + >> result.unknownA[7] >> result.unknownA[8] >> result.unknownA[9] + >> result.unknownA[10] >> result.unknownA[11]; + + *aZoneFileStream >> result.stateBits[0] >> result.stateBits[1] + >> result.textureCount >> result.constCount >> result.techSetPtr + >> result.texturePtr >> result.constPtr; + + + return result; +} + +SoundAsset ZoneFile_COD2::pParseAsset_Sound(QDataStream *aZoneFileStream) { + SoundAsset result; + + qDebug() << aZoneFileStream->device()->pos(); + + QByteArray rootNamePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rootNamePtr.data(), 4); + qDebug() << "Root name ptr: " << (QString)rootNamePtr.toHex(); + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.count; + + if (rootNamePtr.toHex() == "ffffffff") { + // Read in sound file name + char soundNameChar; + *aZoneFileStream >> soundNameChar; + while (soundNameChar != 0) { + result.name += soundNameChar; + *aZoneFileStream >> soundNameChar; + } + } + + int tagCount = 0; + int resultCount = 0; + for (quint32 i = 0; i < result.count; i++) { + aZoneFileStream->skipRawData(12); + + QByteArray tagPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(tagPtr.data(), 4); + + if (tagPtr.toHex() == "ffffffff") { + qDebug() << "Tag Ptr: " << tagPtr.toHex(); + tagCount++; + } + aZoneFileStream->skipRawData(4); + + QByteArray pathPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pathPtr.data(), 4); + + if (pathPtr.toHex() == "ffffffff") { + qDebug() << "Path Ptr: " << pathPtr.toHex(); + resultCount++; + } + + aZoneFileStream->skipRawData(160); + } + + for (int i = 0; i < tagCount; i++) { + // Read in tag? + QString tag; + char tagChar; + *aZoneFileStream >> tagChar; + while (tagChar != 0) { + tag += tagChar; + *aZoneFileStream >> tagChar; + } + qDebug() << "Tag: " << tag; + } + + for (int i = 0; i < resultCount; i++) { + Sound sound; + + if (aZoneFileStream->device()->peek(12).toHex().contains("ffffffff00000000")) { + aZoneFileStream->skipRawData(12); + } + + aZoneFileStream->skipRawData(8); + + qDebug() << "- " << aZoneFileStream->device()->pos(); + QByteArray aliasPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(aliasPtr.data(), 4); + + QByteArray namePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(namePtr.data(), 4); + + *aZoneFileStream >> sound.dataLength; + qDebug() << "- Data length: " << sound.dataLength; + + if (aliasPtr.toHex() == "ffffffff") { + // Read in sound alias name + char soundAliasChar; + *aZoneFileStream >> soundAliasChar; + while (soundAliasChar != 0) { + sound.alias += soundAliasChar; + *aZoneFileStream >> soundAliasChar; + } + qDebug() << "- Alias: " << sound.alias; + } + + if (aZoneFileStream->device()->peek(4) == "RIFF") { + sound.path = sound.alias; + sound.alias = ""; + } else if (namePtr.toHex() == "ffffffff") { + // Read in sound file path + char soundPathChar; + *aZoneFileStream >> soundPathChar; + while (soundPathChar != 0) { + sound.path += soundPathChar; + *aZoneFileStream >> soundPathChar; + } + sound.path.replace(",", ""); + qDebug() << "- Path: " << sound.path; + } + + if (sound.dataLength) { + QByteArray data(sound.dataLength, Qt::Uninitialized); + aZoneFileStream->readRawData(data.data(), sound.dataLength); + sound.data = data; + } + result.sounds.append(sound); + } + qDebug() << "- " << aZoneFileStream->device()->pos(); + + return result; +} + +void ZoneFile_COD2::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_GameMapSP(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_GameMapSP(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_GameMapMP(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_GameMapMP(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_LightDef(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_LightDef(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_UIMap(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_UIMap(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_AIType(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_AIType(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_FX(QDataStream *aZoneFileStream) { Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_FX(QDataStream *aZoneFileStream) { - Q_UNUSED(aZoneFileStream); -} - -Animation ZoneFile::pParseAsset_Animation(QDataStream *aZoneFileStream) { +Animation ZoneFile_COD2::pParseAsset_Animation(QDataStream *aZoneFileStream) { Animation result; aZoneFileStream->skipRawData(4); *aZoneFileStream - >> result.dataByteCount - >> result.dataShortCount - >> result.dataIntCount - >> result.randomDataByteCount - >> result.randomDataIntCount - >> result.numframes - >> result.isLooped - >> result.isDelta - >> result.noneRotatedBoneCount - >> result.twoDRotatedBoneCount - >> result.normalRotatedBoneCount - >> result.twoDRotatedBoneCount - >> result.normalRotatedBoneCount - >> result.normalTranslatedBoneCount - >> result.preciseTranslatedBoneCount - >> result.staticTranslatedBoneCount - >> result.noneTranslatedBoneCount - >> result.totalBoneCount - >> result.otherBoneCount1 - >> result.otherBoneCount2 - >> result.notifyCount - >> result.assetType - >> result.pad - >> result.randomDataShortCount - >> result.indexCount - >> result.frameRate - >> result.frequency - >> result.boneIDsPtr - >> result.dataBytePtr - >> result.dataShortPtr - >> result.dataIntPtr - >> result.randomDataShortPtr - >> result.randomDataBytePtr - >> result.randomDataIntPtr - >> result.longIndiciesPtr - >> result.notificationsPtr - >> result.deltaPartsPtr; + >> result.dataByteCount + >> result.dataShortCount + >> result.dataIntCount + >> result.randomDataByteCount + >> result.randomDataIntCount + >> result.numframes + >> result.isLooped + >> result.isDelta + >> result.noneRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.normalTranslatedBoneCount + >> result.preciseTranslatedBoneCount + >> result.staticTranslatedBoneCount + >> result.noneTranslatedBoneCount + >> result.totalBoneCount + >> result.otherBoneCount1 + >> result.otherBoneCount2 + >> result.notifyCount + >> result.assetType + >> result.pad + >> result.randomDataShortCount + >> result.indexCount + >> result.frameRate + >> result.frequency + >> result.boneIDsPtr + >> result.dataBytePtr + >> result.dataShortPtr + >> result.dataIntPtr + >> result.randomDataShortPtr + >> result.randomDataBytePtr + >> result.randomDataIntPtr + >> result.longIndiciesPtr + >> result.notificationsPtr + >> result.deltaPartsPtr; // Read in x_anim file name QString xAnimName; @@ -791,7 +767,7 @@ Animation ZoneFile::pParseAsset_Animation(QDataStream *aZoneFileStream) { return result; } -MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { +MenuFile ZoneFile_COD2::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { //MENU_FILE MenuFile result; @@ -836,21 +812,21 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { quint32 hClientAlignInt, vClientAlignInt, styleInt, borderInt; *aZoneFileStream >> hClientAlignInt >> vClientAlignInt >> menu.groupPtr - >> styleInt >> borderInt >> menu.ownerDraw >> menu.ownerDrawFlags - >> menu.borderSize >> menu.staticFlags >> menu.dynamicFlags >> menu.nextTime; + >> styleInt >> borderInt >> menu.ownerDraw >> menu.ownerDrawFlags + >> menu.borderSize >> menu.staticFlags >> menu.dynamicFlags >> menu.nextTime; menu.hClientAlign = (MENU_H_ALIGNMENT)hClientAlignInt; menu.vClientAlign = (MENU_V_ALIGNMENT)vClientAlignInt; menu.style = (MENU_WINDOW_STYLE)styleInt; menu.border = (MENU_WINDOW_BORDER)borderInt; float foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA, - backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA, - borderColorR, borderColorG, borderColorB, borderColorA, - outlineColorR, outlineColorG, outlineColorB, outlineColorA; + backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA, + borderColorR, borderColorG, borderColorB, borderColorA, + outlineColorR, outlineColorG, outlineColorB, outlineColorA; *aZoneFileStream >> foregroundColorR >> foregroundColorG >> foregroundColorB >> foregroundColorA - >> backgroundColorR >> backgroundColorG >> backgroundColorB >> backgroundColorA - >> borderColorR >> borderColorG >> borderColorB >> borderColorA - >> outlineColorR >> outlineColorG >> outlineColorB >> outlineColorA; + >> backgroundColorR >> backgroundColorG >> backgroundColorB >> backgroundColorA + >> borderColorR >> borderColorG >> borderColorB >> borderColorA + >> outlineColorR >> outlineColorG >> outlineColorB >> outlineColorA; menu.foregroundColor = QColor(foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA); menu.backgroundColor = QColor(backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA); @@ -858,16 +834,16 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { menu.outlineColor = QColor(outlineColorR, outlineColorG, outlineColorB, outlineColorA); *aZoneFileStream >> menu.materialPtr >> menu.fontPtr >> menu.fullScreen >> menu.itemCount - >> menu.fontIndex >> menu.cursorItem >> menu.fadeCycle >> menu.fadeClamp - >> menu.fadeAmount >> menu.fadeInAmount >> menu.blurRadius >> menu.onOpenPtr - >> menu.onFocusPtr >> menu.onClosePtr >> menu.onESCPtr >> menu.onKeyPtr - >> menu.visibleExpCount >> menu.expEntryPtr >> menu.allowedBindingPtr - >> menu.soundNamePtr >> menu.imageTrack; + >> menu.fontIndex >> menu.cursorItem >> menu.fadeCycle >> menu.fadeClamp + >> menu.fadeAmount >> menu.fadeInAmount >> menu.blurRadius >> menu.onOpenPtr + >> menu.onFocusPtr >> menu.onClosePtr >> menu.onESCPtr >> menu.onKeyPtr + >> menu.visibleExpCount >> menu.expEntryPtr >> menu.allowedBindingPtr + >> menu.soundNamePtr >> menu.imageTrack; float focusColorR, focusColorG, focusColorB, focusColorA, - disabledColorR, disabledColorG, disabledColorB, disabledColorA; + disabledColorR, disabledColorG, disabledColorB, disabledColorA; *aZoneFileStream >> focusColorR >> focusColorG >> focusColorB >> focusColorA - >> disabledColorR >> disabledColorG >> disabledColorB >> disabledColorA; + >> disabledColorR >> disabledColorG >> disabledColorB >> disabledColorA; menu.focusColor = QColor(focusColorR, focusColorG, focusColorB, focusColorA); menu.disabledColor = QColor(disabledColorR, disabledColorG, disabledColorB, disabledColorA); @@ -901,18 +877,18 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { menu.itemRect = QRectF(itemRectX, itemRectY, itemRectWidth, itemRectHeight); *aZoneFileStream >> menu.itemHAlignment >> menu.itemVAlignment >> menu.itemGroupPtr - >> menu.itemWindowStyle >> menu.itemWindowBorder >> menu.itemOwnerDraw - >> menu.itemOwnerDrawFlags >> menu.itemBorderSize >> menu.itemStaticFlags - >> menu.itemDynamicFlags >> menu.itemNextTime; + >> menu.itemWindowStyle >> menu.itemWindowBorder >> menu.itemOwnerDraw + >> menu.itemOwnerDrawFlags >> menu.itemBorderSize >> menu.itemStaticFlags + >> menu.itemDynamicFlags >> menu.itemNextTime; float itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA, - itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA, - itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA, - itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA; + itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA, + itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA, + itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA; *aZoneFileStream >> itemForegroundColorR >> itemForegroundColorG >> itemForegroundColorB >> itemForegroundColorA - >> itemBackgroundColorR >> itemBackgroundColorG >> itemBackgroundColorB >> itemBackgroundColorA - >> itemBorderColorR >> itemBorderColorG >> itemBorderColorB >> itemBorderColorA - >> itemOutlineColorR >> itemOutlineColorG >> itemOutlineColorB >> itemOutlineColorA; + >> itemBackgroundColorR >> itemBackgroundColorG >> itemBackgroundColorB >> itemBackgroundColorA + >> itemBorderColorR >> itemBorderColorG >> itemBorderColorB >> itemBorderColorA + >> itemOutlineColorR >> itemOutlineColorG >> itemOutlineColorB >> itemOutlineColorA; menu.itemForegroundColor = QColor(itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA); menu.itemBackgroundColor = QColor(itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA); @@ -927,9 +903,9 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { quint32 hItemTextAlignInt, vItemTextAlignInt, itemType, fontTypeInt, textStyleInt; *aZoneFileStream >> hItemTextAlignInt >> vItemTextAlignInt >> itemType >> menu.dataType - >> menu.alignment >> fontTypeInt >> menu.textAlignMode >> menu.textalignx >> menu.textaligny - >> menu.textscale >> textStyleInt >> menu.gameMsgWindowIndex >> menu.gameMsgWindowMode - >> menu.testPtr >> menu.textSavegameInfo >> menu.parentPtr; + >> menu.alignment >> fontTypeInt >> menu.textAlignMode >> menu.textalignx >> menu.textaligny + >> menu.textscale >> textStyleInt >> menu.gameMsgWindowIndex >> menu.gameMsgWindowMode + >> menu.testPtr >> menu.textSavegameInfo >> menu.parentPtr; menu.itemText_hAlign = (MENU_H_ALIGNMENT)hItemTextAlignInt; menu.itemText_vAlign = (MENU_V_ALIGNMENT)vItemTextAlignInt; menu.itemType = (MENU_ITEM_TYPE)itemType; @@ -937,9 +913,9 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { menu.textStyle = (MENU_ITEM_TEXTSTYLE)textStyleInt; *aZoneFileStream >> menu.mouseEnterText >> menu.mouseExitText >> menu.mouseEnter >> menu.mouseExit - >> menu.action >> menu.onAccept >> menu.onFocus >> menu.leaveFocus >> menu.dvar >> menu.dvarTest - >> menu.keyHandlerPtr >> menu.enableDvarPtr >> menu.dvarFlags >> menu.focusSoundPtr - >> menu.special >> menu.cursorPos; + >> menu.action >> menu.onAccept >> menu.onFocus >> menu.leaveFocus >> menu.dvar >> menu.dvarTest + >> menu.keyHandlerPtr >> menu.enableDvarPtr >> menu.dvarFlags >> menu.focusSoundPtr + >> menu.special >> menu.cursorPos; // itemDefData_t typeData; @@ -959,11 +935,11 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { *aZoneFileStream >> menu.notselectable >> menu.noScrollBars >> menu.usePaging; float itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA, - itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA, - itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA; + itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA, + itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA; *aZoneFileStream >> itemSelectBorderColorR >> itemSelectBorderColorG >> itemSelectBorderColorB >> itemSelectBorderColorA - >> itemDisableColorR >> itemDisableColorG >> itemDisableColorB >> itemDisableColorA - >> itemFocusColorR >> itemFocusColorG >> itemFocusColorB >> itemFocusColorA; + >> itemDisableColorR >> itemDisableColorG >> itemDisableColorB >> itemDisableColorA + >> itemFocusColorR >> itemFocusColorG >> itemFocusColorB >> itemFocusColorA; menu.itemSelectBorderColor = QColor(itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA); menu.itemDisableColor = QColor(itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA); menu.itemFocusColor = QColor(itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA); @@ -973,7 +949,7 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { // editFieldDef_s *editField; *aZoneFileStream >> menu.minVal >> menu.maxVal >> menu.defVal >> menu.range >> menu.maxChars - >> menu.maxCharsGotoNext >> menu.maxPaintChars >> menu.paintOffset; + >> menu.maxCharsGotoNext >> menu.maxPaintChars >> menu.paintOffset; // multiDef_s *multi; @@ -997,7 +973,7 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { *aZoneFileStream >> menu.count >> menu.strDef >> menu.enumDvarNamePtr; aZoneFileStream->skipRawData(4); - //>> menu.dataPtr + //>> menu.dataPtr *aZoneFileStream >> menu.itemImageTrack; qDebug() << aZoneFileStream->device()->pos(); @@ -1015,24 +991,24 @@ MenuFile ZoneFile::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { return result; } -void ZoneFile::pParseAsset_Weapon(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_Weapon(QDataStream *aZoneFileStream) { //WEAPON_FILE Q_UNUSED(aZoneFileStream); } -void ZoneFile::pParseAsset_D3DBSP(QDataStream *aZoneFileStream) { +void ZoneFile_COD2::pParseAsset_D3DBSP(QDataStream *aZoneFileStream) { //D3DBSP_DUMP Q_UNUSED(aZoneFileStream); } -StringTable ZoneFile::pParseAsset_StringTable(QDataStream *aZoneFileStream) { +StringTable ZoneFile_COD2::pParseAsset_StringTable(QDataStream *aZoneFileStream) { StringTable result; aZoneFileStream->skipRawData(4); *aZoneFileStream - >> result.columnCount - >> result.rowCount; + >> result.columnCount + >> result.rowCount; // Todo fix this result.columnCount = 0; @@ -1048,16 +1024,15 @@ StringTable ZoneFile::pParseAsset_StringTable(QDataStream *aZoneFileStream) { *aZoneFileStream >> stringTableNameChar; } - QVector tablePointers = QVector(); for (quint32 i = 0; i < result.rowCount; i++) { QByteArray pointerData(4, Qt::Uninitialized); aZoneFileStream->readRawData(pointerData.data(), 4); - tablePointers.push_back(pointerData.toHex()); + result.tablePointers.push_back(pointerData.toHex()); aZoneFileStream->skipRawData(4); } - for (const QString &pointerAddr : tablePointers) { + for (const QString &pointerAddr : result.tablePointers) { QString leadingContent = ""; if (pointerAddr == "FFFFFFFF") { char leadingContentChar; @@ -1077,13 +1052,40 @@ StringTable ZoneFile::pParseAsset_StringTable(QDataStream *aZoneFileStream) { content += contentChar; *aZoneFileStream >> contentChar; } - QPair tableEntry = QPair(); - tableEntry.first = leadingContent; - tableEntry.second = content; - //if (!mStrTableMap.contains(stringTableName)) { - // mStrTableMap[stringTableName] = QVector>(); - //} - //mStrTableMap[stringTableName].push_back(tableEntry); + result.content[leadingContent] = content; } return result; } +// "parts", +// "model", +// "material", +// "image", +// "sound", +// "sndCurve", +// "clipMap", +// "world", +// "lightDef", +// "font", +// "menuList", +// "menu", +// "localize", +// "weapon", +// "sndDriverGlobals", +// "fx", +// "impactFx", +// "rawfile", +// "data" + +QString ZoneFile_COD2::AssetTypeToString(const QString aAssetType) { + const QString cleanedType = aAssetType.toUpper(); + if (cleanedType == "") { // localized string PARTIALLY VERIFIED + return ""; + } + return aAssetType; +} + +QByteArray ZoneFile_COD2::GetBinaryData() { + QByteArray result; + + return result; +} diff --git a/libs/zonefile/zonefile_cod2.h b/libs/zonefile/zonefile_cod2.h new file mode 100644 index 0000000..9d560f7 --- /dev/null +++ b/libs/zonefile/zonefile_cod2.h @@ -0,0 +1,52 @@ +#ifndef ZONEFILE_COD2_H +#define ZONEFILE_COD2_H + +#include "zonefile.h" + +class ZoneFile_COD2 : public ZoneFile +{ +public: + ZoneFile_COD2(); + ~ZoneFile_COD2(); + + bool Load(const QByteArray aFileData, FF_PLATFORM aPlatform) override; + QString AssetTypeToString(const QString aAssetType); + + QByteArray GetBinaryData() override; + +protected: + void pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) override; + quint32 pParseZoneSize(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsA(QDataStream *aZoneFileStream) override; + quint32 pParseZoneTagCount(QDataStream *aZoneFileStream) override; + quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsB(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsC(QDataStream *aZoneFileStream) override; + QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) override; + QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) override; + AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) override; + LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream) override; + RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream) override; + void pParseAsset_PhysPreset(QDataStream *aZoneFileStream) override; + Model pParseAsset_Model(QDataStream *aZoneFileStream) override; + Material pParseAsset_Material(QDataStream *aZoneFileStream) override; + Shader pParseAsset_Shader(QDataStream *aZoneFileStream) override; + TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream) override; + Image pParseAsset_Image(QDataStream *aZoneFileStream) override; + SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream) override; + void pParseAsset_ColMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapSP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_LightDef(QDataStream *aZoneFileStream) override; + void pParseAsset_UIMap(QDataStream *aZoneFileStream) override; + void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) override; + void pParseAsset_AIType(QDataStream *aZoneFileStream) override; + void pParseAsset_FX(QDataStream *aZoneFileStream) override; + Animation pParseAsset_Animation(QDataStream *aZoneFileStream) override; + MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream) override; + void pParseAsset_Weapon(QDataStream *aZoneFileStream) override; + void pParseAsset_D3DBSP(QDataStream *aZoneFileStream) override; + StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream) override; +}; + +#endif // ZONEFILE_COD2_H diff --git a/libs/zonefile/zonefile_cod5.cpp b/libs/zonefile/zonefile_cod5.cpp new file mode 100644 index 0000000..ed96244 --- /dev/null +++ b/libs/zonefile/zonefile_cod5.cpp @@ -0,0 +1,1094 @@ +#include "zonefile_cod5.h" + +#include +#include +#include + +ZoneFile_COD5::ZoneFile_COD5() + : ZoneFile() { + +} + +ZoneFile_COD5::~ZoneFile_COD5() { + +} + +bool ZoneFile_COD5::Load(const QByteArray aFileData, FF_PLATFORM aPlatform) { + // Open zone file as little endian stream + QDataStream zoneFileStream(aFileData); + if (aPlatform == FF_PLATFORM_PC) { + zoneFileStream.setByteOrder(QDataStream::LittleEndian); + } else { + zoneFileStream.setByteOrder(QDataStream::BigEndian); + } + + // Parse data from zone file header + pParseZoneHeader(&zoneFileStream, aPlatform); + SetRecords(pParseZoneIndex(&zoneFileStream, GetRecordCount())); + SetAssetMap(pParseAssets(&zoneFileStream, GetRecords())); + + return true; +} + +void ZoneFile_COD5::pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) { + SetSize(pParseZoneSize(aZoneFileStream)); + pParseZoneUnknownsA(aZoneFileStream); + + SetTagCount(pParseZoneTagCount(aZoneFileStream)); + pParseZoneUnknownsB(aZoneFileStream); + + SetRecordCount(pParseZoneRecordCount(aZoneFileStream)); + + quint32 tagCount = GetTagCount(); + if (tagCount) { + pParseZoneUnknownsC(aZoneFileStream); + SetTags(pParseZoneTags(aZoneFileStream, tagCount)); + } else { + aZoneFileStream->skipRawData(4); + } +} + +quint32 ZoneFile_COD5::pParseZoneSize(QDataStream *aZoneFileStream) { + quint32 zoneFileSize; + *aZoneFileStream >> zoneFileSize; + if (zoneFileSize <= 0) { + qDebug() << "Tried to open empty zone file!"; + exit(-1); + } + zoneFileSize += 36; + return zoneFileSize; +} + +/* + ParseZoneUnknownsA() + + Parses the 1st section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD5::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { + // Byte 4-7, 8-11, 12-15: unknown + QByteArray unknown1(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown1.data(), 4); + + QByteArray unknown2(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown2.data(), 4); + + QByteArray unknown3(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown3.data(), 4); + + // Byte 16-19, 20-23: empty/unknown + QByteArray unknown4(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown4.data(), 4); + + QByteArray unknown5(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown5.data(), 4); + + // Byte 24-27: somehow related to the filesize, but smaller value + QByteArray unknown6(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown6.data(), 4); + + // Byte 28-31, 32-35: unknown + QByteArray unknown7(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown7.data(), 4); + + QByteArray unknown8(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown8.data(), 4); +} + +/* + ParseZoneTagCount() + + Parses the number of string tags in the zone index +*/ +quint32 ZoneFile_COD5::pParseZoneTagCount(QDataStream *aZoneFileStream) { + quint32 tagCount; + *aZoneFileStream >> tagCount; + return tagCount; +} + +/* + ParseZoneRecordCount() + + Parses the number of records in the zone index +*/ +quint32 ZoneFile_COD5::pParseZoneRecordCount(QDataStream *aZoneFileStream) { + quint32 recordCount; + *aZoneFileStream >> recordCount; + return recordCount; +} + +/* + ParseZoneUnknownsB() + + Parses the 2nd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD5::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { + // Byte 44-47: Unknown/empty? + QByteArray unknown9(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown9.data(), 4); +} + +/* + pParseZoneUnknownsC() + + Parses the 3rd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD5::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { + // Byte 40-43: Unknown/empty? + QByteArray unknown10(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown10.data(), 4); + + // Byte 44-47: Unknown/empty? + QByteArray unknown11(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown11.data(), 4); +} + +/* + ParseZoneTags() + + Parses the string tags ate the start of zone file +*/ +QStringList ZoneFile_COD5::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { + QStringList tags; + + // Byte 48-51: Repeated separators? ÿÿÿÿ x i + aZoneFileStream->skipRawData(4 * (tagCount - 1)); + + // Parse tags/strings before index + QString zoneTag; + char zoneTagChar; + for (quint32 i = 0; i < tagCount; i++) { + *aZoneFileStream >> zoneTagChar; + while (zoneTagChar != 0) { + zoneTag += zoneTagChar; + *aZoneFileStream >> zoneTagChar; + } + tags << zoneTag; + zoneTag.clear(); + } + return tags; +} + +/* + ParseZoneIndex() + + Parse the binary zone index data and populate table +*/ +QStringList ZoneFile_COD5::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { + QStringList result; + + // Don't parse if no records + if (!recordCount) { return result; } + + if (aZoneFileStream->device()->peek(4).toHex().contains("ffff")) { + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 2); + } + + // Parse index & map found asset types + for (quint32 i = 0; i <= recordCount; i++) { + // Skip record start + QByteArray rawAssetType(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rawAssetType.data(), 4); + result << rawAssetType.toHex(); + + // Skip separator + aZoneFileStream->skipRawData(4); + } + return result; +} + +AssetMap ZoneFile_COD5::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { + AssetMap result; + + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); + + for (int i = 0; i < assetOrder.size(); i++) { + const QString typeHex = assetOrder[i]; + const QString typeStr = AssetTypeToString(typeHex); + + if (typeStr == "LOCAL STRING") { // localized string asset + result.localStrings << pParseAsset_LocalString(aZoneFileStream); + } else if (typeStr == "RAW FILE") { // gsc + auto rawFile = pParseAsset_RawFile(aZoneFileStream); + if (rawFile.length) { + result.rawFiles << rawFile; + } + } else if (typeStr == "PHYS PRESET") { // physpreset + pParseAsset_PhysPreset(aZoneFileStream); + } else if (typeStr == "MODEL") { // xmodel + result.models << pParseAsset_Model(aZoneFileStream); + } else if (typeStr == "MATERIAL") { // material + pParseAsset_Material(aZoneFileStream); + } else if (typeStr == "SHADER") { // pixelshader + pParseAsset_Shader(aZoneFileStream); + } else if (typeStr == "TECH SET") { // techset include + result.techSets << pParseAsset_TechSet(aZoneFileStream); + } else if (typeStr == "IMAGE") { // image + result.images << pParseAsset_Image(aZoneFileStream); + } else if (typeStr == "SOUND") { // loaded_sound + result.sounds << pParseAsset_Sound(aZoneFileStream); + } else if (typeStr == "COLLISION MAP") { // col_map_mp + pParseAsset_ColMapMP(aZoneFileStream); + } else if (typeStr == "MP MAP") { // game_map_sp + pParseAsset_GameMapSP(aZoneFileStream); + } else if (typeStr == "SP MAP") { // game_map_mp + pParseAsset_GameMapMP(aZoneFileStream); + } else if (typeStr == "LIGHT DEF") { // lightdef + pParseAsset_LightDef(aZoneFileStream); + } else if (typeStr == "UI MAP") { // ui_map + pParseAsset_UIMap(aZoneFileStream); + } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals + pParseAsset_SNDDriverGlobals(aZoneFileStream); + } else if (typeStr == "AI TYPE") { // aitype + pParseAsset_AIType(aZoneFileStream); + } else if (typeStr == "EFFECT") { // aitype + pParseAsset_FX(aZoneFileStream); + } else if (typeStr == "ANIMATION") { // aitype + result.animations << pParseAsset_Animation(aZoneFileStream); + } else if (typeStr == "STRING TABLE") { // string_table + result.stringTables << pParseAsset_StringTable(aZoneFileStream); + } else if (typeStr == "MENU") { // string_table + result.menuFiles << pParseAsset_MenuFile(aZoneFileStream); + } else if (typeStr == "WEAPON") { // string_table + pParseAsset_Weapon(aZoneFileStream); + } else if (typeStr == "D3DBSP DUMP") { // string_table + pParseAsset_D3DBSP(aZoneFileStream); + } else if (typeStr != "UNKNOWN") { + qDebug() << "Found bad asset type!" << typeStr; + } + } + return result; +} + +LocalString ZoneFile_COD5::pParseAsset_LocalString(QDataStream *aZoneFileStream) { + LocalString result; + + quint32 stringPtr, aliasPtr; + *aZoneFileStream >> stringPtr >> aliasPtr; + if (stringPtr == 4294967295) { + // Parse local string asset contents + QString localStr; + char localStrChar; + *aZoneFileStream >> localStrChar; + while (localStrChar != 0) { + result.string += localStrChar; + *aZoneFileStream >> localStrChar; + } + } else { + result.string = "String Ptr: " + QString::number(stringPtr); + } + + if (aliasPtr == 4294967295) { + // Parse rawfile name + QString aliasName; + char aliasNameChar; + *aZoneFileStream >> aliasNameChar; + while (aliasNameChar != 0) { + result.alias += aliasNameChar; + *aZoneFileStream >> aliasNameChar; + } + } else { + result.string = "Alias Ptr: " + QString::number(aliasPtr); + } + + return result; +} + +RawFile ZoneFile_COD5::pParseAsset_RawFile(QDataStream *aZoneFileStream) { + RawFile result; + + // Skip start separator FF FF FF FF (pointer?) + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.length; + + // Skip unknown 4 byte data + aZoneFileStream->skipRawData(4); + + // Parse rawfile path + char scriptPathChar; + *aZoneFileStream >> scriptPathChar; + while (scriptPathChar != 0) { + result.path += scriptPathChar; + *aZoneFileStream >> scriptPathChar; + } + result.path.replace(",", ""); + const QStringList pathParts = result.path.split('/'); + if (pathParts.size() == 0) { + qDebug() << "Failed to parse ff path! " << result.path; + exit(-1); + } + + // Parse gsc contents + char rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + while (rawFileContentsChar != 0 && rawFileContentsChar != -1) { + result.contents += rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + } + return result; +} + +void ZoneFile_COD5::pParseAsset_PhysPreset(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Model ZoneFile_COD5::pParseAsset_Model(QDataStream *aZoneFileStream) { + Model result; + + qDebug() << "Model Info:"; + + *aZoneFileStream >> result.namePtr >> result.tagCount >> result.rootTagCount + >> result.surfCount >> result.unknownCount >> result.boneNamePtr + >> result.parentListPtr >> result.quatsPtr >> result.transPtr + >> result.partClassPtr >> result.baseMatPtr + >> result.surfsPtr >> result.materialHandlesPtr; + + // Parse XModelLodInfo + for (int i = 1; i <= 4; i++) { + qDebug() << "- Lod Info " << i; + + quint32 intDist; + *aZoneFileStream >> intDist; + + std::memcpy(&result.lodInfo[i].dist, &intDist, sizeof(result.lodInfo[i].dist)); + *aZoneFileStream >> result.lodInfo[i].numsurfs >> result.lodInfo[i].surfIndex; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.lodInfo[i].partBits[0] + >> result.lodInfo[i].partBits[1] + >> result.lodInfo[i].partBits[2] + >> result.lodInfo[i].partBits[3]; + } + + *aZoneFileStream >> result.collSurfsPtr >> result.numCollSurfs >> result.contents >> result.boneInfoPtr; + + quint32 intRadius, intMins[3], intMaxs[3]; + *aZoneFileStream >> intRadius >> intMins[0] >> intMins[1] + >> intMins[2] >> intMaxs[0] >> intMaxs[1] >> intMaxs[2]; + + std::memcpy(&result.radius, &intRadius, sizeof(result.radius)); + + std::memcpy(&result.mins[0], &intMins[0], sizeof(result.mins[0])); + std::memcpy(&result.mins[1], &intMins[1], sizeof(result.mins[1])); + std::memcpy(&result.mins[2], &intMins[2], sizeof(result.mins[2])); + + std::memcpy(&result.maxs[0], &intMaxs[0], sizeof(result.maxs[0])); + std::memcpy(&result.maxs[1], &intMaxs[1], sizeof(result.maxs[2])); + std::memcpy(&result.maxs[2], &intMaxs[2], sizeof(result.maxs[3])); + + *aZoneFileStream >> result.numLods >> result.collLod >> result.streamInfoPtr + >> result.memUsage >> result.flags >> result.physPresetPtr >> result.physGeomsPtr; + + // Parse model name + char modelNameChar; + *aZoneFileStream >> modelNameChar; + while (modelNameChar == 0) { + *aZoneFileStream >> modelNameChar; + } + while (modelNameChar != 0) { + result.modelName += modelNameChar; + *aZoneFileStream >> modelNameChar; + } + return result; +} + +Material ZoneFile_COD5::pParseAsset_Material(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + + return Material(); +} + +Shader ZoneFile_COD5::pParseAsset_Shader(QDataStream *aZoneFileStream) { + Shader result = Shader(); + + quint8 minorVersion, majorVersion; + *aZoneFileStream >> minorVersion >> majorVersion; + + quint32 magic; + *aZoneFileStream >> magic; + + // Verify .fxc magic + if (magic != 65534 && magic != 65535) { return result; } + + SHADER_TYPE type = SHADER_NONE; + quint16 rawShaderType; + *aZoneFileStream >> rawShaderType; + if (rawShaderType == 65535) { // Pixel shader + type = SHADER_PIXEL; + } else if (rawShaderType == 65534) { // Vertex shader + type = SHADER_VERTEX; + } + Q_UNUSED(type); + + while (true) { + quint32 instructionToken; + *aZoneFileStream >> instructionToken; + + SHADER_OPCODE opCode = (SHADER_OPCODE)(instructionToken & 0xffff); + + int size; + if (opCode == OPCODE_End) { + break; + } else if (opCode == OPCODE_Comment) { + size = (int)((instructionToken >> 16) & 0x7FFF); + } else { + size = (int)((instructionToken >> 24) & 0x0f); + } + Q_UNUSED(size); + } + + + return result; +} + +TechSet ZoneFile_COD5::pParseAsset_TechSet(QDataStream *aZoneFileStream) { + TechSet result; + + for (int i = 1; i <= 62; i++) { + quint32 ptr; + *aZoneFileStream >> ptr; + + result.pointers << ptr; + } + qDebug() << aZoneFileStream->device()->pos(); + + // Parse techset name + char techSetNameChar; + *aZoneFileStream >> techSetNameChar; + while (techSetNameChar != 0) { + result.name += techSetNameChar; + *aZoneFileStream >> techSetNameChar; + } + result.name.replace(",", ""); + + return result; +} + +Image ZoneFile_COD5::pParseAsset_Image(QDataStream *aZoneFileStream) { + Image result; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknowna >> result.unknownb + >> result.unknownc >> result.unknownd + >> result.unknowne >> result.unknownf + >> result.unknowng; + + aZoneFileStream->skipRawData(15 * 4); + *aZoneFileStream >> result.unknownh >> result.unknowni; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknownj; + + aZoneFileStream->skipRawData(4); + + char materialNameChar; + *aZoneFileStream >> materialNameChar; + while (materialNameChar != 0) { + result.materialName += materialNameChar; + *aZoneFileStream >> materialNameChar; + } + result.materialName.replace(",", ""); + + if (result.unknowna) { + *aZoneFileStream >> result.unknownk; + *aZoneFileStream >> result.unknownl; + *aZoneFileStream >> result.unknownm; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown1; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown2 >> result.unknown3 + >> result.size1 >> result.size2 + >> result.unknown4 >> result.unknown5; + + aZoneFileStream->skipRawData(4); + + char imageNameChar; + *aZoneFileStream >> imageNameChar; + while (imageNameChar != 0) { + result.name += imageNameChar; + *aZoneFileStream >> imageNameChar; + } + + *aZoneFileStream >> result.unknown6 >> result.unknown7; + + QByteArray compressionData(8, Qt::Uninitialized); + aZoneFileStream->readRawData(compressionData.data(), 8); + if (compressionData.contains("DXT1")) { + result.compression = COMPRESSION_DXT1; + } else if (compressionData.contains("DXT3")) { + result.compression = COMPRESSION_DXT3; + } else if (compressionData.contains("DXT5")) { + result.compression = COMPRESSION_DXT5; + } else { + result.compression = COMPRESSION_NONE; + } + + *aZoneFileStream >> result.unknown8 >> result.unknown9; + } + + return result; +} + +SoundAsset ZoneFile_COD5::pParseAsset_Sound(QDataStream *aZoneFileStream) { + SoundAsset result; + + qDebug() << aZoneFileStream->device()->pos(); + + QByteArray rootNamePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rootNamePtr.data(), 4); + qDebug() << "Root name ptr: " << (QString)rootNamePtr.toHex(); + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.count; + + if (rootNamePtr.toHex() == "ffffffff") { + // Read in sound file name + char soundNameChar; + *aZoneFileStream >> soundNameChar; + while (soundNameChar != 0) { + result.name += soundNameChar; + *aZoneFileStream >> soundNameChar; + } + } + + int tagCount = 0; + int resultCount = 0; + for (quint32 i = 0; i < result.count; i++) { + aZoneFileStream->skipRawData(12); + + QByteArray tagPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(tagPtr.data(), 4); + + if (tagPtr.toHex() == "ffffffff") { + qDebug() << "Tag Ptr: " << tagPtr.toHex(); + tagCount++; + } + aZoneFileStream->skipRawData(4); + + QByteArray pathPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pathPtr.data(), 4); + + if (pathPtr.toHex() == "ffffffff") { + qDebug() << "Path Ptr: " << pathPtr.toHex(); + resultCount++; + } + + aZoneFileStream->skipRawData(160); + } + + for (int i = 0; i < tagCount; i++) { + // Read in tag? + QString tag; + char tagChar; + *aZoneFileStream >> tagChar; + while (tagChar != 0) { + tag += tagChar; + *aZoneFileStream >> tagChar; + } + qDebug() << "Tag: " << tag; + } + + for (int i = 0; i < resultCount; i++) { + Sound sound; + + if (aZoneFileStream->device()->peek(12).toHex().contains("ffffffff00000000")) { + aZoneFileStream->skipRawData(12); + } + + aZoneFileStream->skipRawData(8); + + qDebug() << "- " << aZoneFileStream->device()->pos(); + QByteArray aliasPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(aliasPtr.data(), 4); + + QByteArray namePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(namePtr.data(), 4); + + *aZoneFileStream >> sound.dataLength; + qDebug() << "- Data length: " << sound.dataLength; + + if (aliasPtr.toHex() == "ffffffff") { + // Read in sound alias name + char soundAliasChar; + *aZoneFileStream >> soundAliasChar; + while (soundAliasChar != 0) { + sound.alias += soundAliasChar; + *aZoneFileStream >> soundAliasChar; + } + qDebug() << "- Alias: " << sound.alias; + } + + if (aZoneFileStream->device()->peek(4) == "RIFF") { + sound.path = sound.alias; + sound.alias = ""; + } else if (namePtr.toHex() == "ffffffff") { + // Read in sound file path + char soundPathChar; + *aZoneFileStream >> soundPathChar; + while (soundPathChar != 0) { + sound.path += soundPathChar; + *aZoneFileStream >> soundPathChar; + } + sound.path.replace(",", ""); + qDebug() << "- Path: " << sound.path; + } + + if (sound.dataLength) { + QByteArray data(sound.dataLength, Qt::Uninitialized); + aZoneFileStream->readRawData(data.data(), sound.dataLength); + sound.data = data; + } + result.sounds.append(sound); + } + qDebug() << "- " << aZoneFileStream->device()->pos(); + + return result; +} + +void ZoneFile_COD5::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_GameMapSP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_GameMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_LightDef(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_UIMap(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_AIType(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_FX(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Animation ZoneFile_COD5::pParseAsset_Animation(QDataStream *aZoneFileStream) { + Animation result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.dataByteCount + >> result.dataShortCount + >> result.dataIntCount + >> result.randomDataByteCount + >> result.randomDataIntCount + >> result.numframes + >> result.isLooped + >> result.isDelta + >> result.noneRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.normalTranslatedBoneCount + >> result.preciseTranslatedBoneCount + >> result.staticTranslatedBoneCount + >> result.noneTranslatedBoneCount + >> result.totalBoneCount + >> result.otherBoneCount1 + >> result.otherBoneCount2 + >> result.notifyCount + >> result.assetType + >> result.pad + >> result.randomDataShortCount + >> result.indexCount + >> result.frameRate + >> result.frequency + >> result.boneIDsPtr + >> result.dataBytePtr + >> result.dataShortPtr + >> result.dataIntPtr + >> result.randomDataShortPtr + >> result.randomDataBytePtr + >> result.randomDataIntPtr + >> result.longIndiciesPtr + >> result.notificationsPtr + >> result.deltaPartsPtr; + + // Read in x_anim file name + QString xAnimName; + char xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + while (xAnimNameChar != 0) { + result.name += xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + } + + // Parse x_anim index header + QVector sectionLengths; + for (int i = 0; i < result.numframes; i++) { + quint8 sectionlength; + *aZoneFileStream >> sectionlength; + sectionLengths.push_back(sectionlength); + // Skip padding + aZoneFileStream->skipRawData(1); + } + // Skip unknown section + aZoneFileStream->skipRawData(2 * 8); + + return result; +} + +MenuFile ZoneFile_COD5::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { + //MENU_FILE + MenuFile result; + + aZoneFileStream->skipRawData(4); // Separator + + // Parse menu def count + *aZoneFileStream >> result.menuCount; + + // Clearly misparsed, never have this much + if (result.menuCount > 1000) { + qDebug() << "Failure reported when parsing menu file."; + return result; + } + for (uint i = 0; i < result.menuCount; i++) { + Menu menu; + + aZoneFileStream->skipRawData(4); // Separator + + // Read in x_anim file name + char menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + while (menuFilepathChar != 0) { + menu.filePath += menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + } + aZoneFileStream->skipRawData(4); // Separator + + *aZoneFileStream >> menu.menuNamePtr; + + float menuRectX, menuRectY, menuRectWidth, menuRectHeight; + *aZoneFileStream >> menuRectX >> menuRectY >> menuRectWidth >> menuRectHeight; + menu.rect = QRectF(menuRectX, menuRectY, menuRectWidth, menuRectHeight); + + quint32 hAlignInt, vAlignInt; + *aZoneFileStream >> hAlignInt >> vAlignInt; + menu.hAlign = (MENU_H_ALIGNMENT)hAlignInt; + menu.vAlign = (MENU_V_ALIGNMENT)vAlignInt; + + float rectClientX, rectClientY, rectClientWidth, rectClientHeight; + *aZoneFileStream >> rectClientX >> rectClientY >> rectClientWidth >> rectClientHeight; + menu.clientRect = QRectF(rectClientX, rectClientY, rectClientWidth, rectClientHeight); + + quint32 hClientAlignInt, vClientAlignInt, styleInt, borderInt; + *aZoneFileStream >> hClientAlignInt >> vClientAlignInt >> menu.groupPtr + >> styleInt >> borderInt >> menu.ownerDraw >> menu.ownerDrawFlags + >> menu.borderSize >> menu.staticFlags >> menu.dynamicFlags >> menu.nextTime; + menu.hClientAlign = (MENU_H_ALIGNMENT)hClientAlignInt; + menu.vClientAlign = (MENU_V_ALIGNMENT)vClientAlignInt; + menu.style = (MENU_WINDOW_STYLE)styleInt; + menu.border = (MENU_WINDOW_BORDER)borderInt; + + float foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA, + backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA, + borderColorR, borderColorG, borderColorB, borderColorA, + outlineColorR, outlineColorG, outlineColorB, outlineColorA; + *aZoneFileStream >> foregroundColorR >> foregroundColorG >> foregroundColorB >> foregroundColorA + >> backgroundColorR >> backgroundColorG >> backgroundColorB >> backgroundColorA + >> borderColorR >> borderColorG >> borderColorB >> borderColorA + >> outlineColorR >> outlineColorG >> outlineColorB >> outlineColorA; + + menu.foregroundColor = QColor(foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA); + menu.backgroundColor = QColor(backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA); + menu.borderColor = QColor(borderColorR, borderColorG, borderColorB, borderColorA); + menu.outlineColor = QColor(outlineColorR, outlineColorG, outlineColorB, outlineColorA); + + *aZoneFileStream >> menu.materialPtr >> menu.fontPtr >> menu.fullScreen >> menu.itemCount + >> menu.fontIndex >> menu.cursorItem >> menu.fadeCycle >> menu.fadeClamp + >> menu.fadeAmount >> menu.fadeInAmount >> menu.blurRadius >> menu.onOpenPtr + >> menu.onFocusPtr >> menu.onClosePtr >> menu.onESCPtr >> menu.onKeyPtr + >> menu.visibleExpCount >> menu.expEntryPtr >> menu.allowedBindingPtr + >> menu.soundNamePtr >> menu.imageTrack; + + float focusColorR, focusColorG, focusColorB, focusColorA, + disabledColorR, disabledColorG, disabledColorB, disabledColorA; + *aZoneFileStream >> focusColorR >> focusColorG >> focusColorB >> focusColorA + >> disabledColorR >> disabledColorG >> disabledColorB >> disabledColorA; + menu.focusColor = QColor(focusColorR, focusColorG, focusColorB, focusColorA); + menu.disabledColor = QColor(disabledColorR, disabledColorG, disabledColorB, disabledColorA); + + *aZoneFileStream >> menu.rectXExpCount >> menu.rectXExpPtr >> menu.rectYExpCount >> menu.rectYExpPtr; + + aZoneFileStream->skipRawData(4); // Separator + + char menuDefNameChar; + int menuDefNameLen = 0; + *aZoneFileStream >> menuDefNameChar; + while (menuDefNameChar != 0 && menuDefNameLen < 30) { + menuDefNameLen++; + menu.name += menuDefNameChar; + *aZoneFileStream >> menuDefNameChar; + } + + char defStringChar; + int defStringLen = 0; + *aZoneFileStream >> defStringChar; + while (defStringChar != 0 && defStringLen < 30) { + defStringLen++; + menu.definition += defStringChar; + *aZoneFileStream >> defStringChar; + } + aZoneFileStream->skipRawData(4 * 10); + + *aZoneFileStream >> menu.itemWindowDefNamePtr; + + float itemRectX, itemRectY, itemRectWidth, itemRectHeight; + *aZoneFileStream >> itemRectX >> itemRectY >> itemRectWidth >> itemRectHeight; + menu.itemRect = QRectF(itemRectX, itemRectY, itemRectWidth, itemRectHeight); + + *aZoneFileStream >> menu.itemHAlignment >> menu.itemVAlignment >> menu.itemGroupPtr + >> menu.itemWindowStyle >> menu.itemWindowBorder >> menu.itemOwnerDraw + >> menu.itemOwnerDrawFlags >> menu.itemBorderSize >> menu.itemStaticFlags + >> menu.itemDynamicFlags >> menu.itemNextTime; + + float itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA, + itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA, + itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA, + itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA; + *aZoneFileStream >> itemForegroundColorR >> itemForegroundColorG >> itemForegroundColorB >> itemForegroundColorA + >> itemBackgroundColorR >> itemBackgroundColorG >> itemBackgroundColorB >> itemBackgroundColorA + >> itemBorderColorR >> itemBorderColorG >> itemBorderColorB >> itemBorderColorA + >> itemOutlineColorR >> itemOutlineColorG >> itemOutlineColorB >> itemOutlineColorA; + + menu.itemForegroundColor = QColor(itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA); + menu.itemBackgroundColor = QColor(itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA); + menu.itemBorderColor = QColor(itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA); + menu.itemOutlineColor = QColor(itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA); + + *aZoneFileStream >> menu.itemMaterialPtr; + + float itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight; + *aZoneFileStream >> itemTextRectX >> itemTextRectY >> itemTextRectWidth >> itemTextRectHeight; + menu.itemTextRect = QRectF(itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight); + + quint32 hItemTextAlignInt, vItemTextAlignInt, itemType, fontTypeInt, textStyleInt; + *aZoneFileStream >> hItemTextAlignInt >> vItemTextAlignInt >> itemType >> menu.dataType + >> menu.alignment >> fontTypeInt >> menu.textAlignMode >> menu.textalignx >> menu.textaligny + >> menu.textscale >> textStyleInt >> menu.gameMsgWindowIndex >> menu.gameMsgWindowMode + >> menu.testPtr >> menu.textSavegameInfo >> menu.parentPtr; + menu.itemText_hAlign = (MENU_H_ALIGNMENT)hItemTextAlignInt; + menu.itemText_vAlign = (MENU_V_ALIGNMENT)vItemTextAlignInt; + menu.itemType = (MENU_ITEM_TYPE)itemType; + menu.fontEnum = (MENU_FONT_TYPE)fontTypeInt; + menu.textStyle = (MENU_ITEM_TEXTSTYLE)textStyleInt; + + *aZoneFileStream >> menu.mouseEnterText >> menu.mouseExitText >> menu.mouseEnter >> menu.mouseExit + >> menu.action >> menu.onAccept >> menu.onFocus >> menu.leaveFocus >> menu.dvar >> menu.dvarTest + >> menu.keyHandlerPtr >> menu.enableDvarPtr >> menu.dvarFlags >> menu.focusSoundPtr + >> menu.special >> menu.cursorPos; + + // itemDefData_t typeData; + + // listBoxDef_s *listBox; + + *aZoneFileStream >> menu.startPos >> menu.endPos >> menu.drawPadding; + + *aZoneFileStream >> menu.elementWidth >> menu.elementHeight; + + *aZoneFileStream >> menu.elementStyle >> menu.numColumns; + + //columnInfo_s columnInfo[16]; + + *aZoneFileStream >> menu.doubleClickPtr; + + + *aZoneFileStream >> menu.notselectable >> menu.noScrollBars >> menu.usePaging; + + float itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA, + itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA, + itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA; + *aZoneFileStream >> itemSelectBorderColorR >> itemSelectBorderColorG >> itemSelectBorderColorB >> itemSelectBorderColorA + >> itemDisableColorR >> itemDisableColorG >> itemDisableColorB >> itemDisableColorA + >> itemFocusColorR >> itemFocusColorG >> itemFocusColorB >> itemFocusColorA; + menu.itemSelectBorderColor = QColor(itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA); + menu.itemDisableColor = QColor(itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA); + menu.itemFocusColor = QColor(itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA); + + *aZoneFileStream >> menu.selectIconPtr >> menu.backgroundItemListboxPtr >> menu.highlightTexturePtr; + + // editFieldDef_s *editField; + + *aZoneFileStream >> menu.minVal >> menu.maxVal >> menu.defVal >> menu.range >> menu.maxChars + >> menu.maxCharsGotoNext >> menu.maxPaintChars >> menu.paintOffset; + + // multiDef_s *multi; + + for (int i = 0; i < 32; i++) { + quint32 dvarList; + *aZoneFileStream >> dvarList; + menu.dvarListPtrs.push_back(dvarList); + } + + for (int i = 0; i < 32; i++) { + quint32 dvarStr; + *aZoneFileStream >> dvarStr; + menu.dvarStrPtrs.push_back(dvarStr); + } + + for (int i = 0; i < 32; i++) { + float dvarValue; + *aZoneFileStream >> dvarValue; + menu.dvarValues.push_back(dvarValue); + } + + *aZoneFileStream >> menu.count >> menu.strDef >> menu.enumDvarNamePtr; + aZoneFileStream->skipRawData(4); + //>> menu.dataPtr + *aZoneFileStream >> menu.itemImageTrack; + + qDebug() << aZoneFileStream->device()->pos(); + + //statement_s visibleExp; + //statement_s textExp; + //statement_s materialExp; + //statement_s rectXExp; + //statement_s rectYExp; + //statement_s rectWExp; + //statement_s rectHExp; + //statement_s foreColorAExp; + result.menuDefs << menu; + } + return result; +} + +void ZoneFile_COD5::pParseAsset_Weapon(QDataStream *aZoneFileStream) { + //WEAPON_FILE + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD5::pParseAsset_D3DBSP(QDataStream *aZoneFileStream) { + //D3DBSP_DUMP + Q_UNUSED(aZoneFileStream); +} + +StringTable ZoneFile_COD5::pParseAsset_StringTable(QDataStream *aZoneFileStream) { + StringTable result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.columnCount + >> result.rowCount; + + // Todo fix this + result.columnCount = 0; + result.rowCount = 0; + + aZoneFileStream->skipRawData(4); + + QString stringTableName; + char stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + while (stringTableNameChar != 0) { + result.name += stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + } + + for (quint32 i = 0; i < result.rowCount; i++) { + QByteArray pointerData(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pointerData.data(), 4); + result.tablePointers.push_back(pointerData.toHex()); + + aZoneFileStream->skipRawData(4); + } + + for (const QString &pointerAddr : result.tablePointers) { + QString leadingContent = ""; + if (pointerAddr == "FFFFFFFF") { + char leadingContentChar; + *aZoneFileStream >> leadingContentChar; + while (leadingContentChar != 0) { + leadingContent += leadingContentChar; + *aZoneFileStream >> leadingContentChar; + } + } else { + leadingContent = pointerAddr; + } + + QString content; + char contentChar; + *aZoneFileStream >> contentChar; + while (contentChar != 0) { + content += contentChar; + *aZoneFileStream >> contentChar; + } + result.content[leadingContent] = content; + } + return result; +} + +QString ZoneFile_COD5::AssetTypeToString(const QString aAssetType) { + const QString cleanedType = aAssetType.toUpper(); + if (cleanedType == "17000000") { // localized string PARTIALLY VERIFIED + return "RAW FILE"; + } else if (cleanedType == "20000000") { // raw_file PARTIALLY VERIFIED + return "GSC FILE"; + } else if (cleanedType == "1A000000") { // fx PARTIALLY VERIFIED + return "EFFECT"; + } else if (cleanedType == "09000000") { // loaded_sound PARTIALLY VERIFIED + return "SOUND"; + } else if (cleanedType == "04000000") { // x_anim PARTIALLY VERIFIED + return "ANIMATION"; + } else if (cleanedType == "0C000000") { // collision_map PARTIALLY VERIFIED + return "COLLISION MAP"; + } else if (cleanedType == "21000000") { // string_table PARTIALLY VERIFIED + return "STRING TABLE"; + } else if (cleanedType == "15000000") { // menu_file PARTIALLY VERIFIED + return "MENU"; + } else if (cleanedType == "07000000") { // tech set PARTIALLY VERIFIED + return "TECH SET"; + } else if (cleanedType == "18000000") { // weapon PARTIALLY VERIFIED + return "WEAPON"; + } else if (cleanedType == "11000000") { // gfx map PARTIALLY VERIFIED + return "GFX MAP"; + } else if (cleanedType == "12000000") { // light_def PARTIALLY VERIFIED + return "LIGHT DEF"; + } else if (cleanedType == "14000000") { // font PARTIALLY VERIFIED + return "FONT"; + } else if (cleanedType == "05000000") { // xmodel PARTIALLY VERIFIED + return "MODEL"; + } else if (cleanedType == "0D000000") { // d3dbsp PARTIALLY VERIFIED + return "D3DBSP"; + } else if (cleanedType == "06000000") { // image PARTIALLY VERIFIED + return "IMAGE"; + } else if (cleanedType == "0E000000") { // game map sp PARTIALLY VERIFIED + return "GAME MAP SP"; + } else if (cleanedType == "0B000000") { // col map sp PARTIALLY VERIFIED + return "COL MAP SP"; + } else if (cleanedType == "01000000") { // physics preset PARTIALLY VERIFIED + return "PHYS PRESET"; + } else if (cleanedType == "03000000") { // destructible def PARTIALLY VERIFIED + return "DESTRUCTIBLE"; + } + return aAssetType; +} + +QByteArray ZoneFile_COD5::GetBinaryData() { + QByteArray result; + + return result; +} diff --git a/libs/zonefile/zonefile_cod5.h b/libs/zonefile/zonefile_cod5.h new file mode 100644 index 0000000..b7294fb --- /dev/null +++ b/libs/zonefile/zonefile_cod5.h @@ -0,0 +1,54 @@ +#ifndef ZONEFILE_COD5_H +#define ZONEFILE_COD5_H + +#include + +#include "zonefile.h" + +class ZoneFile_COD5 : public ZoneFile +{ +public: + ZoneFile_COD5(); + ~ZoneFile_COD5(); + + bool Load(const QByteArray aFileData, FF_PLATFORM aPlatform) override; + QString AssetTypeToString(const QString aAssetType) override; + + QByteArray GetBinaryData() override; + +private: + void pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) override; + quint32 pParseZoneSize(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsA(QDataStream *aZoneFileStream) override; + quint32 pParseZoneTagCount(QDataStream *aZoneFileStream) override; + quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsB(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsC(QDataStream *aZoneFileStream) override; + QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) override; + QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) override; + AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) override; + LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream) override; + RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream) override; + void pParseAsset_PhysPreset(QDataStream *aZoneFileStream) override; + Model pParseAsset_Model(QDataStream *aZoneFileStream) override; + Material pParseAsset_Material(QDataStream *aZoneFileStream) override; + Shader pParseAsset_Shader(QDataStream *aZoneFileStream) override; + TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream) override; + Image pParseAsset_Image(QDataStream *aZoneFileStream) override; + SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream) override; + void pParseAsset_ColMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapSP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_LightDef(QDataStream *aZoneFileStream) override; + void pParseAsset_UIMap(QDataStream *aZoneFileStream) override; + void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) override; + void pParseAsset_AIType(QDataStream *aZoneFileStream) override; + void pParseAsset_FX(QDataStream *aZoneFileStream) override; + Animation pParseAsset_Animation(QDataStream *aZoneFileStream) override; + MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream) override; + void pParseAsset_Weapon(QDataStream *aZoneFileStream) override; + void pParseAsset_D3DBSP(QDataStream *aZoneFileStream) override; + StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream) override; +}; + +#endif // ZONEFILE_COD5_H diff --git a/libs/zonefile/zonefile_cod7.cpp b/libs/zonefile/zonefile_cod7.cpp new file mode 100644 index 0000000..a6d6988 --- /dev/null +++ b/libs/zonefile/zonefile_cod7.cpp @@ -0,0 +1,1103 @@ +#include "zonefile_cod7.h" + +#include +#include +#include + +ZoneFile_COD7::ZoneFile_COD7() +{ + +} + +ZoneFile_COD7::~ZoneFile_COD7() +{ + +} + +bool ZoneFile_COD7::Load(const QByteArray aFileData, FF_PLATFORM aPlatform) { + // Open zone file as little endian stream + QDataStream zoneFileStream(aFileData); + if (aPlatform == FF_PLATFORM_PC) { + zoneFileStream.setByteOrder(QDataStream::LittleEndian); + } else { + zoneFileStream.setByteOrder(QDataStream::BigEndian); + } + + // Parse data from zone file header + pParseZoneHeader(&zoneFileStream, aPlatform); + zoneFileStream.device()->seek(zoneFileStream.device()->pos() - 1); + SetRecords(pParseZoneIndex(&zoneFileStream, GetRecordCount())); + SetAssetMap(pParseAssets(&zoneFileStream, GetRecords())); + + return true; +} + +void ZoneFile_COD7::pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) { + quint32 size = pParseZoneSize(aZoneFileStream); + SetSize(size); + + if (aPlatform == FF_PLATFORM_WII) { + aZoneFileStream->skipRawData(36); + } else { + pParseZoneUnknownsA(aZoneFileStream); + } + + quint32 tagCount = pParseZoneTagCount(aZoneFileStream); + SetTagCount(tagCount); + if (aPlatform == FF_PLATFORM_WII) { + SetTagCount(GetTagCount() - 1); + } + + pParseZoneUnknownsB(aZoneFileStream); + + quint32 recordCount = pParseZoneRecordCount(aZoneFileStream); + SetRecordCount(recordCount); + + if (tagCount) { + pParseZoneUnknownsC(aZoneFileStream); + SetTags(pParseZoneTags(aZoneFileStream, tagCount)); + } else { + aZoneFileStream->skipRawData(4); + } +} + +quint32 ZoneFile_COD7::pParseZoneSize(QDataStream *aZoneFileStream) { + quint32 zoneFileSize; + *aZoneFileStream >> zoneFileSize; + if (zoneFileSize <= 0) { + qDebug() << "Tried to open empty zone file!"; + exit(-1); + } + zoneFileSize += 36; + return zoneFileSize; +} + +/* + ParseZoneUnknownsA() + + Parses the 1st section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD7::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { + // Byte 4-7, 8-11, 12-15: unknown + QByteArray unknown1(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown1.data(), 4); + + QByteArray unknown2(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown2.data(), 4); + + QByteArray unknown3(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown3.data(), 4); + + // Byte 16-19, 20-23: empty/unknown + QByteArray unknown4(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown4.data(), 4); + + QByteArray unknown5(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown5.data(), 4); + + // Byte 24-27: somehow related to the filesize, but smaller value + QByteArray unknown6(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown6.data(), 4); + + // Byte 28-31, 32-35: unknown + QByteArray unknown7(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown7.data(), 4); + + QByteArray unknown8(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown8.data(), 4); +} + +/* + ParseZoneTagCount() + + Parses the number of string tags in the zone index +*/ +quint32 ZoneFile_COD7::pParseZoneTagCount(QDataStream *aZoneFileStream) { + quint32 tagCount; + *aZoneFileStream >> tagCount; + return tagCount; +} + +/* + ParseZoneRecordCount() + + Parses the number of records in the zone index +*/ +quint32 ZoneFile_COD7::pParseZoneRecordCount(QDataStream *aZoneFileStream) { + quint32 recordCount; + *aZoneFileStream >> recordCount; + return recordCount; +} + +/* + ParseZoneUnknownsB() + + Parses the 2nd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD7::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { + // Byte 44-47: Unknown/empty? + QByteArray unknown9(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown9.data(), 4); +} + +/* + pParseZoneUnknownsC() + + Parses the 3rd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD7::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { + // Byte 40-43: Unknown/empty? + QByteArray unknown10(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown10.data(), 4); + + // Byte 44-47: Unknown/empty? + QByteArray unknown11(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown11.data(), 4); +} + +/* + ParseZoneTags() + + Parses the string tags ate the start of zone file +*/ +QStringList ZoneFile_COD7::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { + QStringList tags; + + // Byte 48-51: Repeated separators? ÿÿÿÿ x i + aZoneFileStream->skipRawData(4 * (tagCount - 1)); + + // Parse tags/strings before index + QString zoneTag; + char zoneTagChar; + for (quint32 i = 0; i < tagCount; i++) { + *aZoneFileStream >> zoneTagChar; + while (zoneTagChar != 0) { + zoneTag += zoneTagChar; + *aZoneFileStream >> zoneTagChar; + } + tags << zoneTag; + zoneTag.clear(); + } + return tags; +} + +/* + ParseZoneIndex() + + Parse the binary zone index data and populate table +*/ +QStringList ZoneFile_COD7::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { + QStringList result; + + // Don't parse if no records + if (!recordCount) { return result; } + + if (aZoneFileStream->device()->peek(4).toHex().contains("ffff")) { + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 2); + } + + // Parse index & map found asset types + for (quint32 i = 0; i <= recordCount; i++) { + // Skip record start + QByteArray rawAssetType(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rawAssetType.data(), 4); + result << rawAssetType.toHex(); + + // Skip separator + aZoneFileStream->skipRawData(4); + } + return result; +} + +AssetMap ZoneFile_COD7::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { + AssetMap result; + + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); + + for (int i = 0; i < assetOrder.size(); i++) { + const QString typeHex = assetOrder[i]; + const QString typeStr = AssetTypeToString(typeHex); + + if (typeStr == "LOCAL STRING") { // localized string asset + result.localStrings << pParseAsset_LocalString(aZoneFileStream); + } else if (typeStr == "RAW FILE") { // gsc + auto rawFile = pParseAsset_RawFile(aZoneFileStream); + if (rawFile.length) { + result.rawFiles << rawFile; + } + } else if (typeStr == "PHYS PRESET") { // physpreset + pParseAsset_PhysPreset(aZoneFileStream); + } else if (typeStr == "MODEL") { // xmodel + result.models << pParseAsset_Model(aZoneFileStream); + } else if (typeStr == "MATERIAL") { // material + pParseAsset_Material(aZoneFileStream); + } else if (typeStr == "SHADER") { // pixelshader + pParseAsset_Shader(aZoneFileStream); + } else if (typeStr == "TECH SET") { // techset include + result.techSets << pParseAsset_TechSet(aZoneFileStream); + } else if (typeStr == "IMAGE") { // image + result.images << pParseAsset_Image(aZoneFileStream); + } else if (typeStr == "SOUND") { // loaded_sound + result.sounds << pParseAsset_Sound(aZoneFileStream); + } else if (typeStr == "COLLISION MAP") { // col_map_mp + pParseAsset_ColMapMP(aZoneFileStream); + } else if (typeStr == "MP MAP") { // game_map_sp + pParseAsset_GameMapSP(aZoneFileStream); + } else if (typeStr == "SP MAP") { // game_map_mp + pParseAsset_GameMapMP(aZoneFileStream); + } else if (typeStr == "LIGHT DEF") { // lightdef + pParseAsset_LightDef(aZoneFileStream); + } else if (typeStr == "UI MAP") { // ui_map + pParseAsset_UIMap(aZoneFileStream); + } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals + pParseAsset_SNDDriverGlobals(aZoneFileStream); + } else if (typeStr == "AI TYPE") { // aitype + pParseAsset_AIType(aZoneFileStream); + } else if (typeStr == "EFFECT") { // aitype + pParseAsset_FX(aZoneFileStream); + } else if (typeStr == "ANIMATION") { // aitype + result.animations << pParseAsset_Animation(aZoneFileStream); + } else if (typeStr == "STRING TABLE") { // string_table + result.stringTables << pParseAsset_StringTable(aZoneFileStream); + } else if (typeStr == "MENU") { // string_table + result.menuFiles << pParseAsset_MenuFile(aZoneFileStream); + } else if (typeStr == "WEAPON") { // string_table + pParseAsset_Weapon(aZoneFileStream); + } else if (typeStr == "D3DBSP DUMP") { // string_table + pParseAsset_D3DBSP(aZoneFileStream); + } else if (typeStr != "UNKNOWN") { + qDebug() << "Found bad asset type!" << typeStr; + } + } + return result; +} + +LocalString ZoneFile_COD7::pParseAsset_LocalString(QDataStream *aZoneFileStream) { + LocalString result; + + quint32 stringPtr, aliasPtr; + *aZoneFileStream >> stringPtr >> aliasPtr; + if (stringPtr == 4294967295) { + // Parse local string asset contents + QString localStr; + char localStrChar; + *aZoneFileStream >> localStrChar; + while (localStrChar != 0) { + result.string += localStrChar; + *aZoneFileStream >> localStrChar; + } + } else { + result.string = "String Ptr: " + QString::number(stringPtr); + } + + if (aliasPtr == 4294967295) { + // Parse rawfile name + QString aliasName; + char aliasNameChar; + *aZoneFileStream >> aliasNameChar; + while (aliasNameChar != 0) { + result.alias += aliasNameChar; + *aZoneFileStream >> aliasNameChar; + } + } else { + result.string = "Alias Ptr: " + QString::number(aliasPtr); + } + + return result; +} + +RawFile ZoneFile_COD7::pParseAsset_RawFile(QDataStream *aZoneFileStream) { + RawFile result; + + // Skip start separator FF FF FF FF (pointer?) + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.length; + + // Skip unknown 4 byte data + aZoneFileStream->skipRawData(4); + + // Parse rawfile path + char scriptPathChar; + *aZoneFileStream >> scriptPathChar; + while (scriptPathChar != 0) { + result.path += scriptPathChar; + *aZoneFileStream >> scriptPathChar; + } + result.path.replace(",", ""); + const QStringList pathParts = result.path.split('/'); + if (pathParts.size() == 0) { + qDebug() << "Failed to parse ff path! " << result.path; + exit(-1); + } + + // Parse gsc contents + char rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + while (rawFileContentsChar != 0 && rawFileContentsChar != -1) { + result.contents += rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + } + return result; +} + +void ZoneFile_COD7::pParseAsset_PhysPreset(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Model ZoneFile_COD7::pParseAsset_Model(QDataStream *aZoneFileStream) { + Model result; + + qDebug() << "Model Info:"; + + *aZoneFileStream >> result.namePtr >> result.tagCount >> result.rootTagCount + >> result.surfCount >> result.unknownCount >> result.boneNamePtr + >> result.parentListPtr >> result.quatsPtr >> result.transPtr + >> result.partClassPtr >> result.baseMatPtr + >> result.surfsPtr >> result.materialHandlesPtr; + + // Parse XModelLodInfo + for (int i = 1; i <= 4; i++) { + qDebug() << "- Lod Info " << i; + + quint32 intDist; + *aZoneFileStream >> intDist; + + std::memcpy(&result.lodInfo[i].dist, &intDist, sizeof(result.lodInfo[i].dist)); + *aZoneFileStream >> result.lodInfo[i].numsurfs >> result.lodInfo[i].surfIndex; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.lodInfo[i].partBits[0] + >> result.lodInfo[i].partBits[1] + >> result.lodInfo[i].partBits[2] + >> result.lodInfo[i].partBits[3]; + } + + *aZoneFileStream >> result.collSurfsPtr >> result.numCollSurfs >> result.contents >> result.boneInfoPtr; + + quint32 intRadius, intMins[3], intMaxs[3]; + *aZoneFileStream >> intRadius >> intMins[0] >> intMins[1] + >> intMins[2] >> intMaxs[0] >> intMaxs[1] >> intMaxs[2]; + + std::memcpy(&result.radius, &intRadius, sizeof(result.radius)); + + std::memcpy(&result.mins[0], &intMins[0], sizeof(result.mins[0])); + std::memcpy(&result.mins[1], &intMins[1], sizeof(result.mins[1])); + std::memcpy(&result.mins[2], &intMins[2], sizeof(result.mins[2])); + + std::memcpy(&result.maxs[0], &intMaxs[0], sizeof(result.maxs[0])); + std::memcpy(&result.maxs[1], &intMaxs[1], sizeof(result.maxs[2])); + std::memcpy(&result.maxs[2], &intMaxs[2], sizeof(result.maxs[3])); + + *aZoneFileStream >> result.numLods >> result.collLod >> result.streamInfoPtr + >> result.memUsage >> result.flags >> result.physPresetPtr >> result.physGeomsPtr; + + // Parse model name + char modelNameChar; + *aZoneFileStream >> modelNameChar; + while (modelNameChar == 0) { + *aZoneFileStream >> modelNameChar; + } + while (modelNameChar != 0) { + result.modelName += modelNameChar; + *aZoneFileStream >> modelNameChar; + } + return result; +} + +Material ZoneFile_COD7::pParseAsset_Material(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + + return Material(); +} + +Shader ZoneFile_COD7::pParseAsset_Shader(QDataStream *aZoneFileStream) { + Shader result = Shader(); + + quint8 minorVersion, majorVersion; + *aZoneFileStream >> minorVersion >> majorVersion; + + quint32 magic; + *aZoneFileStream >> magic; + + // Verify .fxc magic + if (magic != 65534 && magic != 65535) { return result; } + + SHADER_TYPE type = SHADER_NONE; + quint16 rawShaderType; + *aZoneFileStream >> rawShaderType; + if (rawShaderType == 65535) { // Pixel shader + type = SHADER_PIXEL; + } else if (rawShaderType == 65534) { // Vertex shader + type = SHADER_VERTEX; + } + Q_UNUSED(type); + + while (true) { + quint32 instructionToken; + *aZoneFileStream >> instructionToken; + + SHADER_OPCODE opCode = (SHADER_OPCODE)(instructionToken & 0xffff); + + int size; + if (opCode == OPCODE_End) { + break; + } else if (opCode == OPCODE_Comment) { + size = (int)((instructionToken >> 16) & 0x7FFF); + } else { + size = (int)((instructionToken >> 24) & 0x0f); + } + Q_UNUSED(size); + } + + + return result; +} + +TechSet ZoneFile_COD7::pParseAsset_TechSet(QDataStream *aZoneFileStream) { + TechSet result; + + for (int i = 1; i <= 62; i++) { + quint32 ptr; + *aZoneFileStream >> ptr; + + result.pointers << ptr; + } + qDebug() << aZoneFileStream->device()->pos(); + + // Parse techset name + char techSetNameChar; + *aZoneFileStream >> techSetNameChar; + while (techSetNameChar != 0) { + result.name += techSetNameChar; + *aZoneFileStream >> techSetNameChar; + } + result.name.replace(",", ""); + + return result; +} + +Image ZoneFile_COD7::pParseAsset_Image(QDataStream *aZoneFileStream) { + Image result; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknowna >> result.unknownb + >> result.unknownc >> result.unknownd + >> result.unknowne >> result.unknownf + >> result.unknowng; + + aZoneFileStream->skipRawData(15 * 4); + *aZoneFileStream >> result.unknownh >> result.unknowni; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknownj; + + aZoneFileStream->skipRawData(4); + + char materialNameChar; + *aZoneFileStream >> materialNameChar; + while (materialNameChar != 0) { + result.materialName += materialNameChar; + *aZoneFileStream >> materialNameChar; + } + result.materialName.replace(",", ""); + + if (result.unknowna) { + *aZoneFileStream >> result.unknownk; + *aZoneFileStream >> result.unknownl; + *aZoneFileStream >> result.unknownm; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown1; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown2 >> result.unknown3 + >> result.size1 >> result.size2 + >> result.unknown4 >> result.unknown5; + + aZoneFileStream->skipRawData(4); + + char imageNameChar; + *aZoneFileStream >> imageNameChar; + while (imageNameChar != 0) { + result.name += imageNameChar; + *aZoneFileStream >> imageNameChar; + } + + *aZoneFileStream >> result.unknown6 >> result.unknown7; + + QByteArray compressionData(8, Qt::Uninitialized); + aZoneFileStream->readRawData(compressionData.data(), 8); + if (compressionData.contains("DXT1")) { + result.compression = COMPRESSION_DXT1; + } else if (compressionData.contains("DXT3")) { + result.compression = COMPRESSION_DXT3; + } else if (compressionData.contains("DXT5")) { + result.compression = COMPRESSION_DXT5; + } else { + result.compression = COMPRESSION_NONE; + } + + *aZoneFileStream >> result.unknown8 >> result.unknown9; + } + + return result; +} + +SoundAsset ZoneFile_COD7::pParseAsset_Sound(QDataStream *aZoneFileStream) { + SoundAsset result; + + qDebug() << aZoneFileStream->device()->pos(); + + QByteArray rootNamePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rootNamePtr.data(), 4); + qDebug() << "Root name ptr: " << (QString)rootNamePtr.toHex(); + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.count; + + if (rootNamePtr.toHex() == "ffffffff") { + // Read in sound file name + char soundNameChar; + *aZoneFileStream >> soundNameChar; + while (soundNameChar != 0) { + result.name += soundNameChar; + *aZoneFileStream >> soundNameChar; + } + } + + int tagCount = 0; + int resultCount = 0; + for (quint32 i = 0; i < result.count; i++) { + aZoneFileStream->skipRawData(12); + + QByteArray tagPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(tagPtr.data(), 4); + + if (tagPtr.toHex() == "ffffffff") { + qDebug() << "Tag Ptr: " << tagPtr.toHex(); + tagCount++; + } + aZoneFileStream->skipRawData(4); + + QByteArray pathPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pathPtr.data(), 4); + + if (pathPtr.toHex() == "ffffffff") { + qDebug() << "Path Ptr: " << pathPtr.toHex(); + resultCount++; + } + + aZoneFileStream->skipRawData(160); + } + + for (int i = 0; i < tagCount; i++) { + // Read in tag? + QString tag; + char tagChar; + *aZoneFileStream >> tagChar; + while (tagChar != 0) { + tag += tagChar; + *aZoneFileStream >> tagChar; + } + qDebug() << "Tag: " << tag; + } + + for (int i = 0; i < resultCount; i++) { + Sound sound; + + if (aZoneFileStream->device()->peek(12).toHex().contains("ffffffff00000000")) { + aZoneFileStream->skipRawData(12); + } + + aZoneFileStream->skipRawData(8); + + qDebug() << "- " << aZoneFileStream->device()->pos(); + QByteArray aliasPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(aliasPtr.data(), 4); + + QByteArray namePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(namePtr.data(), 4); + + *aZoneFileStream >> sound.dataLength; + qDebug() << "- Data length: " << sound.dataLength; + + if (aliasPtr.toHex() == "ffffffff") { + // Read in sound alias name + char soundAliasChar; + *aZoneFileStream >> soundAliasChar; + while (soundAliasChar != 0) { + sound.alias += soundAliasChar; + *aZoneFileStream >> soundAliasChar; + } + qDebug() << "- Alias: " << sound.alias; + } + + if (aZoneFileStream->device()->peek(4) == "RIFF") { + sound.path = sound.alias; + sound.alias = ""; + } else if (namePtr.toHex() == "ffffffff") { + // Read in sound file path + char soundPathChar; + *aZoneFileStream >> soundPathChar; + while (soundPathChar != 0) { + sound.path += soundPathChar; + *aZoneFileStream >> soundPathChar; + } + sound.path.replace(",", ""); + qDebug() << "- Path: " << sound.path; + } + + if (sound.dataLength) { + QByteArray data(sound.dataLength, Qt::Uninitialized); + aZoneFileStream->readRawData(data.data(), sound.dataLength); + sound.data = data; + } + result.sounds.append(sound); + } + qDebug() << "- " << aZoneFileStream->device()->pos(); + + return result; +} + +void ZoneFile_COD7::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_GameMapSP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_GameMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_LightDef(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_UIMap(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_AIType(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_FX(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Animation ZoneFile_COD7::pParseAsset_Animation(QDataStream *aZoneFileStream) { + Animation result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.dataByteCount + >> result.dataShortCount + >> result.dataIntCount + >> result.randomDataByteCount + >> result.randomDataIntCount + >> result.numframes + >> result.isLooped + >> result.isDelta + >> result.noneRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.normalTranslatedBoneCount + >> result.preciseTranslatedBoneCount + >> result.staticTranslatedBoneCount + >> result.noneTranslatedBoneCount + >> result.totalBoneCount + >> result.otherBoneCount1 + >> result.otherBoneCount2 + >> result.notifyCount + >> result.assetType + >> result.pad + >> result.randomDataShortCount + >> result.indexCount + >> result.frameRate + >> result.frequency + >> result.boneIDsPtr + >> result.dataBytePtr + >> result.dataShortPtr + >> result.dataIntPtr + >> result.randomDataShortPtr + >> result.randomDataBytePtr + >> result.randomDataIntPtr + >> result.longIndiciesPtr + >> result.notificationsPtr + >> result.deltaPartsPtr; + + // Read in x_anim file name + QString xAnimName; + char xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + while (xAnimNameChar != 0) { + result.name += xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + } + + // Parse x_anim index header + QVector sectionLengths; + for (int i = 0; i < result.numframes; i++) { + quint8 sectionlength; + *aZoneFileStream >> sectionlength; + sectionLengths.push_back(sectionlength); + // Skip padding + aZoneFileStream->skipRawData(1); + } + // Skip unknown section + aZoneFileStream->skipRawData(2 * 8); + + return result; +} + +MenuFile ZoneFile_COD7::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { + //MENU_FILE + MenuFile result; + + aZoneFileStream->skipRawData(4); // Separator + + // Parse menu def count + *aZoneFileStream >> result.menuCount; + + // Clearly misparsed, never have this much + if (result.menuCount > 1000) { + qDebug() << "Failure reported when parsing menu file."; + return result; + } + for (uint i = 0; i < result.menuCount; i++) { + Menu menu; + + aZoneFileStream->skipRawData(4); // Separator + + // Read in x_anim file name + char menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + while (menuFilepathChar != 0) { + menu.filePath += menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + } + aZoneFileStream->skipRawData(4); // Separator + + *aZoneFileStream >> menu.menuNamePtr; + + float menuRectX, menuRectY, menuRectWidth, menuRectHeight; + *aZoneFileStream >> menuRectX >> menuRectY >> menuRectWidth >> menuRectHeight; + menu.rect = QRectF(menuRectX, menuRectY, menuRectWidth, menuRectHeight); + + quint32 hAlignInt, vAlignInt; + *aZoneFileStream >> hAlignInt >> vAlignInt; + menu.hAlign = (MENU_H_ALIGNMENT)hAlignInt; + menu.vAlign = (MENU_V_ALIGNMENT)vAlignInt; + + float rectClientX, rectClientY, rectClientWidth, rectClientHeight; + *aZoneFileStream >> rectClientX >> rectClientY >> rectClientWidth >> rectClientHeight; + menu.clientRect = QRectF(rectClientX, rectClientY, rectClientWidth, rectClientHeight); + + quint32 hClientAlignInt, vClientAlignInt, styleInt, borderInt; + *aZoneFileStream >> hClientAlignInt >> vClientAlignInt >> menu.groupPtr + >> styleInt >> borderInt >> menu.ownerDraw >> menu.ownerDrawFlags + >> menu.borderSize >> menu.staticFlags >> menu.dynamicFlags >> menu.nextTime; + menu.hClientAlign = (MENU_H_ALIGNMENT)hClientAlignInt; + menu.vClientAlign = (MENU_V_ALIGNMENT)vClientAlignInt; + menu.style = (MENU_WINDOW_STYLE)styleInt; + menu.border = (MENU_WINDOW_BORDER)borderInt; + + float foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA, + backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA, + borderColorR, borderColorG, borderColorB, borderColorA, + outlineColorR, outlineColorG, outlineColorB, outlineColorA; + *aZoneFileStream >> foregroundColorR >> foregroundColorG >> foregroundColorB >> foregroundColorA + >> backgroundColorR >> backgroundColorG >> backgroundColorB >> backgroundColorA + >> borderColorR >> borderColorG >> borderColorB >> borderColorA + >> outlineColorR >> outlineColorG >> outlineColorB >> outlineColorA; + + menu.foregroundColor = QColor(foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA); + menu.backgroundColor = QColor(backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA); + menu.borderColor = QColor(borderColorR, borderColorG, borderColorB, borderColorA); + menu.outlineColor = QColor(outlineColorR, outlineColorG, outlineColorB, outlineColorA); + + *aZoneFileStream >> menu.materialPtr >> menu.fontPtr >> menu.fullScreen >> menu.itemCount + >> menu.fontIndex >> menu.cursorItem >> menu.fadeCycle >> menu.fadeClamp + >> menu.fadeAmount >> menu.fadeInAmount >> menu.blurRadius >> menu.onOpenPtr + >> menu.onFocusPtr >> menu.onClosePtr >> menu.onESCPtr >> menu.onKeyPtr + >> menu.visibleExpCount >> menu.expEntryPtr >> menu.allowedBindingPtr + >> menu.soundNamePtr >> menu.imageTrack; + + float focusColorR, focusColorG, focusColorB, focusColorA, + disabledColorR, disabledColorG, disabledColorB, disabledColorA; + *aZoneFileStream >> focusColorR >> focusColorG >> focusColorB >> focusColorA + >> disabledColorR >> disabledColorG >> disabledColorB >> disabledColorA; + menu.focusColor = QColor(focusColorR, focusColorG, focusColorB, focusColorA); + menu.disabledColor = QColor(disabledColorR, disabledColorG, disabledColorB, disabledColorA); + + *aZoneFileStream >> menu.rectXExpCount >> menu.rectXExpPtr >> menu.rectYExpCount >> menu.rectYExpPtr; + + aZoneFileStream->skipRawData(4); // Separator + + char menuDefNameChar; + int menuDefNameLen = 0; + *aZoneFileStream >> menuDefNameChar; + while (menuDefNameChar != 0 && menuDefNameLen < 30) { + menuDefNameLen++; + menu.name += menuDefNameChar; + *aZoneFileStream >> menuDefNameChar; + } + + char defStringChar; + int defStringLen = 0; + *aZoneFileStream >> defStringChar; + while (defStringChar != 0 && defStringLen < 30) { + defStringLen++; + menu.definition += defStringChar; + *aZoneFileStream >> defStringChar; + } + aZoneFileStream->skipRawData(4 * 10); + + *aZoneFileStream >> menu.itemWindowDefNamePtr; + + float itemRectX, itemRectY, itemRectWidth, itemRectHeight; + *aZoneFileStream >> itemRectX >> itemRectY >> itemRectWidth >> itemRectHeight; + menu.itemRect = QRectF(itemRectX, itemRectY, itemRectWidth, itemRectHeight); + + *aZoneFileStream >> menu.itemHAlignment >> menu.itemVAlignment >> menu.itemGroupPtr + >> menu.itemWindowStyle >> menu.itemWindowBorder >> menu.itemOwnerDraw + >> menu.itemOwnerDrawFlags >> menu.itemBorderSize >> menu.itemStaticFlags + >> menu.itemDynamicFlags >> menu.itemNextTime; + + float itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA, + itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA, + itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA, + itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA; + *aZoneFileStream >> itemForegroundColorR >> itemForegroundColorG >> itemForegroundColorB >> itemForegroundColorA + >> itemBackgroundColorR >> itemBackgroundColorG >> itemBackgroundColorB >> itemBackgroundColorA + >> itemBorderColorR >> itemBorderColorG >> itemBorderColorB >> itemBorderColorA + >> itemOutlineColorR >> itemOutlineColorG >> itemOutlineColorB >> itemOutlineColorA; + + menu.itemForegroundColor = QColor(itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA); + menu.itemBackgroundColor = QColor(itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA); + menu.itemBorderColor = QColor(itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA); + menu.itemOutlineColor = QColor(itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA); + + *aZoneFileStream >> menu.itemMaterialPtr; + + float itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight; + *aZoneFileStream >> itemTextRectX >> itemTextRectY >> itemTextRectWidth >> itemTextRectHeight; + menu.itemTextRect = QRectF(itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight); + + quint32 hItemTextAlignInt, vItemTextAlignInt, itemType, fontTypeInt, textStyleInt; + *aZoneFileStream >> hItemTextAlignInt >> vItemTextAlignInt >> itemType >> menu.dataType + >> menu.alignment >> fontTypeInt >> menu.textAlignMode >> menu.textalignx >> menu.textaligny + >> menu.textscale >> textStyleInt >> menu.gameMsgWindowIndex >> menu.gameMsgWindowMode + >> menu.testPtr >> menu.textSavegameInfo >> menu.parentPtr; + menu.itemText_hAlign = (MENU_H_ALIGNMENT)hItemTextAlignInt; + menu.itemText_vAlign = (MENU_V_ALIGNMENT)vItemTextAlignInt; + menu.itemType = (MENU_ITEM_TYPE)itemType; + menu.fontEnum = (MENU_FONT_TYPE)fontTypeInt; + menu.textStyle = (MENU_ITEM_TEXTSTYLE)textStyleInt; + + *aZoneFileStream >> menu.mouseEnterText >> menu.mouseExitText >> menu.mouseEnter >> menu.mouseExit + >> menu.action >> menu.onAccept >> menu.onFocus >> menu.leaveFocus >> menu.dvar >> menu.dvarTest + >> menu.keyHandlerPtr >> menu.enableDvarPtr >> menu.dvarFlags >> menu.focusSoundPtr + >> menu.special >> menu.cursorPos; + + // itemDefData_t typeData; + + // listBoxDef_s *listBox; + + *aZoneFileStream >> menu.startPos >> menu.endPos >> menu.drawPadding; + + *aZoneFileStream >> menu.elementWidth >> menu.elementHeight; + + *aZoneFileStream >> menu.elementStyle >> menu.numColumns; + + //columnInfo_s columnInfo[16]; + + *aZoneFileStream >> menu.doubleClickPtr; + + + *aZoneFileStream >> menu.notselectable >> menu.noScrollBars >> menu.usePaging; + + float itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA, + itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA, + itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA; + *aZoneFileStream >> itemSelectBorderColorR >> itemSelectBorderColorG >> itemSelectBorderColorB >> itemSelectBorderColorA + >> itemDisableColorR >> itemDisableColorG >> itemDisableColorB >> itemDisableColorA + >> itemFocusColorR >> itemFocusColorG >> itemFocusColorB >> itemFocusColorA; + menu.itemSelectBorderColor = QColor(itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA); + menu.itemDisableColor = QColor(itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA); + menu.itemFocusColor = QColor(itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA); + + *aZoneFileStream >> menu.selectIconPtr >> menu.backgroundItemListboxPtr >> menu.highlightTexturePtr; + + // editFieldDef_s *editField; + + *aZoneFileStream >> menu.minVal >> menu.maxVal >> menu.defVal >> menu.range >> menu.maxChars + >> menu.maxCharsGotoNext >> menu.maxPaintChars >> menu.paintOffset; + + // multiDef_s *multi; + + for (int i = 0; i < 32; i++) { + quint32 dvarList; + *aZoneFileStream >> dvarList; + menu.dvarListPtrs.push_back(dvarList); + } + + for (int i = 0; i < 32; i++) { + quint32 dvarStr; + *aZoneFileStream >> dvarStr; + menu.dvarStrPtrs.push_back(dvarStr); + } + + for (int i = 0; i < 32; i++) { + float dvarValue; + *aZoneFileStream >> dvarValue; + menu.dvarValues.push_back(dvarValue); + } + + *aZoneFileStream >> menu.count >> menu.strDef >> menu.enumDvarNamePtr; + aZoneFileStream->skipRawData(4); + //>> menu.dataPtr + *aZoneFileStream >> menu.itemImageTrack; + + qDebug() << aZoneFileStream->device()->pos(); + + //statement_s visibleExp; + //statement_s textExp; + //statement_s materialExp; + //statement_s rectXExp; + //statement_s rectYExp; + //statement_s rectWExp; + //statement_s rectHExp; + //statement_s foreColorAExp; + result.menuDefs << menu; + } + return result; +} + +void ZoneFile_COD7::pParseAsset_Weapon(QDataStream *aZoneFileStream) { + //WEAPON_FILE + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD7::pParseAsset_D3DBSP(QDataStream *aZoneFileStream) { + //D3DBSP_DUMP + Q_UNUSED(aZoneFileStream); +} + +StringTable ZoneFile_COD7::pParseAsset_StringTable(QDataStream *aZoneFileStream) { + StringTable result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.columnCount + >> result.rowCount; + + // Todo fix this + result.columnCount = 0; + result.rowCount = 0; + + aZoneFileStream->skipRawData(4); + + QString stringTableName; + char stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + while (stringTableNameChar != 0) { + result.name += stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + } + + for (quint32 i = 0; i < result.rowCount; i++) { + QByteArray pointerData(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pointerData.data(), 4); + result.tablePointers.push_back(pointerData.toHex()); + + aZoneFileStream->skipRawData(4); + } + + for (const QString &pointerAddr : result.tablePointers) { + QString leadingContent = ""; + if (pointerAddr == "FFFFFFFF") { + char leadingContentChar; + *aZoneFileStream >> leadingContentChar; + while (leadingContentChar != 0) { + leadingContent += leadingContentChar; + *aZoneFileStream >> leadingContentChar; + } + } else { + leadingContent = pointerAddr; + } + + QString content; + char contentChar; + *aZoneFileStream >> contentChar; + while (contentChar != 0) { + content += contentChar; + *aZoneFileStream >> contentChar; + } + result.content[leadingContent] = content; + } + return result; +} + +QString ZoneFile_COD7::AssetTypeToString(const QString aAssetType) { + const QString cleanedType = aAssetType.toUpper(); + if (cleanedType == "17000000") { // localized string PARTIALLY VERIFIED + return "LOCAL STRING"; + } else if (cleanedType == "20000000") { // raw_file PARTIALLY VERIFIED + return "RAW FILE"; + } else if (cleanedType == "1A000000") { // fx PARTIALLY VERIFIED + return "EFFECT"; + } else if (cleanedType == "09000000") { // loaded_sound PARTIALLY VERIFIED + return "SOUND"; + } else if (cleanedType == "04000000") { // x_anim PARTIALLY VERIFIED + return "ANIMATION"; + } else if (cleanedType == "0C000000") { // collision_map PARTIALLY VERIFIED + return "COLLISION MAP"; + } else if (cleanedType == "21000000") { // string_table PARTIALLY VERIFIED + return "STRING TABLE"; + } else if (cleanedType == "15000000") { // menu_file PARTIALLY VERIFIED + return "MENU"; + } else if (cleanedType == "07000000") { // tech set PARTIALLY VERIFIED + return "TECH SET"; + } else if (cleanedType == "18000000") { // weapon PARTIALLY VERIFIED + return "WEAPON"; + } else if (cleanedType == "11000000") { // gfx map PARTIALLY VERIFIED + return "GFX MAP"; + } else if (cleanedType == "12000000") { // light_def PARTIALLY VERIFIED + return "LIGHT DEF"; + } else if (cleanedType == "14000000") { // font PARTIALLY VERIFIED + return "FONT"; + } else if (cleanedType == "05000000") { // xmodel PARTIALLY VERIFIED + return "MODEL"; + } else if (cleanedType == "0D000000") { // d3dbsp PARTIALLY VERIFIED + return "D3DBSP"; + } else if (cleanedType == "06000000") { // image PARTIALLY VERIFIED + return "IMAGE"; + } else if (cleanedType == "0E000000") { // game map sp PARTIALLY VERIFIED + return "GAME MAP SP"; + } else if (cleanedType == "0B000000") { // col map sp PARTIALLY VERIFIED + return "COL MAP SP"; + } + return aAssetType; +} + +QByteArray ZoneFile_COD7::GetBinaryData() { + QByteArray result; + + return result; +} diff --git a/libs/zonefile/zonefile_cod7.h b/libs/zonefile/zonefile_cod7.h new file mode 100644 index 0000000..6509b4e --- /dev/null +++ b/libs/zonefile/zonefile_cod7.h @@ -0,0 +1,52 @@ +#ifndef ZONEFILE_COD7_H +#define ZONEFILE_COD7_H + +#include "zonefile.h" + +class ZoneFile_COD7 : public ZoneFile +{ +public: + ZoneFile_COD7(); + ~ZoneFile_COD7(); + + bool Load(const QByteArray aFileData, FF_PLATFORM aPlatform) override; + QString AssetTypeToString(const QString aAssetType); + + QByteArray GetBinaryData() override; + +protected: + void pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) override; + quint32 pParseZoneSize(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsA(QDataStream *aZoneFileStream) override; + quint32 pParseZoneTagCount(QDataStream *aZoneFileStream) override; + quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsB(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsC(QDataStream *aZoneFileStream) override; + QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) override; + QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) override; + AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) override; + LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream) override; + RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream) override; + void pParseAsset_PhysPreset(QDataStream *aZoneFileStream) override; + Model pParseAsset_Model(QDataStream *aZoneFileStream) override; + Material pParseAsset_Material(QDataStream *aZoneFileStream) override; + Shader pParseAsset_Shader(QDataStream *aZoneFileStream) override; + TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream) override; + Image pParseAsset_Image(QDataStream *aZoneFileStream) override; + SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream) override; + void pParseAsset_ColMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapSP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_LightDef(QDataStream *aZoneFileStream) override; + void pParseAsset_UIMap(QDataStream *aZoneFileStream) override; + void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) override; + void pParseAsset_AIType(QDataStream *aZoneFileStream) override; + void pParseAsset_FX(QDataStream *aZoneFileStream) override; + Animation pParseAsset_Animation(QDataStream *aZoneFileStream) override; + MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream) override; + void pParseAsset_Weapon(QDataStream *aZoneFileStream) override; + void pParseAsset_D3DBSP(QDataStream *aZoneFileStream) override; + StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream) override; +}; + +#endif // ZONEFILE_COD7_H diff --git a/libs/zonefile/zonefile_cod9.cpp b/libs/zonefile/zonefile_cod9.cpp new file mode 100644 index 0000000..a4ab4cd --- /dev/null +++ b/libs/zonefile/zonefile_cod9.cpp @@ -0,0 +1,1091 @@ +#include "zonefile_cod9.h" + +#include +#include +#include + +ZoneFile_COD9::ZoneFile_COD9() +{ + +} + +ZoneFile_COD9::~ZoneFile_COD9() +{ + +} + +bool ZoneFile_COD9::Load(const QByteArray aFileData, FF_PLATFORM aPlatform) { + // Open zone file as little endian stream + QDataStream zoneFileStream(aFileData); + if (aPlatform == FF_PLATFORM_PC) { + zoneFileStream.setByteOrder(QDataStream::LittleEndian); + } else { + zoneFileStream.setByteOrder(QDataStream::BigEndian); + } + + // Parse data from zone file header + pParseZoneHeader(&zoneFileStream, aPlatform); + SetRecords(pParseZoneIndex(&zoneFileStream, GetRecordCount())); + SetAssetMap(pParseAssets(&zoneFileStream, GetRecords())); + + return true; +} + +void ZoneFile_COD9::pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) { + SetSize(pParseZoneSize(aZoneFileStream)); + pParseZoneUnknownsA(aZoneFileStream); + + SetTagCount(pParseZoneTagCount(aZoneFileStream)); + pParseZoneUnknownsB(aZoneFileStream); + + SetRecordCount(pParseZoneRecordCount(aZoneFileStream)); + + quint32 tagCount = GetTagCount(); + if (tagCount) { + pParseZoneUnknownsC(aZoneFileStream); + SetTags(pParseZoneTags(aZoneFileStream, tagCount)); + } else { + aZoneFileStream->skipRawData(4); + } +} + +quint32 ZoneFile_COD9::pParseZoneSize(QDataStream *aZoneFileStream) { + quint32 zoneFileSize; + *aZoneFileStream >> zoneFileSize; + if (zoneFileSize <= 0) { + qDebug() << "Tried to open empty zone file!"; + exit(-1); + } + zoneFileSize += 36; + return zoneFileSize; +} + +/* + ParseZoneUnknownsA() + + Parses the 1st section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD9::pParseZoneUnknownsA(QDataStream *aZoneFileStream) { + // Byte 4-7, 8-11, 12-15: unknown + QByteArray unknown1(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown1.data(), 4); + + QByteArray unknown2(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown2.data(), 4); + + QByteArray unknown3(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown3.data(), 4); + + // Byte 16-19, 20-23: empty/unknown + QByteArray unknown4(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown4.data(), 4); + + QByteArray unknown5(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown5.data(), 4); + + // Byte 24-27: somehow related to the filesize, but smaller value + QByteArray unknown6(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown6.data(), 4); + + // Byte 28-31, 32-35: unknown + QByteArray unknown7(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown7.data(), 4); + + QByteArray unknown8(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown8.data(), 4); +} + +/* + ParseZoneTagCount() + + Parses the number of string tags in the zone index +*/ +quint32 ZoneFile_COD9::pParseZoneTagCount(QDataStream *aZoneFileStream) { + quint32 tagCount; + *aZoneFileStream >> tagCount; + return tagCount; +} + +/* + ParseZoneRecordCount() + + Parses the number of records in the zone index +*/ +quint32 ZoneFile_COD9::pParseZoneRecordCount(QDataStream *aZoneFileStream) { + quint32 recordCount; + *aZoneFileStream >> recordCount; + return recordCount; +} + +/* + ParseZoneUnknownsB() + + Parses the 2nd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD9::pParseZoneUnknownsB(QDataStream *aZoneFileStream) { + // Byte 44-47: Unknown/empty? + QByteArray unknown9(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown9.data(), 4); +} + +/* + pParseZoneUnknownsC() + + Parses the 3rd section of unknowns as hex vals and uint32s +*/ +void ZoneFile_COD9::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { + // Byte 40-43: Unknown/empty? + QByteArray unknown10(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown10.data(), 4); + + // Byte 44-47: Unknown/empty? + QByteArray unknown11(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown11.data(), 4); +} + +/* + ParseZoneTags() + + Parses the string tags ate the start of zone file +*/ +QStringList ZoneFile_COD9::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { + QStringList tags; + + // Byte 48-51: Repeated separators? ÿÿÿÿ x i + aZoneFileStream->skipRawData(4 * (tagCount - 1)); + + // Parse tags/strings before index + QString zoneTag; + char zoneTagChar; + for (quint32 i = 0; i < tagCount; i++) { + *aZoneFileStream >> zoneTagChar; + while (zoneTagChar != 0) { + zoneTag += zoneTagChar; + *aZoneFileStream >> zoneTagChar; + } + tags << zoneTag; + zoneTag.clear(); + } + return tags; +} + +/* + ParseZoneIndex() + + Parse the binary zone index data and populate table +*/ +QStringList ZoneFile_COD9::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { + QStringList result; + + // Don't parse if no records + if (!recordCount) { return result; } + + if (aZoneFileStream->device()->peek(4).toHex().contains("ffff")) { + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 2); + } + + // Parse index & map found asset types + for (quint32 i = 0; i <= recordCount; i++) { + // Skip record start + QByteArray rawAssetType(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rawAssetType.data(), 4); + result << rawAssetType.toHex(); + + // Skip separator + aZoneFileStream->skipRawData(4); + } + return result; +} + +AssetMap ZoneFile_COD9::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { + AssetMap result; + + aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); + + for (int i = 0; i < assetOrder.size(); i++) { + const QString typeHex = assetOrder[i]; + const QString typeStr = AssetTypeToString(typeHex); + + if (typeStr == "LOCAL STRING") { // localized string asset + result.localStrings << pParseAsset_LocalString(aZoneFileStream); + } else if (typeStr == "RAW FILE") { // gsc + auto rawFile = pParseAsset_RawFile(aZoneFileStream); + if (rawFile.length) { + result.rawFiles << rawFile; + } + } else if (typeStr == "PHYS PRESET") { // physpreset + pParseAsset_PhysPreset(aZoneFileStream); + } else if (typeStr == "MODEL") { // xmodel + result.models << pParseAsset_Model(aZoneFileStream); + } else if (typeStr == "MATERIAL") { // material + pParseAsset_Material(aZoneFileStream); + } else if (typeStr == "SHADER") { // pixelshader + pParseAsset_Shader(aZoneFileStream); + } else if (typeStr == "TECH SET") { // techset include + result.techSets << pParseAsset_TechSet(aZoneFileStream); + } else if (typeStr == "IMAGE") { // image + result.images << pParseAsset_Image(aZoneFileStream); + } else if (typeStr == "SOUND") { // loaded_sound + result.sounds << pParseAsset_Sound(aZoneFileStream); + } else if (typeStr == "COLLISION MAP") { // col_map_mp + pParseAsset_ColMapMP(aZoneFileStream); + } else if (typeStr == "MP MAP") { // game_map_sp + pParseAsset_GameMapSP(aZoneFileStream); + } else if (typeStr == "SP MAP") { // game_map_mp + pParseAsset_GameMapMP(aZoneFileStream); + } else if (typeStr == "LIGHT DEF") { // lightdef + pParseAsset_LightDef(aZoneFileStream); + } else if (typeStr == "UI MAP") { // ui_map + pParseAsset_UIMap(aZoneFileStream); + } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals + pParseAsset_SNDDriverGlobals(aZoneFileStream); + } else if (typeStr == "AI TYPE") { // aitype + pParseAsset_AIType(aZoneFileStream); + } else if (typeStr == "EFFECT") { // aitype + pParseAsset_FX(aZoneFileStream); + } else if (typeStr == "ANIMATION") { // aitype + result.animations << pParseAsset_Animation(aZoneFileStream); + } else if (typeStr == "STRING TABLE") { // string_table + result.stringTables << pParseAsset_StringTable(aZoneFileStream); + } else if (typeStr == "MENU") { // string_table + result.menuFiles << pParseAsset_MenuFile(aZoneFileStream); + } else if (typeStr == "WEAPON") { // string_table + pParseAsset_Weapon(aZoneFileStream); + } else if (typeStr == "D3DBSP DUMP") { // string_table + pParseAsset_D3DBSP(aZoneFileStream); + } else if (typeStr != "UNKNOWN") { + qDebug() << "Found bad asset type!" << typeStr; + } + } + return result; +} + +LocalString ZoneFile_COD9::pParseAsset_LocalString(QDataStream *aZoneFileStream) { + LocalString result; + + quint32 stringPtr, aliasPtr; + *aZoneFileStream >> stringPtr >> aliasPtr; + if (stringPtr == 4294967295) { + // Parse local string asset contents + QString localStr; + char localStrChar; + *aZoneFileStream >> localStrChar; + while (localStrChar != 0) { + result.string += localStrChar; + *aZoneFileStream >> localStrChar; + } + } else { + result.string = "String Ptr: " + QString::number(stringPtr); + } + + if (aliasPtr == 4294967295) { + // Parse rawfile name + QString aliasName; + char aliasNameChar; + *aZoneFileStream >> aliasNameChar; + while (aliasNameChar != 0) { + result.alias += aliasNameChar; + *aZoneFileStream >> aliasNameChar; + } + } else { + result.string = "Alias Ptr: " + QString::number(aliasPtr); + } + + return result; +} + +RawFile ZoneFile_COD9::pParseAsset_RawFile(QDataStream *aZoneFileStream) { + RawFile result; + + // Skip start separator FF FF FF FF (pointer?) + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.length; + + // Skip unknown 4 byte data + aZoneFileStream->skipRawData(4); + + // Parse rawfile path + char scriptPathChar; + *aZoneFileStream >> scriptPathChar; + while (scriptPathChar != 0) { + result.path += scriptPathChar; + *aZoneFileStream >> scriptPathChar; + } + result.path.replace(",", ""); + const QStringList pathParts = result.path.split('/'); + if (pathParts.size() == 0) { + qDebug() << "Failed to parse ff path! " << result.path; + exit(-1); + } + + // Parse gsc contents + char rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + while (rawFileContentsChar != 0 && rawFileContentsChar != -1) { + result.contents += rawFileContentsChar; + *aZoneFileStream >> rawFileContentsChar; + } + return result; +} + +void ZoneFile_COD9::pParseAsset_PhysPreset(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Model ZoneFile_COD9::pParseAsset_Model(QDataStream *aZoneFileStream) { + Model result; + + qDebug() << "Model Info:"; + + *aZoneFileStream >> result.namePtr >> result.tagCount >> result.rootTagCount + >> result.surfCount >> result.unknownCount >> result.boneNamePtr + >> result.parentListPtr >> result.quatsPtr >> result.transPtr + >> result.partClassPtr >> result.baseMatPtr + >> result.surfsPtr >> result.materialHandlesPtr; + + // Parse XModelLodInfo + for (int i = 1; i <= 4; i++) { + qDebug() << "- Lod Info " << i; + + quint32 intDist; + *aZoneFileStream >> intDist; + + std::memcpy(&result.lodInfo[i].dist, &intDist, sizeof(result.lodInfo[i].dist)); + *aZoneFileStream >> result.lodInfo[i].numsurfs >> result.lodInfo[i].surfIndex; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.lodInfo[i].partBits[0] + >> result.lodInfo[i].partBits[1] + >> result.lodInfo[i].partBits[2] + >> result.lodInfo[i].partBits[3]; + } + + *aZoneFileStream >> result.collSurfsPtr >> result.numCollSurfs >> result.contents >> result.boneInfoPtr; + + quint32 intRadius, intMins[3], intMaxs[3]; + *aZoneFileStream >> intRadius >> intMins[0] >> intMins[1] + >> intMins[2] >> intMaxs[0] >> intMaxs[1] >> intMaxs[2]; + + std::memcpy(&result.radius, &intRadius, sizeof(result.radius)); + + std::memcpy(&result.mins[0], &intMins[0], sizeof(result.mins[0])); + std::memcpy(&result.mins[1], &intMins[1], sizeof(result.mins[1])); + std::memcpy(&result.mins[2], &intMins[2], sizeof(result.mins[2])); + + std::memcpy(&result.maxs[0], &intMaxs[0], sizeof(result.maxs[0])); + std::memcpy(&result.maxs[1], &intMaxs[1], sizeof(result.maxs[2])); + std::memcpy(&result.maxs[2], &intMaxs[2], sizeof(result.maxs[3])); + + *aZoneFileStream >> result.numLods >> result.collLod >> result.streamInfoPtr + >> result.memUsage >> result.flags >> result.physPresetPtr >> result.physGeomsPtr; + + // Parse model name + char modelNameChar; + *aZoneFileStream >> modelNameChar; + while (modelNameChar == 0) { + *aZoneFileStream >> modelNameChar; + } + while (modelNameChar != 0) { + result.modelName += modelNameChar; + *aZoneFileStream >> modelNameChar; + } + return result; +} + +Material ZoneFile_COD9::pParseAsset_Material(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + + return Material(); +} + +Shader ZoneFile_COD9::pParseAsset_Shader(QDataStream *aZoneFileStream) { + Shader result = Shader(); + + quint8 minorVersion, majorVersion; + *aZoneFileStream >> minorVersion >> majorVersion; + + quint32 magic; + *aZoneFileStream >> magic; + + // Verify .fxc magic + if (magic != 65534 && magic != 65535) { return result; } + + SHADER_TYPE type = SHADER_NONE; + quint16 rawShaderType; + *aZoneFileStream >> rawShaderType; + if (rawShaderType == 65535) { // Pixel shader + type = SHADER_PIXEL; + } else if (rawShaderType == 65534) { // Vertex shader + type = SHADER_VERTEX; + } + Q_UNUSED(type); + + while (true) { + quint32 instructionToken; + *aZoneFileStream >> instructionToken; + + SHADER_OPCODE opCode = (SHADER_OPCODE)(instructionToken & 0xffff); + + int size; + if (opCode == OPCODE_End) { + break; + } else if (opCode == OPCODE_Comment) { + size = (int)((instructionToken >> 16) & 0x7FFF); + } else { + size = (int)((instructionToken >> 24) & 0x0f); + } + Q_UNUSED(size); + } + + + return result; +} + +TechSet ZoneFile_COD9::pParseAsset_TechSet(QDataStream *aZoneFileStream) { + TechSet result; + + for (int i = 1; i <= 62; i++) { + quint32 ptr; + *aZoneFileStream >> ptr; + + result.pointers << ptr; + } + qDebug() << aZoneFileStream->device()->pos(); + + // Parse techset name + char techSetNameChar; + *aZoneFileStream >> techSetNameChar; + while (techSetNameChar != 0) { + result.name += techSetNameChar; + *aZoneFileStream >> techSetNameChar; + } + result.name.replace(",", ""); + + return result; +} + +Image ZoneFile_COD9::pParseAsset_Image(QDataStream *aZoneFileStream) { + Image result; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknowna >> result.unknownb + >> result.unknownc >> result.unknownd + >> result.unknowne >> result.unknownf + >> result.unknowng; + + aZoneFileStream->skipRawData(15 * 4); + *aZoneFileStream >> result.unknownh >> result.unknowni; + + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> result.unknownj; + + aZoneFileStream->skipRawData(4); + + char materialNameChar; + *aZoneFileStream >> materialNameChar; + while (materialNameChar != 0) { + result.materialName += materialNameChar; + *aZoneFileStream >> materialNameChar; + } + result.materialName.replace(",", ""); + + if (result.unknowna) { + *aZoneFileStream >> result.unknownk; + *aZoneFileStream >> result.unknownl; + *aZoneFileStream >> result.unknownm; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown1; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.unknown2 >> result.unknown3 + >> result.size1 >> result.size2 + >> result.unknown4 >> result.unknown5; + + aZoneFileStream->skipRawData(4); + + char imageNameChar; + *aZoneFileStream >> imageNameChar; + while (imageNameChar != 0) { + result.name += imageNameChar; + *aZoneFileStream >> imageNameChar; + } + + *aZoneFileStream >> result.unknown6 >> result.unknown7; + + QByteArray compressionData(8, Qt::Uninitialized); + aZoneFileStream->readRawData(compressionData.data(), 8); + if (compressionData.contains("DXT1")) { + result.compression = COMPRESSION_DXT1; + } else if (compressionData.contains("DXT3")) { + result.compression = COMPRESSION_DXT3; + } else if (compressionData.contains("DXT5")) { + result.compression = COMPRESSION_DXT5; + } else { + result.compression = COMPRESSION_NONE; + } + + *aZoneFileStream >> result.unknown8 >> result.unknown9; + } + + return result; +} + +SoundAsset ZoneFile_COD9::pParseAsset_Sound(QDataStream *aZoneFileStream) { + SoundAsset result; + + qDebug() << aZoneFileStream->device()->pos(); + + QByteArray rootNamePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(rootNamePtr.data(), 4); + qDebug() << "Root name ptr: " << (QString)rootNamePtr.toHex(); + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream >> result.count; + + if (rootNamePtr.toHex() == "ffffffff") { + // Read in sound file name + char soundNameChar; + *aZoneFileStream >> soundNameChar; + while (soundNameChar != 0) { + result.name += soundNameChar; + *aZoneFileStream >> soundNameChar; + } + } + + int tagCount = 0; + int resultCount = 0; + for (quint32 i = 0; i < result.count; i++) { + aZoneFileStream->skipRawData(12); + + QByteArray tagPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(tagPtr.data(), 4); + + if (tagPtr.toHex() == "ffffffff") { + qDebug() << "Tag Ptr: " << tagPtr.toHex(); + tagCount++; + } + aZoneFileStream->skipRawData(4); + + QByteArray pathPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pathPtr.data(), 4); + + if (pathPtr.toHex() == "ffffffff") { + qDebug() << "Path Ptr: " << pathPtr.toHex(); + resultCount++; + } + + aZoneFileStream->skipRawData(160); + } + + for (int i = 0; i < tagCount; i++) { + // Read in tag? + QString tag; + char tagChar; + *aZoneFileStream >> tagChar; + while (tagChar != 0) { + tag += tagChar; + *aZoneFileStream >> tagChar; + } + qDebug() << "Tag: " << tag; + } + + for (int i = 0; i < resultCount; i++) { + Sound sound; + + if (aZoneFileStream->device()->peek(12).toHex().contains("ffffffff00000000")) { + aZoneFileStream->skipRawData(12); + } + + aZoneFileStream->skipRawData(8); + + qDebug() << "- " << aZoneFileStream->device()->pos(); + QByteArray aliasPtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(aliasPtr.data(), 4); + + QByteArray namePtr(4, Qt::Uninitialized); + aZoneFileStream->readRawData(namePtr.data(), 4); + + *aZoneFileStream >> sound.dataLength; + qDebug() << "- Data length: " << sound.dataLength; + + if (aliasPtr.toHex() == "ffffffff") { + // Read in sound alias name + char soundAliasChar; + *aZoneFileStream >> soundAliasChar; + while (soundAliasChar != 0) { + sound.alias += soundAliasChar; + *aZoneFileStream >> soundAliasChar; + } + qDebug() << "- Alias: " << sound.alias; + } + + if (aZoneFileStream->device()->peek(4) == "RIFF") { + sound.path = sound.alias; + sound.alias = ""; + } else if (namePtr.toHex() == "ffffffff") { + // Read in sound file path + char soundPathChar; + *aZoneFileStream >> soundPathChar; + while (soundPathChar != 0) { + sound.path += soundPathChar; + *aZoneFileStream >> soundPathChar; + } + sound.path.replace(",", ""); + qDebug() << "- Path: " << sound.path; + } + + if (sound.dataLength) { + QByteArray data(sound.dataLength, Qt::Uninitialized); + aZoneFileStream->readRawData(data.data(), sound.dataLength); + sound.data = data; + } + result.sounds.append(sound); + } + qDebug() << "- " << aZoneFileStream->device()->pos(); + + return result; +} + +void ZoneFile_COD9::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_GameMapSP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_GameMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_LightDef(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_UIMap(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_AIType(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_FX(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); +} + +Animation ZoneFile_COD9::pParseAsset_Animation(QDataStream *aZoneFileStream) { + Animation result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.dataByteCount + >> result.dataShortCount + >> result.dataIntCount + >> result.randomDataByteCount + >> result.randomDataIntCount + >> result.numframes + >> result.isLooped + >> result.isDelta + >> result.noneRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.twoDRotatedBoneCount + >> result.normalRotatedBoneCount + >> result.normalTranslatedBoneCount + >> result.preciseTranslatedBoneCount + >> result.staticTranslatedBoneCount + >> result.noneTranslatedBoneCount + >> result.totalBoneCount + >> result.otherBoneCount1 + >> result.otherBoneCount2 + >> result.notifyCount + >> result.assetType + >> result.pad + >> result.randomDataShortCount + >> result.indexCount + >> result.frameRate + >> result.frequency + >> result.boneIDsPtr + >> result.dataBytePtr + >> result.dataShortPtr + >> result.dataIntPtr + >> result.randomDataShortPtr + >> result.randomDataBytePtr + >> result.randomDataIntPtr + >> result.longIndiciesPtr + >> result.notificationsPtr + >> result.deltaPartsPtr; + + // Read in x_anim file name + QString xAnimName; + char xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + while (xAnimNameChar != 0) { + result.name += xAnimNameChar; + *aZoneFileStream >> xAnimNameChar; + } + + // Parse x_anim index header + QVector sectionLengths; + for (int i = 0; i < result.numframes; i++) { + quint8 sectionlength; + *aZoneFileStream >> sectionlength; + sectionLengths.push_back(sectionlength); + // Skip padding + aZoneFileStream->skipRawData(1); + } + // Skip unknown section + aZoneFileStream->skipRawData(2 * 8); + + return result; +} + +MenuFile ZoneFile_COD9::pParseAsset_MenuFile(QDataStream *aZoneFileStream) { + //MENU_FILE + MenuFile result; + + aZoneFileStream->skipRawData(4); // Separator + + // Parse menu def count + *aZoneFileStream >> result.menuCount; + + // Clearly misparsed, never have this much + if (result.menuCount > 1000) { + qDebug() << "Failure reported when parsing menu file."; + return result; + } + for (uint i = 0; i < result.menuCount; i++) { + Menu menu; + + aZoneFileStream->skipRawData(4); // Separator + + // Read in x_anim file name + char menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + while (menuFilepathChar != 0) { + menu.filePath += menuFilepathChar; + *aZoneFileStream >> menuFilepathChar; + } + aZoneFileStream->skipRawData(4); // Separator + + *aZoneFileStream >> menu.menuNamePtr; + + float menuRectX, menuRectY, menuRectWidth, menuRectHeight; + *aZoneFileStream >> menuRectX >> menuRectY >> menuRectWidth >> menuRectHeight; + menu.rect = QRectF(menuRectX, menuRectY, menuRectWidth, menuRectHeight); + + quint32 hAlignInt, vAlignInt; + *aZoneFileStream >> hAlignInt >> vAlignInt; + menu.hAlign = (MENU_H_ALIGNMENT)hAlignInt; + menu.vAlign = (MENU_V_ALIGNMENT)vAlignInt; + + float rectClientX, rectClientY, rectClientWidth, rectClientHeight; + *aZoneFileStream >> rectClientX >> rectClientY >> rectClientWidth >> rectClientHeight; + menu.clientRect = QRectF(rectClientX, rectClientY, rectClientWidth, rectClientHeight); + + quint32 hClientAlignInt, vClientAlignInt, styleInt, borderInt; + *aZoneFileStream >> hClientAlignInt >> vClientAlignInt >> menu.groupPtr + >> styleInt >> borderInt >> menu.ownerDraw >> menu.ownerDrawFlags + >> menu.borderSize >> menu.staticFlags >> menu.dynamicFlags >> menu.nextTime; + menu.hClientAlign = (MENU_H_ALIGNMENT)hClientAlignInt; + menu.vClientAlign = (MENU_V_ALIGNMENT)vClientAlignInt; + menu.style = (MENU_WINDOW_STYLE)styleInt; + menu.border = (MENU_WINDOW_BORDER)borderInt; + + float foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA, + backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA, + borderColorR, borderColorG, borderColorB, borderColorA, + outlineColorR, outlineColorG, outlineColorB, outlineColorA; + *aZoneFileStream >> foregroundColorR >> foregroundColorG >> foregroundColorB >> foregroundColorA + >> backgroundColorR >> backgroundColorG >> backgroundColorB >> backgroundColorA + >> borderColorR >> borderColorG >> borderColorB >> borderColorA + >> outlineColorR >> outlineColorG >> outlineColorB >> outlineColorA; + + menu.foregroundColor = QColor(foregroundColorR, foregroundColorG, foregroundColorB, foregroundColorA); + menu.backgroundColor = QColor(backgroundColorR, backgroundColorG, backgroundColorB, backgroundColorA); + menu.borderColor = QColor(borderColorR, borderColorG, borderColorB, borderColorA); + menu.outlineColor = QColor(outlineColorR, outlineColorG, outlineColorB, outlineColorA); + + *aZoneFileStream >> menu.materialPtr >> menu.fontPtr >> menu.fullScreen >> menu.itemCount + >> menu.fontIndex >> menu.cursorItem >> menu.fadeCycle >> menu.fadeClamp + >> menu.fadeAmount >> menu.fadeInAmount >> menu.blurRadius >> menu.onOpenPtr + >> menu.onFocusPtr >> menu.onClosePtr >> menu.onESCPtr >> menu.onKeyPtr + >> menu.visibleExpCount >> menu.expEntryPtr >> menu.allowedBindingPtr + >> menu.soundNamePtr >> menu.imageTrack; + + float focusColorR, focusColorG, focusColorB, focusColorA, + disabledColorR, disabledColorG, disabledColorB, disabledColorA; + *aZoneFileStream >> focusColorR >> focusColorG >> focusColorB >> focusColorA + >> disabledColorR >> disabledColorG >> disabledColorB >> disabledColorA; + menu.focusColor = QColor(focusColorR, focusColorG, focusColorB, focusColorA); + menu.disabledColor = QColor(disabledColorR, disabledColorG, disabledColorB, disabledColorA); + + *aZoneFileStream >> menu.rectXExpCount >> menu.rectXExpPtr >> menu.rectYExpCount >> menu.rectYExpPtr; + + aZoneFileStream->skipRawData(4); // Separator + + char menuDefNameChar; + int menuDefNameLen = 0; + *aZoneFileStream >> menuDefNameChar; + while (menuDefNameChar != 0 && menuDefNameLen < 30) { + menuDefNameLen++; + menu.name += menuDefNameChar; + *aZoneFileStream >> menuDefNameChar; + } + + char defStringChar; + int defStringLen = 0; + *aZoneFileStream >> defStringChar; + while (defStringChar != 0 && defStringLen < 30) { + defStringLen++; + menu.definition += defStringChar; + *aZoneFileStream >> defStringChar; + } + aZoneFileStream->skipRawData(4 * 10); + + *aZoneFileStream >> menu.itemWindowDefNamePtr; + + float itemRectX, itemRectY, itemRectWidth, itemRectHeight; + *aZoneFileStream >> itemRectX >> itemRectY >> itemRectWidth >> itemRectHeight; + menu.itemRect = QRectF(itemRectX, itemRectY, itemRectWidth, itemRectHeight); + + *aZoneFileStream >> menu.itemHAlignment >> menu.itemVAlignment >> menu.itemGroupPtr + >> menu.itemWindowStyle >> menu.itemWindowBorder >> menu.itemOwnerDraw + >> menu.itemOwnerDrawFlags >> menu.itemBorderSize >> menu.itemStaticFlags + >> menu.itemDynamicFlags >> menu.itemNextTime; + + float itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA, + itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA, + itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA, + itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA; + *aZoneFileStream >> itemForegroundColorR >> itemForegroundColorG >> itemForegroundColorB >> itemForegroundColorA + >> itemBackgroundColorR >> itemBackgroundColorG >> itemBackgroundColorB >> itemBackgroundColorA + >> itemBorderColorR >> itemBorderColorG >> itemBorderColorB >> itemBorderColorA + >> itemOutlineColorR >> itemOutlineColorG >> itemOutlineColorB >> itemOutlineColorA; + + menu.itemForegroundColor = QColor(itemForegroundColorR, itemForegroundColorG, itemForegroundColorB, itemForegroundColorA); + menu.itemBackgroundColor = QColor(itemBackgroundColorR, itemBackgroundColorG, itemBackgroundColorB, itemBackgroundColorA); + menu.itemBorderColor = QColor(itemBorderColorR, itemBorderColorG, itemBorderColorB, itemBorderColorA); + menu.itemOutlineColor = QColor(itemOutlineColorR, itemOutlineColorG, itemOutlineColorB, itemOutlineColorA); + + *aZoneFileStream >> menu.itemMaterialPtr; + + float itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight; + *aZoneFileStream >> itemTextRectX >> itemTextRectY >> itemTextRectWidth >> itemTextRectHeight; + menu.itemTextRect = QRectF(itemTextRectX, itemTextRectY, itemTextRectWidth, itemTextRectHeight); + + quint32 hItemTextAlignInt, vItemTextAlignInt, itemType, fontTypeInt, textStyleInt; + *aZoneFileStream >> hItemTextAlignInt >> vItemTextAlignInt >> itemType >> menu.dataType + >> menu.alignment >> fontTypeInt >> menu.textAlignMode >> menu.textalignx >> menu.textaligny + >> menu.textscale >> textStyleInt >> menu.gameMsgWindowIndex >> menu.gameMsgWindowMode + >> menu.testPtr >> menu.textSavegameInfo >> menu.parentPtr; + menu.itemText_hAlign = (MENU_H_ALIGNMENT)hItemTextAlignInt; + menu.itemText_vAlign = (MENU_V_ALIGNMENT)vItemTextAlignInt; + menu.itemType = (MENU_ITEM_TYPE)itemType; + menu.fontEnum = (MENU_FONT_TYPE)fontTypeInt; + menu.textStyle = (MENU_ITEM_TEXTSTYLE)textStyleInt; + + *aZoneFileStream >> menu.mouseEnterText >> menu.mouseExitText >> menu.mouseEnter >> menu.mouseExit + >> menu.action >> menu.onAccept >> menu.onFocus >> menu.leaveFocus >> menu.dvar >> menu.dvarTest + >> menu.keyHandlerPtr >> menu.enableDvarPtr >> menu.dvarFlags >> menu.focusSoundPtr + >> menu.special >> menu.cursorPos; + + // itemDefData_t typeData; + + // listBoxDef_s *listBox; + + *aZoneFileStream >> menu.startPos >> menu.endPos >> menu.drawPadding; + + *aZoneFileStream >> menu.elementWidth >> menu.elementHeight; + + *aZoneFileStream >> menu.elementStyle >> menu.numColumns; + + //columnInfo_s columnInfo[16]; + + *aZoneFileStream >> menu.doubleClickPtr; + + + *aZoneFileStream >> menu.notselectable >> menu.noScrollBars >> menu.usePaging; + + float itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA, + itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA, + itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA; + *aZoneFileStream >> itemSelectBorderColorR >> itemSelectBorderColorG >> itemSelectBorderColorB >> itemSelectBorderColorA + >> itemDisableColorR >> itemDisableColorG >> itemDisableColorB >> itemDisableColorA + >> itemFocusColorR >> itemFocusColorG >> itemFocusColorB >> itemFocusColorA; + menu.itemSelectBorderColor = QColor(itemSelectBorderColorR, itemSelectBorderColorG, itemSelectBorderColorB, itemSelectBorderColorA); + menu.itemDisableColor = QColor(itemDisableColorR, itemDisableColorG, itemDisableColorB, itemDisableColorA); + menu.itemFocusColor = QColor(itemFocusColorR, itemFocusColorG, itemFocusColorB, itemFocusColorA); + + *aZoneFileStream >> menu.selectIconPtr >> menu.backgroundItemListboxPtr >> menu.highlightTexturePtr; + + // editFieldDef_s *editField; + + *aZoneFileStream >> menu.minVal >> menu.maxVal >> menu.defVal >> menu.range >> menu.maxChars + >> menu.maxCharsGotoNext >> menu.maxPaintChars >> menu.paintOffset; + + // multiDef_s *multi; + + for (int i = 0; i < 32; i++) { + quint32 dvarList; + *aZoneFileStream >> dvarList; + menu.dvarListPtrs.push_back(dvarList); + } + + for (int i = 0; i < 32; i++) { + quint32 dvarStr; + *aZoneFileStream >> dvarStr; + menu.dvarStrPtrs.push_back(dvarStr); + } + + for (int i = 0; i < 32; i++) { + float dvarValue; + *aZoneFileStream >> dvarValue; + menu.dvarValues.push_back(dvarValue); + } + + *aZoneFileStream >> menu.count >> menu.strDef >> menu.enumDvarNamePtr; + aZoneFileStream->skipRawData(4); + //>> menu.dataPtr + *aZoneFileStream >> menu.itemImageTrack; + + qDebug() << aZoneFileStream->device()->pos(); + + //statement_s visibleExp; + //statement_s textExp; + //statement_s materialExp; + //statement_s rectXExp; + //statement_s rectYExp; + //statement_s rectWExp; + //statement_s rectHExp; + //statement_s foreColorAExp; + result.menuDefs << menu; + } + return result; +} + +void ZoneFile_COD9::pParseAsset_Weapon(QDataStream *aZoneFileStream) { + //WEAPON_FILE + Q_UNUSED(aZoneFileStream); +} + +void ZoneFile_COD9::pParseAsset_D3DBSP(QDataStream *aZoneFileStream) { + //D3DBSP_DUMP + Q_UNUSED(aZoneFileStream); +} + +StringTable ZoneFile_COD9::pParseAsset_StringTable(QDataStream *aZoneFileStream) { + StringTable result; + + aZoneFileStream->skipRawData(4); + + *aZoneFileStream + >> result.columnCount + >> result.rowCount; + + // Todo fix this + result.columnCount = 0; + result.rowCount = 0; + + aZoneFileStream->skipRawData(4); + + QString stringTableName; + char stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + while (stringTableNameChar != 0) { + result.name += stringTableNameChar; + *aZoneFileStream >> stringTableNameChar; + } + + for (quint32 i = 0; i < result.rowCount; i++) { + QByteArray pointerData(4, Qt::Uninitialized); + aZoneFileStream->readRawData(pointerData.data(), 4); + result.tablePointers.push_back(pointerData.toHex()); + + aZoneFileStream->skipRawData(4); + } + + for (const QString &pointerAddr : result.tablePointers) { + QString leadingContent = ""; + if (pointerAddr == "FFFFFFFF") { + char leadingContentChar; + *aZoneFileStream >> leadingContentChar; + while (leadingContentChar != 0) { + leadingContent += leadingContentChar; + *aZoneFileStream >> leadingContentChar; + } + } else { + leadingContent = pointerAddr; + } + + QString content; + char contentChar; + *aZoneFileStream >> contentChar; + while (contentChar != 0) { + content += contentChar; + *aZoneFileStream >> contentChar; + } + result.content[leadingContent] = content; + } + return result; +} + +QString ZoneFile_COD9::AssetTypeToString(const QString aAssetType) { + const QString cleanedType = aAssetType.toUpper(); + if (cleanedType == "17000000") { // localized string PARTIALLY VERIFIED + return "LOCAL STRING"; + } else if (cleanedType == "20000000") { // raw_file PARTIALLY VERIFIED + return "RAW FILE"; + } else if (cleanedType == "1A000000") { // fx PARTIALLY VERIFIED + return "EFFECT"; + } else if (cleanedType == "09000000") { // loaded_sound PARTIALLY VERIFIED + return "SOUND"; + } else if (cleanedType == "04000000") { // x_anim PARTIALLY VERIFIED + return "ANIMATION"; + } else if (cleanedType == "0C000000") { // collision_map PARTIALLY VERIFIED + return "COLLISION MAP"; + } else if (cleanedType == "21000000") { // string_table PARTIALLY VERIFIED + return "STRING TABLE"; + } else if (cleanedType == "15000000") { // menu_file PARTIALLY VERIFIED + return "MENU"; + } else if (cleanedType == "07000000") { // tech set PARTIALLY VERIFIED + return "TECH SET"; + } else if (cleanedType == "18000000") { // weapon PARTIALLY VERIFIED + return "WEAPON"; + } else if (cleanedType == "11000000") { // gfx map PARTIALLY VERIFIED + return "GFX MAP"; + } else if (cleanedType == "12000000") { // light_def PARTIALLY VERIFIED + return "LIGHT DEF"; + } else if (cleanedType == "14000000") { // font PARTIALLY VERIFIED + return "FONT"; + } else if (cleanedType == "05000000") { // xmodel PARTIALLY VERIFIED + return "MODEL"; + } else if (cleanedType == "0D000000") { // d3dbsp PARTIALLY VERIFIED + return "D3DBSP"; + } else if (cleanedType == "06000000") { // image PARTIALLY VERIFIED + return "IMAGE"; + } else if (cleanedType == "0E000000") { // game map sp PARTIALLY VERIFIED + return "GAME MAP SP"; + } else if (cleanedType == "0B000000") { // col map sp PARTIALLY VERIFIED + return "COL MAP SP"; + } + return aAssetType; +} + +QByteArray ZoneFile_COD9::GetBinaryData() { + QByteArray result; + + return result; +} diff --git a/libs/zonefile/zonefile_cod9.h b/libs/zonefile/zonefile_cod9.h new file mode 100644 index 0000000..a77aa9a --- /dev/null +++ b/libs/zonefile/zonefile_cod9.h @@ -0,0 +1,52 @@ +#ifndef ZONEFILE_COD9_H +#define ZONEFILE_COD9_H + +#include "zonefile.h" + +class ZoneFile_COD9 : public ZoneFile +{ +public: + ZoneFile_COD9(); + ~ZoneFile_COD9(); + + bool Load(const QByteArray aFileData, FF_PLATFORM aPlatform) override; + QString AssetTypeToString(const QString aAssetType); + + QByteArray GetBinaryData() override; + +protected: + void pParseZoneHeader(QDataStream *aZoneFileStream, FF_PLATFORM aPlatform) override; + quint32 pParseZoneSize(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsA(QDataStream *aZoneFileStream) override; + quint32 pParseZoneTagCount(QDataStream *aZoneFileStream) override; + quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsB(QDataStream *aZoneFileStream) override; + void pParseZoneUnknownsC(QDataStream *aZoneFileStream) override; + QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) override; + QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) override; + AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) override; + LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream) override; + RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream) override; + void pParseAsset_PhysPreset(QDataStream *aZoneFileStream) override; + Model pParseAsset_Model(QDataStream *aZoneFileStream) override; + Material pParseAsset_Material(QDataStream *aZoneFileStream) override; + Shader pParseAsset_Shader(QDataStream *aZoneFileStream) override; + TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream) override; + Image pParseAsset_Image(QDataStream *aZoneFileStream) override; + SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream) override; + void pParseAsset_ColMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapSP(QDataStream *aZoneFileStream) override; + void pParseAsset_GameMapMP(QDataStream *aZoneFileStream) override; + void pParseAsset_LightDef(QDataStream *aZoneFileStream) override; + void pParseAsset_UIMap(QDataStream *aZoneFileStream) override; + void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) override; + void pParseAsset_AIType(QDataStream *aZoneFileStream) override; + void pParseAsset_FX(QDataStream *aZoneFileStream) override; + Animation pParseAsset_Animation(QDataStream *aZoneFileStream) override; + MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream) override; + void pParseAsset_Weapon(QDataStream *aZoneFileStream) override; + void pParseAsset_D3DBSP(QDataStream *aZoneFileStream) override; + StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream) override; +}; + +#endif // ZONEFILE_COD9_H diff --git a/lzx.h b/lzx.h deleted file mode 100644 index ee49541..0000000 --- a/lzx.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef LZX_H -#define LZX_H - -#pragma once - -#include -#include -#include -#include - -namespace LZX { - -/*! - * \brief Decompresses LZX-compressed data. - * - * This function decodes a compressed QByteArray until there are no more - * bits available. (In a real LZX stream there is an end-of-stream marker; - * here we simply stop if a required field cannot be read.) - * - * \param compressedData The input LZX–compressed data. - * \param windowBits The logâ‚‚ of the sliding window size (default 15 → 32K). - * \return A QByteArray containing the decompressed data. - */ -static QByteArray DecompressLZX(const QByteArray &compressedData, uint32_t windowBits = 15) -{ - if (compressedData.isEmpty()) - return QByteArray(); - - // Calculate sliding window size. - const uint32_t windowSize = 1u << windowBits; - std::vector window(windowSize, 0); - uint32_t windowPos = 0; - - // Use a dynamic output buffer. - QByteArray outArray; - // Reserve an initial capacity. - outArray.reserve(1024); - - // --- Bitstream state --- - const uint8_t *inData = reinterpret_cast(compressedData.constData()); - size_t inSize = compressedData.size(); - size_t inPos = 0; - uint32_t bitBuffer = 0; - int bitsInBuffer = 0; - - // Lambda: Ensure at least 'count' bits are available. - auto ensureBits = [&](int count) -> bool { - while (bitsInBuffer < count) { - if (inPos < inSize) { - bitBuffer = (bitBuffer << 8) | inData[inPos++]; - bitsInBuffer += 8; - } else { - return false; - } - } - return true; - }; - - // Lambda: Get (and remove) 'count' bits from the bit buffer. - auto getBits = [&](int count) -> uint32_t { - if (!ensureBits(count)) - return 0; - uint32_t result = (bitBuffer >> (bitsInBuffer - count)) & ((1u << count) - 1); - bitsInBuffer -= count; - return result; - }; - - // --- Main decompression loop --- - // In this simplified placeholder format: - // - A flag bit of 1 means a literal byte follows (8 bits). - // - A flag bit of 0 means a match follows: first 4 bits for match length (plus base 2) - // then windowBits bits for the match offset (relative to the current sliding window). - while (true) { - // Try to read a flag bit; if not available, we assume the stream is complete. - if (!ensureBits(1)) - break; - uint32_t flag = getBits(1); - if (flag == 1) { - // Literal: next 8 bits form a literal byte. - if (!ensureBits(8)) { - qWarning() << "Unexpected end of input while reading literal."; - break; - } - uint8_t literal = static_cast(getBits(8)); - outArray.append(static_cast(literal)); - // Update the sliding window. - window[windowPos] = literal; - windowPos = (windowPos + 1) % windowSize; - } else { - // Match: first read a 4-bit match length (with a base of 2). - if (!ensureBits(4)) { - qWarning() << "Unexpected end of input while reading match length."; - break; - } - uint32_t matchLength = getBits(4) + 2; - // Then read the match offset (fixed number of bits equals windowBits). - if (!ensureBits(windowBits)) { - qWarning() << "Unexpected end of input while reading match offset."; - break; - } - uint32_t matchOffset = getBits(windowBits); - // Compute the source position in the sliding window. - uint32_t copyPos = (windowPos + windowSize - matchOffset) % windowSize; - // Copy matchLength bytes from the sliding window. - for (uint32_t i = 0; i < matchLength; i++) { - uint8_t byte = window[(copyPos + i) % windowSize]; - outArray.append(static_cast(byte)); - // Update the sliding window with the decompressed byte. - window[windowPos] = byte; - windowPos = (windowPos + 1) % windowSize; - } - } - } - - return outArray; -} - -} // namespace LZX - -#endif // LZX_H diff --git a/modelviewer.cpp b/modelviewer.cpp deleted file mode 100644 index 27058b0..0000000 --- a/modelviewer.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "modelviewer.h" - -ModelViewer::ModelViewer(QWidget *parent) - : QWidget{parent} { - Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow(); - view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x4d4d4f))); - - QWidget *container = QWidget::createWindowContainer(view); - QSize screenSize = view->screen()->size(); - container->setMinimumSize(QSize(200, 100)); - container->setMaximumSize(screenSize); - - QHBoxLayout *hLayout = new QHBoxLayout(this); - QVBoxLayout *vLayout = new QVBoxLayout(); - vLayout->setAlignment(Qt::AlignTop); - hLayout->addWidget(container, 1); - hLayout->addLayout(vLayout); - - // Root entity - Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity(); - - // Camera - Qt3DRender::QCamera *cameraEntity = view->camera(); - - cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f); - cameraEntity->setPosition(QVector3D(0, 0, 50.0f)); // Move farther along Z-axis - cameraEntity->setUpVector(QVector3D(0, 1, 0)); - cameraEntity->setViewCenter(QVector3D(0, 0, 0)); - - Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(rootEntity); - Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight(lightEntity); - light->setColor("white"); - light->setIntensity(1); - lightEntity->addComponent(light); - - Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform(lightEntity); - lightTransform->setTranslation(cameraEntity->position()); - lightEntity->addComponent(lightTransform); - - // For camera controls - Qt3DExtras::QFirstPersonCameraController *camController = new Qt3DExtras::QFirstPersonCameraController(rootEntity); - camController->setCamera(cameraEntity); - - // Set root object of the scene - view->setRootEntity(rootEntity); - - // Load custom 3D model - Qt3DRender::QMesh *customMesh = new Qt3DRender::QMesh(); - customMesh->setSource(QUrl::fromLocalFile(":/obj/data/obj/defaultactor_LOD0.obj")); - - // Adjust the model transformation - Qt3DCore::QTransform *customTransform = new Qt3DCore::QTransform(); - customTransform->setRotationX(-90); - customTransform->setRotationY(-90); - - // Keep translation if necessary - customTransform->setTranslation(QVector3D(0.0f, -100.0f, -200.0f)); - - Qt3DExtras::QNormalDiffuseMapMaterial *customMaterial = new Qt3DExtras::QNormalDiffuseMapMaterial(); - - Qt3DRender::QTextureLoader *normalMap = new Qt3DRender::QTextureLoader(); - normalMap->setSource(QUrl::fromLocalFile(":/obj/data/obj/normalmap.png")); - customMaterial->setNormal(normalMap); - - Qt3DRender::QTextureLoader *diffuseMap = new Qt3DRender::QTextureLoader(); - diffuseMap->setSource(QUrl::fromLocalFile(":/obj/data/obj/diffusemap.png")); - customMaterial->setDiffuse(diffuseMap); - - Qt3DCore::QEntity *m_torusEntity = new Qt3DCore::QEntity(rootEntity); - m_torusEntity->addComponent(customMesh); - m_torusEntity->addComponent(customMaterial); - m_torusEntity->addComponent(customTransform); -} diff --git a/modelviewer.h b/modelviewer.h deleted file mode 100644 index c56d376..0000000 --- a/modelviewer.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef MODELVIEWER_H -#define MODELVIEWER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -// Qt3DCore includes -#include -#include -#include -#include -#include -#include -// Qt3DInput includes -#include -// Qt3DRender includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// Qt3DExtras includes -#include -#include -#include -#include -#include -#include -#include - -class ModelViewer : public QWidget -{ - Q_OBJECT -public: - explicit ModelViewer(QWidget *parent = nullptr); - -signals: -}; - -#endif // MODELVIEWER_H diff --git a/tests/360/autotest_cod10_360.cpp b/tests/360/autotest_cod10_360.cpp new file mode 100644 index 0000000..a69ad7b --- /dev/null +++ b/tests/360/autotest_cod10_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD10_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod10/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD10_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD10_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod10_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_360::testDecompression() { + QFETCH(QString, fastFilePath_cod10_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod10_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod10_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod10_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod10_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD10_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod10_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_360::testCompression() { + QFETCH(QString, zoneFilePath_cod10_360); + + QFile zoneFile(zoneFilePath_cod10_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod10_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod10_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD10_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod10_360.moc" diff --git a/tests/360/autotest_cod11_360.cpp b/tests/360/autotest_cod11_360.cpp new file mode 100644 index 0000000..cb91e7e --- /dev/null +++ b/tests/360/autotest_cod11_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD11_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod11/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD11_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD11_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod11_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD11_360::testDecompression() { + QFETCH(QString, fastFilePath_cod11_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod11_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod11_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod11_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod11_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD11_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod11_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD11_360::testCompression() { + QFETCH(QString, zoneFilePath_cod11_360); + + QFile zoneFile(zoneFilePath_cod11_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod11_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod11_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD11_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod11_360.moc" diff --git a/tests/360/autotest_cod12_360.cpp b/tests/360/autotest_cod12_360.cpp new file mode 100644 index 0000000..926c0a5 --- /dev/null +++ b/tests/360/autotest_cod12_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD12_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod12/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD12_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD12_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod12_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD12_360::testDecompression() { + QFETCH(QString, fastFilePath_cod12_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod12_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod12_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod12_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod12_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD12_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod12_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD12_360::testCompression() { + QFETCH(QString, zoneFilePath_cod12_360); + + QFile zoneFile(zoneFilePath_cod12_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod12_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod12_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD12_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod12_360.moc" diff --git a/tests/360/autotest_cod2_360.cpp b/tests/360/autotest_cod2_360.cpp new file mode 100644 index 0000000..9ecadec --- /dev/null +++ b/tests/360/autotest_cod2_360.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD2_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod2/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD2_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD2_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod2_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD2_360::testDecompression() { + QFETCH(QString, fastFilePath_cod2_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod2_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod2_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(20); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + // TODO: Find new way to verify as cod2 doesn't store size in zone file + //QVERIFY2(zoneSize + 44 == testZoneData.size(), + // qPrintable("Decompression validation failed for: " + fastFilePath_cod2_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod2_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD2_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod2_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD2_360::testCompression() { + QFETCH(QString, zoneFilePath_cod2_360); + + QFile zoneFile(zoneFilePath_cod2_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod2_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod2_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(20); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD2_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod2_360.moc" diff --git a/tests/360/autotest_cod4_360.cpp b/tests/360/autotest_cod4_360.cpp new file mode 100644 index 0000000..39e2a6a --- /dev/null +++ b/tests/360/autotest_cod4_360.cpp @@ -0,0 +1,119 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD4_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod4/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD4_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD4_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod4_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_360::testDecompression() { + QFETCH(QString, fastFilePath_cod4_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod4_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod4_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::BigEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod4_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod4_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD4_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod4_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_360::testCompression() { + QFETCH(QString, zoneFilePath_cod4_360); + + QFile zoneFile(zoneFilePath_cod4_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod4_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod4_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD4_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod4_360.moc" diff --git a/tests/360/autotest_cod5_360.cpp b/tests/360/autotest_cod5_360.cpp new file mode 100644 index 0000000..bc78cc7 --- /dev/null +++ b/tests/360/autotest_cod5_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD5_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod5/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD5_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD5_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod5_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_360::testDecompression() { + QFETCH(QString, fastFilePath_cod5_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod5_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod5_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod5_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod5_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD5_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod5_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_360::testCompression() { + QFETCH(QString, zoneFilePath_cod5_360); + + QFile zoneFile(zoneFilePath_cod5_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod5_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod5_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD5_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod5_360.moc" diff --git a/tests/360/autotest_cod6_360.cpp b/tests/360/autotest_cod6_360.cpp new file mode 100644 index 0000000..994446d --- /dev/null +++ b/tests/360/autotest_cod6_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD6_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod6/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD6_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD6_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod6_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_360::testDecompression() { + QFETCH(QString, fastFilePath_cod6_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod6_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod6_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod6_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod6_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD6_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod6_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_360::testCompression() { + QFETCH(QString, zoneFilePath_cod6_360); + + QFile zoneFile(zoneFilePath_cod6_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod6_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod6_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD6_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod6_360.moc" diff --git a/tests/360/autotest_cod7_360.cpp b/tests/360/autotest_cod7_360.cpp new file mode 100644 index 0000000..a1bdb9a --- /dev/null +++ b/tests/360/autotest_cod7_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD7_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod7/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD7_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD7_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod7_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_360::testDecompression() { + QFETCH(QString, fastFilePath_cod7_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod7_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod7_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod7_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod7_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD7_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod7_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_360::testCompression() { + QFETCH(QString, zoneFilePath_cod7_360); + + QFile zoneFile(zoneFilePath_cod7_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod7_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod7_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD7_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod7_360.moc" diff --git a/tests/360/autotest_cod8_360.cpp b/tests/360/autotest_cod8_360.cpp new file mode 100644 index 0000000..99b4251 --- /dev/null +++ b/tests/360/autotest_cod8_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD8_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod8/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD8_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD8_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod8_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD8_360::testDecompression() { + QFETCH(QString, fastFilePath_cod8_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod8_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod8_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod8_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod8_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD8_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod8_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD8_360::testCompression() { + QFETCH(QString, zoneFilePath_cod8_360); + + QFile zoneFile(zoneFilePath_cod8_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod8_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod8_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD8_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod8_360.moc" diff --git a/tests/360/autotest_cod9_360.cpp b/tests/360/autotest_cod9_360.cpp new file mode 100644 index 0000000..386901d --- /dev/null +++ b/tests/360/autotest_cod9_360.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD9_360 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod9/360"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD9_360::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD9_360::testDecompression_data() { + QTest::addColumn("fastFilePath_cod9_360"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_360::testDecompression() { + QFETCH(QString, fastFilePath_cod9_360); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod9_360); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod9_360)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod9_360)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod9_360); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD9_360::testCompression_data() { + QTest::addColumn("zoneFilePath_cod9_360"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_360::testCompression() { + QFETCH(QString, zoneFilePath_cod9_360); + + QFile zoneFile(zoneFilePath_cod9_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod9_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod9_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD9_360::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod9_360.moc" diff --git a/tests/PC/autotest_cod10_pc.cpp b/tests/PC/autotest_cod10_pc.cpp new file mode 100644 index 0000000..ad2a304 --- /dev/null +++ b/tests/PC/autotest_cod10_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD10_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod8/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD10_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD10_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod10_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + + +void AutoTest_COD10_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod10_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod10_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod10_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod10_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod10_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD10_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod10_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod10_pc); + + QFile zoneFile(zoneFilePath_cod10_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod10_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod10_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD10_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod10_pc.moc" diff --git a/tests/PC/autotest_cod11_pc.cpp b/tests/PC/autotest_cod11_pc.cpp new file mode 100644 index 0000000..999a038 --- /dev/null +++ b/tests/PC/autotest_cod11_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD11_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod11/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD11_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD11_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod11_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + + +void AutoTest_COD11_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod11_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod11_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod11_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod11_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod11_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD11_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod11_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD11_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod11_pc); + + QFile zoneFile(zoneFilePath_cod11_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod11_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod11_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD11_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod11_pc.moc" diff --git a/tests/PC/autotest_cod12_pc.cpp b/tests/PC/autotest_cod12_pc.cpp new file mode 100644 index 0000000..ef8b276 --- /dev/null +++ b/tests/PC/autotest_cod12_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD12_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod12/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD12_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD12_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod12_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + + +void AutoTest_COD12_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod12_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod12_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod12_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod12_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod12_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD12_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod12_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD12_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod12_pc); + + QFile zoneFile(zoneFilePath_cod12_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod12_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod12_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD12_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod12_pc.moc" diff --git a/tests/PC/autotest_cod4_pc.cpp b/tests/PC/autotest_cod4_pc.cpp new file mode 100644 index 0000000..b89ae4f --- /dev/null +++ b/tests/PC/autotest_cod4_pc.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + + +class AutoTest_COD4_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod4/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD4_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD4_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod4_pc"); + + QStringList fastFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : fastFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod4_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod4_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod4_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod4_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod4_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD4_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod4_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod4_pc); + + QFile zoneFile(zoneFilePath_cod4_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod4_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod4_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD4_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod4_pc.moc" diff --git a/tests/PC/autotest_cod5_pc.cpp b/tests/PC/autotest_cod5_pc.cpp new file mode 100644 index 0000000..57f0d78 --- /dev/null +++ b/tests/PC/autotest_cod5_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD5_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod5/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD5_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD5_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod5_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod5_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod5_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod5_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod5_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod5_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD5_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod5_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod5_pc); + + QFile zoneFile(zoneFilePath_cod5_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod5_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod5_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData = Compression::CompressZLIB(decompressedData); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD5_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod5_pc.moc" diff --git a/tests/PC/autotest_cod6_pc.cpp b/tests/PC/autotest_cod6_pc.cpp new file mode 100644 index 0000000..33d0f48 --- /dev/null +++ b/tests/PC/autotest_cod6_pc.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD6_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod6/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD6_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD6_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod6_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod6_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod6_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod6_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(21); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 40 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod6_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod6_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD6_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod6_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod6_pc); + + QFile zoneFile(zoneFilePath_cod6_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod6_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod6_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(21); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION); + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD6_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod6_pc.moc" diff --git a/tests/PC/autotest_cod7_pc.cpp b/tests/PC/autotest_cod7_pc.cpp new file mode 100644 index 0000000..3d26478 --- /dev/null +++ b/tests/PC/autotest_cod7_pc.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD7_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod7/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD7_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD7_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod7_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod7_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod7_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod7_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod7_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod7_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD7_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod7_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod7_pc); + + QFile zoneFile(zoneFilePath_cod7_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod7_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod7_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD7_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod7_pc.moc" diff --git a/tests/PC/autotest_cod8_pc.cpp b/tests/PC/autotest_cod8_pc.cpp new file mode 100644 index 0000000..decf3b3 --- /dev/null +++ b/tests/PC/autotest_cod8_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD8_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod8/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD8_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD8_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod8_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + + +void AutoTest_COD8_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod8_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod8_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod8_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod8_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod8_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD8_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod8_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD8_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod8_pc); + + QFile zoneFile(zoneFilePath_cod8_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod8_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod8_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD8_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod8_pc.moc" diff --git a/tests/PC/autotest_cod9_pc.cpp b/tests/PC/autotest_cod9_pc.cpp new file mode 100644 index 0000000..f8a1e64 --- /dev/null +++ b/tests/PC/autotest_cod9_pc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD9_PC : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod9/pc"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD9_PC::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD9_PC::testDecompression_data() { + QTest::addColumn("fastFilePath_cod9_pc"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + + +void AutoTest_COD9_PC::testDecompression() { + QFETCH(QString, fastFilePath_cod9_pc); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod9_pc); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod9_pc)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 36 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod9_pc)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod9_pc); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD9_PC::testCompression_data() { + QTest::addColumn("zoneFilePath_cod9_pc"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_PC::testCompression() { + QFETCH(QString, zoneFilePath_cod9_pc); + + QFile zoneFile(zoneFilePath_cod9_pc); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod9_pc)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod9_pc); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + QByteArray newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_SPEED); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD9_PC::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod9_pc.moc" diff --git a/tests/PS3/autotest_cod10_ps3.cpp b/tests/PS3/autotest_cod10_ps3.cpp new file mode 100644 index 0000000..6432d3b --- /dev/null +++ b/tests/PS3/autotest_cod10_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD10_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod10/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD10_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD10_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod10_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod10_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod10_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod10_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod10_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod10_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD10_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod10_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod10_ps3); + + QFile zoneFile(zoneFilePath_cod10_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod10_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod10_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD10_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod10_ps3.moc" diff --git a/tests/PS3/autotest_cod11_ps3.cpp b/tests/PS3/autotest_cod11_ps3.cpp new file mode 100644 index 0000000..e20a48b --- /dev/null +++ b/tests/PS3/autotest_cod11_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD11_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod11/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD11_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD11_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod11_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD11_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod11_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod11_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod11_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod11_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod11_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD11_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod11_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD11_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod11_ps3); + + QFile zoneFile(zoneFilePath_cod11_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod11_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod11_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD11_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod11_ps3.moc" diff --git a/tests/PS3/autotest_cod12_ps3.cpp b/tests/PS3/autotest_cod12_ps3.cpp new file mode 100644 index 0000000..8a23cd2 --- /dev/null +++ b/tests/PS3/autotest_cod12_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD12_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod12/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD12_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD12_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod12_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD12_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod12_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod12_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod12_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod12_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod12_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD12_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod12_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD12_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod12_ps3); + + QFile zoneFile(zoneFilePath_cod12_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod12_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod12_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD12_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod12_ps3.moc" diff --git a/tests/PS3/autotest_cod4_ps3.cpp b/tests/PS3/autotest_cod4_ps3.cpp new file mode 100644 index 0000000..8ca2dc2 --- /dev/null +++ b/tests/PS3/autotest_cod4_ps3.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD4_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod4/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD4_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD4_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod4_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod4_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod4_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod4_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressDeflate(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + //QVERIFY2(zoneSize + 44 == testZoneData.size(), + // qPrintable("Decompression validation failed for: " + fastFilePath_cod4_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod4_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD4_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod4_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD4_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod4_ps3); + + QFile zoneFile(zoneFilePath_cod4_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod4_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod4_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData = Compression::CompressDeflateWithSettings(decompressedData, Z_BEST_COMPRESSION); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD4_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod4_ps3.moc" diff --git a/tests/PS3/autotest_cod5_ps3.cpp b/tests/PS3/autotest_cod5_ps3.cpp new file mode 100644 index 0000000..a04c1d2 --- /dev/null +++ b/tests/PS3/autotest_cod5_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD5_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod5/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD5_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD5_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod5_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod5_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod5_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod5_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod5_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod5_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD5_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod5_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD5_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod5_ps3); + + QFile zoneFile(zoneFilePath_cod5_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod5_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod5_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD5_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod5_ps3.moc" diff --git a/tests/PS3/autotest_cod6_ps3.cpp b/tests/PS3/autotest_cod6_ps3.cpp new file mode 100644 index 0000000..b5b5234 --- /dev/null +++ b/tests/PS3/autotest_cod6_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD6_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod6/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD6_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD6_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod6_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod6_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod6_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod6_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod6_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod6_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD6_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod6_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD6_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod6_ps3); + + QFile zoneFile(zoneFilePath_cod6_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod6_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod6_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD6_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod6_ps3.moc" diff --git a/tests/PS3/autotest_cod7_ps3.cpp b/tests/PS3/autotest_cod7_ps3.cpp new file mode 100644 index 0000000..a49fa55 --- /dev/null +++ b/tests/PS3/autotest_cod7_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD7_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod7/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD7_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD7_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod7_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod7_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod7_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod7_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod7_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod7_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD7_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod7_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod7_ps3); + + QFile zoneFile(zoneFilePath_cod7_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod7_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod7_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD7_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod7_ps3.moc" diff --git a/tests/PS3/autotest_cod8_ps3.cpp b/tests/PS3/autotest_cod8_ps3.cpp new file mode 100644 index 0000000..fdae339 --- /dev/null +++ b/tests/PS3/autotest_cod8_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD8_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod8/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD8_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD8_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod8_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD8_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod8_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod8_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod8_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod8_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod8_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD8_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod8_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD8_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod8_ps3); + + QFile zoneFile(zoneFilePath_cod8_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod8_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod8_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD8_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod8_ps3.moc" diff --git a/tests/PS3/autotest_cod9_ps3.cpp b/tests/PS3/autotest_cod9_ps3.cpp new file mode 100644 index 0000000..660f16b --- /dev/null +++ b/tests/PS3/autotest_cod9_ps3.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD9_PS3 : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod9/PS3"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD9_PS3::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD9_PS3::testDecompression_data() { + QTest::addColumn("fastFilePath_cod9_ps3"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_PS3::testDecompression() { + QFETCH(QString, fastFilePath_cod9_ps3); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod9_ps3); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod9_ps3)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod9_ps3)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod9_ps3); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD9_PS3::testCompression_data() { + QTest::addColumn("zoneFilePath_cod9_ps3"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_PS3::testCompression() { + QFETCH(QString, zoneFilePath_cod9_ps3); + + QFile zoneFile(zoneFilePath_cod9_ps3); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod9_ps3)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod9_ps3); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD9_PS3::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod9_ps3.moc" diff --git a/tests/Wii/autotest_cod7_wii.cpp b/tests/Wii/autotest_cod7_wii.cpp new file mode 100644 index 0000000..69948a5 --- /dev/null +++ b/tests/Wii/autotest_cod7_wii.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD7_Wii : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod7/Wii"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD7_Wii::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD7_Wii::testDecompression_data() { + QTest::addColumn("fastFilePath_cod7_wii"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_Wii::testDecompression() { + QFETCH(QString, fastFilePath_cod7_wii); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod7_wii); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod7_wii)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod7_wii)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod7_wii); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD7_Wii::testCompression_data() { + QTest::addColumn("zoneFilePath_cod7_wii"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD7_Wii::testCompression() { + QFETCH(QString, zoneFilePath_cod2_360); + + QFile zoneFile(zoneFilePath_cod2_360); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod2_360)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod2_360); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD7_Wii::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod7_wii.moc" diff --git a/tests/WiiU/autotest_cod10_wiiu.cpp b/tests/WiiU/autotest_cod10_wiiu.cpp new file mode 100644 index 0000000..ec487f4 --- /dev/null +++ b/tests/WiiU/autotest_cod10_wiiu.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD10_WiiU : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod10/WiiU"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD10_WiiU::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD10_WiiU::testDecompression_data() { + QTest::addColumn("fastFilePath_cod10_wiiu"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_WiiU::testDecompression() { + QFETCH(QString, fastFilePath_cod10_wiiu); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod10_wiiu); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod10_wiiu)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod10_wiiu)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod10_wiiu); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD10_WiiU::testCompression_data() { + QTest::addColumn("zoneFilePath_cod10_wiiu"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD10_WiiU::testCompression() { + QFETCH(QString, zoneFilePath_cod10_wiiu); + + QFile zoneFile(zoneFilePath_cod10_wiiu); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod10_wiiu)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod10_wiiu); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD10_WiiU::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod10_wiiu.moc" diff --git a/tests/WiiU/autotest_cod9_wiiu.cpp b/tests/WiiU/autotest_cod9_wiiu.cpp new file mode 100644 index 0000000..819f170 --- /dev/null +++ b/tests/WiiU/autotest_cod9_wiiu.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "autotest_cod.h" +#include "compression.h" + +class AutoTest_COD9_WiiU : public AutoTest_COD { + Q_OBJECT + + const QString EXPORT_DIR = "./exports/cod9/WiiU"; + +private slots: + void initTestCase(); + // Data-driven test for decompression + void testDecompression_data(); + void testDecompression(); + // Data-driven test for recompression (compression) + void testCompression_data(); + void testCompression(); + void cleanupTestCase(); +}; + +void AutoTest_COD9_WiiU::initTestCase() { + // Ensure the exports directory exists. + createDirectory(EXPORT_DIR); +} + +void AutoTest_COD9_WiiU::testDecompression_data() { + QTest::addColumn("fastFilePath_cod9_wiiu"); + + QStringList ffFiles = findFastFiles(getFastFileDirectory()); + for (const QString &filePath : ffFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_WiiU::testDecompression() { + QFETCH(QString, fastFilePath_cod9_wiiu); + + // Open the original .ff file. + QFile testFastFile(fastFilePath_cod9_wiiu); + QVERIFY2(testFastFile.open(QIODevice::ReadOnly), + qPrintable("Failed to open test fastfile: " + fastFilePath_cod9_wiiu)); + const QByteArray testFFData = testFastFile.readAll(); + testFastFile.close(); + + // Assume the first 12 bytes are a header; the rest is zlib-compressed zone data. + const QByteArray compressedData = testFFData.mid(12); + const QByteArray testZoneData = Compression::DecompressZLIB(compressedData); + + // Verify the decompressed data via its embedded zone size. + QDataStream zoneStream(testZoneData); + zoneStream.setByteOrder(QDataStream::LittleEndian); + quint32 zoneSize; + zoneStream >> zoneSize; + QVERIFY2(zoneSize + 44 == testZoneData.size(), + qPrintable("Decompression validation failed for: " + fastFilePath_cod9_wiiu)); + + // Write the decompressed zone data to the exports folder with a .zone extension. + QFileInfo fi(fastFilePath_cod9_wiiu); + QString outputFileName = fi.completeBaseName() + ".zone"; + QString outputFilePath = QDir(EXPORT_DIR).filePath(outputFileName); + QFile outputFile(outputFilePath); + QVERIFY2(outputFile.open(QIODevice::WriteOnly), + qPrintable("Failed to open output file for writing: " + outputFilePath)); + outputFile.write(testZoneData); + outputFile.close(); +} + +void AutoTest_COD9_WiiU::testCompression_data() { + QTest::addColumn("zoneFilePath_cod9_wiiu"); + + QStringList zoneFiles = findZoneFiles(getZoneFileDirectory()); + for (const QString &filePath : zoneFiles) { + QString fileName = QFileInfo(filePath).fileName(); + QTest::newRow(qPrintable(fileName)) << filePath; + } +} + +void AutoTest_COD9_WiiU::testCompression() { + QFETCH(QString, zoneFilePath_cod9_wiiu); + + QFile zoneFile(zoneFilePath_cod9_wiiu); + QVERIFY2(zoneFile.open(QIODevice::ReadOnly), qPrintable("Failed to open zone file: " + zoneFilePath_cod9_wiiu)); + QByteArray decompressedData = zoneFile.readAll(); + zoneFile.close(); + + QFileInfo fi(zoneFilePath_cod9_wiiu); + QString originalFFPath = QDir(getFastFileDirectory()).filePath(fi.completeBaseName() + ".ff"); + + QFile originalFile(originalFFPath); + QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable("Failed to open original .ff file: " + originalFFPath)); + QByteArray originalFFData = originalFile.readAll(); + originalFile.close(); + + QByteArray header = originalFFData.left(12); + + QByteArray newCompressedData;// = Compressor::CompressZLIB(decompressedData, Z_BEST_COMPRESSION); + newCompressedData = Compression::CompressZLIBWithSettings(decompressedData, Z_BEST_COMPRESSION, MAX_WBITS, 8, Z_DEFAULT_STRATEGY, {}); + + int remainder = (newCompressedData.size() + 12) % 32; + if (remainder != 0) { + int paddingNeeded = 32 - remainder; + newCompressedData.append(QByteArray(paddingNeeded, '\0')); + } + + QByteArray recompressedData = header + newCompressedData; + + QString recompressedFilePath = QDir(EXPORT_DIR).filePath(fi.completeBaseName() + ".ff"); + QFile recompressedFile(recompressedFilePath); + QVERIFY2(recompressedFile.open(QIODevice::WriteOnly), qPrintable("Failed to write recompressed file.")); + recompressedFile.write(recompressedData); + recompressedFile.close(); + + QCOMPARE(recompressedData, originalFFData); +} + +void AutoTest_COD9_WiiU::cleanupTestCase() { + // Any cleanup if necessary. +} + +// Don't generate a main() function +#include "autotest_cod9_wiiu.moc" diff --git a/tests/autotest_cod.h b/tests/autotest_cod.h new file mode 100644 index 0000000..4c4b2e3 --- /dev/null +++ b/tests/autotest_cod.h @@ -0,0 +1,69 @@ +#ifndef AUTOTEST_COD_CPP +#define AUTOTEST_COD_CPP + +#include + +class AutoTest_COD : public QObject { + Q_OBJECT + +public: + void setFastFileDirectory(const QString aFastFileDir) { + mFastFileDirectory = aFastFileDir; + } + QString getFastFileDirectory() { + return mFastFileDirectory; + } + + void setZoneFileDirectory(const QString aZoneFileDir) { + mZoneFileDirectory = aZoneFileDir; + } + QString getZoneFileDirectory() { + return mZoneFileDirectory; + } + + void createDirectory(const QString aDir) { + QDir newDir(aDir); + if (!newDir.exists()) { + newDir.mkpath(aDir); + } + } + QStringList findFastFiles(const QString &aBaseDir, int aMaxIter = MAX_ITER) { + QStringList fastFiles; + + QDirIterator it(aBaseDir, QStringList() << "*.ff", QDir::Files, QDirIterator::Subdirectories); + + int i = 0; + while (it.hasNext() && i < aMaxIter) { + fastFiles << it.next(); + ++i; + } + + return fastFiles; + } + QStringList findZoneFiles(const QString &aBaseDir, int aMaxIter = MAX_ITER) { + QStringList zoneFiles; + + QDirIterator it(aBaseDir, QStringList() << "*.zone", QDir::Files, QDirIterator::Subdirectories); + + int i = 0; + while (it.hasNext() && i < aMaxIter) { + zoneFiles << it.next(); + ++i; + } + + return zoneFiles; + } + virtual void initTestCase() = 0; + virtual void testDecompression_data() = 0; + virtual void testDecompression() = 0; + virtual void testCompression_data() = 0; + virtual void testCompression() = 0; + virtual void cleanupTestCase() = 0; + +private: + static const int MAX_ITER = -1; + QString mFastFileDirectory; + QString mZoneFileDirectory; +}; + +#endif // AUTOTEST_COD_CPP diff --git a/tests/autotest_xplor.cpp b/tests/autotest_xplor.cpp new file mode 100644 index 0000000..bb24eb4 --- /dev/null +++ b/tests/autotest_xplor.cpp @@ -0,0 +1,9 @@ +#include + +class AutoTest_XPlor : public QObject { + Q_OBJECT + +private slots: +}; + +#include "autotest_xplor.moc" diff --git a/tests/data.qrc b/tests/data.qrc new file mode 100644 index 0000000..fa64d6c --- /dev/null +++ b/tests/data.qrc @@ -0,0 +1,194 @@ + + + data/cod5/zonefile/ber1.zone + data/cod5/zonefile/ber1_load.zone + data/cod5/zonefile/ber2.zone + data/cod5/zonefile/ber2_load.zone + data/cod5/zonefile/ber3.zone + data/cod5/zonefile/ber3_load.zone + data/cod5/zonefile/ber3b.zone + data/cod5/zonefile/ber3b_load.zone + data/cod5/zonefile/code_post_gfx_mp.zone + data/cod5/zonefile/common_ignore.zone + data/cod5/zonefile/default.zone + data/cod5/zonefile/localized_mp_asylum.zone + data/cod5/zonefile/localized_mp_castle.zone + data/cod5/zonefile/localized_mp_kneedeep.zone + data/cod5/zonefile/mak.zone + data/cod5/zonefile/mak_load.zone + data/cod5/zonefile/mp_airfield_load.zone + data/cod5/zonefile/mp_asylum.zone + data/cod5/zonefile/mp_asylum_load.zone + data/cod5/zonefile/mp_bgate.zone + data/cod5/zonefile/mp_bgate_load.zone + data/cod5/zonefile/mp_castle_load.zone + data/cod5/zonefile/mp_courtyard_load.zone + data/cod5/zonefile/mp_docks.zone + data/cod5/zonefile/mp_docks_load.zone + data/cod5/zonefile/mp_dome_load.zone + data/cod5/zonefile/mp_downfall.zone + data/cod5/zonefile/mp_downfall_load.zone + data/cod5/zonefile/mp_drum_load.zone + data/cod5/zonefile/mp_hangar.zone + data/cod5/zonefile/mp_hangar_load.zone + data/cod5/zonefile/mp_kneedeep.zone + data/cod5/zonefile/mp_kneedeep_load.zone + data/cod5/zonefile/mp_kwai.zone + data/cod5/zonefile/mp_kwai_load.zone + data/cod5/zonefile/mp_makin.zone + data/cod5/zonefile/mp_makin_day.zone + data/cod5/zonefile/mp_makin_day_load.zone + data/cod5/zonefile/mp_makin_load.zone + data/cod5/zonefile/mp_nachtfeuer.zone + data/cod5/zonefile/mp_nachtfeuer_load.zone + data/cod5/zonefile/mp_outskirts.zone + data/cod5/zonefile/mp_outskirts_load.zone + data/cod5/zonefile/mp_roundhouse.zone + data/cod5/zonefile/mp_roundhouse_load.zone + data/cod5/zonefile/mp_seelow.zone + data/cod5/zonefile/mp_seelow_load.zone + data/cod5/zonefile/mp_shrine.zone + data/cod5/zonefile/mp_shrine_load.zone + data/cod5/zonefile/mp_stalingrad.zone + data/cod5/zonefile/mp_stalingrad_load.zone + data/cod5/zonefile/mp_suburban.zone + data/cod5/zonefile/mp_suburban_load.zone + data/cod5/zonefile/mp_subway.zone + data/cod5/zonefile/mp_subway_load.zone + data/cod5/zonefile/mp_vodka.zone + data/cod5/zonefile/mp_vodka_load.zone + data/cod5/zonefile/nazi_zombie_asylum.zone + data/cod5/zonefile/nazi_zombie_asylum_load.zone + data/cod5/zonefile/nazi_zombie_asylum_patch.zone + data/cod5/zonefile/nazi_zombie_factory.zone + data/cod5/zonefile/nazi_zombie_factory_load.zone + data/cod5/zonefile/nazi_zombie_factory_patch.zone + data/cod5/zonefile/nazi_zombie_prototype.zone + data/cod5/zonefile/nazi_zombie_prototype_load.zone + data/cod5/zonefile/nazi_zombie_sumpf.zone + data/cod5/zonefile/nazi_zombie_sumpf_load.zone + data/cod5/zonefile/nazi_zombie_sumpf_patch.zone + data/cod5/zonefile/oki2.zone + data/cod5/zonefile/oki2_load.zone + data/cod5/zonefile/oki3.zone + data/cod5/zonefile/oki3_load.zone + data/cod5/zonefile/outro.zone + data/cod5/zonefile/patch.zone + data/cod5/zonefile/patch_mp.zone + data/cod5/zonefile/pby_fly.zone + data/cod5/zonefile/pby_fly_load.zone + data/cod5/zonefile/pel1.zone + data/cod5/zonefile/pel1_load.zone + data/cod5/zonefile/pel1a.zone + data/cod5/zonefile/pel1a_load.zone + data/cod5/zonefile/pel1b.zone + data/cod5/zonefile/pel1b_load.zone + data/cod5/zonefile/pel2.zone + data/cod5/zonefile/pel2_load.zone + data/cod5/zonefile/see1.zone + data/cod5/zonefile/see1_load.zone + data/cod5/zonefile/see2.zone + data/cod5/zonefile/see2_load.zone + data/cod5/zonefile/sniper.zone + data/cod5/zonefile/sniper_load.zone + data/cod5/zonefile/ui.zone + data/cod5/zonefile/ui_mp.zone + data/cod5/zonefile/xcommon_rtx.zone + + + data/cod5/fastfile/ber1.ff + data/cod5/fastfile/ber1_load.ff + data/cod5/fastfile/ber2.ff + data/cod5/fastfile/ber2_load.ff + data/cod5/fastfile/ber3.ff + data/cod5/fastfile/ber3_load.ff + data/cod5/fastfile/ber3b.ff + data/cod5/fastfile/ber3b_load.ff + data/cod5/fastfile/code_post_gfx_mp.ff + data/cod5/fastfile/common_ignore.ff + data/cod5/fastfile/default.ff + data/cod5/fastfile/localized_mp_asylum.ff + data/cod5/fastfile/localized_mp_castle.ff + data/cod5/fastfile/localized_mp_kneedeep.ff + data/cod5/fastfile/mak.ff + data/cod5/fastfile/mak_load.ff + data/cod5/fastfile/mp_airfield_load.ff + data/cod5/fastfile/mp_asylum.ff + data/cod5/fastfile/mp_asylum_load.ff + data/cod5/fastfile/mp_bgate.ff + data/cod5/fastfile/mp_bgate_load.ff + data/cod5/fastfile/mp_castle_load.ff + data/cod5/fastfile/mp_courtyard_load.ff + data/cod5/fastfile/mp_docks.ff + data/cod5/fastfile/mp_docks_load.ff + data/cod5/fastfile/mp_dome_load.ff + data/cod5/fastfile/mp_downfall.ff + data/cod5/fastfile/mp_downfall_load.ff + data/cod5/fastfile/mp_drum_load.ff + data/cod5/fastfile/mp_hangar.ff + data/cod5/fastfile/mp_hangar_load.ff + data/cod5/fastfile/mp_kneedeep.ff + data/cod5/fastfile/mp_kneedeep_load.ff + data/cod5/fastfile/mp_kwai.ff + data/cod5/fastfile/mp_kwai_load.ff + data/cod5/fastfile/mp_makin.ff + data/cod5/fastfile/mp_makin_day.ff + data/cod5/fastfile/mp_makin_day_load.ff + data/cod5/fastfile/mp_makin_load.ff + data/cod5/fastfile/mp_nachtfeuer.ff + data/cod5/fastfile/mp_nachtfeuer_load.ff + data/cod5/fastfile/mp_outskirts.ff + data/cod5/fastfile/mp_outskirts_load.ff + data/cod5/fastfile/mp_roundhouse.ff + data/cod5/fastfile/mp_roundhouse_load.ff + data/cod5/fastfile/mp_seelow.ff + data/cod5/fastfile/mp_seelow_load.ff + data/cod5/fastfile/mp_shrine.ff + data/cod5/fastfile/mp_shrine_load.ff + data/cod5/fastfile/mp_stalingrad.ff + data/cod5/fastfile/mp_stalingrad_load.ff + data/cod5/fastfile/mp_suburban.ff + data/cod5/fastfile/mp_suburban_load.ff + data/cod5/fastfile/mp_subway.ff + data/cod5/fastfile/mp_subway_load.ff + data/cod5/fastfile/mp_vodka.ff + data/cod5/fastfile/mp_vodka_load.ff + data/cod5/fastfile/nazi_zombie_asylum.ff + data/cod5/fastfile/nazi_zombie_asylum_load.ff + data/cod5/fastfile/nazi_zombie_asylum_patch.ff + data/cod5/fastfile/nazi_zombie_factory.ff + data/cod5/fastfile/nazi_zombie_factory_load.ff + data/cod5/fastfile/nazi_zombie_factory_patch.ff + data/cod5/fastfile/nazi_zombie_prototype.ff + data/cod5/fastfile/nazi_zombie_prototype_load.ff + data/cod5/fastfile/nazi_zombie_sumpf.ff + data/cod5/fastfile/nazi_zombie_sumpf_load.ff + data/cod5/fastfile/nazi_zombie_sumpf_patch.ff + data/cod5/fastfile/oki2.ff + data/cod5/fastfile/oki2_load.ff + data/cod5/fastfile/oki3.ff + data/cod5/fastfile/oki3_load.ff + data/cod5/fastfile/outro.ff + data/cod5/fastfile/patch.ff + data/cod5/fastfile/patch_mp.ff + data/cod5/fastfile/pby_fly.ff + data/cod5/fastfile/pby_fly_load.ff + data/cod5/fastfile/pel1.ff + data/cod5/fastfile/pel1_load.ff + data/cod5/fastfile/pel1a.ff + data/cod5/fastfile/pel1a_load.ff + data/cod5/fastfile/pel1b.ff + data/cod5/fastfile/pel1b_load.ff + data/cod5/fastfile/pel2.ff + data/cod5/fastfile/pel2_load.ff + data/cod5/fastfile/see1.ff + data/cod5/fastfile/see1_load.ff + data/cod5/fastfile/see2.ff + data/cod5/fastfile/see2_load.ff + data/cod5/fastfile/sniper.ff + data/cod5/fastfile/sniper_load.ff + data/cod5/fastfile/ui.ff + data/cod5/fastfile/ui_mp.ff + data/cod5/fastfile/xcommon_rtx.ff + + diff --git a/tests/test_main.cpp b/tests/test_main.cpp new file mode 100644 index 0000000..7f3a60c --- /dev/null +++ b/tests/test_main.cpp @@ -0,0 +1,392 @@ +#include +//#include "autotest_xplor.cpp" + +#include "360/autotest_cod2_360.cpp" +#include "360/autotest_cod4_360.cpp" +#include "360/autotest_cod5_360.cpp" +#include "360/autotest_cod6_360.cpp" +#include "360/autotest_cod7_360.cpp" +#include "360/autotest_cod8_360.cpp" +#include "360/autotest_cod9_360.cpp" +#include "360/autotest_cod10_360.cpp" +#include "360/autotest_cod11_360.cpp" +#include "360/autotest_cod12_360.cpp" + +#include "PC/autotest_cod4_pc.cpp" +#include "PC/autotest_cod5_pc.cpp" +#include "PC/autotest_cod6_pc.cpp" +#include "PC/autotest_cod7_pc.cpp" +#include "PC/autotest_cod8_pc.cpp" +#include "PC/autotest_cod9_pc.cpp" +#include "PC/autotest_cod10_pc.cpp" +#include "PC/autotest_cod11_pc.cpp" +#include "PC/autotest_cod12_pc.cpp" + +#include "PS3/autotest_cod4_ps3.cpp" +#include "PS3/autotest_cod5_ps3.cpp" +#include "PS3/autotest_cod6_ps3.cpp" +#include "PS3/autotest_cod7_ps3.cpp" +#include "PS3/autotest_cod8_ps3.cpp" +#include "PS3/autotest_cod9_ps3.cpp" +#include "PS3/autotest_cod10_ps3.cpp" +#include "PS3/autotest_cod11_ps3.cpp" +#include "PS3/autotest_cod12_ps3.cpp" + +#include "Wii/autotest_cod7_wii.cpp" + +#include "WiiU/autotest_cod9_wiiu.cpp" +#include "WiiU/autotest_cod10_wiiu.cpp" + +// clearly named defines for filtering logic +#define TEST_EVERYTHING 1 + +// Global filters +#define TEST_ALL_PLATFORMS 0 +#define TEST_ALL_COD_GAMES 0 + +// individual games +#define TEST_COD2 0 +#define TEST_COD4 1 +#define TEST_COD5 0 +#define TEST_COD6 0 +#define TEST_COD7 0 +#define TEST_COD8 0 +#define TEST_COD9 0 +#define TEST_COD10 0 +#define TEST_COD11 0 +#define TEST_COD12 0 + +// individual platforms +#define TEST_360 0 +#define TEST_PC 0 +#define TEST_PS3 0 +#define TEST_WII 0 +#define TEST_WIIU 0 + +int main(int argc, char *argv[]) { + /**********************************/ + /********* XPLOR UI TESTS *********/ + /**********************************/ + + // AutoTest_XPlor test_xplor; + // if (!QTest::qExec(&test_xplor, argc, argv)) { + // return -1; + // } + + /**********************************/ + /********* 360 COD TESTS *********/ + /**********************************/ + + QVector cod2Tests; + QVector cod4Tests; + QVector cod5Tests; + QVector cod6Tests; + QVector cod7Tests; + QVector cod8Tests; + QVector cod9Tests; + QVector cod10Tests; + QVector cod11Tests; + QVector cod12Tests; + + QVector xbox360Tests; + QVector pcTests; + QVector ps3Tests; + QVector wiiTests; + QVector wiiUTests; + + AutoTest_COD2_360 *test_cod2_360 = new AutoTest_COD2_360(); + test_cod2_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD2"); + test_cod2_360->setZoneFileDirectory("./exports/cod2/360"); + cod2Tests << test_cod2_360; + xbox360Tests << test_cod2_360; + + AutoTest_COD4_360 *test_cod4_360 = new AutoTest_COD4_360(); + test_cod4_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD4"); + test_cod4_360->setZoneFileDirectory("./exports/cod4/360"); + cod4Tests << test_cod4_360; + xbox360Tests << test_cod4_360; + + AutoTest_COD5_360 *test_cod5_360 = new AutoTest_COD5_360(); + test_cod5_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD5"); + test_cod5_360->setZoneFileDirectory("./exports/cod5/360"); + cod5Tests << test_cod5_360; + xbox360Tests << test_cod5_360; + + AutoTest_COD6_360 *test_cod6_360 = new AutoTest_COD6_360(); + test_cod6_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD6"); + test_cod6_360->setZoneFileDirectory("./exports/cod6/360"); + cod6Tests << test_cod6_360; + xbox360Tests << test_cod6_360; + + AutoTest_COD7_360 *test_cod7_360 = new AutoTest_COD7_360(); + test_cod7_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD7"); + test_cod7_360->setZoneFileDirectory("./exports/cod7/360"); + cod7Tests << test_cod7_360; + xbox360Tests << test_cod7_360; + + AutoTest_COD8_360 *test_cod8_360 = new AutoTest_COD8_360(); + test_cod8_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD8"); + test_cod8_360->setZoneFileDirectory("./exports/cod8/360"); + cod8Tests << test_cod8_360; + xbox360Tests << test_cod8_360; + + AutoTest_COD9_360 *test_cod9_360 = new AutoTest_COD9_360(); + test_cod9_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD9"); + test_cod9_360->setZoneFileDirectory("./exports/cod9/360"); + cod9Tests << test_cod9_360; + xbox360Tests << test_cod9_360; + + AutoTest_COD10_360 *test_cod10_360 = new AutoTest_COD10_360(); + test_cod10_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD10"); + test_cod10_360->setZoneFileDirectory("./exports/cod10/360"); + cod10Tests << test_cod10_360; + xbox360Tests << test_cod10_360; + + AutoTest_COD11_360 *test_cod11_360 = new AutoTest_COD11_360(); + test_cod11_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD11"); + test_cod11_360->setZoneFileDirectory("./exports/cod11/360"); + cod11Tests << test_cod11_360; + xbox360Tests << test_cod11_360; + + AutoTest_COD12_360 *test_cod12_360 = new AutoTest_COD12_360(); + test_cod12_360->setFastFileDirectory("G:/Fast Files/Xbox360/COD12"); + test_cod12_360->setZoneFileDirectory("./exports/cod12/360"); + cod12Tests << test_cod12_360; + xbox360Tests << test_cod12_360; + + /**********************************/ + /********* PC COD TESTS *********/ + /**********************************/ + + AutoTest_COD4_PC *test_cod4_pc = new AutoTest_COD4_PC(); + test_cod4_pc->setFastFileDirectory("G:/Fast Files/PC/COD4"); + test_cod4_pc->setZoneFileDirectory("./exports/cod4/PC"); + cod4Tests << test_cod4_pc; + pcTests << test_cod4_pc; + + AutoTest_COD5_PC *test_cod5_pc = new AutoTest_COD5_PC(); + test_cod5_pc->setFastFileDirectory("G:/Fast Files/PC/COD5"); + test_cod5_pc->setZoneFileDirectory("./exports/cod5/PC"); + cod5Tests << test_cod5_pc; + pcTests << test_cod5_pc; + + AutoTest_COD6_PC *test_cod6_pc = new AutoTest_COD6_PC(); + test_cod6_pc->setFastFileDirectory("G:/Fast Files/PC/COD6"); + test_cod6_pc->setZoneFileDirectory("./exports/cod6/PC"); + cod6Tests << test_cod6_pc; + pcTests << test_cod6_pc; + + AutoTest_COD7_PC *test_cod7_pc = new AutoTest_COD7_PC(); + test_cod7_pc->setFastFileDirectory("G:/Fast Files/PC/COD7"); + test_cod7_pc->setZoneFileDirectory("./exports/cod7/PC"); + cod7Tests << test_cod7_pc; + pcTests << test_cod7_pc; + + AutoTest_COD8_PC *test_cod8_pc = new AutoTest_COD8_PC(); + test_cod8_pc->setFastFileDirectory("G:/Fast Files/PC/COD8"); + test_cod8_pc->setZoneFileDirectory("./exports/cod8/PC"); + cod8Tests << test_cod8_pc; + pcTests << test_cod8_pc; + + AutoTest_COD9_PC *test_cod9_pc = new AutoTest_COD9_PC(); + test_cod9_pc->setFastFileDirectory("G:/Fast Files/PC/COD9"); + test_cod9_pc->setZoneFileDirectory("./exports/cod9/PC"); + cod9Tests << test_cod9_pc; + pcTests << test_cod9_pc; + + AutoTest_COD10_PC *test_cod10_pc = new AutoTest_COD10_PC(); + test_cod10_pc->setFastFileDirectory("G:/Fast Files/PC/COD10"); + test_cod10_pc->setZoneFileDirectory("./exports/cod10/PC"); + cod10Tests << test_cod10_pc; + pcTests << test_cod10_pc; + + AutoTest_COD11_PC *test_cod11_pc = new AutoTest_COD11_PC(); + test_cod11_pc->setFastFileDirectory("G:/Fast Files/PC/COD11"); + test_cod11_pc->setZoneFileDirectory("./exports/cod11/PC"); + cod11Tests << test_cod11_pc; + pcTests << test_cod11_pc; + + AutoTest_COD12_PC *test_cod12_pc = new AutoTest_COD12_PC(); + test_cod12_pc->setFastFileDirectory("G:/Fast Files/PC/COD12"); + test_cod12_pc->setZoneFileDirectory("./exports/cod12/PC"); + cod12Tests << test_cod12_pc; + pcTests << test_cod12_pc; + + /**********************************/ + /********* PS3 COD TESTS *********/ + /**********************************/ + + AutoTest_COD4_PS3 *test_cod4_ps3 = new AutoTest_COD4_PS3(); + test_cod4_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD4"); + test_cod4_ps3->setZoneFileDirectory("./exports/cod4/PS3"); + cod4Tests << test_cod4_ps3; + ps3Tests << test_cod4_ps3; + + AutoTest_COD5_PS3 *test_cod5_ps3 = new AutoTest_COD5_PS3(); + test_cod5_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD5"); + test_cod5_ps3->setZoneFileDirectory("./exports/cod5/PS3"); + cod5Tests << test_cod5_ps3; + ps3Tests << test_cod5_ps3; + + AutoTest_COD6_PS3 *test_cod6_ps3 = new AutoTest_COD6_PS3(); + test_cod6_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD6"); + test_cod6_ps3->setZoneFileDirectory("./exports/cod6/PS3"); + cod6Tests << test_cod6_ps3; + ps3Tests << test_cod6_ps3; + + AutoTest_COD7_PS3 *test_cod7_ps3 = new AutoTest_COD7_PS3(); + test_cod7_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD7"); + test_cod7_ps3->setZoneFileDirectory("./exports/cod7/PS3"); + cod7Tests << test_cod7_ps3; + ps3Tests << test_cod7_ps3; + + AutoTest_COD8_PS3 *test_cod8_ps3 = new AutoTest_COD8_PS3(); + test_cod8_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD8"); + test_cod8_ps3->setZoneFileDirectory("./exports/cod8/PS3"); + cod8Tests << test_cod8_ps3; + ps3Tests << test_cod8_ps3; + + AutoTest_COD9_PS3 *test_cod9_ps3 = new AutoTest_COD9_PS3(); + test_cod9_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD9"); + test_cod9_ps3->setZoneFileDirectory("./exports/cod9/PS3"); + cod9Tests << test_cod9_ps3; + ps3Tests << test_cod9_ps3; + + AutoTest_COD10_PS3 *test_cod10_ps3 = new AutoTest_COD10_PS3(); + test_cod10_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD10"); + test_cod10_ps3->setZoneFileDirectory("./exports/cod10/PS3"); + cod10Tests << test_cod10_ps3; + ps3Tests << test_cod10_ps3; + + AutoTest_COD11_PS3 *test_cod11_ps3 = new AutoTest_COD11_PS3(); + test_cod11_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD11"); + test_cod11_ps3->setZoneFileDirectory("./exports/cod11/PS3"); + cod11Tests << test_cod11_ps3; + ps3Tests << test_cod11_ps3; + + AutoTest_COD12_PS3 *test_cod12_ps3 = new AutoTest_COD12_PS3(); + test_cod12_ps3->setFastFileDirectory("G:/Fast Files/PS3/COD12"); + test_cod12_ps3->setZoneFileDirectory("./exports/cod12/PS3"); + cod12Tests << test_cod12_ps3; + ps3Tests << test_cod12_ps3; + + /**********************************/ + /********* Wii COD TESTS *********/ + /**********************************/ + + AutoTest_COD7_Wii *test_cod7_wii = new AutoTest_COD7_Wii(); + test_cod7_wii->setFastFileDirectory("G:/Fast Files/Wii/COD7"); + test_cod7_wii->setZoneFileDirectory("./exports/cod7/Wii"); + cod7Tests << test_cod7_wii; + wiiTests << test_cod7_wii; + + /**********************************/ + /********* WiiU COD TESTS *********/ + /**********************************/ + + AutoTest_COD9_WiiU *test_cod9_wiiu = new AutoTest_COD9_WiiU(); + test_cod9_wiiu->setFastFileDirectory("G:/Fast Files/WiiU/COD9"); + test_cod9_wiiu->setZoneFileDirectory("./exports/cod9/WiiU"); + cod9Tests << test_cod9_wiiu; + wiiUTests << test_cod9_wiiu; + + AutoTest_COD10_WiiU *test_cod10_wiiu = new AutoTest_COD10_WiiU(); + test_cod10_wiiu->setFastFileDirectory("G:/Fast Files/WiiU/COD10"); + test_cod10_wiiu->setZoneFileDirectory("./exports/cod10/PS3"); + cod10Tests << test_cod10_wiiu; + wiiUTests << test_cod10_wiiu; + + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD2) { + qDebug() << "-- RUNNING TEST_COD2 --"; + foreach (auto test, cod2Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD4) { + qDebug() << "-- RUNNING TEST_COD4 --"; + foreach (auto test, cod4Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD5) { + qDebug() << "-- RUNNING TEST_COD5 --"; + foreach (auto test, cod5Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD6) { + qDebug() << "-- RUNNING TEST_COD6 --"; + foreach (auto test, cod6Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD7) { + qDebug() << "-- RUNNING TEST_COD7 --"; + foreach (auto test, cod7Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD8) { + qDebug() << "-- RUNNING TEST_COD8 --"; + foreach (auto test, cod8Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD9) { + qDebug() << "-- RUNNING TEST_COD9 --"; + foreach (auto test, cod9Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD10) { + qDebug() << "-- RUNNING TEST_COD10 --"; + foreach (auto test, cod10Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD11) { + qDebug() << "-- RUNNING TEST_COD11 --"; + foreach (auto test, cod11Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_COD_GAMES || TEST_COD12) { + qDebug() << "-- RUNNING TEST_COD12 --"; + foreach (auto test, cod12Tests) { + QTest::qExec(test, argc, argv); + } + } + + if (TEST_EVERYTHING || TEST_ALL_PLATFORMS || TEST_360) { + qDebug() << "-- RUNNING TEST_360 --"; + foreach (auto test, xbox360Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_PLATFORMS || TEST_PC) { + qDebug() << "-- RUNNING TEST_PC --"; + foreach (auto test, pcTests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_PLATFORMS || TEST_PS3) { + qDebug() << "-- RUNNING TEST_PS3 --"; + foreach (auto test, ps3Tests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_PLATFORMS || TEST_WII) { + qDebug() << "-- RUNNING TEST_WII --"; + foreach (auto test, wiiTests) { + QTest::qExec(test, argc, argv); + } + } + if (TEST_EVERYTHING || TEST_ALL_PLATFORMS || TEST_WIIU) { + qDebug() << "-- RUNNING TEST_WIIU --"; + foreach (auto test, wiiUTests) { + QTest::qExec(test, argc, argv); + } + } + + return 0; +} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..f47aa91 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,87 @@ +TEMPLATE = app +CONFIG += no_main + +# Enable the testlib module +QT += testlib concurrent + +# Define a test-specific flag +DEFINES += QT_TESTS + +TARGET = tests + +# List all test source files +SOURCES += \ + # 360 autotests + 360/autotest_cod2_360.cpp \ + 360/autotest_cod4_360.cpp \ + 360/autotest_cod5_360.cpp \ + 360/autotest_cod6_360.cpp \ + 360/autotest_cod7_360.cpp \ + 360/autotest_cod8_360.cpp \ + 360/autotest_cod9_360.cpp \ + 360/autotest_cod10_360.cpp \ + 360/autotest_cod11_360.cpp \ + 360/autotest_cod12_360.cpp \ + # PC autotests + PC/autotest_cod4_pc.cpp \ + PC/autotest_cod5_pc.cpp \ + PC/autotest_cod6_pc.cpp \ + PC/autotest_cod7_pc.cpp \ + PC/autotest_cod8_pc.cpp \ + PC/autotest_cod9_pc.cpp \ + PC/autotest_cod10_pc.cpp \ + PC/autotest_cod11_pc.cpp \ + PC/autotest_cod12_pc.cpp \ + # PS3 autotests + PS3/autotest_cod4_ps3.cpp \ + PS3/autotest_cod5_ps3.cpp \ + PS3/autotest_cod6_ps3.cpp \ + PS3/autotest_cod7_ps3.cpp \ + PS3/autotest_cod8_ps3.cpp \ + PS3/autotest_cod9_ps3.cpp \ + PS3/autotest_cod10_ps3.cpp \ + PS3/autotest_cod11_ps3.cpp \ + PS3/autotest_cod12_ps3.cpp \ + # Wii autotests + Wii/autotest_cod7_wii.cpp \ + # WiiU autotests + WiiU/autotest_cod9_wiiu.cpp \ + WiiU/autotest_cod10_wiiu.cpp \ + # XPlor autotests + autotest_xplor.cpp \ + # Main test function + test_main.cpp + +HEADERS += \ + autotest_cod.h + + +# Prevent tests from being built in release mode (optional) +# CONFIG(debug, debug|release) { +# message("Including test files in Debug mode") +# } else { +# SOURCES -= autotest_cod5.cpp +# } + +app.depends += \ + libs/core \ + libs/compression \ + libs/encryption \ + libs/fastfile + +LIBS += \ + -L$$OUT_PWD/../libs/ -lcore -lencryption -lcompression -lfastfile + +INCLUDEPATH += \ + $$PWD/../libs/core \ + $$PWD/../libs/encryption \ + $$PWD/../libs/compression \ + $$PWD/../libs/fastfile + +DEPENDPATH += \ + $$PWD/../libs/core \ + $$PWD/../libs/encryption \ + $$PWD/../libs/compression \ + $$PWD/../libs/fastfile + +RESOURCES += diff --git a/DevILSDK/include/IL/DevIL.i b/third_party/devil_sdk/include/IL/DevIL.i similarity index 100% rename from DevILSDK/include/IL/DevIL.i rename to third_party/devil_sdk/include/IL/DevIL.i diff --git a/DevILSDK/include/IL/build-lua b/third_party/devil_sdk/include/IL/build-lua similarity index 100% rename from DevILSDK/include/IL/build-lua rename to third_party/devil_sdk/include/IL/build-lua diff --git a/DevILSDK/include/IL/build-python b/third_party/devil_sdk/include/IL/build-python similarity index 100% rename from DevILSDK/include/IL/build-python rename to third_party/devil_sdk/include/IL/build-python diff --git a/DevILSDK/include/IL/config.h.win b/third_party/devil_sdk/include/IL/config.h.win similarity index 100% rename from DevILSDK/include/IL/config.h.win rename to third_party/devil_sdk/include/IL/config.h.win diff --git a/DevILSDK/include/IL/devil_cpp_wrapper.hpp b/third_party/devil_sdk/include/IL/devil_cpp_wrapper.hpp similarity index 100% rename from DevILSDK/include/IL/devil_cpp_wrapper.hpp rename to third_party/devil_sdk/include/IL/devil_cpp_wrapper.hpp diff --git a/DevILSDK/include/IL/devil_internal_exports.h b/third_party/devil_sdk/include/IL/devil_internal_exports.h similarity index 100% rename from DevILSDK/include/IL/devil_internal_exports.h rename to third_party/devil_sdk/include/IL/devil_internal_exports.h diff --git a/DevILSDK/include/IL/il.h b/third_party/devil_sdk/include/IL/il.h similarity index 100% rename from DevILSDK/include/IL/il.h rename to third_party/devil_sdk/include/IL/il.h diff --git a/DevILSDK/include/IL/il_wrap.h b/third_party/devil_sdk/include/IL/il_wrap.h similarity index 100% rename from DevILSDK/include/IL/il_wrap.h rename to third_party/devil_sdk/include/IL/il_wrap.h diff --git a/DevILSDK/include/IL/ilu.h b/third_party/devil_sdk/include/IL/ilu.h similarity index 100% rename from DevILSDK/include/IL/ilu.h rename to third_party/devil_sdk/include/IL/ilu.h diff --git a/DevILSDK/include/IL/ilu_region.h b/third_party/devil_sdk/include/IL/ilu_region.h similarity index 100% rename from DevILSDK/include/IL/ilu_region.h rename to third_party/devil_sdk/include/IL/ilu_region.h diff --git a/DevILSDK/include/IL/ilut.h b/third_party/devil_sdk/include/IL/ilut.h similarity index 100% rename from DevILSDK/include/IL/ilut.h rename to third_party/devil_sdk/include/IL/ilut.h diff --git a/DevILSDK/include/IL/ilut_config.h b/third_party/devil_sdk/include/IL/ilut_config.h similarity index 100% rename from DevILSDK/include/IL/ilut_config.h rename to third_party/devil_sdk/include/IL/ilut_config.h diff --git a/DevILSDK/include/IL/luadevil.c b/third_party/devil_sdk/include/IL/luadevil.c similarity index 100% rename from DevILSDK/include/IL/luadevil.c rename to third_party/devil_sdk/include/IL/luadevil.c diff --git a/DevILSDK/include/IL/stamp-h.in b/third_party/devil_sdk/include/IL/stamp-h.in similarity index 100% rename from DevILSDK/include/IL/stamp-h.in rename to third_party/devil_sdk/include/IL/stamp-h.in diff --git a/DevILSDK/lib/x64/Release/DevIL.dll b/third_party/devil_sdk/lib/DevIL.dll similarity index 100% rename from DevILSDK/lib/x64/Release/DevIL.dll rename to third_party/devil_sdk/lib/DevIL.dll diff --git a/DevILSDK/lib/x64/Release/DevIL.lib b/third_party/devil_sdk/lib/DevIL.lib similarity index 100% rename from DevILSDK/lib/x64/Release/DevIL.lib rename to third_party/devil_sdk/lib/DevIL.lib diff --git a/DevILSDK/lib/x64/Release/ILU.dll b/third_party/devil_sdk/lib/ILU.dll similarity index 100% rename from DevILSDK/lib/x64/Release/ILU.dll rename to third_party/devil_sdk/lib/ILU.dll diff --git a/DevILSDK/lib/x64/Release/ILU.lib b/third_party/devil_sdk/lib/ILU.lib similarity index 100% rename from DevILSDK/lib/x64/Release/ILU.lib rename to third_party/devil_sdk/lib/ILU.lib diff --git a/DevILSDK/lib/x64/Release/ILUT.dll b/third_party/devil_sdk/lib/ILUT.dll similarity index 100% rename from DevILSDK/lib/x64/Release/ILUT.dll rename to third_party/devil_sdk/lib/ILUT.dll diff --git a/DevILSDK/lib/x64/Release/ILUT.lib b/third_party/devil_sdk/lib/ILUT.lib similarity index 100% rename from DevILSDK/lib/x64/Release/ILUT.lib rename to third_party/devil_sdk/lib/ILUT.lib diff --git a/third_party/zlib/include/deflate.h b/third_party/zlib/include/deflate.h new file mode 100644 index 0000000..a5d5acc --- /dev/null +++ b/third_party/zlib/include/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Byte_zf *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Byte_zf *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte_z method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Byte_zf *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/third_party/zlib/include/zconf.h b/third_party/zlib/include/zconf.h new file mode 100644 index 0000000..b9ad50d --- /dev/null +++ b/third_party/zlib/include/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte_z z_Byte_z +# define uInt z_uInt +# define uLong z_uLong +# define Byte_zf z_Byte_zf +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte_z; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Byte_zf Byte_z FAR +#else + typedef Byte_z FAR Byte_zf; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte_z const *voidpc; + typedef Byte_z FAR *voidpf; + typedef Byte_z *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/third_party/zlib/include/zlib.h b/third_party/zlib/include/zlib.h new file mode 100644 index 0000000..08759ed --- /dev/null +++ b/third_party/zlib/include/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Byte_zf *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Byte_zf *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Byte_zf *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Byte_zf *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Byte_zf *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Byte_zf *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Byte_zf *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Byte_zf *dest, uLongf *destLen, + const Byte_zf *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Byte_zf *dest, uLongf *destLen, + const Byte_zf *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Byte_zf *dest, uLongf *destLen, + const Byte_zf *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Byte_zf *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Byte_zf *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/third_party/zlib/include/zutil.h b/third_party/zlib/include/zutil.h new file mode 100644 index 0000000..6cc3fc1 --- /dev/null +++ b/third_party/zlib/include/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Byte_zf* dest, const Byte_zf* source, uInt len)); + extern int zmemcmp OF((const Byte_zf* s1, const Byte_zf* s2, uInt len)); + extern void zmemzero OF((Byte_zf* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/third_party/zlib/lib/zlib.lib b/third_party/zlib/lib/zlib.lib new file mode 100644 index 0000000..36a53de Binary files /dev/null and b/third_party/zlib/lib/zlib.lib differ diff --git a/xtreewidget.cpp b/xtreewidget.cpp deleted file mode 100644 index 691aba7..0000000 --- a/xtreewidget.cpp +++ /dev/null @@ -1,508 +0,0 @@ -#include "xtreewidget.h" -#include "qheaderview.h" -#include "qmenu.h" -#include "utils.h" - -XTreeWidget::XTreeWidget(QWidget *parent) - : QTreeWidget(parent) { - mFastFiles = QMap>(); - mZoneFiles = QMap>(); - mDDSFiles = QMap>(); - mIWIFiles = QMap>(); - - setContextMenuPolicy(Qt::CustomContextMenu); - setSelectionMode(QTreeWidget::SingleSelection); - setColumnCount(3); - setColumnWidth(0, 275); - setColumnWidth(1, 50); - setColumnWidth(2, 50); - header()->hide(); - setMinimumWidth(350); - - connect(this, &QTreeWidget::itemSelectionChanged, this, &XTreeWidget::ItemSelectionChanged); - connect(this, &XTreeWidget::customContextMenuRequested, this, &XTreeWidget::PrepareContextMenu); -} - -XTreeWidget::~XTreeWidget() { - -} - -void XTreeWidget::AddFastFile(std::shared_ptr aFastFile) { - QTreeWidgetItem *fastFileItem = new QTreeWidgetItem(this); - fastFileItem->setText(0, aFastFile->GetFileStem()); - fastFileItem->setIcon(0, QIcon(":/icons/icons/Icon_FastFile.png")); - if (aFastFile->GetPlatform() == "PC") { - fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_PC.png")); - } else if (aFastFile->GetPlatform() == "360") { - fastFileItem->setIcon(1, QIcon(":/icons/icons/Icon_Xbox.png")); - } - if (aFastFile->GetGame() == "COD4") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD4.png")); - } else if (aFastFile->GetGame() == "COD5") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD5.png")); - } else if (aFastFile->GetGame() == "COD6") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD6.png")); - } else if (aFastFile->GetGame() == "COD7") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD7.png")); - } else if (aFastFile->GetGame() == "COD8") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD8.png")); - } else if (aFastFile->GetGame() == "COD9") { - fastFileItem->setIcon(2, QIcon(":/icons/icons/Icon_COD9.png")); - } - - AddZoneFile(std::make_shared(aFastFile->GetZoneFile()), fastFileItem); - - mFastFiles[aFastFile->GetFileStem().section(".", 0, 0)] = aFastFile; - - resizeColumnToContents(1); -} - -void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetItem *aParentItem) { - QTreeWidgetItem *zoneItem; - if (aParentItem != nullptr) { - zoneItem = new QTreeWidgetItem(aParentItem); - } else { - zoneItem = new QTreeWidgetItem(this); - } - zoneItem->setIcon(0, QIcon(":/icons/icons/Icon_ZoneFile.png")); - zoneItem->setText(0, aZoneFile->GetFileStem()); - - auto assetMap = aZoneFile->GetAssetMap(); - - if (!assetMap.localStrings.isEmpty()) { - QTreeWidgetItem *localStrRoot = new QTreeWidgetItem(zoneItem); - localStrRoot->setText(0, "String Files"); - localStrRoot->setIcon(0, QIcon(":/icons/icons/Icon_StringFile.png")); - - QTreeWidgetItem *localStrItem = new QTreeWidgetItem(localStrRoot); - localStrItem->setText(0, aZoneFile->GetFileStem().section('.', 0, 0) + ".str"); - localStrItem->setIcon(0, QIcon(":/icons/icons/Icon_StringFile.png")); - } - - if (!assetMap.techSets.isEmpty()) { - QTreeWidgetItem *techSetRoot = new QTreeWidgetItem(zoneItem); - techSetRoot->setText(0, "Tech Sets"); - techSetRoot->setIcon(0, QIcon(":/icons/icons/Icon_TechSetFile.png")); - - for (TechSet techSet : assetMap.techSets) { - QTreeWidgetItem *techSetItem = new QTreeWidgetItem(techSetRoot); - techSetItem->setText(0, techSet.name); - techSetItem->setIcon(0, QIcon(":/icons/icons/Icon_TechSetFile.png")); - } - } - - if (!assetMap.rawFiles.isEmpty()) { - QTreeWidgetItem *rawFileRoot = new QTreeWidgetItem(zoneItem); - rawFileRoot->setText(0, "Raw Files"); - rawFileRoot->setIcon(0, QIcon(":/icons/icons/Icon_GSCFile.png")); - for (RawFile rawFile : assetMap.rawFiles) { - if (!rawFile.length) { continue; } - - QTreeWidgetItem *tempItem = rawFileRoot; - for (const QString &pathPart : rawFile.path.split('/')) { - bool childFound = false; - for (int i = 0; i < tempItem->childCount(); i++) { - QTreeWidgetItem *childItem = tempItem->child(i); - //qDebug() << "Child text: " << childItem->text(0); - if (childItem->text(0) == pathPart) { - tempItem = childItem; - - childFound = true; - break; - } - } - - if (pathPart.contains(".gsc")) { - QTreeWidgetItem *rawFileItem = new QTreeWidgetItem(tempItem); - rawFileItem->setText(0, pathPart); - - tempItem = rawFileItem; - } else if (!childFound) { - tempItem = new QTreeWidgetItem(tempItem); - tempItem->setText(0, pathPart); - } - - } - tempItem->setIcon(0, QIcon(":/icons/icons/Icon_GSCFile.png")); - } - } - - if (!assetMap.menuFiles.isEmpty()) { - QTreeWidgetItem *menuRoot = new QTreeWidgetItem(zoneItem); - menuRoot->setText(0, "Menu Files"); - menuRoot->setIcon(0, QIcon(":/icons/icons/Icon_MenuFile.png")); - - int menuIndex = 1; - for (MenuFile menuFile : assetMap.menuFiles) { - QTreeWidgetItem *menuFileRoot = new QTreeWidgetItem(menuRoot); - menuFileRoot->setText(0, QString("Menu %1").arg(menuIndex)); - for (Menu menu : menuFile.menuDefs) { - QTreeWidgetItem *menuItem = new QTreeWidgetItem(menuFileRoot); - menuItem->setText(0, menu.name); - menuItem->setIcon(0, QIcon(":/icons/icons/Icon_MenuFile.png")); - } - menuIndex++; - } - } - - if (!assetMap.images.isEmpty()) { - QTreeWidgetItem *imageRoot = new QTreeWidgetItem(zoneItem); - imageRoot->setText(0, "Images"); - imageRoot->setIcon(0, QIcon(":/icons/icons/Icon_Image.png")); - - for (Image image : assetMap.images) { - QTreeWidgetItem *imageItem = new QTreeWidgetItem(imageRoot); - imageItem->setText(0, image.materialName); - imageItem->setIcon(0, QIcon(":/icons/icons/Icon_Image.png")); - } - } - - mZoneFiles[aZoneFile->GetFileStem().section(".", 0, 0)] = aZoneFile; -} - -void XTreeWidget::PrepareContextMenu(const QPoint &pos) { - auto activeItem = itemAt(pos); - if (!activeItem) { return; } - if (activeItem->text(0).isEmpty()) { return; } - - QString activeText = activeItem->text(0); - QMenu *contextMenu = new QMenu(this); - - if (activeText.contains(".dds")) { - const QString fileStem = activeText.replace(".dds", ""); - if (!mDDSFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in DDS map!"; - return; - } - - QAction *closeAction = new QAction("Close File"); - contextMenu->addAction(closeAction); - connect(closeAction, &QAction::triggered, this, [this, &fileStem, &activeItem](bool checked) { - Q_UNUSED(checked); - - mDDSFiles.remove(fileStem); - invisibleRootItem()->removeChild(activeItem); - }); - - QMenu *exportSubmenu = new QMenu("Export...", this); - contextMenu->addMenu(exportSubmenu); - - std::shared_ptr ddsFile = mDDSFiles[fileStem]; - - QAction *exportIWIAction = new QAction("Export as IWI"); - exportSubmenu->addAction(exportIWIAction); - connect(exportIWIAction, &QAction::triggered, this, [ddsFile](bool checked) { - Q_UNUSED(checked); - - ddsFile->SaveIWI(); - }); - - QAction *exportPNGAction = new QAction("Export as PNG"); - exportSubmenu->addAction(exportPNGAction); - connect(exportPNGAction, &QAction::triggered, this, [ddsFile](bool checked) { - Q_UNUSED(checked); - - ddsFile->SavePNG(); - }); - - QAction *exportJPGAction = new QAction("Export as JPG"); - exportSubmenu->addAction(exportJPGAction); - connect(exportJPGAction, &QAction::triggered, this, [ddsFile](bool checked) { - Q_UNUSED(checked); - - ddsFile->SaveJPG(); - }); - } else if (activeText.contains(".iwi")) { - const QString fileStem = activeText.replace(".iwi", ""); - if (!mIWIFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in IWI map!"; - return; - } - - QAction *closeAction = new QAction("Close File"); - contextMenu->addAction(closeAction); - connect(closeAction, &QAction::triggered, this, [this, &fileStem, &activeItem](bool checked) { - Q_UNUSED(checked); - - mIWIFiles.remove(fileStem); - invisibleRootItem()->removeChild(activeItem); - }); - - QMenu *exportSubmenu = new QMenu("Export...", this); - contextMenu->addMenu(exportSubmenu); - - std::shared_ptr iwiFile = mIWIFiles[fileStem]; - - QAction *exportDDSAction = new QAction("Export as DDS"); - exportSubmenu->addAction(exportDDSAction); - connect(exportDDSAction, &QAction::triggered, this, [iwiFile](bool checked) { - Q_UNUSED(checked); - - iwiFile->SaveDDS(); - }); - - QAction *exportPNGAction = new QAction("Export as PNG"); - exportSubmenu->addAction(exportPNGAction); - connect(exportPNGAction, &QAction::triggered, this, [iwiFile](bool checked) { - Q_UNUSED(checked); - - iwiFile->SavePNG(); - }); - - QAction *exportJPGAction = new QAction("Export as JPG"); - exportSubmenu->addAction(exportJPGAction); - connect(exportJPGAction, &QAction::triggered, this, [iwiFile](bool checked) { - Q_UNUSED(checked); - - iwiFile->SaveJPG(); - }); - } else if (activeText.contains(".ff")) { - QMenu *closeMultipleAction = new QMenu("Close Multiple Tabs"); - - QAction *closeAllAction = new QAction("Close All"); - closeMultipleAction->addAction(closeAllAction); - connect(closeAllAction, &QAction::triggered, this, [this](bool checked) { - Q_UNUSED(checked); - - clear(); - }); - - QAction *closeAllButAction = new QAction("Close All BUT This"); - closeMultipleAction->addAction(closeAllButAction); - connect(closeAllButAction, &QAction::triggered, this, [this, &activeItem](bool checked) { - Q_UNUSED(checked); - - for (int i = 0; i < invisibleRootItem()->childCount(); i++) { - auto childItem = invisibleRootItem()->child(i); - if (childItem == activeItem) { continue; } - invisibleRootItem()->removeChild(childItem); - i--; - } - }); - - QAction *closeAboveAction = new QAction("Close All Above"); - closeMultipleAction->addAction(closeAboveAction); - connect(closeAboveAction, &QAction::triggered, this, [this, &activeItem](bool checked) { - Q_UNUSED(checked); - - for (int i = 0; i < invisibleRootItem()->childCount(); i++) { - auto childItem = invisibleRootItem()->child(i); - if (childItem == activeItem) { return; } - invisibleRootItem()->removeChild(childItem); - i--; - } - }); - - QAction *closeBelowAction = new QAction("Close All Below"); - closeMultipleAction->addAction(closeBelowAction); - connect(closeBelowAction, &QAction::triggered, this, [this, &activeItem](bool checked) { - Q_UNUSED(checked); - - bool ready = false; - for (int i = 0; i < invisibleRootItem()->childCount(); i++) { - auto childItem = invisibleRootItem()->child(i); - if (!ready && (childItem != activeItem)) { continue; } - if (childItem == activeItem) { - ready = true; - continue; - } - invisibleRootItem()->removeChild(childItem); - i--; - } - }); - - contextMenu->addMenu(closeMultipleAction); - const QString fileStem = activeText.replace(".ff", ""); - if (!mFastFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Fast File map!"; - return; - } - - QAction *closeAction = new QAction("Close File"); - contextMenu->addAction(closeAction); - connect(closeAction, &QAction::triggered, this, [this, &fileStem, &activeItem](bool checked) { - Q_UNUSED(checked); - - mFastFiles.remove(fileStem); - invisibleRootItem()->removeChild(activeItem); - }); - - QMenu *exportSubmenu = new QMenu("Export...", this); - contextMenu->addMenu(exportSubmenu); - - std::shared_ptr fastFile = mFastFiles[fileStem]; - - QAction *exportDDSAction = new QAction("Export as Zone File"); - exportSubmenu->addAction(exportDDSAction); - connect(exportDDSAction, &QAction::triggered, this, [fastFile](bool checked) { - Q_UNUSED(checked); - - //fastFile->SaveZone(); - }); - } else if (activeText.contains(".zone")) { - const QString fileStem = activeText.replace(".zone", ""); - if (!mZoneFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Zone File map!"; - return; - } - - QMenu *exportSubmenu = new QMenu("Export...", this); - contextMenu->addMenu(exportSubmenu); - - std::shared_ptr zoneFile = mZoneFiles[fileStem]; - - QAction *exportDDSAction = new QAction("Export as Fast File"); - exportSubmenu->addAction(exportDDSAction); - connect(exportDDSAction, &QAction::triggered, this, [zoneFile](bool checked) { - Q_UNUSED(checked); - - //zoneFile->SaveFastFile(); - }); - } - - QPoint pt(pos); - contextMenu->exec(mapToGlobal(pt)); - - delete contextMenu; -} - -void XTreeWidget::ItemSelectionChanged() { - if (selectedItems().isEmpty()) { return; } - - QTreeWidgetItem *selectedItem = selectedItems().first(); - if (!selectedItem) { return; } - if (selectedItem->text(0).isEmpty()) { return; } - QString selectedText = selectedItem->text(0); - const QString fileStem = selectedText.section(".", 0, 0); - - QTreeWidgetItem *parentItem = selectedItem->parent(); - - if (selectedText.contains(".dds")) { - if (!mDDSFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in DDS map!"; - return; - } - std::shared_ptr ddsFile = mDDSFiles[fileStem]; - emit DDSFileSelected(ddsFile); - } else if (selectedText.contains(".iwi")) { - if (!mIWIFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in IWI map!"; - return; - } - emit IWIFileSelected(mIWIFiles[fileStem]); - } else if (selectedText.contains(".ff")) { - if (!mFastFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Fast File map!"; - return; - } - emit FastFileSelected(mFastFiles[fileStem]); - } else if (selectedText.contains(".zone")) { - if (!mZoneFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Zone File map!"; - return; - } - emit ZoneFileSelected(mZoneFiles[fileStem]); - } else if (selectedText.contains(".str")) { - if (!mZoneFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Zone File map!"; - return; - } - emit LocalStringSelected(mZoneFiles[fileStem]); - } else if (selectedText.contains(".gsc")) { - QTreeWidgetItem *zoneRoot = selectedItem; - if (!zoneRoot) { return; } - - while (!zoneRoot->text(0).contains(".zone")) { - zoneRoot = zoneRoot->parent(); - if (!zoneRoot) { return; } - } - - const QString fileStem = zoneRoot->text(0).section('.', 0, 0); - if (!mZoneFiles.contains(fileStem)) { - qDebug() << "Error: Could not find " << fileStem << " in Zone File map!"; - return; - } - - QVector rawFiles = mZoneFiles[fileStem]->GetAssetMap().rawFiles; - for (RawFile rawFile : rawFiles) { - if (rawFile.path.contains(selectedText)) { - emit RawFileSelected(std::make_shared(rawFile)); - return; - } - } - } else if (parentItem && (parentItem->text(0) == "Images")) { - QTreeWidgetItem *grandpaItem = parentItem->parent(); - if (grandpaItem && grandpaItem->text(0).contains(".zone")) { - const QString fileStem = grandpaItem->text(0).section('.', 0, 0); - QVector images = mZoneFiles[fileStem]->GetAssetMap().images; - for (Image image : images) { - if (image.materialName == selectedText) { - emit ImageSelected(std::make_shared(image)); - break; - } - } - } - } else if (parentItem && (parentItem->text(0) == "Tech Sets")) { - QTreeWidgetItem *grandpaItem = parentItem->parent(); - if (grandpaItem && grandpaItem->text(0).contains(".zone")) { - const QString fileStem = grandpaItem->text(0).section('.', 0, 0); - auto techsets = mZoneFiles[fileStem]->GetAssetMap().techSets; - for (auto techset : techsets) { - if (techset.name == selectedText) { - emit TechSetSelected(std::make_shared(techset)); - break; - } - } - } - } -} - -ZoneFile XTreeWidget::pFindZoneFile(const QString aFilePart) { - foreach (auto zoneFile, mZoneFiles) { - if (zoneFile->GetFileStem() == aFilePart) { - return *zoneFile.get(); - } - } - return ZoneFile(); -} - -FastFile XTreeWidget::pFindFastFile(const QString aFilePart) { - foreach (auto fastFile, mFastFiles) { - if (fastFile->GetFileStem() == aFilePart) { - return *fastFile.get(); - } - } - return FastFile(); -} - -void XTreeWidget::AddIWIFile(std::shared_ptr aIWIFile) { - const QString iwiFileName = QString(aIWIFile->fileStem + ".iwi"); - - for (int i = 0; i < invisibleRootItem()->childCount(); i++) { - auto iwiFileItem = invisibleRootItem()->child(i); - if (iwiFileItem->text(0) == iwiFileName) { - delete iwiFileItem; - } - } - - QTreeWidgetItem *iwiItem = new QTreeWidgetItem(this); - iwiItem->setIcon(0, QIcon(":/icons/icons/Icon_IWIFile.png")); - iwiItem->setText(0, iwiFileName); - mIWIFiles[aIWIFile->fileStem.section(".", 0, 0)] = aIWIFile; -} - -void XTreeWidget::AddDDSFile(std::shared_ptr aDDSFile) { - const QString ddsFileName = QString(aDDSFile->fileStem + ".dds"); - - for (int i = 0; i < invisibleRootItem()->childCount(); i++) { - auto ddsFileItem = invisibleRootItem()->child(i); - if (ddsFileItem->text(0) == ddsFileName) { - delete ddsFileItem; - } - } - - QTreeWidgetItem *ddsItem = new QTreeWidgetItem(this); - ddsItem->setIcon(0, QIcon(":/icons/icons/Icon_DDSFile.png")); - ddsItem->setText(0, ddsFileName); - mDDSFiles[aDDSFile->fileStem.section(".", 0, 0)] = aDDSFile; -} diff --git a/xtreewidget.h b/xtreewidget.h deleted file mode 100644 index 0d49910..0000000 --- a/xtreewidget.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef XTREEWIDGET_H -#define XTREEWIDGET_H - -#include "d3dbsp_structs.h" -#include "asset_structs.h" -#include "ddsfile.h" -#include "iwifile.h" -#include "fastfile.h" -#include "zonefile.h" - -#include - -class XTreeWidget : public QTreeWidget -{ - Q_OBJECT -public: - explicit XTreeWidget(QWidget *parent = nullptr); - ~XTreeWidget(); - - void AddFastFile(std::shared_ptr aFastFile); - void AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetItem *aParentItem = nullptr); - void AddIWIFile(std::shared_ptr aIWIFile); - void AddDDSFile(std::shared_ptr aDDSFile); - -signals: - void DDSFileSelected(std::shared_ptr aDDSFile); - void IWIFileSelected(std::shared_ptr aIWIFile); - void FastFileSelected(std::shared_ptr aFastFile); - void ZoneFileSelected(std::shared_ptr aZoneFile); - void LocalStringSelected(std::shared_ptr aZoneFile); - void RawFileSelected(std::shared_ptr aRawFile); - void ImageSelected(std::shared_ptr aImage); - void TechSetSelected(std::shared_ptr aZoneFile); - void MenuSelected(std::shared_ptr aMenu); - -protected: - void ItemSelectionChanged(); - void PrepareContextMenu(const QPoint &pos); - -private: - QMap> mFastFiles; - QMap> mZoneFiles; - QMap> mDDSFiles; - QMap> mIWIFiles; - - ZoneFile pFindZoneFile(const QString aFilePart); - FastFile pFindFastFile(const QString aFilePart); -}; - -#endif // XTREEWIDGET_H diff --git a/zonefile.h b/zonefile.h deleted file mode 100644 index 93d8cb7..0000000 --- a/zonefile.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ZONEFILE_H -#define ZONEFILE_H - -#include "asset_structs.h" - -#include - -class ZoneFile -{ -public: - ZoneFile(); - ~ZoneFile(); - ZoneFile(const ZoneFile &aZoneFile); - ZoneFile &operator=(const ZoneFile &other); - - bool Load(const QString aFilePath, FF_PLATFORM platform = FF_PLATFORM_PC); - bool Load(const QByteArray aFileData, const QString aFileStem, FF_PLATFORM platform = FF_PLATFORM_PC); - - QString GetFileStem(); - quint32 GetSize(); - quint32 GetTagCount(); - QStringList GetTags(); - quint32 GetRecordCount(); - QStringList GetRecords(); - AssetMap GetAssetMap(); - -private slots: - void pParseZoneHeader(QDataStream *aZoneFileStream); - quint32 pParseZoneSize(QDataStream *aZoneFileStream); - void pParseZoneUnknownsA(QDataStream *aZoneFileStream); - quint32 pParseZoneTagCount(QDataStream *aZoneFileStream); - quint32 pParseZoneRecordCount(QDataStream *aZoneFileStream); - void pParseZoneUnknownsB(QDataStream *aZoneFileStream); - void pParseZoneUnknownsC(QDataStream *aZoneFileStream); - QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount); - QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount); - AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder); - LocalString pParseAsset_LocalString(QDataStream *aZoneFileStream); - RawFile pParseAsset_RawFile(QDataStream *aZoneFileStream); - void pParseAsset_PhysPreset(QDataStream *aZoneFileStream); - Model pParseAsset_Model(QDataStream *aZoneFileStream); - void pParseAsset_Material(QDataStream *aZoneFileStream); - Shader pParseAsset_Shader(QDataStream *aZoneFileStream); - bool pReadUntilString(QDataStream* stream, const QString& targetString); - bool pReadUntilHex(QDataStream* stream, const QString& hexString); - TechSet pParseAsset_TechSet(QDataStream *aZoneFileStream); - Image pParseAsset_Image(QDataStream *aZoneFileStream); - void pParseAsset_LoadedSound(QDataStream *aZoneFileStream); - void pParseAsset_ColMapMP(QDataStream *aZoneFileStream); - void pParseAsset_GameMapSP(QDataStream *aZoneFileStream); - void pParseAsset_GameMapMP(QDataStream *aZoneFileStream); - void pParseAsset_LightDef(QDataStream *aZoneFileStream); - void pParseAsset_UIMap(QDataStream *aZoneFileStream); - void pParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream); - void pParseAsset_AIType(QDataStream *aZoneFileStream); - void pParseAsset_FX(QDataStream *aZoneFileStream); - Animation pParseAsset_Animation(QDataStream *aZoneFileStream); - MenuFile pParseAsset_MenuFile(QDataStream *aZoneFileStream); - void pParseAsset_Weapon(QDataStream *aZoneFileStream); - void pParseAsset_D3DBSP(QDataStream *aZoneFileStream); - StringTable pParseAsset_StringTable(QDataStream *aZoneFileStream); - -private: - QString fileStem; - quint32 size; - quint32 tagCount; - QStringList tags; - quint32 recordCount; - QStringList records; - AssetMap assetMap; - QString platform; - QString game; -}; - -#endif // ZONEFILE_H diff --git a/zonefileviewer.ui b/zonefileviewer.ui deleted file mode 100644 index dbc14ad..0000000 --- a/zonefileviewer.ui +++ /dev/null @@ -1,140 +0,0 @@ - - - ZoneFileViewer - - - - 0 - 0 - 942 - 445 - - - - - 942 - 445 - - - - Form - - - - - - - Roboto - 16 - true - - - - ZoneFile 0 - - - - - - - - - - - - 225 - 0 - - - - - Roboto - 9 - - - - Tags - - - - - - - - Search: - - - - - - - Search tags... - - - - - - - - - - - - - - - - 179 - 0 - - - - Record Counts - - - - - - - - - - - - - 179 - 0 - - - - Record Order - - - - - - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - - -