diff --git a/XPlor.pro b/XPlor.pro index 0d07980..ff67012 100644 --- a/XPlor.pro +++ b/XPlor.pro @@ -1,4 +1,4 @@ -QT += core gui 3dcore 3drender 3dinput 3dextras +QT += core gui multimedia RC_ICONS = XPlor.ico @@ -11,6 +11,8 @@ SOURCES += \ ddsfile.cpp \ ddsviewer.cpp \ fastfile.cpp \ + fastfile_cod2.cpp \ + fastfile_cod5.cpp \ fastfileviewer.cpp \ imagewidget.cpp \ iwifile.cpp \ @@ -19,10 +21,13 @@ SOURCES += \ lzokay.cpp \ main.cpp \ mainwindow.cpp \ - modelviewer.cpp \ iwifile.cpp \ + preferenceeditor.cpp \ + soundviewer.cpp \ + stringtableviewer.cpp \ techsetviewer.cpp \ xtreewidget.cpp \ + xtreewidgetitem.cpp \ zonefile.cpp \ zonefileviewer.cpp @@ -36,6 +41,8 @@ HEADERS += \ ddsviewer.h \ enums.h \ fastfile.h \ + fastfile_cod2.h \ + fastfile_cod5.h \ fastfileviewer.h \ imagewidget.h \ ipak_structs.h \ @@ -45,11 +52,14 @@ HEADERS += \ lzokay.hpp \ lzx.h \ mainwindow.h \ - modelviewer.h \ + preferenceeditor.h \ + soundviewer.h \ + stringtableviewer.h \ techsetviewer.h \ utils.h \ xtreewidget.h \ iwifile.h \ + xtreewidgetitem.h \ zonefile.h \ zonefileviewer.h @@ -61,11 +71,14 @@ FORMS += \ iwiviewer.ui \ localstringviewer.ui \ mainwindow.ui \ + modelviewer.ui \ + preferenceeditor.ui \ + soundviewer.ui \ + stringtableviewer.ui \ techsetviewer.ui \ zonefileviewer.ui -RESOURCES += \ - data/Data.qrc +RESOURCES += data/data.qrc LIBS += -L$$PWD/DevILSDK/lib/x64/Unicode/Release -lDevIL LIBS += -L$$PWD/DevILSDK/lib/x64/Unicode/Release -lILU diff --git a/asset_structs.h b/asset_structs.h index f3bb206..ae6f5b8 100644 --- a/asset_structs.h +++ b/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 { @@ -319,6 +322,20 @@ 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; @@ -328,7 +345,7 @@ struct AssetMap { //QVector shaders; QVector techSets; QVector images; - //QVector sounds; + QVector sounds; //QVector collMaps; //QVector lightDefs; //QVector uiMaps; diff --git a/data/Data.qrc b/data/Data.qrc index 44abea1..6334224 100644 --- a/data/Data.qrc +++ b/data/Data.qrc @@ -53,5 +53,23 @@ 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 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_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_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_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_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_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/enums.h b/enums.h index 8818afa..b5f8204 100644 --- a/enums.h +++ b/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/fastfile.cpp b/fastfile.cpp index 4e57009..328a382 100644 --- a/fastfile.cpp +++ b/fastfile.cpp @@ -4,42 +4,21 @@ #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(); + mStem = other.GetStem(); + mType = other.GetType(); + mCompany = other.GetCompany(); + mSignage = other.GetSignage(); + mMagic = other.GetMagic(); + mVersion = other.GetVersion(); + mZoneFile = other.GetZoneFile(); + mGame = other.GetGame(); + mPlatform = other.GetPlatform(); } return *this; } @@ -52,13 +31,24 @@ bool FastFile::Load(const QByteArray 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 (fastFileStream.device()->peek(2).toHex() == "0000") { + company = COMPANY_INFINITY_WARD; + fileType = FILETYPE_FAST_FILE; + signage = SIGNAGE_UNSIGNED; + magic = 0; + version = 0; + platform = "360"; + game = "COD2"; + + } else { + 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. @@ -189,6 +179,19 @@ bool FastFile::Load(const QByteArray aData) { // Load the zone file with the decompressed data (using an Xbox platform flag). zoneFile.Load(decompressedData, fileStem.section('.', 0, 0) + ".zone", FF_PLATFORM_XBOX); + } else if (game == "COD2") { + Utils::ReadUntilHex(&fastFileStream, "78"); + QByteArray compressedData = aData.mid(fastFileStream.device()->pos()); + QByteArray decompressedData = Compressor::DecompressZLIB(compressedData); + + QFile testFile("exports/" + fileStem.split('.')[0] + ".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, FF_GAME_COD2); } return true; @@ -222,42 +225,6 @@ bool FastFile::Load(const QString aFilePath) { 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; } diff --git a/fastfile.h b/fastfile.h index 0277d1b..de02d34 100644 --- a/fastfile.h +++ b/fastfile.h @@ -10,42 +10,42 @@ class FastFile { public: - FastFile(); - ~FastFile(); - FastFile(const FastFile &aFastFile); - FastFile &operator=(const FastFile &other); + virtual ~FastFile(); + virtual FastFile &operator=(const FastFile &other); - bool Load(const QString aFilePath); - bool Load(const QByteArray aData); + virtual bool Load(const QString aFilePath) = 0; + virtual bool Load(const QByteArray aData) = 0; - 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; + virtual QString GetStem() const { return mStem; } + virtual FF_FILETYPE GetType() const { return mType; } + virtual FF_COMPANY GetCompany() const { return mCompany; } + virtual FF_SIGNAGE GetSignage() const { return mSignage; } + virtual QString GetMagic() const { return mMagic; } + virtual quint32 GetVersion() const { return mVersion; } + virtual ZoneFile GetZoneFile() const { return mZoneFile; } + virtual QString GetGame() const { return mGame; } + virtual QString GetPlatform() const { return mPlatform; } + + virtual void SetStem(const QString aStem) { mStem = aStem; } + virtual void SetType(const FF_FILETYPE aType) { mType = aType; } + virtual void SetCompany(const FF_COMPANY aCompany) { mCompany = aCompany; } + virtual void SetSignage(const FF_SIGNAGE aSignage) { mSignage = aSignage; } + virtual void SetMagic(const QString aMagic) { mMagic = aMagic; } + virtual void SetVersion(const quint32 aVersion) { mVersion = aVersion; } + virtual void SetZoneFile(const ZoneFile aZoneFile) { mZoneFile = aZoneFile; } + virtual void SetGame(const QString aGame) { mGame = aGame; } + virtual void SetPlatform(const QString aPlatform) { mPlatform = aPlatform; } 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); + QString mStem; + FF_FILETYPE mType; + FF_COMPANY mCompany; + FF_SIGNAGE mSignage; + QString mMagic; + quint32 mVersion; + ZoneFile mZoneFile; + QString mGame; + QString mPlatform; }; #endif // FASTFILE_H diff --git a/fastfile_cod2.cpp b/fastfile_cod2.cpp new file mode 100644 index 0000000..4eebd3e --- /dev/null +++ b/fastfile_cod2.cpp @@ -0,0 +1,70 @@ +#include "fastfile_cod2.h" + +#include +#include + +FastFile_COD2::FastFile_COD2() { + +} + +FastFile_COD2::~FastFile_COD2() { + +} + +FastFile &FastFile_COD2::operator=(const FastFile &other) { + +} + +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); + 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 = Compressor::DecompressZLIB(compressedData); + + QFile testFile("exports/" + GetStem().split('.')[0] + ".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, GetStem().section('.', 0, 0) + ".zone", FF_PLATFORM_XBOX, FF_GAME_COD2); +} diff --git a/fastfile_cod2.h b/fastfile_cod2.h new file mode 100644 index 0000000..f6fee49 --- /dev/null +++ b/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(); + + FastFile &operator=(const FastFile &other) override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD2_H diff --git a/fastfile_cod5.cpp b/fastfile_cod5.cpp new file mode 100644 index 0000000..501ab90 --- /dev/null +++ b/fastfile_cod5.cpp @@ -0,0 +1,72 @@ +#include "fastfile_cod5.h" + +#include +#include + +FastFile_COD5::FastFile_COD5() { + +} + +FastFile_COD5::~FastFile_COD5() { + +} + +FastFile &FastFile_COD5::operator=(const FastFile &other) { + +} + +bool FastFile_COD5::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_COD5::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)); + fileType = pParseFFFileType(&fastFileStream); + signage = pParseFFSignage(&fastFileStream); + magic = pParseFFMagic(&fastFileStream); + version = pParseFFVersion(&fastFileStream); + platform = pCalculateFFPlatform(); + game = pCalculateFFGame(); + + // 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"); + + return true; +} diff --git a/fastfile_cod5.h b/fastfile_cod5.h new file mode 100644 index 0000000..f82d3d0 --- /dev/null +++ b/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(); + + FastFile &operator=(const FastFile &other) override; + + bool Load(const QString aFilePath) override; + bool Load(const QByteArray aData) override; +}; + +#endif // FASTFILE_COD5_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 8715004..37e4880 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,7 +1,10 @@ #include "mainwindow.h" #include "aboutdialog.h" #include "fastfile.h" +#include "preferenceeditor.h" #include "qheaderview.h" +#include "soundviewer.h" +#include "stringtableviewer.h" #include "techsetviewer.h" #include "ui_mainwindow.h" #include "compressor.h" @@ -34,6 +37,11 @@ MainWindow::MainWindow(QWidget *parent) //ModelViewer *mModelViewer = new ModelViewer(container); //mModelViewer->setAcceptDrops(false); + connect(ui->actionPreferences, &QAction::triggered, this, [this](bool checked = false) { + 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,6 +112,10 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->removeTab(index); }); + connect(mTreeWidget, &XTreeWidget::Cleared, this, [this]() { + ui->tabWidget->clear(); + }); + connect(mTreeWidget, &XTreeWidget::RawFileSelected, this, [this](std::shared_ptr rawFile) { QPlainTextEdit *scriptEditor = new QPlainTextEdit(this); scriptEditor->setAcceptDrops(false); @@ -250,6 +262,51 @@ MainWindow::MainWindow(QWidget *parent) ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); }); + connect(mTreeWidget, &XTreeWidget::StrTableSelected, this, [this](std::shared_ptr aStrTable) { + + StringTableViewer *strTableViewer = new StringTableViewer(this); + strTableViewer->setAcceptDrops(false); + strTableViewer->SetStringTable(aStrTable); + + 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) { + + SoundViewer *soundViewer = new SoundViewer(this); + soundViewer->setAcceptDrops(false); + soundViewer->SetSound(aSound); + + 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::TabSelected, this, [this](QString tabName) { + for (int i = 0; i < ui->tabWidget->count(); i++) { + if (ui->tabWidget->tabText(i) == tabName) { + ui->tabWidget->setCurrentIndex(i); + break; + } + } + }); + // Connect Help > About dialog connect(ui->actionAbout, &QAction::triggered, this, [this](bool checked) { Q_UNUSED(checked); diff --git a/mainwindow.ui b/mainwindow.ui index 9ed5eb0..1b257d7 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -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/modelviewer.cpp b/modelviewer.cpp index 27058b0..a461223 100644 --- a/modelviewer.cpp +++ b/modelviewer.cpp @@ -1,73 +1,14 @@ #include "modelviewer.h" +#include "ui_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); + : QWidget(parent) + , ui(new Ui::ModelViewer) +{ + ui->setupUi(this); +} + +ModelViewer::~ModelViewer() +{ + delete ui; } diff --git a/modelviewer.h b/modelviewer.h index c56d376..1fa493f 100644 --- a/modelviewer.h +++ b/modelviewer.h @@ -1,53 +1,22 @@ #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 +namespace Ui { +class ModelViewer; +} class ModelViewer : public QWidget { Q_OBJECT + public: explicit ModelViewer(QWidget *parent = nullptr); + ~ModelViewer(); -signals: +private: + Ui::ModelViewer *ui; }; #endif // MODELVIEWER_H diff --git a/modelviewer.ui b/modelviewer.ui new file mode 100644 index 0000000..d321671 --- /dev/null +++ b/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/preferenceeditor.cpp b/preferenceeditor.cpp new file mode 100644 index 0000000..44b0ae1 --- /dev/null +++ b/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/preferenceeditor.h b/preferenceeditor.h new file mode 100644 index 0000000..60ed8c0 --- /dev/null +++ b/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/preferenceeditor.ui b/preferenceeditor.ui new file mode 100644 index 0000000..ab80157 --- /dev/null +++ b/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/soundviewer.cpp b/soundviewer.cpp new file mode 100644 index 0000000..9007e9f --- /dev/null +++ b/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/soundviewer.h b/soundviewer.h new file mode 100644 index 0000000..825c28c --- /dev/null +++ b/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/soundviewer.ui b/soundviewer.ui new file mode 100644 index 0000000..e6b290a --- /dev/null +++ b/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/stringtableviewer.cpp b/stringtableviewer.cpp new file mode 100644 index 0000000..b71b8e5 --- /dev/null +++ b/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/stringtableviewer.h b/stringtableviewer.h new file mode 100644 index 0000000..29dc08a --- /dev/null +++ b/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/stringtableviewer.ui b/stringtableviewer.ui new file mode 100644 index 0000000..55ce6e0 --- /dev/null +++ b/stringtableviewer.ui @@ -0,0 +1,24 @@ + + + StringTableViewer + + + + 0 + 0 + 525 + 752 + + + + Form + + + + + + + + + + diff --git a/utils.h b/utils.h index 49c21dd..f7e9117 100644 --- a/utils.h +++ b/utils.h @@ -10,6 +10,74 @@ class Utils { public: + 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/xtreewidget.cpp b/xtreewidget.cpp index 691aba7..a3fd0c2 100644 --- a/xtreewidget.cpp +++ b/xtreewidget.cpp @@ -13,11 +13,20 @@ XTreeWidget::XTreeWidget(QWidget *parent) setContextMenuPolicy(Qt::CustomContextMenu); setSelectionMode(QTreeWidget::SingleSelection); setColumnCount(3); - setColumnWidth(0, 275); - setColumnWidth(1, 50); - setColumnWidth(2, 50); 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, &QTreeWidget::itemSelectionChanged, this, &XTreeWidget::ItemSelectionChanged); connect(this, &XTreeWidget::customContextMenuRequested, this, &XTreeWidget::PrepareContextMenu); @@ -28,7 +37,7 @@ XTreeWidget::~XTreeWidget() { } void XTreeWidget::AddFastFile(std::shared_ptr aFastFile) { - QTreeWidgetItem *fastFileItem = new QTreeWidgetItem(this); + XTreeWidgetItem *fastFileItem = new XTreeWidgetItem(this); fastFileItem->setText(0, aFastFile->GetFileStem()); fastFileItem->setIcon(0, QIcon(":/icons/icons/Icon_FastFile.png")); if (aFastFile->GetPlatform() == "PC") { @@ -55,14 +64,15 @@ void XTreeWidget::AddFastFile(std::shared_ptr aFastFile) { mFastFiles[aFastFile->GetFileStem().section(".", 0, 0)] = aFastFile; resizeColumnToContents(1); + setSortingEnabled(true); } -void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetItem *aParentItem) { - QTreeWidgetItem *zoneItem; +void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, XTreeWidgetItem *aParentItem) { + XTreeWidgetItem *zoneItem; if (aParentItem != nullptr) { - zoneItem = new QTreeWidgetItem(aParentItem); + zoneItem = new XTreeWidgetItem(aParentItem); } else { - zoneItem = new QTreeWidgetItem(this); + zoneItem = new XTreeWidgetItem(this); } zoneItem->setIcon(0, QIcon(":/icons/icons/Icon_ZoneFile.png")); zoneItem->setText(0, aZoneFile->GetFileStem()); @@ -70,40 +80,39 @@ void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetIt auto assetMap = aZoneFile->GetAssetMap(); if (!assetMap.localStrings.isEmpty()) { - QTreeWidgetItem *localStrRoot = new QTreeWidgetItem(zoneItem); + XTreeWidgetItem *localStrRoot = new XTreeWidgetItem(zoneItem); localStrRoot->setText(0, "String Files"); localStrRoot->setIcon(0, QIcon(":/icons/icons/Icon_StringFile.png")); - QTreeWidgetItem *localStrItem = new QTreeWidgetItem(localStrRoot); + XTreeWidgetItem *localStrItem = new XTreeWidgetItem(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); + XTreeWidgetItem *techSetRoot = new XTreeWidgetItem(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); + XTreeWidgetItem *techSetItem = new XTreeWidgetItem(techSetRoot); techSetItem->setText(0, techSet.name); techSetItem->setIcon(0, QIcon(":/icons/icons/Icon_TechSetFile.png")); } } if (!assetMap.rawFiles.isEmpty()) { - QTreeWidgetItem *rawFileRoot = new QTreeWidgetItem(zoneItem); + 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; } - QTreeWidgetItem *tempItem = rawFileRoot; + XTreeWidgetItem *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); + XTreeWidgetItem *childItem = dynamic_cast(tempItem->child(i)); if (childItem->text(0) == pathPart) { tempItem = childItem; @@ -113,12 +122,12 @@ void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetIt } if (pathPart.contains(".gsc")) { - QTreeWidgetItem *rawFileItem = new QTreeWidgetItem(tempItem); + XTreeWidgetItem *rawFileItem = new XTreeWidgetItem(tempItem); rawFileItem->setText(0, pathPart); tempItem = rawFileItem; } else if (!childFound) { - tempItem = new QTreeWidgetItem(tempItem); + tempItem = new XTreeWidgetItem(tempItem); tempItem->setText(0, pathPart); } @@ -128,16 +137,16 @@ void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetIt } if (!assetMap.menuFiles.isEmpty()) { - QTreeWidgetItem *menuRoot = new QTreeWidgetItem(zoneItem); + 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) { - QTreeWidgetItem *menuFileRoot = new QTreeWidgetItem(menuRoot); + XTreeWidgetItem *menuFileRoot = new XTreeWidgetItem(menuRoot); menuFileRoot->setText(0, QString("Menu %1").arg(menuIndex)); for (Menu menu : menuFile.menuDefs) { - QTreeWidgetItem *menuItem = new QTreeWidgetItem(menuFileRoot); + XTreeWidgetItem *menuItem = new XTreeWidgetItem(menuFileRoot); menuItem->setText(0, menu.name); menuItem->setIcon(0, QIcon(":/icons/icons/Icon_MenuFile.png")); } @@ -146,17 +155,81 @@ void XTreeWidget::AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetIt } if (!assetMap.images.isEmpty()) { - QTreeWidgetItem *imageRoot = new QTreeWidgetItem(zoneItem); + XTreeWidgetItem *imageRoot = new XTreeWidgetItem(zoneItem); imageRoot->setText(0, "Images"); imageRoot->setIcon(0, QIcon(":/icons/icons/Icon_Image.png")); for (Image image : assetMap.images) { - QTreeWidgetItem *imageItem = new QTreeWidgetItem(imageRoot); + 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.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->GetFileStem().section(".", 0, 0)] = aZoneFile; } @@ -265,6 +338,8 @@ void XTreeWidget::PrepareContextMenu(const QPoint &pos) { Q_UNUSED(checked); clear(); + + emit Cleared(); }); QAction *closeAllButAction = new QAction("Close All BUT This"); @@ -358,6 +433,98 @@ void XTreeWidget::PrepareContextMenu(const QPoint &pos) { //zoneFile->SaveFastFile(); }); + } 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); @@ -369,13 +536,15 @@ void XTreeWidget::PrepareContextMenu(const QPoint &pos) { void XTreeWidget::ItemSelectionChanged() { if (selectedItems().isEmpty()) { return; } - QTreeWidgetItem *selectedItem = selectedItems().first(); + XTreeWidgetItem *selectedItem = dynamic_cast(selectedItems().first()); if (!selectedItem) { return; } if (selectedItem->text(0).isEmpty()) { return; } QString selectedText = selectedItem->text(0); + emit TabSelected(selectedText); + const QString fileStem = selectedText.section(".", 0, 0); - QTreeWidgetItem *parentItem = selectedItem->parent(); + XTreeWidgetItem *parentItem = dynamic_cast(selectedItem->parent()); if (selectedText.contains(".dds")) { if (!mDDSFiles.contains(fileStem)) { @@ -409,11 +578,11 @@ void XTreeWidget::ItemSelectionChanged() { } emit LocalStringSelected(mZoneFiles[fileStem]); } else if (selectedText.contains(".gsc")) { - QTreeWidgetItem *zoneRoot = selectedItem; + XTreeWidgetItem *zoneRoot = selectedItem; if (!zoneRoot) { return; } while (!zoneRoot->text(0).contains(".zone")) { - zoneRoot = zoneRoot->parent(); + zoneRoot = dynamic_cast(zoneRoot->parent()); if (!zoneRoot) { return; } } @@ -431,7 +600,7 @@ void XTreeWidget::ItemSelectionChanged() { } } } else if (parentItem && (parentItem->text(0) == "Images")) { - QTreeWidgetItem *grandpaItem = parentItem->parent(); + 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; @@ -443,7 +612,7 @@ void XTreeWidget::ItemSelectionChanged() { } } } else if (parentItem && (parentItem->text(0) == "Tech Sets")) { - QTreeWidgetItem *grandpaItem = parentItem->parent(); + 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; @@ -454,6 +623,39 @@ void XTreeWidget::ItemSelectionChanged() { } } } + } else if (parentItem && (parentItem->text(0) == "String Tables")) { + XTreeWidgetItem *grandpaItem = dynamic_cast(parentItem->parent()); + if (grandpaItem && grandpaItem->text(0).contains(".zone")) { + const QString fileStem = grandpaItem->text(0).section('.', 0, 0); + QVector strTables = mZoneFiles[fileStem]->GetAssetMap().stringTables; + for (StringTable strTable : strTables) { + if (strTable.name == selectedText) { + emit StrTableSelected(std::make_shared(strTable)); + 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)); + break; + } + } + } + } } } @@ -485,7 +687,7 @@ void XTreeWidget::AddIWIFile(std::shared_ptr aIWIFile) { } } - QTreeWidgetItem *iwiItem = new QTreeWidgetItem(this); + 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; @@ -501,7 +703,7 @@ void XTreeWidget::AddDDSFile(std::shared_ptr aDDSFile) { } } - QTreeWidgetItem *ddsItem = new QTreeWidgetItem(this); + 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/xtreewidget.h b/xtreewidget.h index 0d49910..c134576 100644 --- a/xtreewidget.h +++ b/xtreewidget.h @@ -6,6 +6,7 @@ #include "ddsfile.h" #include "iwifile.h" #include "fastfile.h" +#include "xtreewidgetitem.h" #include "zonefile.h" #include @@ -18,7 +19,7 @@ public: ~XTreeWidget(); void AddFastFile(std::shared_ptr aFastFile); - void AddZoneFile(std::shared_ptr aZoneFile, QTreeWidgetItem *aParentItem = nullptr); + void AddZoneFile(std::shared_ptr aZoneFile, XTreeWidgetItem *aParentItem = nullptr); void AddIWIFile(std::shared_ptr aIWIFile); void AddDDSFile(std::shared_ptr aDDSFile); @@ -31,7 +32,11 @@ signals: void RawFileSelected(std::shared_ptr aRawFile); void ImageSelected(std::shared_ptr aImage); void TechSetSelected(std::shared_ptr aZoneFile); + void StrTableSelected(std::shared_ptr aStrTable); void MenuSelected(std::shared_ptr aMenu); + void SoundSelected(std::shared_ptr aSound); + void TabSelected(const QString aTabName); + void Cleared(); protected: void ItemSelectionChanged(); diff --git a/xtreewidgetitem.cpp b/xtreewidgetitem.cpp new file mode 100644 index 0000000..af9d707 --- /dev/null +++ b/xtreewidgetitem.cpp @@ -0,0 +1,37 @@ +#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) { + if ((this->childCount() > 0) != (otherItem->childCount() > 0)) + return this->childCount() <= 0; // true if this item is a group and other is not + } + // 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/xtreewidgetitem.h b/xtreewidgetitem.h new file mode 100644 index 0000000..e189d04 --- /dev/null +++ b/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/zonefile.cpp b/zonefile.cpp index e423d78..1044041 100644 --- a/zonefile.cpp +++ b/zonefile.cpp @@ -69,7 +69,9 @@ bool ZoneFile::Load(const QString aFilePath, FF_PLATFORM platform) { return true; } -bool ZoneFile::Load(const QByteArray aFileData, const QString aFileStem, FF_PLATFORM platform) { +bool ZoneFile::Load(const QByteArray aFileData, const QString aFileStem, FF_PLATFORM platform, FF_GAME game) { + fileStem = aFileStem; + // Open zone file as little endian stream QDataStream zoneFileStream(aFileData); if (platform == FF_PLATFORM_PC) { @@ -79,12 +81,11 @@ bool ZoneFile::Load(const QByteArray aFileData, const QString aFileStem, FF_PLAT } // Parse data from zone file header - pParseZoneHeader(&zoneFileStream); - fileStem = aFileStem; + pParseZoneHeader(&zoneFileStream, game); records = - pParseZoneIndex(&zoneFileStream, recordCount); + pParseZoneIndex(&zoneFileStream, recordCount, game); assetMap = - pParseAssets(&zoneFileStream, records); + pParseAssets(&zoneFileStream, records, game); return true; } @@ -117,23 +118,66 @@ AssetMap ZoneFile::GetAssetMap() { return assetMap; } -void ZoneFile::pParseZoneHeader(QDataStream *aZoneFileStream) { - size = pParseZoneSize(aZoneFileStream); - pParseZoneUnknownsA(aZoneFileStream); +void ZoneFile::pParseZoneHeader(QDataStream *aZoneFileStream, FF_GAME game) { + if (game != FF_GAME_COD2) { + size = pParseZoneSize(aZoneFileStream); + pParseZoneUnknownsA(aZoneFileStream); + } tagCount = pParseZoneTagCount(aZoneFileStream); - pParseZoneUnknownsB(aZoneFileStream); + quint32 extraCount; + if (game == FF_GAME_COD2) { + aZoneFileStream->skipRawData(4); + *aZoneFileStream >> extraCount; + qDebug() << "Extra Count: " << extraCount; + aZoneFileStream->skipRawData(4); + } else { + pParseZoneUnknownsB(aZoneFileStream); + } recordCount = pParseZoneRecordCount(aZoneFileStream); if (tagCount) { - pParseZoneUnknownsC(aZoneFileStream); - if (tagCount > 1) { - tags = pParseZoneTags(aZoneFileStream, tagCount); + if (game == FF_GAME_COD2) { + + } else { + pParseZoneUnknownsC(aZoneFileStream); } + tags = pParseZoneTags(aZoneFileStream, tagCount, game); } else { aZoneFileStream->skipRawData(4); } + + int thingCount = 0; + if (game == FF_GAME_COD2) { + if (extraCount != 4294967295) { + qDebug() << "Pre Pos: " << aZoneFileStream->device()->pos(); + for (int i = 0; i < extraCount; i++) { + quint32 thing; + *aZoneFileStream >> thing; + + if (thing == 4294967295) { + thingCount++; + } + } + qDebug() << "Post Pos: " << aZoneFileStream->device()->pos(); + qDebug() << "Thing Count: " << thingCount; + + QStringList tags2; + QString tag; + char zoneTagChar; + for (quint32 i = 0; i < thingCount; i++) { + *aZoneFileStream >> zoneTagChar; + while (zoneTagChar != 0) { + tag += zoneTagChar; + *aZoneFileStream >> zoneTagChar; + } + tags2 << tag; + tag.clear(); + } + qDebug() << tags2; + } + } } quint32 ZoneFile::pParseZoneSize(QDataStream *aZoneFileStream) { @@ -235,11 +279,15 @@ void ZoneFile::pParseZoneUnknownsC(QDataStream *aZoneFileStream) { Parses the string tags ate the start of zone file */ -QStringList ZoneFile::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount) { +QStringList ZoneFile::pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount, FF_GAME game) { QStringList tags; // Byte 48-51: Repeated separators? ÿÿÿÿ x i - aZoneFileStream->skipRawData(4 * (tagCount - 1)); + if (game == FF_GAME_COD2) { + aZoneFileStream->skipRawData(4 * (tagCount + 1)); + } else { + aZoneFileStream->skipRawData(4 * (tagCount - 1)); + } // Parse tags/strings before index QString zoneTag; @@ -252,9 +300,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,12 +309,16 @@ 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::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount, FF_GAME game) { 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 @@ -283,7 +332,7 @@ QStringList ZoneFile::pParseZoneIndex(QDataStream *aZoneFileStream, quint32 reco return result; } -AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder) { +AssetMap ZoneFile::pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder, FF_GAME game) { AssetMap result; aZoneFileStream->device()->seek(aZoneFileStream->device()->pos() - 8); @@ -312,7 +361,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 @@ -527,74 +576,6 @@ 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 result; @@ -687,8 +668,121 @@ Image ZoneFile::pParseAsset_Image(QDataStream *aZoneFileStream) { return result; } -void ZoneFile::pParseAsset_LoadedSound(QDataStream *aZoneFileStream) { - Q_UNUSED(aZoneFileStream); +SoundAsset ZoneFile::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 (int 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::pParseAsset_ColMapMP(QDataStream *aZoneFileStream) { @@ -1048,16 +1142,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 +1170,7 @@ 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; } diff --git a/zonefile.h b/zonefile.h index 93d8cb7..ede2c28 100644 --- a/zonefile.h +++ b/zonefile.h @@ -14,7 +14,7 @@ public: 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); + bool Load(const QByteArray aFileData, const QString aFileStem, FF_PLATFORM platform = FF_PLATFORM_NONE, FF_GAME game = FF_GAME_NONE); QString GetFileStem(); quint32 GetSize(); @@ -25,27 +25,25 @@ public: AssetMap GetAssetMap(); private slots: - void pParseZoneHeader(QDataStream *aZoneFileStream); + void pParseZoneHeader(QDataStream *aZoneFileStream, FF_GAME game); 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); + QStringList pParseZoneTags(QDataStream *aZoneFileStream, quint32 tagCount, FF_GAME game); + QStringList pParseZoneIndex(QDataStream *aZoneFileStream, quint32 recordCount, FF_GAME game); + AssetMap pParseAssets(QDataStream *aZoneFileStream, QStringList assetOrder, FF_GAME game); 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); + SoundAsset pParseAsset_Sound(QDataStream *aZoneFileStream); void pParseAsset_ColMapMP(QDataStream *aZoneFileStream); void pParseAsset_GameMapSP(QDataStream *aZoneFileStream); void pParseAsset_GameMapMP(QDataStream *aZoneFileStream);