Lotta new stuff.

This commit is contained in:
= 2025-02-14 16:06:27 -05:00
parent 2df69cebce
commit 840f194195
49 changed files with 4961 additions and 403 deletions

View File

@ -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

View File

@ -6,6 +6,7 @@
#include <QString>
#include <QColor>
#include <QRectF>
#include <QMap>
struct LocalString {
QString string;
@ -142,6 +143,8 @@ struct StringTable {
quint32 columnCount;
quint32 rowCount;
QString name;
QVector<QString> tablePointers;
QMap<QString, QString> content;
};
struct Image {
@ -319,6 +322,20 @@ struct MenuFile {
QVector<Menu> menuDefs;
};
struct Sound {
QString path;
QString alias;
quint32 dataPtr;
quint32 dataLength;
QByteArray data;
};
struct SoundAsset {
QString name;
quint32 count;
QVector<Sound> sounds;
};
struct AssetMap {
QVector<LocalString> localStrings;
QVector<RawFile> rawFiles;
@ -328,7 +345,7 @@ struct AssetMap {
//QVector<Shader> shaders;
QVector<TechSet> techSets;
QVector<Image> images;
//QVector<Sound> sounds;
QVector<SoundAsset> sounds;
//QVector<CollisionMap> collMaps;
//QVector<LightDefinition> lightDefs;
//QVector<UiMap> uiMaps;

View File

@ -53,5 +53,23 @@
<file>icons/Icon_WAVFile.png</file>
<file>icons/Icon_MenuFile.png</file>
<file>icons/Icon_Image.png</file>
<file>icons/Icon_Model.png</file>
<file>icons/Icon_StringTable.png</file>
<file>icons/Icon_Sound.png</file>
<file>icons/Icon_Pause.png</file>
<file>icons/Icon_Play.png</file>
<file>icons/Icon_SkipBack.png</file>
<file>icons/Icon_SkipForward.png</file>
<file>icons/Icon_Stop.png</file>
<file>icons/Icon_Editor.png</file>
<file>icons/Icon_Views.png</file>
<file>icons/Icon_Tree.png</file>
<file>icons/Icon_Copy.png</file>
<file>icons/Icon_Cut.png</file>
<file>icons/Icon_Find.png</file>
<file>icons/Icon_NewFile.png</file>
<file>icons/Icon_Paste.png</file>
<file>icons/Icon_Save.png</file>
<file>icons/Icon_OpenFile.png</file>
</qresource>
</RCC>

BIN
data/icons/Icon_Copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
data/icons/Icon_Cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
data/icons/Icon_Editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
data/icons/Icon_Find.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
data/icons/Icon_Model.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
data/icons/Icon_NewFile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

BIN
data/icons/Icon_Paste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

BIN
data/icons/Icon_Pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

BIN
data/icons/Icon_Play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
data/icons/Icon_Save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
data/icons/Icon_Sound.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
data/icons/Icon_Stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
data/icons/Icon_Tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

BIN
data/icons/Icon_Views.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

15
enums.h
View File

@ -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 {

View File

@ -4,42 +4,21 @@
#include <QFile>
#include <QDebug>
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,6 +31,16 @@ bool FastFile::Load(const QByteArray aData) {
fastFileStream.setByteOrder(QDataStream::LittleEndian);
// Parse header values.
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);
@ -59,6 +48,7 @@ bool FastFile::Load(const QByteArray aData) {
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; }

View File

@ -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

70
fastfile_cod2.cpp Normal file
View File

@ -0,0 +1,70 @@
#include "fastfile_cod2.h"
#include <QFile>
#include <QDebug>
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);
}

18
fastfile_cod2.h Normal file
View File

@ -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

72
fastfile_cod5.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "fastfile_cod5.h"
#include <QFile>
#include <QDebug>
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;
}

18
fastfile_cod5.h Normal file
View File

@ -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

View File

@ -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> 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<StringTable> 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<Sound> 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);

View File

@ -58,17 +58,11 @@
<property name="title">
<string>Recent...</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentOpenRecent"/>
</property>
</widget>
<widget class="QMenu" name="menuImport">
<property name="title">
<string>Import...</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::FolderNew"/>
</property>
</widget>
<addaction name="actionNew_File_2"/>
<addaction name="actionNew_Fast_File"/>
@ -114,6 +108,8 @@
<addaction name="menuUndo_History"/>
<addaction name="menuRedo_History"/>
<addaction name="actionClear_Undo_History"/>
<addaction name="separator"/>
<addaction name="actionPreferences"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
@ -140,7 +136,8 @@
<widget class="QStatusBar" name="statusBar"/>
<action name="actionNew_File_2">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentNew"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_NewFile.png</normaloff>:/icons/icons/Icon_NewFile.png</iconset>
</property>
<property name="text">
<string>New</string>
@ -148,7 +145,8 @@
</action>
<action name="actionNew_Fast_File">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentNew"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_NewFile.png</normaloff>:/icons/icons/Icon_NewFile.png</iconset>
</property>
<property name="text">
<string>New Fast File</string>
@ -156,7 +154,8 @@
</action>
<action name="actionNew_Zone_File">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentNew"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_NewFile.png</normaloff>:/icons/icons/Icon_NewFile.png</iconset>
</property>
<property name="text">
<string>New Zone File</string>
@ -164,7 +163,8 @@
</action>
<action name="actionOpen_Fast_File">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentOpen"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_OpenFile.png</normaloff>:/icons/icons/Icon_OpenFile.png</iconset>
</property>
<property name="text">
<string>Open Fast File</string>
@ -172,7 +172,8 @@
</action>
<action name="actionOpen_Zone_File">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentOpen"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_OpenFile.png</normaloff>:/icons/icons/Icon_OpenFile.png</iconset>
</property>
<property name="text">
<string>Open Zone File</string>
@ -180,7 +181,8 @@
</action>
<action name="actionOpen_Folder">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentOpen"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_OpenFile.png</normaloff>:/icons/icons/Icon_OpenFile.png</iconset>
</property>
<property name="text">
<string>Open Folder</string>
@ -188,16 +190,14 @@
</action>
<action name="actionSave">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentSave"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_Save.png</normaloff>:/icons/icons/Icon_Save.png</iconset>
</property>
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionSave_As">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentSaveAs"/>
</property>
<property name="text">
<string>Save As</string>
</property>
@ -248,24 +248,19 @@
</property>
</action>
<action name="actionUndo">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditUndo"/>
</property>
<property name="text">
<string>Undo</string>
</property>
</action>
<action name="actionRedo">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditRedo"/>
</property>
<property name="text">
<string>Redo</string>
</property>
</action>
<action name="actionCut">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditCut"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_Cut.png</normaloff>:/icons/icons/Icon_Cut.png</iconset>
</property>
<property name="text">
<string>Cut</string>
@ -273,7 +268,8 @@
</action>
<action name="actionCopy">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditCopy"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_Copy.png</normaloff>:/icons/icons/Icon_Copy.png</iconset>
</property>
<property name="text">
<string>Copy</string>
@ -281,16 +277,14 @@
</action>
<action name="actionPaste">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditPaste"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_Paste.png</normaloff>:/icons/icons/Icon_Paste.png</iconset>
</property>
<property name="text">
<string>Paste</string>
</property>
</action>
<action name="actionRename">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MailMessageNew"/>
</property>
<property name="text">
<string>Rename</string>
</property>
@ -306,9 +300,6 @@
</property>
</action>
<action name="actionDelete">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditDelete"/>
</property>
<property name="text">
<string>Delete</string>
</property>
@ -334,9 +325,6 @@
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::HelpAbout"/>
</property>
<property name="text">
<string>About</string>
</property>
@ -347,22 +335,27 @@
</property>
</action>
<action name="actionCheck_for_Updates">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::SyncSynchronizing"/>
</property>
<property name="text">
<string>Check for Updates</string>
</property>
</action>
<action name="actionFind_2">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditFind"/>
<iconset resource="data/data.qrc">
<normaloff>:/icons/icons/Icon_Find.png</normaloff>:/icons/icons/Icon_Find.png</iconset>
</property>
<property name="text">
<string>Find</string>
</property>
</action>
<action name="actionPreferences">
<property name="text">
<string>Preferences...</string>
</property>
</action>
</widget>
<resources/>
<resources>
<include location="data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -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;
}

View File

@ -1,53 +1,22 @@
#ifndef MODELVIEWER_H
#define MODELVIEWER_H
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QCommandLinkButton>
#include <QScreen>
#include <QHBoxLayout>
#include <QVBoxLayout>
// Qt3DCore includes
#include <QEntity>
#include <QTransform>
#include <QAspectEngine>
#include <qtransform.h>
#include <qaspectengine.h>
#include <qentity.h>
// Qt3DInput includes
#include <QInputAspect>
// Qt3DRender includes
#include <QRenderAspect>
#include <QGeometryRenderer>
#include <QCamera>
#include <QCameraLens>
#include <QMesh>
#include <QTechnique>
#include <QMaterial>
#include <QEffect>
#include <QTexture>
#include <QRenderPass>
#include <QSceneLoader>
#include <QPointLight>
// Qt3DExtras includes
#include <Qt3DWindow>
#include <QFirstPersonCameraController>
#include <QNormalDiffuseMapMaterial>
#include <QForwardRenderer>
#include <QPhongMaterial>
#include <QSphereMesh>
#include <QTorusMesh>
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

624
modelviewer.ui Normal file
View File

@ -0,0 +1,624 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModelViewer</class>
<widget class="QWidget" name="ModelViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1001</width>
<height>897</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Properties</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name Pointer:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBox_NamePtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_42">
<property name="text">
<string>Model Name:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Tag Count:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinBox_TagCount">
<property name="suffix">
<string> tags</string>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Root Tag Count: </string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBox_RootTagCount">
<property name="suffix">
<string> root tags</string>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Surface Count: </string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBox_SurfaceCount">
<property name="suffix">
<string> surfaces</string>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Unknown A:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="spinBox_UnknownA">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Bone Name Pointer:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Parent List Pointer:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="spinBox_ParentListPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Quats Pointer:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="spinBox_QuatsPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Transformation Pointer:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="spinBox_TransPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Classification Pointer:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Base Material Pointer:</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QSpinBox" name="spinBox_BaseMatPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Surfaces Pointer;</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QSpinBox" name="spinBox_SurfacesPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Material Handlers Pointer:</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QSpinBox" name="spinBox_MatHandlesPtr">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Coll Surf Pointer:</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_2">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Coll Surface Count:</string>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_3">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Contents:</string>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_4">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="17" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Bone Info Pointer:</string>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_5">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="18" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Radius:</string>
</property>
</widget>
</item>
<item row="18" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_2"/>
</item>
<item row="19" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Min X: </string>
</property>
</widget>
</item>
<item row="19" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_3"/>
</item>
<item row="20" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Min Y: </string>
</property>
</widget>
</item>
<item row="20" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_4"/>
</item>
<item row="21" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>Min Z: </string>
</property>
</widget>
</item>
<item row="21" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_5"/>
</item>
<item row="22" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Max X: </string>
</property>
</widget>
</item>
<item row="22" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_6"/>
</item>
<item row="23" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>Max Y: </string>
</property>
</widget>
</item>
<item row="23" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_7"/>
</item>
<item row="24" column="0">
<widget class="QLabel" name="label_34">
<property name="text">
<string>Max Z: </string>
</property>
</widget>
</item>
<item row="24" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_8"/>
</item>
<item row="25" column="0">
<widget class="QLabel" name="label_35">
<property name="text">
<string>Lod Count:</string>
</property>
</widget>
</item>
<item row="25" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_7">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="26" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Coll Lod:</string>
</property>
</widget>
</item>
<item row="26" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_8">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="27" column="0">
<widget class="QLabel" name="label_37">
<property name="text">
<string>Stream Info Pointer:</string>
</property>
</widget>
</item>
<item row="27" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_9">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="28" column="0">
<widget class="QLabel" name="label_38">
<property name="text">
<string>Memory Usage:</string>
</property>
</widget>
</item>
<item row="28" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_10">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="29" column="0">
<widget class="QLabel" name="label_39">
<property name="text">
<string>Flags:</string>
</property>
</widget>
</item>
<item row="29" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_11">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="30" column="0">
<widget class="QLabel" name="label_40">
<property name="text">
<string>Phys Preset Pointer:</string>
</property>
</widget>
</item>
<item row="30" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_12">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="31" column="0">
<widget class="QLabel" name="label_41">
<property name="text">
<string>Phys Geometry Pointer:</string>
</property>
</widget>
</item>
<item row="31" column="1">
<widget class="QSpinBox" name="spinBox_ClassPtr_13">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Lod Info</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Lod Info Index:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox_LodIndex"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Distance:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Surface Count:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_2">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Surface Index:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_3">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Part Bit 1: </string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_4">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Part Bit 2: </string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_5">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Part Bit 3: </string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_6">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Part Bit 4: </string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_7">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Part Bit 5: </string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="spinBox_BoneNamePtr_8">
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3DWindow">
<property name="title">
<string>3D Window</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

35
preferenceeditor.cpp Normal file
View File

@ -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;
}

22
preferenceeditor.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef PREFERENCEEDITOR_H
#define PREFERENCEEDITOR_H
#include <QDialog>
namespace Ui {
class PreferenceEditor;
}
class PreferenceEditor : public QDialog
{
Q_OBJECT
public:
explicit PreferenceEditor(QWidget *parent = nullptr);
~PreferenceEditor();
private:
Ui::PreferenceEditor *ui;
};
#endif // PREFERENCEEDITOR_H

528
preferenceeditor.ui Normal file
View File

@ -0,0 +1,528 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PreferenceEditor</class>
<widget class="QDialog" name="PreferenceEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1118</width>
<height>861</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>703</width>
<height>512</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget_Categories">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="currentRow">
<number>0</number>
</property>
<item>
<property name="text">
<string>View</string>
</property>
<property name="icon">
<iconset resource="data/Data.qrc">
<normaloff>:/icons/icons/Icon_Views.png</normaloff>:/icons/icons/Icon_Views.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Tree Widget</string>
</property>
<property name="icon">
<iconset resource="data/Data.qrc">
<normaloff>:/icons/icons/Icon_Tree.png</normaloff>:/icons/icons/Icon_Tree.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>File Editors</string>
</property>
<property name="icon">
<iconset resource="data/Data.qrc">
<normaloff>:/icons/icons/Icon_Editor.png</normaloff>:/icons/icons/Icon_Editor.png</iconset>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame_View">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="data/Data.qrc">:/icons/icons/Icon_Views.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>View</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget_Editors_3">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Font &amp;&amp; Colors</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Font</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Family:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_FontFamily"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_FontSize"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Zoom:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_ViewZoom">
<property name="suffix">
<string>%</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>588</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_TreeWidget">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_9">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="data/Data.qrc">:/icons/icons/Icon_Tree.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Tree Widget</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget_Editors">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_FileEditors">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_7">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="data/Data.qrc">:/icons/icons/Icon_Editor.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>File Editors</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget_Editors_2">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="currentIndex">
<number>7</number>
</property>
<widget class="QWidget" name="tab_FastFile">
<attribute name="title">
<string>Fast File</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_Zone">
<attribute name="title">
<string>Zone File</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_IWIFile">
<attribute name="title">
<string>Images</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_LocalString">
<attribute name="title">
<string>Local Strings</string>
</attribute>
</widget>
<widget class="QWidget" name="tab__StringTable">
<attribute name="title">
<string>String Table</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_Sounds">
<attribute name="title">
<string>Sounds</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_TechSet">
<attribute name="title">
<string>Tech Set</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_Model">
<attribute name="title">
<string>Model</string>
</attribute>
</widget>
<widget class="QWidget" name="tab_6">
<attribute name="title">
<string>Page</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="font">
<font>
<family>Roboto</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="data/Data.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PreferenceEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PreferenceEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

80
soundviewer.cpp Normal file
View File

@ -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<Sound> 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);
}

34
soundviewer.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef SOUNDVIEWER_H
#define SOUNDVIEWER_H
#include "asset_structs.h"
#include <QWidget>
#include <QMediaPlayer>
#include <QBuffer>
#include <QAudioDevice>
#include <QMediaDevices>
#include <QAudioOutput>
namespace Ui {
class SoundViewer;
}
class SoundViewer : public QWidget
{
Q_OBJECT
public:
explicit SoundViewer(QWidget *parent = nullptr);
~SoundViewer();
void SetSound(std::shared_ptr<Sound> aSound);
void SetOutput(QAudioOutput *aOutput);
private:
Ui::SoundViewer *ui;
QMediaPlayer *player;
QBuffer *buffer;
};
#endif // SOUNDVIEWER_H

2573
soundviewer.ui Normal file

File diff suppressed because it is too large Load Diff

36
stringtableviewer.cpp Normal file
View File

@ -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<StringTable> 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++;
}
}

25
stringtableviewer.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef STRINGTABLEVIEWER_H
#define STRINGTABLEVIEWER_H
#include "asset_structs.h"
#include <QWidget>
namespace Ui {
class StringTableViewer;
}
class StringTableViewer : public QWidget
{
Q_OBJECT
public:
explicit StringTableViewer(QWidget *parent = nullptr);
~StringTableViewer();
void SetStringTable(std::shared_ptr<StringTable> aStringTable);
private:
Ui::StringTableViewer *ui;
};
#endif // STRINGTABLEVIEWER_H

24
stringtableviewer.ui Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StringTableViewer</class>
<widget class="QWidget" name="StringTableViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>525</width>
<height>752</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableWidget" name="tableWidget_Strings"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

68
utils.h
View File

@ -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<char>(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<char>(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()

View File

@ -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<FastFile> 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<FastFile> aFastFile) {
mFastFiles[aFastFile->GetFileStem().section(".", 0, 0)] = aFastFile;
resizeColumnToContents(1);
setSortingEnabled(true);
}
void XTreeWidget::AddZoneFile(std::shared_ptr<ZoneFile> aZoneFile, QTreeWidgetItem *aParentItem) {
QTreeWidgetItem *zoneItem;
void XTreeWidget::AddZoneFile(std::shared_ptr<ZoneFile> 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<ZoneFile> 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<XTreeWidgetItem*>(tempItem->child(i));
if (childItem->text(0) == pathPart) {
tempItem = childItem;
@ -113,12 +122,12 @@ void XTreeWidget::AddZoneFile(std::shared_ptr<ZoneFile> 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<ZoneFile> 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<ZoneFile> 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<XTreeWidgetItem*>(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<XTreeWidgetItem*>(activeItem->parent());
while (parentItem && !parentItem->text(0).contains(".zone")) {
parentItem = dynamic_cast<XTreeWidgetItem*>(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<SoundAsset> 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<XTreeWidgetItem*>(activeItem->parent());
while (parentItem && !parentItem->text(0).contains(".zone")) {
parentItem = dynamic_cast<XTreeWidgetItem*>(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<XTreeWidgetItem*>(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<XTreeWidgetItem*>(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<XTreeWidgetItem*>(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<XTreeWidgetItem*>(parentItem->parent());
if (grandpaItem && grandpaItem->text(0).contains(".zone")) {
const QString fileStem = grandpaItem->text(0).section('.', 0, 0);
QVector<Image> 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<XTreeWidgetItem*>(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<XTreeWidgetItem*>(parentItem->parent());
if (grandpaItem && grandpaItem->text(0).contains(".zone")) {
const QString fileStem = grandpaItem->text(0).section('.', 0, 0);
QVector<StringTable> strTables = mZoneFiles[fileStem]->GetAssetMap().stringTables;
for (StringTable strTable : strTables) {
if (strTable.name == selectedText) {
emit StrTableSelected(std::make_shared<StringTable>(strTable));
break;
}
}
}
} else if (parentItem && selectedText.contains(".wav")) {
XTreeWidgetItem *grandpaItem = dynamic_cast<XTreeWidgetItem*>(parentItem->parent());
while (grandpaItem && !grandpaItem->text(0).contains(".zone")) {
grandpaItem = dynamic_cast<XTreeWidgetItem*>(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<SoundAsset> 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>(sound));
break;
}
}
}
}
}
}
@ -485,7 +687,7 @@ void XTreeWidget::AddIWIFile(std::shared_ptr<IWIFile> 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<DDSFile> 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;

View File

@ -6,6 +6,7 @@
#include "ddsfile.h"
#include "iwifile.h"
#include "fastfile.h"
#include "xtreewidgetitem.h"
#include "zonefile.h"
#include <QTreeWidget>
@ -18,7 +19,7 @@ public:
~XTreeWidget();
void AddFastFile(std::shared_ptr<FastFile> aFastFile);
void AddZoneFile(std::shared_ptr<ZoneFile> aZoneFile, QTreeWidgetItem *aParentItem = nullptr);
void AddZoneFile(std::shared_ptr<ZoneFile> aZoneFile, XTreeWidgetItem *aParentItem = nullptr);
void AddIWIFile(std::shared_ptr<IWIFile> aIWIFile);
void AddDDSFile(std::shared_ptr<DDSFile> aDDSFile);
@ -31,7 +32,11 @@ signals:
void RawFileSelected(std::shared_ptr<RawFile> aRawFile);
void ImageSelected(std::shared_ptr<Image> aImage);
void TechSetSelected(std::shared_ptr<TechSet> aZoneFile);
void StrTableSelected(std::shared_ptr<StringTable> aStrTable);
void MenuSelected(std::shared_ptr<Menu> aMenu);
void SoundSelected(std::shared_ptr<Sound> aSound);
void TabSelected(const QString aTabName);
void Cleared();
protected:
void ItemSelectionChanged();

37
xtreewidgetitem.cpp Normal file
View File

@ -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<const XTreeWidgetItem*>(&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;
}

24
xtreewidgetitem.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef XTREEWIDGETITEM_H
#define XTREEWIDGETITEM_H
#include <QTreeWidget>
#include <QTreeWidgetItem>
// 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

View File

@ -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) {
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) {
if (game == FF_GAME_COD2) {
} else {
pParseZoneUnknownsC(aZoneFileStream);
if (tagCount > 1) {
tags = pParseZoneTags(aZoneFileStream, tagCount);
}
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
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<char>(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<char>(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<QString> tablePointers = QVector<QString>();
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<QString, QString> tableEntry = QPair<QString, QString>();
tableEntry.first = leadingContent;
tableEntry.second = content;
//if (!mStrTableMap.contains(stringTableName)) {
// mStrTableMap[stringTableName] = QVector<QPair<QString, QString>>();
//}
//mStrTableMap[stringTableName].push_back(tableEntry);
result.content[leadingContent] = content;
}
return result;
}

View File

@ -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);