diff --git a/Data.qrc b/Data.qrc index 0325361..0372e66 100644 --- a/Data.qrc +++ b/Data.qrc @@ -21,4 +21,16 @@ data/d3dbsp/asset_viewer.d3dbsp data/d3dbsp/barebones.d3dbsp + + data/images/XPlor.png + data/images/copy.svg + data/images/cut.svg + data/images/new_file.svg + data/images/open_file.svg + data/images/open_folder.svg + data/images/paste.svg + data/images/refresh.svg + data/images/save.svg + data/images/multiple.png + diff --git a/FastFile_WaW.pro b/FastFile_WaW.pro index c155886..2ebf94b 100644 --- a/FastFile_WaW.pro +++ b/FastFile_WaW.pro @@ -1,6 +1,7 @@ QT += core gui 3dcore 3drender 3dinput 3dextras greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +RC_ICONS = XPlor.ico CONFIG += c++17 @@ -13,16 +14,24 @@ CONFIG += c++17 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + aboutdialog.cpp \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + modelviewer.cpp HEADERS += \ + aboutdialog.h \ + compression.h \ enums.h \ + ffparser.h \ mainwindow.h \ + modelviewer.h \ structs.h \ - utils.h + utils.h \ + zfparser.h FORMS += \ + aboutdialog.ui \ mainwindow.ui # Default rules for deployment. diff --git a/FastFile_WaW.pro.user b/FastFile_WaW.pro.user index 4e96dc1..7fd21e9 100644 --- a/FastFile_WaW.pro.user +++ b/FastFile_WaW.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -244,10 +244,10 @@ false -e cpu-cycles --call-graph "dwarf,4096" -F 250 - + FastFile_WaW2 Qt4ProjectManager.Qt4RunConfiguration: - C:/Users/njohnson/Projects/FastFile_WaW/FastFile_WaW.pro - false + C:/Users/njohnson/Projects/XPlor/FastFile_WaW.pro + true true true true diff --git a/XPlor.ico b/XPlor.ico new file mode 100644 index 0000000..68c58cd Binary files /dev/null and b/XPlor.ico differ diff --git a/aboutdialog.cpp b/aboutdialog.cpp new file mode 100644 index 0000000..c3ba1b4 --- /dev/null +++ b/aboutdialog.cpp @@ -0,0 +1,14 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" + +AboutDialog::AboutDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::AboutDialog) +{ + ui->setupUi(this); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} diff --git a/aboutdialog.h b/aboutdialog.h new file mode 100644 index 0000000..4474ece --- /dev/null +++ b/aboutdialog.h @@ -0,0 +1,22 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = nullptr); + ~AboutDialog(); + +private: + Ui::AboutDialog *ui; +}; + +#endif // ABOUTDIALOG_H diff --git a/aboutdialog.ui b/aboutdialog.ui new file mode 100644 index 0000000..27665f5 --- /dev/null +++ b/aboutdialog.ui @@ -0,0 +1,241 @@ + + + AboutDialog + + + + 0 + 0 + 350 + 200 + + + + + 350 + 200 + + + + + 350 + 200 + + + + About XPlor + + + + + + + + + 80 + 80 + + + + + 80 + 80 + + + + + + + :/images/data/images/XPlor.png + + + true + + + Qt::AlignmentFlag::AlignCenter + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + Roboto + 16 + false + + + + XPlor v1.5 + + + + + + + + 0 + 0 + + + + + Roboto + + + + Copyright © 2024 RedLine Solutions LLC + + + + + + + + 0 + 0 + + + + + Roboto + + + + For more, check out redline.llc + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + + Roboto + + + + With Help From: + + + + + + + + 0 + 0 + + + + + Roboto + + + + - Paging Red + + + + + + + + 0 + 0 + + + + + Roboto + + + + - ISOCheated + + + + + + + + 0 + 0 + + + + + Roboto + + + + - SureShotIan + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + diff --git a/compression.h b/compression.h new file mode 100644 index 0000000..7f97f83 --- /dev/null +++ b/compression.h @@ -0,0 +1,34 @@ +#ifndef COMPRESSION_H +#define COMPRESSION_H + +#include "utils.h" +#include "QtZlib/zlib.h" + +#include +#include + +class Compressor { +public: + static QByteArray DecompressZLIB(QByteArray compressedData) { + QByteArray decompressedData; + uLongf decompressedSize = compressedData.size() * 4; + decompressedData.resize(static_cast(decompressedSize)); + + Bytef *destination = reinterpret_cast(decompressedData.data()); + uLongf *destLen = &decompressedSize; + const Bytef *source = reinterpret_cast(compressedData.data()); + uLong sourceLen = compressedData.size(); + + int result = uncompress(destination, destLen, source, sourceLen); + + if (result == Z_OK) { + decompressedData.resize(static_cast(decompressedSize)); + } else { + decompressedData.clear(); + qDebug() << QString("In DecompressZLIB: %1").arg(Utils::ZLibErrorToString(result)).toLatin1(); + } + return decompressedData; + } +}; + +#endif // COMPRESSION_H diff --git a/data/images/XPlor.png b/data/images/XPlor.png new file mode 100644 index 0000000..babe14d Binary files /dev/null and b/data/images/XPlor.png differ diff --git a/data/images/copy.svg b/data/images/copy.svg new file mode 100644 index 0000000..f09d902 --- /dev/null +++ b/data/images/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/cut.svg b/data/images/cut.svg new file mode 100644 index 0000000..c563fb2 --- /dev/null +++ b/data/images/cut.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/multiple.png b/data/images/multiple.png new file mode 100644 index 0000000..a18bbb4 Binary files /dev/null and b/data/images/multiple.png differ diff --git a/data/images/new_file.svg b/data/images/new_file.svg new file mode 100644 index 0000000..7c19795 --- /dev/null +++ b/data/images/new_file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/open_file.svg b/data/images/open_file.svg new file mode 100644 index 0000000..ee0ee85 --- /dev/null +++ b/data/images/open_file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/open_folder.svg b/data/images/open_folder.svg new file mode 100644 index 0000000..3e7b372 --- /dev/null +++ b/data/images/open_folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/paste.svg b/data/images/paste.svg new file mode 100644 index 0000000..bb4e796 --- /dev/null +++ b/data/images/paste.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/refresh.svg b/data/images/refresh.svg new file mode 100644 index 0000000..4e6d30c --- /dev/null +++ b/data/images/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/images/save.svg b/data/images/save.svg new file mode 100644 index 0000000..4f76105 --- /dev/null +++ b/data/images/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/enums.h b/enums.h index 415bf63..46f68fc 100644 --- a/enums.h +++ b/enums.h @@ -98,4 +98,23 @@ enum BSPVERSION_TYPE 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 +}; + #endif // ENUMS_H diff --git a/ffparser.h b/ffparser.h new file mode 100644 index 0000000..03056e2 --- /dev/null +++ b/ffparser.h @@ -0,0 +1,103 @@ +#ifndef FFPARSER_H +#define FFPARSER_H + +#include "enums.h" +#include "structs.h" + +#include +#include + +class FastFileParser { +public: + static FF_COMPANY ParseFFCompany(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; + } + + static FF_FILETYPE ParseFFFileType(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; + } + + static FF_SIGNAGE ParseFFSignage(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") { + qDebug() << "Found valid signage: Signed"; + return SIGNAGE_SIGNED; + } else { + qDebug() << "Failed to determine signage of fastfile!"; + } + return SIGNAGE_NONE; + } + + static QString ParseFFMagic(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 ""; + } + + static quint32 ParseFFVersion(QDataStream *afastFileStream) { + // Parse version + quint32 version; + *afastFileStream >> version; + qDebug() << "Version:" << version; + if (version == 387) { + qDebug() << QString("Found valid version: '%1'").arg(version); + return 387; + } else { + qDebug() << "Version invalid!"; + } + return -1; + } + + static FastFile ParseFFHeader(QDataStream *afastFileStream) { + FastFile fastFile; + fastFile.company = ParseFFCompany(afastFileStream); + fastFile.fileType = ParseFFFileType(afastFileStream); + fastFile.signage = ParseFFSignage(afastFileStream); + fastFile.magic = ParseFFMagic(afastFileStream); + fastFile.version = ParseFFVersion(afastFileStream); + return fastFile; + } +}; + +#endif // FFPARSER_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 8cd2ff6..0555716 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,4 +1,5 @@ #include "mainwindow.h" +#include "qheaderview.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) @@ -15,98 +16,37 @@ MainWindow::MainWindow(QWidget *parent) mDiskLumpCount = 0; mDiskLumpOrder = QVector(); mLumps = QMap(); + mTreeWidget = new QTreeWidget(); + mRootItem = mTreeWidget->invisibleRootItem(); + mScriptEditor = new QPlainTextEdit(); + mModelViewer = new ModelViewer(); - connect(ui->treeWidget_Scripts, &QTreeWidget::itemSelectionChanged, this, &MainWindow::ScriptSelected); - connect(ui->comboBox_StringTable, &QComboBox::currentTextChanged, this, &MainWindow::StrTableSelected); + // Connect Help > About dialog + connect(ui->actionAbout, &QAction::triggered, this, [this](bool checked) { + Q_UNUSED(checked); - // Initialize Asset Index Table - ui->tableWidget_Index->setColumnCount(3); - ui->tableWidget_Index->setHorizontalHeaderLabels({"Asset Type", "Asset Name", "Asset Count"}); - ui->tableWidget_Index->verticalHeader()->setVisible(false); - ui->tableWidget_Index->setEditTriggers(QAbstractItemView::NoEditTriggers); - ui->tableWidget_Index->setSelectionBehavior(QAbstractItemView::SelectRows); - ui->tableWidget_Index->setSelectionMode(QAbstractItemView::SingleSelection); - ui->tableWidget_Index->setShowGrid(false); - ui->tableWidget_Index->setStyleSheet("QTableView {selection-background-color: red;}"); + AboutDialog *aboutDialog = new AboutDialog(this); + aboutDialog->exec(); - // Initialize Asset Order Table - ui->tableWidget_Order->setColumnCount(3); - ui->tableWidget_Order->setHorizontalHeaderLabels({"Asset Type", "Asset Name", "Asset Count"}); - ui->tableWidget_Order->verticalHeader()->setVisible(false); - ui->tableWidget_Order->setEditTriggers(QAbstractItemView::NoEditTriggers); - ui->tableWidget_Order->setSelectionBehavior(QAbstractItemView::SelectRows); - ui->tableWidget_Order->setSelectionMode(QAbstractItemView::SingleSelection); - ui->tableWidget_Order->setShowGrid(false); - ui->tableWidget_Order->setStyleSheet("QTableView {selection-background-color: red;}"); + delete aboutDialog; + }); - Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow(); - view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x4d4d4f))); + connect(ui->actionOpen_Fast_File, &QAction::triggered, this, [this](bool checked) { + Q_UNUSED(checked); + OpenFastFile(); + }); + connect(ui->actionOpen_Zone_File, &QAction::triggered, this, [this](bool checked) { + Q_UNUSED(checked); + OpenZoneFile(); + }); - QWidget *container = QWidget::createWindowContainer(view); - QSize screenSize = view->screen()->size(); - container->setMinimumSize(QSize(200, 100)); - container->setMaximumSize(screenSize); - QHBoxLayout *hLayout = new QHBoxLayout(ui->frame_Scene); - QVBoxLayout *vLayout = new QVBoxLayout(); - vLayout->setAlignment(Qt::AlignTop); - hLayout->addWidget(container, 1); - hLayout->addLayout(vLayout); + QDockWidget *treeDockWidget = new QDockWidget(this); + mTreeWidget->header()->hide(); + treeDockWidget->setWidget(mTreeWidget); + addDockWidget(Qt::LeftDockWidgetArea, treeDockWidget); - // 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); + setCentralWidget(mScriptEditor); LoadFile_D3DBSP(":/d3dbsp/data/d3dbsp/barebones.d3dbsp"); @@ -118,72 +58,8 @@ MainWindow::~MainWindow() { } void MainWindow::Reset() { - // Reset tabwidget to 'General' tab - ui->tabWidget->setCurrentIndex(0); - - // Reset 'General' tab fields - ui->lineEdit_FastFile->clear(); - ui->comboBox_Company->setCurrentIndex(0); - ui->comboBox_FileType->setCurrentIndex(0); - ui->checkBox_Signed->setChecked(false); - ui->lineEdit_Magic->clear(); - ui->spinBox_Magic->clear(); - ui->spinBox_TagCount->clear(); - ui->spinBox_FileSize->clear(); - ui->spinBox_RecordCount->clear(); - - // Reset 'Unknowns' tab fields - ui->lineEdit_U1->clear(); - ui->spinBox_U1->clear(); - ui->lineEdit_U2->clear(); - ui->spinBox_U2->clear(); - ui->lineEdit_U3->clear(); - ui->spinBox_U3->clear(); - ui->lineEdit_U4->clear(); - ui->spinBox_U4->clear(); - ui->lineEdit_U5->clear(); - ui->spinBox_U5->clear(); - ui->lineEdit_U6->clear(); - ui->spinBox_U6->clear(); - ui->lineEdit_U7->clear(); - ui->spinBox_U7->clear(); - ui->lineEdit_U8->clear(); - ui->spinBox_U8->clear(); - ui->lineEdit_U9->clear(); - ui->spinBox_U9->clear(); - ui->lineEdit_U10->clear(); - ui->spinBox_U10->clear(); - ui->lineEdit_U11->clear(); - ui->spinBox_U11->clear(); - - // Reset 'Tags' tab fields - ui->listWidget_Tags->clear(); - - // Reset 'Localized Strings' tab fields - ui->listWidget_LocalString->clear(); - - // Reset 'Asset Index/Order' tab fields - ui->tableWidget_Index->clear(); - ui->tableWidget_Order->clear(); - - // Reset 'Raw Files' tab fields - ui->treeWidget_Scripts->clear(); - ui->plainTextEdit_Scripts->clear(); - - // Reset 'Tech Sets' tab fields - ui->listWidget_TechSets->clear(); - - // Reset 'Zone Dump' tab fields - ui->spinBox_DumpIndex->clear(); - ui->comboBox_DumpAsset->setCurrentIndex(0); - ui->plainTextEdit_ZoneDump->clear(); - - // Reset 'String Tables' tab fields - ui->comboBox_StringTable->setCurrentIndex(0); - ui->tableWidget_StringTable->clear(); - - // Reset '3D Scene' tab fields - ui->treeWidget_Models->clear(); + // Clear data tree + mTreeWidget->clear(); // Reset class vars mTypeMap.clear(); @@ -195,62 +71,73 @@ void MainWindow::Reset() { mStrTableMap.clear(); } -void MainWindow::StrTableSelected(QString aStrTableName) { - ui->tableWidget_StringTable->clear(); +/* + OpenFastFile() - ui->tableWidget_StringTable->setColumnCount(2); - int entryIndex = 0; - for (auto strTableEntry : mStrTableMap[aStrTableName]) { - ui->tableWidget_StringTable->insertRow(ui->tableWidget_StringTable->rowCount() + 1); - ui->tableWidget_StringTable->setItem(entryIndex, 0, new QTableWidgetItem(strTableEntry.first)); - ui->tableWidget_StringTable->setItem(entryIndex, 1, new QTableWidgetItem(Utils::AssetTypeToString(strTableEntry.second))); + Opens a file dialog in the steam folder, + and opens the selected file. +*/ +bool MainWindow::OpenFastFile(const QString aFastFilePath) { + if (aFastFilePath.isEmpty()) { return false; } - entryIndex++; - } -} + // Reset dialog before opening new file + Reset(); -void MainWindow::ScriptSelected() { - QTreeWidgetItem *selectedItem = ui->treeWidget_Scripts->selectedItems()[0]; - if (!selectedItem) { - qDebug() << "Attempted to load invalid tree item!"; - return; + // Add fast file as tree widget root + const QString fastFileStem = aFastFilePath.split('/').last(); + QTreeWidgetItem *fastFileItem = new QTreeWidgetItem(mRootItem); + fastFileItem->setText(0, fastFileStem); + mRootItem = fastFileItem; + + // Check fastfile can be read + QFile *fastFileObj = new QFile(aFastFilePath); + if (!fastFileObj->open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, "Warning!", QString("Failed to open FastFile: %1!") + .arg(aFastFilePath)); + return false; } - const QString itemName = selectedItem->text(0); - const QStringList scriptExts = {"gsc", "csc", "atr", "shock", "vision", "rmb"}; - if (!scriptExts.contains(itemName.split('.').last())) { - qDebug() << QString("Attempted to parse invalid raw file: %1!").arg(itemName); - return; + // Decompress fastfile and close + const QByteArray fastFileData = fastFileObj->readAll(); + const QByteArray decompressedData = Compressor::DecompressZLIB(fastFileData); + + // Open zone file as little endian stream + QDataStream fastFileStream(fastFileData); + fastFileStream.setByteOrder(QDataStream::LittleEndian); + + // Parse data from fast file header + FastFile fastFile = FastFileParser::ParseFFHeader(&fastFileStream); + + QTreeWidgetItem *metaDataItem = new QTreeWidgetItem(mRootItem); + metaDataItem->setText(0, "Metadata"); + + QTreeWidgetItem *companyItem = new QTreeWidgetItem(metaDataItem); + companyItem->setText(0, "Company: " + Utils::CompanyEnumToStr(fastFile.company)); + QTreeWidgetItem *fileTypeItem = new QTreeWidgetItem(metaDataItem); + fileTypeItem->setText(0, "File Type: " + Utils::FileTypeEnumToStr(fastFile.fileType)); + QTreeWidgetItem *signageItem = new QTreeWidgetItem(metaDataItem); + signageItem->setText(0, "Signage: " + Utils::SignageEnumToStr(fastFile.signage)); + QTreeWidgetItem *magicItem = new QTreeWidgetItem(metaDataItem); + magicItem->setText(0, "Magic: " + fastFile.magic); + QTreeWidgetItem *versionItem = new QTreeWidgetItem(metaDataItem); + versionItem->setText(0, "Version: " + QString::number(fastFile.version)); + + const QString zoneFilePath = fastFileObj->fileName().replace(".ff", ".zone"); + fastFileObj->close(); + + // Check zone file is writeable + QFile *zoneFile = new QFile(zoneFilePath); + if (!zoneFile->open(QIODevice::ReadWrite)) { + qDebug() << QString("Zone file could not be written to: '%1'").arg(zoneFilePath); + return false; } - ui->plainTextEdit_Scripts->clear(); - for (auto [scriptName, scriptContents] : mRawFileMap.asKeyValueRange()) { - if (scriptName.contains(itemName)) { - ui->plainTextEdit_Scripts->setPlainText(scriptContents); - return; - } - } -} + // Write zone data + zoneFile->write(decompressedData); + zoneFile->close(); -QByteArray MainWindow::DecompressZLIB(QByteArray compressedData) { - QByteArray decompressedData; - uLongf decompressedSize = compressedData.size() * 4; - decompressedData.resize(static_cast(decompressedSize)); - - Bytef *destination = reinterpret_cast(decompressedData.data()); - uLongf *destLen = &decompressedSize; - const Bytef *source = reinterpret_cast(compressedData.data()); - uLong sourceLen = compressedData.size(); - - int result = uncompress(destination, destLen, source, sourceLen); - - if (result == Z_OK) { - decompressedData.resize(static_cast(decompressedSize)); - } else { - decompressedData.clear(); - qDebug() << QString("In DecompressZLIB: %1").arg(Utils::ZLibErrorToString(result)).toLatin1(); - } - return decompressedData; + // Open zone file after decompressing ff and writing + return OpenZoneFile(zoneFilePath); } /* @@ -259,29 +146,13 @@ QByteArray MainWindow::DecompressZLIB(QByteArray compressedData) { Opens a file dialog in the steam folder, and opens the selected file. */ -QFile* MainWindow::OpenFastFile() { - // Reset dialog before opening new file - Reset(); - - // Open file dialog to steam apps - const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; - const QString fastFilePath = QFileDialog::getOpenFileName(this, "Open FastFile", steamPath, "FastFile (*.ff);;All Files (*.*)"); - if (!QFile::exists(fastFilePath)) { - QMessageBox::warning(this, "Warning!", QString("%1 does not exist!.").arg(fastFilePath)); - return nullptr; +bool MainWindow::OpenFastFile() { + const QString fastFileName = Utils::GetOpenFastFileName(); + if (!OpenFastFile(fastFileName)) { + qDebug() << "Failed to open Fast file!"; + return false; } - ui->lineEdit_FastFile->setText(fastFilePath); - - const QString fastFileStem = fastFilePath.split('/').last(); - setWindowTitle(QString("FastFile Wizard - %1").arg(fastFileStem)); - - // Check fastfile can be read - QFile *fastFile = new QFile(fastFilePath); - if (!fastFile->open(QIODevice::ReadOnly)) { - QMessageBox::warning(this, "Warning!", QString("%1 could not be read!.").arg(fastFilePath)); - return nullptr; - } - return fastFile; + return true; } /* @@ -290,884 +161,145 @@ QFile* MainWindow::OpenFastFile() { Opens a file dialog in the steam folder, and opens the selected file. */ -QFile* MainWindow::OpenZoneFile() { +bool MainWindow::OpenZoneFile(const QString aZoneFilePath) { + if (aZoneFilePath.isEmpty()) { return false; } + // Reset dialog before opening new file - Reset(); + //Reset(); - // Open file dialog to steam apps - const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; - const QString zoneFilePath = QFileDialog::getOpenFileName(this, "Open ZoneFile", steamPath, "ZoneFile (*.zone);;All Files (*.*)"); - if (!QFile::exists(zoneFilePath)) { - QMessageBox::warning(this, "Warning!", QString("%1 does not exist!.").arg(zoneFilePath)); - return nullptr; + //ui->lineEdit_ZoneFile->setText(zoneFilePath); + + const QString zoneFileStem = aZoneFilePath.split('/').last(); + QTreeWidgetItem *zoneItem = new QTreeWidgetItem(mRootItem); + zoneItem->setText(0, zoneFileStem); + mRootItem = zoneItem; + + //setWindowTitle(QString("FastFile Wizard - %1").arg(zoneFileStem)); + + // Check zone file can be read + QFile *zoneFileObj = new QFile(aZoneFilePath); + if (!zoneFileObj->open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, "Warning!", QString("%1 could not be read!.").arg(aZoneFilePath)); + return false; } - ui->lineEdit_ZoneFile->setText(zoneFilePath); + const QByteArray decompressedData = zoneFileObj->readAll(); - const QString zoneFileStem = zoneFilePath.split('/').last(); - setWindowTitle(QString("FastFile Wizard - %1").arg(zoneFileStem)); + // Open zone file as little endian stream + QDataStream zoneFileStream(decompressedData); + zoneFileStream.setByteOrder(QDataStream::LittleEndian); - // Check fastfile can be read - QFile *zoneFile = new QFile(zoneFilePath); - if (!zoneFile->open(QIODevice::ReadOnly)) { - QMessageBox::warning(this, "Warning!", QString("%1 could not be read!.").arg(zoneFilePath)); - return nullptr; - } - return zoneFile; -} + // Parse data from zone file header + ZoneFile zoneFile = ZoneFileParser::ParseZoneHeader(&zoneFileStream); -void MainWindow::ParseFFCompany(QDataStream *afastFileStream) { - // Check for null datastream ptr - if (!afastFileStream) { return; } - // Parse company - QByteArray companyData(2, Qt::Uninitialized); - afastFileStream->readRawData(companyData.data(), 2); - if (companyData == "IW") { - qDebug() << "Company found: 'INFINITY_WARD'"; - ui->comboBox_Company->setCurrentIndex(1); - } else if (companyData == "TA") { - qDebug() << "Company found: 'TREYARCH'"; - ui->comboBox_Company->setCurrentIndex(2); - } else if (companyData == "Sl") { - qDebug() << "Company found: 'SLEDGEHAMMER'"; - ui->comboBox_Company->setCurrentIndex(3); - } else if (companyData == "NX") { - qDebug() << "Company found: 'NEVERSOFT'"; - ui->comboBox_Company->setCurrentIndex(4); - } else { - qDebug() << QString("Failed to find company, found '%1'!").arg(companyData); - return; - } -} - -void MainWindow::ParseFFFileType(QDataStream *afastFileStream) { - // Parse filetype - QByteArray fileTypeData(2, Qt::Uninitialized); - afastFileStream->readRawData(fileTypeData.data(), 2); - if (fileTypeData == "ff") { - qDebug() << "File type found: 'FAST_FILE'"; - ui->comboBox_FileType->setCurrentIndex(1); - } else { - qDebug() << "Failed to find file type!"; - return; - } -} - -void MainWindow::ParseFFSignage(QDataStream *afastFileStream) { - // Parse filetype - QByteArray signedData(1, Qt::Uninitialized); - afastFileStream->readRawData(signedData.data(), 1); - if (signedData == "u") { - qDebug() << "Found valid signage: Unsigned"; - ui->checkBox_Signed->setChecked(false); - } else if (signedData == "0") { - qDebug() << "Found valid signage: Signed"; - ui->checkBox_Signed->setChecked(true); - } else { - qDebug() << "Failed to determine signage of fastfile!"; - return; - } -} - -void MainWindow::ParseFFMagic(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); - ui->lineEdit_Magic->setText(magicData.toHex()); - ui->spinBox_Magic->setValue(magicData.toInt()); - } else { - qDebug() << "Magic invalid!"; - return; - } -} - -void MainWindow::ParseFFVersion(QDataStream *afastFileStream) { - // Parse version - quint32 version; - *afastFileStream >> version; - qDebug() << "Version:" << version; - if (version == 387) { - qDebug() << QString("Found valid version: '%1'").arg(version); - ui->spinBox_Version->setValue(version); - } else { - qDebug() << "Version invalid!"; - return; - } -} - -void MainWindow::ParseFFHeader(QFile *aFastFilePtr) { - // Open stream to fastfile - QDataStream afastFileStream(aFastFilePtr); - afastFileStream.setByteOrder(QDataStream::LittleEndian); - - ParseFFCompany(&afastFileStream); - ParseFFFileType(&afastFileStream); - ParseFFSignage(&afastFileStream); - ParseFFMagic(&afastFileStream); - ParseFFVersion(&afastFileStream); -} - -void MainWindow::ParseZoneHeader(QDataStream *aZoneFileStream) { - ParseZoneSize(aZoneFileStream); - ParseZoneUnknownsA(aZoneFileStream); - - ParseZoneTagCount(aZoneFileStream); - ParseZoneUnknownsB(aZoneFileStream); - - ParseZoneRecordCount(aZoneFileStream); - - if (mTagCount) { - ParseZoneUnknownsC(aZoneFileStream); - ParseZoneTags(aZoneFileStream); - } else { - aZoneFileStream->skipRawData(4); - } -} - -void MainWindow::ParseZoneSize(QDataStream *aZoneFileStream) { - // Byte 0-3: (unsigned int?) correlates to the fastfile's - // size after decompression minus 36 bytes (24h) - quint32 zoneFileSize; - *aZoneFileStream >> zoneFileSize; - if (zoneFileSize <= 0) { - qDebug() << "Tried to open empty zone file!"; - exit(-1); - } - zoneFileSize += 36; - ui->spinBox_FileSize->setValue(zoneFileSize); - - qDebug() << QString("Zone file size: '%1'").arg(zoneFileSize); -} - -/* - ParseZoneUnknownsA() - - Parses the 1st section of unknowns as hex vals and uint32s -*/ -void MainWindow::ParseZoneUnknownsA(QDataStream *aZoneFileStream) { - // Byte 4-7, 8-11, 12-15: unknown - QByteArray unknown1(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown1.data(), 4); - ui->lineEdit_U1->setText(unknown1.toHex()); - ui->spinBox_U1->setValue(unknown1.toUInt()); - - QByteArray unknown2(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown2.data(), 4); - ui->lineEdit_U2->setText(unknown2.toHex()); - ui->spinBox_U2->setValue(unknown2.toUInt()); - - QByteArray unknown3(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown3.data(), 4); - ui->lineEdit_U3->setText(unknown3.toHex()); - ui->spinBox_U3->setValue(unknown3.toUInt()); - - // Byte 16-19, 20-23: empty/unknown - QByteArray unknown4(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown4.data(), 4); - ui->lineEdit_U4->setText(unknown4.toHex()); - ui->spinBox_U4->setValue(unknown4.toUInt()); - - QByteArray unknown5(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown5.data(), 4); - ui->lineEdit_U5->setText(unknown5.toHex()); - ui->spinBox_U5->setValue(unknown5.toUInt()); - - // Byte 24-27: somehow related to the filesize, but smaller value - QByteArray unknown6(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown6.data(), 4); - ui->lineEdit_U6->setText(unknown6.toHex()); - ui->spinBox_U6->setValue(unknown6.toUInt()); - - // Byte 28-31, 32-35: unknown - QByteArray unknown7(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown7.data(), 4); - ui->lineEdit_U7->setText(unknown7.toHex()); - ui->spinBox_U7->setValue(unknown7.toUInt()); - - QByteArray unknown8(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown8.data(), 4); - ui->lineEdit_U8->setText(unknown8.toHex()); - ui->spinBox_U8->setValue(unknown8.toUInt()); - - qDebug() << QString("Unknowns A: '%1''%2''%3''%4''%5''%6''%7''%8'") - .arg(unknown1.toHex()) - .arg(unknown2.toHex()) - .arg(unknown3.toHex()) - .arg(unknown4.toHex()) - .arg(unknown5.toHex()) - .arg(unknown6.toHex()) - .arg(unknown7.toHex()) - .arg(unknown8.toHex()); -} - -/* - ParseZoneTagCount() - - Parses the number of string tags in the zone index -*/ -void MainWindow::ParseZoneTagCount(QDataStream *aZoneFileStream) { - // Byte 36-39: might indicate where the index record starts, - // calculation unknown - *aZoneFileStream >> mTagCount; - ui->spinBox_TagCount->setValue(mTagCount); - qDebug() << QString("Tag count: '%1'").arg(mTagCount); -} - -/* - ParseZoneRecordCount() - - Parses the number of records in the zone index -*/ -void MainWindow::ParseZoneRecordCount(QDataStream *aZoneFileStream) { - // Byte 44-47: (unsigned int) number of records - *aZoneFileStream >> mRecordCount; - ui->spinBox_RecordCount->setValue(mRecordCount); - qDebug() << QString("Record count: '%1'").arg(mRecordCount); -} - -/* - ParseZoneUnknownsB() - - Parses the 2nd section of unknowns as hex vals and uint32s -*/ -void MainWindow::ParseZoneUnknownsB(QDataStream *aZoneFileStream) { - // Byte 44-47: Unknown/empty? - QByteArray unknown9(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown9.data(), 4); - ui->lineEdit_U9->setText(unknown9.toHex()); - ui->spinBox_U9->setValue(unknown9.toUInt()); - - qDebug() << QString("Unknowns B: \n\t'%1'") - .arg(unknown9.toHex()); -} - -/* - ParseZoneUnknownsC() - - Parses the 3rd section of unknowns as hex vals and uint32s -*/ -void MainWindow::ParseZoneUnknownsC(QDataStream *aZoneFileStream) { - // Byte 40-43: Unknown/empty? - QByteArray unknown10(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown10.data(), 4); - ui->lineEdit_U10->setText(unknown10.toHex()); - ui->spinBox_U10->setValue(unknown10.toUInt()); - - // Byte 44-47: Unknown/empty? - QByteArray unknown11(4, Qt::Uninitialized); - aZoneFileStream->readRawData(unknown11.data(), 4); - ui->lineEdit_U11->setText(unknown11.toHex()); - ui->spinBox_U11->setValue(unknown11.toUInt()); - - qDebug() << QString("Unknowns C: \n\t'%1'\n\t'%2'") - .arg(unknown10.toHex()) - .arg(unknown11.toHex()); -} - -/* - ParseZoneTags() - - Parses the string tags ate the start of zone file -*/ -void MainWindow::ParseZoneTags(QDataStream *aZoneFileStream) { - // Byte 48-51: Repeated separators? ÿÿÿÿ x i - aZoneFileStream->skipRawData(4 * (mTagCount - 1)); - - // Parse tags/strings before index - QString zoneTag; - char zoneTagChar; - for (quint32 i = 0; i < mTagCount - 1; i++) { - *aZoneFileStream >> zoneTagChar; - while (zoneTagChar != 0) { - zoneTag += zoneTagChar; - *aZoneFileStream >> zoneTagChar; + if (zoneFile.tagCount) { + QTreeWidgetItem *tagsItem = new QTreeWidgetItem(mRootItem); + tagsItem->setText(0, QString("Tags [%1]") + .arg(zoneFile.tags.length())); + foreach (const QString tag, zoneFile.tags) { + QTreeWidgetItem *tagItem = new QTreeWidgetItem(tagsItem); + tagItem->setText(0, tag); } - ui->listWidget_Tags->addItem(zoneTag); - // qDebug() << "Tag: " << zoneTag; - zoneTag.clear(); } -} -/* - ParseZoneIndex() + QTreeWidgetItem *indexRecordsItem = new QTreeWidgetItem(mRootItem); + QTreeWidgetItem *assetsItem = new QTreeWidgetItem(mRootItem); - Parse the binary zone index data and populate table -*/ -void MainWindow::ParseZoneIndex(QDataStream *aZoneFileStream) { - // Don't parse if no records - if (!mRecordCount) { return; } + mRootItem = indexRecordsItem; - // Track past assets and counts - int consecutiveIndex = 0; - int consecutiveCount = 0; - QString lastAssetType = ""; + int consecutiveCount = 1; + QString lastRecord = ""; + zoneFile.records = ZoneFileParser::ParseZoneIndex(&zoneFileStream, zoneFile.recordCount); + indexRecordsItem->setText(0, QString("Index Records [%1]") + .arg(zoneFile.records.length())); - // Parse index & map found asset types - for (quint32 i = 0; i < mRecordCount; i++) { - // Skip record start - QByteArray rawAssetType(4, Qt::Uninitialized); - aZoneFileStream->readRawData(rawAssetType.data(), 4); - if (!mTypeMap.contains(rawAssetType.toHex())) { - mTypeMap[rawAssetType.toHex()] = 0; - } - mTypeMap[rawAssetType.toHex()]++; - mTypeOrder << rawAssetType.toHex(); - - // Skip separator - aZoneFileStream->skipRawData(4); - - // Get asset description from type - const QString assetType = rawAssetType.toHex(); - - // Set lastAsset as current if first run - if (lastAssetType.isEmpty()) { - lastAssetType = assetType; - } - - // Track counts or populate asset order table - if (lastAssetType == assetType) { - // Count consecutive assets + foreach (const QString record, zoneFile.records) { + if (lastRecord.isEmpty()) { + lastRecord = record; + continue; + } else if (lastRecord == record) { consecutiveCount++; - } else { - // Insert row and populate for the previous asset type - ui->tableWidget_Order->insertRow(consecutiveIndex); - ui->tableWidget_Order->setItem(consecutiveIndex, 0, new QTableWidgetItem(lastAssetType)); - ui->tableWidget_Order->setItem(consecutiveIndex, 1, new QTableWidgetItem(Utils::AssetTypeToString(lastAssetType))); - ui->tableWidget_Order->setItem(consecutiveIndex, 2, new QTableWidgetItem(QString::number(consecutiveCount))); - - // Update counts and asset type - consecutiveCount = 1; - consecutiveIndex++; - lastAssetType = assetType; + continue; } - } -} - -void MainWindow::ParseAsset_LocalString(QDataStream *aZoneFileStream) { - // Skip separator - aZoneFileStream->skipRawData(8); - - // Parse local string asset contents - QString localStr; - char localStrChar; - *aZoneFileStream >> localStrChar; - while (localStrChar != 0) { - localStr += localStrChar; - *aZoneFileStream >> localStrChar; + QTreeWidgetItem *recordItem = new QTreeWidgetItem(mRootItem); + recordItem->setText(0, QString("%1 [%2]") + .arg(Utils::AssetTypeToString(lastRecord)) + .arg(consecutiveCount)); + lastRecord = record; + consecutiveCount = 1; } - // Parse rawfile name - QString aliasName; - char aliasNameChar; - *aZoneFileStream >> aliasNameChar; - while (aliasNameChar != 0) { - aliasName += aliasNameChar; - *aZoneFileStream >> aliasNameChar; - } - // qDebug() << QString("%1 = %2").arg(aliasName).arg(localStr); - ui->listWidget_LocalString->addItem(QString("%1 = %2").arg(aliasName).arg(localStr)); -} + mRootItem = assetsItem; -void MainWindow::ParseAsset_RawFile(QDataStream *aZoneFileStream) { - // Skip start separator FF FF FF FF (pointer?) - aZoneFileStream->skipRawData(4); + // Parse current and consecutive assets + AssetMap assetMap = ZoneFileParser::ParseAssets(&zoneFileStream, zoneFile.records); + assetsItem->setText(0, QString("Assets")); - quint32 gscLength; - *aZoneFileStream >> gscLength; - - // Skip unknown 4 byte data - aZoneFileStream->skipRawData(4); - - // Parse rawfile path - QString rawFilePath; - char scriptPathChar; - *aZoneFileStream >> scriptPathChar; - while (scriptPathChar != 0) { - rawFilePath += scriptPathChar; - *aZoneFileStream >> scriptPathChar; - } - rawFilePath.replace(",", ""); - const QStringList pathParts = rawFilePath.split('/'); - if (pathParts.size() == 0) { - qDebug() << "Failed to parse ff path! " << rawFilePath; - exit(-1); - } else if (pathParts.size() == 1) { - const QString path = pathParts[0]; - QTreeWidgetItem *newRootItem = new QTreeWidgetItem(ui->treeWidget_Scripts); - newRootItem->setText(0, path); - } else { - const QString path = pathParts[0]; - QTreeWidgetItem *newRootItem; - if (mTreeMap.contains(path)) { - newRootItem = mTreeMap[path]; - } else { - newRootItem = new QTreeWidgetItem(ui->treeWidget_Scripts); - newRootItem->setText(0, path); - mTreeMap[path] = newRootItem; + if (!assetMap.rawFiles.isEmpty()) { + QTreeWidgetItem *rawFilesItem = new QTreeWidgetItem(mRootItem); + foreach (const RawFile rawFile, assetMap.rawFiles) { + QTreeWidgetItem *rawFileItem = new QTreeWidgetItem(rawFilesItem); + rawFileItem->setText(0, rawFile.path); } + rawFilesItem->setText(0, QString("Raw Files [%1]") + .arg(assetMap.rawFiles.length())); + } - QTreeWidgetItem *parentItem = newRootItem; - for (int i = 1; i < pathParts.size(); i++) { - const QString path = pathParts[i]; - QTreeWidgetItem *newChildItem; - if (mTreeMap.contains(path)) { - newChildItem = mTreeMap[path]; - } else { - newChildItem = new QTreeWidgetItem(); - newChildItem->setText(0, path); - mTreeMap[path] = newChildItem; - } - parentItem->addChild(newChildItem); - parentItem = newChildItem; + if (!assetMap.localStrings.isEmpty()) { + QTreeWidgetItem *localStrsItem = new QTreeWidgetItem(mRootItem); + foreach (const LocalString localString, assetMap.localStrings) { + QTreeWidgetItem *localStrItem = new QTreeWidgetItem(localStrsItem); + localStrItem->setText(0, localString.string); } + localStrsItem->setText(0, QString("Local Strings [%1]") + .arg(assetMap.localStrings.length())); } - // Parse gsc contents - QString rawFileContents; - char rawFileContentsChar; - *aZoneFileStream >> rawFileContentsChar; - while (rawFileContentsChar != 0 && rawFileContentsChar != -1) { - rawFileContents += rawFileContentsChar; - *aZoneFileStream >> rawFileContentsChar; - } - mRawFileMap[rawFilePath] = (rawFileContents.isEmpty()) ? ("EMPTY") : (rawFileContents); - // qDebug() << QString("%1: %2").arg(rawFilePath).arg(rawFileContents); -} - -void MainWindow::ParseAsset_PhysPreset(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_XModel(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_Material(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_PixelShader(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_TechSet(QDataStream *aZoneFileStream) { - aZoneFileStream->skipRawData(4); - // Parse techset name - QString techSetName; - char techSetNameChar; - *aZoneFileStream >> techSetNameChar; - while (techSetNameChar == 0) { - *aZoneFileStream >> techSetNameChar; - } - while (techSetNameChar != 0) { - techSetName += techSetNameChar; - *aZoneFileStream >> techSetNameChar; - } - techSetName.replace(",", ""); - ui->listWidget_TechSets->addItem(techSetName); - //qDebug() << "Tech Set: " << techSetName; -} - -void MainWindow::ParseAsset_Image(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_LoadedSound(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_ColMapMP(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_GameMapSP(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_GameMapMP(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_LightDef(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_UIMap(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_AIType(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_FX(QDataStream *aZoneFileStream) { - -} - -void MainWindow::ParseAsset_XAnim(QDataStream *aZoneFileStream) { - // Read in pointer to x_anim name - QByteArray namePtr(4, Qt::Uninitialized); - aZoneFileStream->readRawData(namePtr.data(), 4); - - // Read in counts - quint16 dataByteCount, dataShortCount, - dataIntCount, randomDataByteCount, - randomDataIntCount, numframes; - *aZoneFileStream >> dataByteCount >> dataShortCount >> - dataIntCount >> randomDataByteCount >> - randomDataIntCount >> numframes; - - // Read bool flags - bool isLooped, isDelta; - *aZoneFileStream >> isLooped >> isDelta; - - // Read in more counts - quint8 noneRotatedBoneCount, - twoDRotatedBoneCount, normalRotatedBoneCount, - twoDStaticRotatedBoneCount, normalStaticRotatedBoneCount, - normalTranslatedBoneCount, preciseTranslatedBoneCount, - staticTranslatedBoneCount, noneTranslatedBoneCount, - totalBoneCount, otherBoneCount1, otherBoneCount2; - *aZoneFileStream >> noneRotatedBoneCount >> - twoDRotatedBoneCount >> normalRotatedBoneCount >> - twoDStaticRotatedBoneCount >> normalStaticRotatedBoneCount >> - normalTranslatedBoneCount >> preciseTranslatedBoneCount >> - staticTranslatedBoneCount >> noneTranslatedBoneCount >> - totalBoneCount >> otherBoneCount1 >> otherBoneCount2; - - // Yet more counts - quint8 notifyCount, assetType; - *aZoneFileStream >> notifyCount >> assetType; - - // Read more bool flags - bool pad; - *aZoneFileStream >> pad; - - // Yet more more counts - unsigned int randomDataShortCount, indexCount; - *aZoneFileStream >> randomDataShortCount >> indexCount; - - // Read in floats - float frameRate, frequency; - *aZoneFileStream >> frameRate >> frequency; - - // Read in pointers - quint32 boneIDsPtr, dataBytePtr, dataShortPtr, dataIntPtr, - randomDataShortPtr, randomDataBytePtr, randomDataIntPtr, - longIndiciesPtr, notificationsPtr, deltaPartsPtr; - *aZoneFileStream >> boneIDsPtr >> dataBytePtr >> dataShortPtr - >> dataIntPtr >> randomDataShortPtr >> randomDataBytePtr - >> randomDataIntPtr >> longIndiciesPtr >> notificationsPtr - >> deltaPartsPtr; - - // Read in x_anim file name - QString xAnimName; - char xAnimNameChar; - *aZoneFileStream >> xAnimNameChar; - while (xAnimNameChar != 0) { - xAnimName += xAnimNameChar; - *aZoneFileStream >> xAnimNameChar; - } - - // Parse x_anim index header - QVector sectionLengths; - for (int i = 0; i < numframes; i++) { - quint8 sectionlength; - *aZoneFileStream >> sectionlength; - sectionLengths.push_back(sectionlength); - // Skip padding - aZoneFileStream->skipRawData(1); - } - // Skip unknown section - aZoneFileStream->skipRawData(2 * 8); -} - -void MainWindow::ParseAsset_MenuFile(QDataStream *aZoneFileStream) { - //MENU_FILE -} - -void MainWindow::ParseAsset_Weapon(QDataStream *aZoneFileStream) { - //WEAPON_FILE -} - -void MainWindow::ParseAsset_D3DBSP(QDataStream *aZoneFileStream) { - //D3DBSP_DUMP -} - -void MainWindow::ParseAsset_StringTable(QDataStream *aZoneFileStream) { - aZoneFileStream->skipRawData(4); - - quint32 columnCount, rowCount; - *aZoneFileStream >> columnCount >> rowCount; - columnCount = 0; - rowCount = 0; - - aZoneFileStream->skipRawData(4); - - QString stringTableName; - char stringTableNameChar; - *aZoneFileStream >> stringTableNameChar; - while (stringTableNameChar != 0) { - stringTableName += stringTableNameChar; - *aZoneFileStream >> stringTableNameChar; - } - ui->comboBox_StringTable->addItem(stringTableName); - - QVector tablePointers = QVector(); - for (quint32 i = 0; i < rowCount; i++) { - QByteArray pointerData(4, Qt::Uninitialized); - aZoneFileStream->readRawData(pointerData.data(), 4); - tablePointers.push_back(pointerData.toHex()); - - aZoneFileStream->skipRawData(4); - } - - for (const QString &pointerAddr : tablePointers) { - QString leadingContent = ""; - if (pointerAddr == "FFFFFFFF") { - char leadingContentChar; - *aZoneFileStream >> leadingContentChar; - while (leadingContentChar != 0) { - leadingContent += leadingContentChar; - *aZoneFileStream >> leadingContentChar; - } - } else { - leadingContent = pointerAddr; + if (!assetMap.stringTables.isEmpty()) { + QTreeWidgetItem *strTablesItem = new QTreeWidgetItem(mRootItem); + foreach (const StringTable stringTable, assetMap.stringTables) { + QTreeWidgetItem *strTableItem = new QTreeWidgetItem(strTablesItem); + strTableItem->setText(0, stringTable.name); } - - QString content; - char contentChar; - *aZoneFileStream >> contentChar; - while (contentChar != 0) { - 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); + strTablesItem->setText(0, QString("String Tables [%1]") + .arg(assetMap.stringTables.length())); } + + if (!assetMap.techSets.isEmpty()) { + QTreeWidgetItem *techSetsItem = new QTreeWidgetItem(mRootItem); + foreach (const TechSet techSet, assetMap.techSets) { + QTreeWidgetItem *techSetItem = new QTreeWidgetItem(techSetsItem); + techSetItem->setText(0, techSet.name); + } + techSetsItem->setText(0, QString("Tech Sets [%1]") + .arg(assetMap.techSets.length())); + } + + if (!assetMap.animations.isEmpty()) { + QTreeWidgetItem *animationsItem = new QTreeWidgetItem(mRootItem); + animationsItem->setText(0, "Animations"); + foreach (const Animation animation, assetMap.animations) { + QTreeWidgetItem *animationItem = new QTreeWidgetItem(animationsItem); + animationItem->setText(0, animation.name); + } + animationsItem->setText(0, QString("Animations [%1]") + .arg(assetMap.animations.length())); + } + + // Clean up zone file + zoneFileObj->close(); + delete zoneFileObj; + + return true; } -void MainWindow::on_pushButton_FastFile_clicked() { - // Try to prompt user to open fastfile - QFile *fastFile; - if (!(fastFile = OpenFastFile())) { - QMessageBox::warning(this, "Warning!", QString("Failed to open FastFile!.")); - return; +bool MainWindow::OpenZoneFile() { + const QString zoneFileName = Utils::GetOpenZoneFileName(); + if (!OpenZoneFile(zoneFileName)) { + qDebug() << "Failed to open Zone file!"; + return false; } - - // Parse data from fast file header - ParseFFHeader(fastFile); - - // Decompress fastfile and close - const QByteArray fastFileData = fastFile->readAll(); - const QByteArray decompressedData = DecompressZLIB(fastFileData); - // ui->plainTextEdit_ZoneDump->setPlainText(decompressedData.toHex()); - - const QString zoneFilePath = fastFile->fileName().replace(".ff", ".zone"); - fastFile->close(); - - // Check zone file is writeable - QFile *zoneFile = new QFile(zoneFilePath); - if (!zoneFile->open(QIODevice::ReadWrite)) { - qDebug() << QString("Zone file could not be written to: '%1'").arg(zoneFilePath); - return; - } - // Write zone data - zoneFile->write(decompressedData); - zoneFile->close(); - - // Open zone file as little endian stream - QDataStream zoneFileStream(decompressedData); - zoneFileStream.setByteOrder(QDataStream::LittleEndian); - - // Parse data from zone file header - ParseZoneHeader(&zoneFileStream); - ParseZoneIndex(&zoneFileStream); - - // Track current and consecutive assets - int assetIndex = 0; - - // Iterate asset types found in index - for (auto [assetType, assetCount] : mTypeMap.asKeyValueRange()) { - // Get asset description from type - QString assetStr = Utils::AssetTypeToString(assetType); - - // Insert row and populate - ui->tableWidget_Index->insertRow(assetIndex); - ui->tableWidget_Index->setItem(assetIndex, 0, new QTableWidgetItem(assetType)); - ui->tableWidget_Index->setItem(assetIndex, 1, new QTableWidgetItem(assetStr)); - ui->tableWidget_Index->setItem(assetIndex, 2, new QTableWidgetItem(QString::number(assetCount))); - - // Update count - assetIndex++; - } - - for (int i = 0; i < mTypeOrder.size(); i++) { - const QString typeHex = mTypeOrder[i]; - const QString typeStr = Utils::AssetTypeToString(typeHex); - - // qDebug() << "Parsing Asset of Type: " << typeHex; - if (typeStr == "LOCAL STRING") { // localized string asset - ParseAsset_LocalString(&zoneFileStream); - } else if (typeStr == "RAW FILE") { // gsc - ParseAsset_RawFile(&zoneFileStream); - } else if (typeStr == "PHYS PRESET") { // physpreset - ParseAsset_PhysPreset(&zoneFileStream); - } else if (typeStr == "MODEL") { // xmodel - ParseAsset_XModel(&zoneFileStream); - } else if (typeStr == "MATERIAL") { // material - ParseAsset_Material(&zoneFileStream); - } else if (typeStr == "SHADER") { // pixelshader - ParseAsset_PixelShader(&zoneFileStream); - } else if (typeStr == "TECH SET") { // techset include - ParseAsset_TechSet(&zoneFileStream); - } else if (typeStr == "IMAGE") { // image - ParseAsset_Image(&zoneFileStream); - } else if (typeStr == "SOUND") { // loaded_sound - ParseAsset_LoadedSound(&zoneFileStream); - } else if (typeStr == "COLLISION MAP") { // col_map_mp - ParseAsset_ColMapMP(&zoneFileStream); - } else if (typeStr == "MP MAP") { // game_map_sp - ParseAsset_GameMapSP(&zoneFileStream); - } else if (typeStr == "SP MAP") { // game_map_mp - ParseAsset_GameMapMP(&zoneFileStream); - } else if (typeStr == "LIGHT DEF") { // lightdef - ParseAsset_LightDef(&zoneFileStream); - } else if (typeStr == "UI MAP") { // ui_map - ParseAsset_UIMap(&zoneFileStream); - } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals - ParseAsset_SNDDriverGlobals(&zoneFileStream); - } else if (typeStr == "AI TYPE") { // aitype - ParseAsset_AIType(&zoneFileStream); - } else if (typeStr == "EFFECT") { // aitype - ParseAsset_FX(&zoneFileStream); - } else if (typeStr == "ANIMATION") { // aitype - ParseAsset_XAnim(&zoneFileStream); - } else if (typeStr == "STRING TABLE") { // string_table - ParseAsset_StringTable(&zoneFileStream); - } else if (typeStr == "MENU") { // string_table - ParseAsset_MenuFile(&zoneFileStream); - } else if (typeStr == "WEAPON") { // string_table - ParseAsset_Weapon(&zoneFileStream); - } else if (typeStr == "D3DBSP DUMP") { // string_table - ParseAsset_D3DBSP(&zoneFileStream); - } else if (typeStr != "UNKNOWN") { - qDebug() << "Found bad asset type!" << typeStr; - } - } - - // Close zone file - zoneFile->close(); - - // Clean up - delete zoneFile; - delete fastFile; -} - -void MainWindow::on_pushButton_FastFile_2_clicked() { - // Check zone file is writeable - QFile *zoneFile; - if (!(zoneFile = OpenZoneFile())) { - QMessageBox::warning(this, "Warning!", QString("Failed to open FastFile!.")); - return; - } - const QByteArray decompressedData = zoneFile->readAll(); - - // Open zone file as little endian stream - QDataStream zoneFileStream(decompressedData); - zoneFileStream.setByteOrder(QDataStream::LittleEndian); - - // Parse data from zone file header - ParseZoneHeader(&zoneFileStream); - ParseZoneIndex(&zoneFileStream); - - // Track current and consecutive assets - int assetIndex = 0; - - // Iterate asset types found in index - for (auto [assetType, assetCount] : mTypeMap.asKeyValueRange()) { - // Get asset description from type - QString assetStr = Utils::AssetTypeToString(assetType); - - // Insert row and populate - ui->tableWidget_Index->insertRow(assetIndex); - ui->tableWidget_Index->setItem(assetIndex, 0, new QTableWidgetItem(assetType)); - ui->tableWidget_Index->setItem(assetIndex, 1, new QTableWidgetItem(assetStr)); - ui->tableWidget_Index->setItem(assetIndex, 2, new QTableWidgetItem(QString::number(assetCount))); - - // Update count - assetIndex++; - } - - for (int i = 0; i < mTypeOrder.size(); i++) { - const QString typeHex = mTypeOrder[i]; - const QString typeStr = Utils::AssetTypeToString(typeHex); - - // qDebug() << "Parsing Asset of Type: " << typeHex; - if (typeStr == "LOCAL STRING") { // localized string asset - ParseAsset_LocalString(&zoneFileStream); - } else if (typeStr == "RAW FILE") { // gsc - ParseAsset_RawFile(&zoneFileStream); - } else if (typeStr == "PHYS PRESET") { // physpreset - ParseAsset_PhysPreset(&zoneFileStream); - } else if (typeStr == "MODEL") { // xmodel - ParseAsset_XModel(&zoneFileStream); - } else if (typeStr == "MATERIAL") { // material - ParseAsset_Material(&zoneFileStream); - } else if (typeStr == "SHADER") { // pixelshader - ParseAsset_PixelShader(&zoneFileStream); - } else if (typeStr == "TECH SET") { // techset include - ParseAsset_TechSet(&zoneFileStream); - } else if (typeStr == "IMAGE") { // image - ParseAsset_Image(&zoneFileStream); - } else if (typeStr == "SOUND") { // loaded_sound - ParseAsset_LoadedSound(&zoneFileStream); - } else if (typeStr == "COLLISION MAP") { // col_map_mp - ParseAsset_ColMapMP(&zoneFileStream); - } else if (typeStr == "MP MAP") { // game_map_sp - ParseAsset_GameMapSP(&zoneFileStream); - } else if (typeStr == "SP MAP") { // game_map_mp - ParseAsset_GameMapMP(&zoneFileStream); - } else if (typeStr == "LIGHT DEF") { // lightdef - ParseAsset_LightDef(&zoneFileStream); - } else if (typeStr == "UI MAP") { // ui_map - ParseAsset_UIMap(&zoneFileStream); - } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals - ParseAsset_SNDDriverGlobals(&zoneFileStream); - } else if (typeStr == "AI TYPE") { // aitype - ParseAsset_AIType(&zoneFileStream); - } else if (typeStr == "EFFECT") { // aitype - ParseAsset_FX(&zoneFileStream); - } else if (typeStr == "ANIMATION") { // aitype - ParseAsset_XAnim(&zoneFileStream); - } else if (typeStr == "STRING TABLE") { // string_table - ParseAsset_StringTable(&zoneFileStream); - } else if (typeStr == "MENU") { // string_table - ParseAsset_MenuFile(&zoneFileStream); - } else if (typeStr == "WEAPON") { // string_table - ParseAsset_Weapon(&zoneFileStream); - } else if (typeStr == "D3DBSP DUMP") { // string_table - ParseAsset_D3DBSP(&zoneFileStream); - } else if (typeStr != "UNKNOWN") { - qDebug() << "Found bad asset type!" << typeStr; - } - } - - // Close zone file - zoneFile->close(); - - // Clean up - delete zoneFile; + mRootItem = mTreeWidget->invisibleRootItem(); + return true; } int MainWindow::LoadFile_D3DBSP(const QString aFilePath) { @@ -1195,9 +327,6 @@ int MainWindow::LoadFile_D3DBSP(const QString aFilePath) { // Assign diskLumpOrderSize mDiskLumpOrder.resize(mDiskLumpCount); - qDebug() << "BSP Version:" << mBSPVersion; - qDebug() << "Lump Count:" << mDiskLumpCount; - // Read Lump Index Entries quint32 lumpOffset = sizeof(quint32) * 3 + sizeof(LumpIndexEntry) * mDiskLumpCount; diff --git a/mainwindow.h b/mainwindow.h index cfee96c..a6b3963 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -4,6 +4,11 @@ #include "enums.h" #include "structs.h" #include "utils.h" +#include "aboutdialog.h" +#include "compression.h" +#include "ffparser.h" +#include "zfparser.h" +#include "modelviewer.h" #include #include @@ -12,52 +17,8 @@ #include #include #include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include - +#include +#include #include QT_BEGIN_NAMESPACE @@ -75,60 +36,15 @@ public: ~MainWindow(); void Reset(); - QByteArray DecompressZLIB(QByteArray compressedData); - private slots: - void on_pushButton_FastFile_clicked(); - void on_pushButton_FastFile_2_clicked(); + bool OpenFastFile(const QString aFastFilePath); + bool OpenFastFile(); - QFile *OpenFastFile(); - QFile *OpenZoneFile(); - void ParseFFHeader(QFile *aFastFilePtr); - void ParseFFCompany(QDataStream *aFastFileStream); - void ParseFFFileType(QDataStream *afastFileStream); - void ParseFFSignage(QDataStream *afastFileStream); - void ParseFFMagic(QDataStream *afastFileStream); - void ParseFFVersion(QDataStream *afastFileStream); - - void ParseZoneHeader(QDataStream *aZoneFileStream); - void ParseZoneSize(QDataStream *aZoneFileStream); - - void ParseZoneUnknownsA(QDataStream *aZoneFileStream); - void ParseZoneUnknownsB(QDataStream *aZoneFileStream); - void ParseZoneUnknownsC(QDataStream *aZoneFileStream); - void ParseZoneTagCount(QDataStream *aZoneFileStream); - void ParseZoneRecordCount(QDataStream *aZoneFileStream); - void ParseZoneTags(QDataStream *aZoneFileStream); - void ParseZoneIndex(QDataStream *aZoneFileStream); - - void ParseAsset_LocalString(QDataStream *aZoneFileStream); - void ParseAsset_RawFile(QDataStream *aZoneFileStream); - void ParseAsset_PhysPreset(QDataStream *aZoneFileStream); - void ParseAsset_XModel(QDataStream *aZoneFileStream); - void ParseAsset_Material(QDataStream *aZoneFileStream); - void ParseAsset_PixelShader(QDataStream *aZoneFileStream); - void ParseAsset_TechSet(QDataStream *aZoneFileStream); - void ParseAsset_Image(QDataStream *aZoneFileStream); - void ParseAsset_LoadedSound(QDataStream *aZoneFileStream); - void ParseAsset_ColMapMP(QDataStream *aZoneFileStream); - void ParseAsset_GameMapSP(QDataStream *aZoneFileStream); - void ParseAsset_GameMapMP(QDataStream *aZoneFileStream); - void ParseAsset_LightDef(QDataStream *aZoneFileStream); - void ParseAsset_UIMap(QDataStream *aZoneFileStream); - void ParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream); - void ParseAsset_AIType(QDataStream *aZoneFileStream); - void ParseAsset_FX(QDataStream *aZoneFileStream); - void ParseAsset_XAnim(QDataStream *aZoneFileStream); - void ParseAsset_StringTable(QDataStream *aZoneFileStream); - void ParseAsset_MenuFile(QDataStream *aZoneFileStream); - void ParseAsset_Weapon(QDataStream *aZoneFileStream); - void ParseAsset_D3DBSP(QDataStream *aZoneFileStream); + bool OpenZoneFile(const QString aZoneFilePath); + bool OpenZoneFile(); int LoadFile_D3DBSP(const QString aFilePath); - void ScriptSelected(); - void StrTableSelected(QString aStrTableName); - private: Ui::MainWindow *ui; QMap mTypeMap; @@ -139,6 +55,11 @@ private: QMap mTreeMap; QMap>> mStrTableMap; + QTreeWidget *mTreeWidget; + QTreeWidgetItem *mRootItem; + QPlainTextEdit *mScriptEditor; + ModelViewer *mModelViewer; + quint32 mBSPVersion; quint32 mDiskLumpCount; QVector mDiskLumpOrder; diff --git a/mainwindow.ui b/mainwindow.ui index 0476b70..a6a9903 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,12 +6,18 @@ 0 0 - 850 - 628 + 986 + 692 + + + 550 + 300 + + - FastFile Wizard + XPlor QMainWindow { @@ -19,1060 +25,337 @@ } - - - - - true - - - - 14 - true - - - - WAW - PC - FastFile Viewer - - - Qt::AlignmentFlag::AlignCenter - - - - - - - true - - - Qt::Orientation::Horizontal - - - - - - - true - - - 0 - - - - General - - - - - - - - true - - - FastFile path... - - - - - - - true - - - Open .ff - - - - - - - - - - - true - - - ZoneFile path... - - - - - - - true - - - Open .zone - - - - - - - - - - - true - - - Company: - - - - - - - true - - - - Select Company - - - - - INFINITY_WARD - - - - - TREYARCH - - - - - SLEDGEHAMMER - - - - - NEVERSOFT - - - - - - - - - - - - true - - - File Type: - - - - - - - true - - - - Select File Type - - - - - FAST_FILE - - - - - - - - true - - - Signed - - - - - - - - - - - true - - - Magic: - - - - - - - true - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - true - - - Tag Count - - - - - - - true - - - 999999999 - - - - - - - - - - - true - - - File Size: - - - - - - - true - - - B - - - 999999999 - - - - - - - - - - - true - - - Version: - - - - - - - true - - - 999999999 - - - - - - - - - - - true - - - Record Count - - - - - - - true - - - 999999999 - - - - - - - - - Qt::Orientation::Vertical - - - - 17 - 242 - - - - - - - - - Unknowns - - - - - - - - Unknown 1: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 2: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 4: - - - - - - - - 0 - 0 - - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 3: - - - - - - - - 0 - 0 - - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 6: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 5: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 8: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 7: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 9: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 10: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - - - Unknown 11: - - - - - - - - 0 - 0 - - - - true - - - - - - - true - - - 999999999 - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - Tags - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - - - - - Localized Strings - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Asset Index/Order - - - - - - Asset Index Table - - - - - - 3 - - - 50 - - - true - - - true - - - false - - - - - - - - - - This table shows the total asset counts per type. - - - - - - - - - - Qt::Orientation::Vertical - - - - - - - Asset Order Table - - - - - - 3 - - - 50 - - - true - - - true - - - false - - - - - - - - - - This table shows the consecutive asset counts in order. - - - - - - - - - - - Raw Files - - - - - - - 0 - 0 - - - - - 200 - 16777215 - - - - - 1 - - - - - - - - - 0 - 0 - - - - - - - - - Tech Sets - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Zone Dump - - - - - - - - Select Asset: - - - - - - - - - - - - - - - - - - - String Tables - - - - - - - - String Table: - - - - - - - - - - - - - - - - 3D Scene - - - - - - - 200 - 16777215 - - - - - 1 - - - - - - - - QFrame::Shape::StyledPanel - - - QFrame::Shadow::Raised - - - - - - - - - - - - - true - - - < Back - - - - - - - true - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - true - - - Next > - - - - - - + + + + + 0 + 0 + 986 + 21 + + + + + File + + + + Recent... + + + + + + + + Import... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit + + + + Undo History... + + + + + Redo History... + + + + + + + + + + + + + + + + + + + + + + Help + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + New + + + + + + + + New Fast File + + + + + + + + New Zone File + + + + + + + + Open Fast File + + + + + + + + Open Zone File + + + + + + + + Open Folder + + + + + + + + Save + + + + + + + + Save As + + + + + e + + + + + File + + + + + Folder + + + + + New File + + + + + New Fast File + + + + + New Zone File + + + + + From Clipboard + + + + + Material + + + + + Sound + + + + + + + + Undo + + + + + + + + Redo + + + + + + + + Cut + + + + + + + + Copy + + + + + + + + Paste + + + + + + + + Rename + + + + + Edit Value + + + + + Edit as Hex + + + + + + + + Delete + + + + + d + + + + + d + + + + + Clear Undo History + + + + + Find + + + + + + + + About + + + + + Change Icons + + + + + + + + Check for Updates + + + + + + + + Find + + diff --git a/modelviewer.cpp b/modelviewer.cpp new file mode 100644 index 0000000..27058b0 --- /dev/null +++ b/modelviewer.cpp @@ -0,0 +1,73 @@ +#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 new file mode 100644 index 0000000..c56d376 --- /dev/null +++ b/modelviewer.h @@ -0,0 +1,53 @@ +#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/structs.h b/structs.h index 443470e..6f7efbc 100644 --- a/structs.h +++ b/structs.h @@ -1,7 +1,12 @@ #ifndef STRUCTS_H #define STRUCTS_H +#include "enums.h" + #include +#include +#include +#include // Define Lump Structure struct Lump { @@ -16,4 +21,109 @@ struct LumpIndexEntry { quint32 length; }; +struct LocalString { + QString string; + QString alias; +}; + +struct RawFile { + quint32 length; + QString path; + QString contents; +}; + +struct TechSet { + QString name; +}; + +struct Animation { + quint16 dataByteCount; + quint16 dataShortCount; + quint16 dataIntCount; + quint16 randomDataByteCount; + quint16 randomDataIntCount; + quint16 numframes; + bool isLooped; + bool isDelta; + quint8 noneRotatedBoneCount; + quint8 twoDRotatedBoneCount; + quint8 normalRotatedBoneCount; + quint8 twoDStaticRotatedBoneCount; + quint8 normalStaticRotatedBoneCount; + quint8 normalTranslatedBoneCount; + quint8 preciseTranslatedBoneCount; + quint8 staticTranslatedBoneCount; + quint8 noneTranslatedBoneCount; + quint8 totalBoneCount; + quint8 otherBoneCount1; + quint8 otherBoneCount2; + quint8 notifyCount; + quint8 assetType; + bool pad; + unsigned int randomDataShortCount; + unsigned int indexCount; + float frameRate; + float frequency; + quint32 boneIDsPtr; + quint32 dataBytePtr; + quint32 dataShortPtr; + quint32 dataIntPtr; + quint32 randomDataShortPtr; + quint32 randomDataBytePtr; + quint32 randomDataIntPtr; + quint32 longIndiciesPtr; + quint32 notificationsPtr; + quint32 deltaPartsPtr; + QString name; +}; + +struct StringTable { + quint32 columnCount; + quint32 rowCount; + QString name; +}; + +struct AssetMap { + QVector localStrings; + QVector rawFiles; + //QVector phyPresets; + //QVector models; + //QVector rawFiles; + //QVector shaders; + QVector techSets; + //QVector images; + //QVector sounds; + //QVector collMaps; + //QVector lightDefs; + //QVector uiMaps; + //QVector driverGlobals; + //QVector aiType; + //QVector effects; + QVector animations; + QVector stringTables; + //QVector menus; + //QVector weapons; + //QVector d3dbspDumps; + //QVector spMaps; +}; + +struct ZoneFile { + quint32 size; + quint32 tagCount; + QStringList tags; + quint32 recordCount; + QStringList records; + AssetMap assetMap; +}; + +struct FastFile { + FF_COMPANY company; + FF_FILETYPE fileType; + FF_SIGNAGE signage; + QString magic; + quint32 version; + + ZoneFile zoneFile; +}; + #endif // STRUCTS_H diff --git a/utils.h b/utils.h index 4a0bfc0..2e387c9 100644 --- a/utils.h +++ b/utils.h @@ -5,6 +5,8 @@ #include #include +#include +#include class Utils { public: @@ -164,6 +166,69 @@ public: static quint32 PaddingSize(quint32 size) { return PadInt4(size) - size; } + + static QString GetOpenFastFileName(QWidget *parent = nullptr) { + // Open file dialog to steam apps + const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; + const QString fastFilePath = QFileDialog::getOpenFileName(parent, "Open Fast File", steamPath, "Fast File (*.ff);;All Files (*.*)"); + if (fastFilePath.isNull()) { + // User pressed cancel + return ""; + } else if (!QFile::exists(fastFilePath)) { + QMessageBox::warning(parent, "Warning!", QString("%1 does not exist!.").arg(fastFilePath)); + return ""; + } + return fastFilePath; + } + + static QString GetOpenZoneFileName(QWidget *parent = nullptr) { + // Open file dialog to steam apps + const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; + const QString zoneFilePath = QFileDialog::getOpenFileName(parent, "Open Zone File", steamPath, "Zone File (*.zone);;All Files (*.*)"); + if (zoneFilePath.isNull()) { + // User pressed cancel + return ""; + } else if (!QFile::exists(zoneFilePath)) { + QMessageBox::warning(parent, "Warning!", QString("%1 does not exist!.").arg(zoneFilePath)); + return nullptr; + } + return zoneFilePath; + } + + static QString CompanyEnumToStr(FF_COMPANY aCompany) { + switch (aCompany) { + case COMPANY_NONE: + return "None"; + case COMPANY_INFINITY_WARD: + return "Infinity Ward"; + case COMPANY_TREYARCH: + return "Treyarch"; + case COMPANY_SLEDGEHAMMER: + return "Sledgehammer"; + case COMPANY_NEVERSOFT: + return "Neversoft"; + } + } + + static QString FileTypeEnumToStr(FF_FILETYPE aFileType) { + switch (aFileType) { + case FILETYPE_NONE: + return "None"; + case FILETYPE_FAST_FILE: + return "Fast File"; + } + } + + static QString SignageEnumToStr(FF_SIGNAGE aSignage) { + switch (aSignage) { + case SIGNAGE_NONE: + return "None"; + case SIGNAGE_SIGNED: + return "Signed"; + case SIGNAGE_UNSIGNED: + return "Unsigned"; + } + } }; #endif // UTILS_H diff --git a/zfparser.h b/zfparser.h new file mode 100644 index 0000000..a47cc04 --- /dev/null +++ b/zfparser.h @@ -0,0 +1,523 @@ +#ifndef ZFPARSER_H +#define ZFPARSER_H + +#include "qtypes.h" +#include "utils.h" +#include "structs.h" + +#include +#include +#include + +class ZoneFileParser { +public: + static ZoneFile ParseZoneHeader(QDataStream *aZoneFileStream) { + ZoneFile result; + + result.size = ParseZoneSize(aZoneFileStream); + ParseZoneUnknownsA(aZoneFileStream); + + result.tagCount = ParseZoneTagCount(aZoneFileStream); + ParseZoneUnknownsB(aZoneFileStream); + + result.recordCount = ParseZoneRecordCount(aZoneFileStream); + + if (result.tagCount) { + ParseZoneUnknownsC(aZoneFileStream); + result.tags = ParseZoneTags(aZoneFileStream, result.tagCount); + } else { + aZoneFileStream->skipRawData(4); + } + + return result; + } + + static quint32 ParseZoneSize(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 +*/ + static void ParseZoneUnknownsA(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 +*/ + static quint32 ParseZoneTagCount(QDataStream *aZoneFileStream) { + quint32 tagCount; + *aZoneFileStream >> tagCount; + return tagCount; + } + + /* + ParseZoneRecordCount() + + Parses the number of records in the zone index +*/ + static quint32 ParseZoneRecordCount(QDataStream *aZoneFileStream) { + quint32 recordCount; + *aZoneFileStream >> recordCount; + return recordCount; + } + + /* + ParseZoneUnknownsB() + + Parses the 2nd section of unknowns as hex vals and uint32s +*/ + static void ParseZoneUnknownsB(QDataStream *aZoneFileStream) { + // Byte 44-47: Unknown/empty? + QByteArray unknown9(4, Qt::Uninitialized); + aZoneFileStream->readRawData(unknown9.data(), 4); + } + + /* + ParseZoneUnknownsC() + + Parses the 3rd section of unknowns as hex vals and uint32s +*/ + static void ParseZoneUnknownsC(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 +*/ + static QStringList ParseZoneTags(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 - 1; 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 +*/ + static QStringList ParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount) { + QStringList result; + + // Don't parse if no records + if (!recordCount) { return result; } + + // Track past assets and counts + QString lastAssetType = ""; + + // 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; + } + + static AssetMap ParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { + AssetMap result; + + for (int i = 0; i < assetOrder.size(); i++) { + const QString typeHex = assetOrder[i]; + const QString typeStr = Utils::AssetTypeToString(typeHex); + + if (typeStr == "LOCAL STRING") { // localized string asset + result.localStrings << ParseAsset_LocalString(aZoneFileStream); + } else if (typeStr == "RAW FILE") { // gsc + result.rawFiles << ParseAsset_RawFile(aZoneFileStream); + } else if (typeStr == "PHYS PRESET") { // physpreset + ParseAsset_PhysPreset(aZoneFileStream); + } else if (typeStr == "MODEL") { // xmodel + ParseAsset_Model(aZoneFileStream); + } else if (typeStr == "MATERIAL") { // material + ParseAsset_Material(aZoneFileStream); + } else if (typeStr == "SHADER") { // pixelshader + ParseAsset_PixelShader(aZoneFileStream); + } else if (typeStr == "TECH SET") { // techset include + result.techSets << ParseAsset_TechSet(aZoneFileStream); + } else if (typeStr == "IMAGE") { // image + ParseAsset_Image(aZoneFileStream); + } else if (typeStr == "SOUND") { // loaded_sound + ParseAsset_LoadedSound(aZoneFileStream); + } else if (typeStr == "COLLISION MAP") { // col_map_mp + ParseAsset_ColMapMP(aZoneFileStream); + } else if (typeStr == "MP MAP") { // game_map_sp + ParseAsset_GameMapSP(aZoneFileStream); + } else if (typeStr == "SP MAP") { // game_map_mp + ParseAsset_GameMapMP(aZoneFileStream); + } else if (typeStr == "LIGHT DEF") { // lightdef + ParseAsset_LightDef(aZoneFileStream); + } else if (typeStr == "UI MAP") { // ui_map + ParseAsset_UIMap(aZoneFileStream); + } else if (typeStr == "SND DRIVER GLOBALS") { // snddriverglobals + ParseAsset_SNDDriverGlobals(aZoneFileStream); + } else if (typeStr == "AI TYPE") { // aitype + ParseAsset_AIType(aZoneFileStream); + } else if (typeStr == "EFFECT") { // aitype + ParseAsset_FX(aZoneFileStream); + } else if (typeStr == "ANIMATION") { // aitype + result.animations << ParseAsset_Animation(aZoneFileStream); + } else if (typeStr == "STRING TABLE") { // string_table + result.stringTables << ParseAsset_StringTable(aZoneFileStream); + } else if (typeStr == "MENU") { // string_table + ParseAsset_MenuFile(aZoneFileStream); + } else if (typeStr == "WEAPON") { // string_table + ParseAsset_Weapon(aZoneFileStream); + } else if (typeStr == "D3DBSP DUMP") { // string_table + ParseAsset_D3DBSP(aZoneFileStream); + } else if (typeStr != "UNKNOWN") { + qDebug() << "Found bad asset type!" << typeStr; + } + } + return result; + } + + static LocalString ParseAsset_LocalString(QDataStream *aZoneFileStream) { + LocalString result; + + // Skip separator + aZoneFileStream->skipRawData(8); + + // Parse local string asset contents + QString localStr; + char localStrChar; + *aZoneFileStream >> localStrChar; + while (localStrChar != 0) { + result.string += localStrChar; + *aZoneFileStream >> localStrChar; + } + + // Parse rawfile name + QString aliasName; + char aliasNameChar; + *aZoneFileStream >> aliasNameChar; + while (aliasNameChar != 0) { + result.alias += aliasNameChar; + *aZoneFileStream >> aliasNameChar; + } + return result; + } + + static RawFile ParseAsset_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; + } + + static void ParseAsset_PhysPreset(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_Model(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_Material(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_PixelShader(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static TechSet ParseAsset_TechSet(QDataStream *aZoneFileStream) { + TechSet result; + + aZoneFileStream->skipRawData(4); + // Parse techset name + char techSetNameChar; + *aZoneFileStream >> techSetNameChar; + while (techSetNameChar == 0) { + *aZoneFileStream >> techSetNameChar; + } + while (techSetNameChar != 0) { + result.name += techSetNameChar; + *aZoneFileStream >> techSetNameChar; + } + result.name.replace(",", ""); + + return result; + } + + static void ParseAsset_Image(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_LoadedSound(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_ColMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_GameMapSP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_GameMapMP(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_LightDef(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_UIMap(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_SNDDriverGlobals(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_AIType(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_FX(QDataStream *aZoneFileStream) { + Q_UNUSED(aZoneFileStream); + } + + static Animation ParseAsset_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.twoDStaticRotatedBoneCount + >> result.normalStaticRotatedBoneCount + >> 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; + } + + static void ParseAsset_MenuFile(QDataStream *aZoneFileStream) { + //MENU_FILE + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_Weapon(QDataStream *aZoneFileStream) { + //WEAPON_FILE + Q_UNUSED(aZoneFileStream); + } + + static void ParseAsset_D3DBSP(QDataStream *aZoneFileStream) { + //D3DBSP_DUMP + Q_UNUSED(aZoneFileStream); + } + + static StringTable ParseAsset_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; + } + + 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()); + + aZoneFileStream->skipRawData(4); + } + + for (const QString &pointerAddr : 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; + } + QPair tableEntry = QPair(); + tableEntry.first = leadingContent; + tableEntry.second = content; + //if (!mStrTableMap.contains(stringTableName)) { + // mStrTableMap[stringTableName] = QVector>(); + //} + //mStrTableMap[stringTableName].push_back(tableEntry); + } + return result; + } +}; + +#endif // ZFPARSER_H