1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-05-01 15:57:45 +00:00

Decompile Lua bytecode in Data Explorer

The game has *tons* of Lua scripts for event scripting, and are very
interesting to explore. I had to do manually call luadec before, but now
it should be much easier by automatically decompiling Lua scripts in the
Data Explorer.
This commit is contained in:
Joshua Goins 2025-04-30 19:44:20 -04:00
parent 365d75c754
commit dab4a698b2
11 changed files with 126 additions and 5 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "extern/dxbc"] [submodule "extern/dxbc"]
path = extern/dxbc path = extern/dxbc
url = ../dxbc url = ../dxbc
[submodule "extern/luadec51"]
path = extern/luadec51
url = ../luadec51

View file

@ -45,6 +45,7 @@ target_link_libraries(novus-sagasu
Novus::TexPart Novus::TexPart
Novus::DicPart Novus::DicPart
Novus::MtrlPart Novus::MtrlPart
Novus::LuabPart
Physis::Logger Physis::Logger
Qt6::Concurrent Qt6::Concurrent
Qt6::Network) Qt6::Network)

View file

@ -25,6 +25,7 @@
#include "filepropertieswindow.h" #include "filepropertieswindow.h"
#include "filetypes.h" #include "filetypes.h"
#include "hexpart.h" #include "hexpart.h"
#include "luabpart.h"
#include "mdlpart.h" #include "mdlpart.h"
#include "mtrlpart.h" #include "mtrlpart.h"
#include "shpkpart.h" #include "shpkpart.h"
@ -173,6 +174,11 @@ void MainWindow::refreshParts(const QString &path)
mtrlWidget->load(physis_material_parse(file)); mtrlWidget->load(physis_material_parse(file));
partHolder->addTab(mtrlWidget, i18nc("@title:tab", "Material")); partHolder->addTab(mtrlWidget, i18nc("@title:tab", "Material"));
} break; } break;
case FileType::LuaBytecode: {
auto luabWidget = new LuabPart();
luabWidget->load(file);
partHolder->addTab(luabWidget, i18nc("@title:tab", "Lua"));
} break;
default: default:
break; break;
} }

View file

@ -7,7 +7,20 @@
#include "novuscommon_export.h" #include "novuscommon_export.h"
enum class FileType { Unknown, ExcelList, ExcelHeader, ExcelData, Model, Texture, ShaderPackage, CharaMakeParams, Skeleton, Dictionary, Material }; enum class FileType {
Unknown,
ExcelList,
ExcelHeader,
ExcelData,
Model,
Texture,
ShaderPackage,
CharaMakeParams,
Skeleton,
Dictionary,
Material,
LuaBytecode
};
class NOVUSCOMMON_EXPORT FileTypes class NOVUSCOMMON_EXPORT FileTypes
{ {

View file

@ -15,7 +15,8 @@ const static QMap<QString, FileType> extensionToType{{QStringLiteral("exl"), Fil
{QStringLiteral("cmp"), FileType::CharaMakeParams}, {QStringLiteral("cmp"), FileType::CharaMakeParams},
{QStringLiteral("sklb"), FileType::Skeleton}, {QStringLiteral("sklb"), FileType::Skeleton},
{QStringLiteral("dic"), FileType::Dictionary}, {QStringLiteral("dic"), FileType::Dictionary},
{QStringLiteral("mtrl"), FileType::Material}}; {QStringLiteral("mtrl"), FileType::Material},
{QStringLiteral("luab"), FileType::LuaBytecode}};
const static QMap<FileType, QString> typeToName{{FileType::Unknown, i18n("Unknown")}, const static QMap<FileType, QString> typeToName{{FileType::Unknown, i18n("Unknown")},
{FileType::ExcelList, i18n("Excel List")}, {FileType::ExcelList, i18n("Excel List")},
@ -27,7 +28,8 @@ const static QMap<FileType, QString> typeToName{{FileType::Unknown, i18n("Unknow
{FileType::CharaMakeParams, i18n("Chara Make Params")}, {FileType::CharaMakeParams, i18n("Chara Make Params")},
{FileType::Skeleton, i18n("Skeleton")}, {FileType::Skeleton, i18n("Skeleton")},
{FileType::Dictionary, i18n("Dictionary")}, {FileType::Dictionary, i18n("Dictionary")},
{FileType::Material, i18n("Material")}}; {FileType::Material, i18n("Material")},
{FileType::LuaBytecode, i18n("Lua Bytecode")}};
const static QMap<FileType, QString> typeToIcon{{FileType::Unknown, QStringLiteral("unknown")}, const static QMap<FileType, QString> typeToIcon{{FileType::Unknown, QStringLiteral("unknown")},
{FileType::ExcelList, QStringLiteral("x-office-spreadsheet")}, {FileType::ExcelList, QStringLiteral("x-office-spreadsheet")},
@ -39,7 +41,8 @@ const static QMap<FileType, QString> typeToIcon{{FileType::Unknown, QStringLiter
{FileType::CharaMakeParams, QStringLiteral("step_object_SoftBody-symbolic")}, {FileType::CharaMakeParams, QStringLiteral("step_object_SoftBody-symbolic")},
{FileType::Skeleton, QStringLiteral("user-symbolic")}, {FileType::Skeleton, QStringLiteral("user-symbolic")},
{FileType::Dictionary, QStringLiteral("accessories-dictionary-symbolic")}, {FileType::Dictionary, QStringLiteral("accessories-dictionary-symbolic")},
{FileType::Material, QStringLiteral("map-globe-symbolic")}}; {FileType::Material, QStringLiteral("map-globe-symbolic")},
{FileType::LuaBytecode, QStringLiteral("text-x-lua")}};
FileType FileTypes::getFileType(const QString &extension) FileType FileTypes::getFileType(const QString &extension)
{ {

View file

@ -23,3 +23,8 @@ add_subdirectory(magic_enum EXCLUDE_FROM_ALL)
add_subdirectory(tinygltf EXCLUDE_FROM_ALL) add_subdirectory(tinygltf EXCLUDE_FROM_ALL)
add_subdirectory(imgui EXCLUDE_FROM_ALL) add_subdirectory(imgui EXCLUDE_FROM_ALL)
add_subdirectory(dxbc EXCLUDE_FROM_ALL) add_subdirectory(dxbc EXCLUDE_FROM_ALL)
# For some reason, FFXIV uses a *32-bit* Lua compiler. We have to build it as 32-bit or else loading the bytecode fails.
add_compile_options(-m32)
add_link_options(-m32)
add_subdirectory(luadec51 EXCLUDE_FROM_ALL)

1
extern/luadec51 vendored Submodule

@ -0,0 +1 @@
Subproject commit 94a5e2b8e08b2c0fdbefc46239b92f1bdcdac303

View file

@ -10,4 +10,5 @@ add_subdirectory(mdl)
add_subdirectory(mtrl) add_subdirectory(mtrl)
add_subdirectory(shpk) add_subdirectory(shpk)
add_subdirectory(sklb) add_subdirectory(sklb)
add_subdirectory(tex) add_subdirectory(tex)
add_subdirectory(luab)

16
parts/luab/CMakeLists.txt Normal file
View file

@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
# SPDX-License-Identifier: CC0-1.0
add_library(luabpart STATIC)
target_sources(luabpart PRIVATE luabpart.cpp)
target_link_libraries(luabpart
PUBLIC
KF6::I18n
Physis::Physis
Qt6::Core
Qt6::Widgets)
target_include_directories(luabpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(luabpart PRIVATE TRANSLATION_DOMAIN="novus")
add_dependencies(luabpart luadec)
add_library(Novus::LuabPart ALIAS luabpart)

48
parts/luab/luabpart.cpp Normal file
View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "luabpart.h"
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QProcess>
#include <QTemporaryFile>
#include <QTextEdit>
#include <QVBoxLayout>
#include <physis.hpp>
LuabPart::LuabPart(QWidget *parent)
: QWidget(parent)
{
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
m_codeEdit = new QTextEdit();
m_codeEdit->setReadOnly(true);
layout->addWidget(m_codeEdit);
setLayout(layout);
}
void LuabPart::load(physis_Buffer buffer)
{
QTemporaryFile temporaryFile;
if (temporaryFile.open()) {
QFile file(temporaryFile.fileName());
file.open(QIODevice::WriteOnly);
file.write(reinterpret_cast<const char *>(buffer.data), buffer.size);
file.close();
QProcess luaDecProcess;
luaDecProcess.setProgram(QStringLiteral("./luadec"));
luaDecProcess.setArguments({temporaryFile.fileName()});
luaDecProcess.start();
luaDecProcess.waitForFinished();
m_codeEdit->setText(QString::fromUtf8(luaDecProcess.readAllStandardOutput()));
}
}
#include "moc_luabpart.cpp"

24
parts/luab/luabpart.h Normal file
View file

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QTableWidget>
#include <QWidget>
#include <physis.hpp>
class QTextEdit;
class LuabPart : public QWidget
{
Q_OBJECT
public:
explicit LuabPart(QWidget *parent = nullptr);
void load(physis_Buffer file);
private:
GameData *data = nullptr;
QTextEdit *m_codeEdit = nullptr;
};