mirror of
https://github.com/redstrate/Novus.git
synced 2025-05-07 10:17:44 +00:00
This takes the existing properties widget in the new material editor and makes it reusable across multiple applications. It's now added to the Armoury which shows the gear's used materials. It's also added to the Data Explorer which now supports viewing material files. I fixed the render viewport crashing when hiding it again, and made it even more resilient.
230 lines
8.7 KiB
C++
230 lines
8.7 KiB
C++
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "mainwindow.h"
|
|
|
|
#include <KLocalizedString>
|
|
#include <KZip>
|
|
#include <QApplication>
|
|
#include <QDesktopServices>
|
|
#include <QFileDialog>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QMenuBar>
|
|
#include <QMessageBox>
|
|
#include <QNetworkReply>
|
|
#include <QSplitter>
|
|
#include <QTemporaryDir>
|
|
|
|
#include "cmppart.h"
|
|
#include "dicpart.h"
|
|
#include "exdpart.h"
|
|
#include "exlpart.h"
|
|
#include "filepropertieswindow.h"
|
|
#include "filetypes.h"
|
|
#include "hexpart.h"
|
|
#include "mdlpart.h"
|
|
#include "mtrlpart.h"
|
|
#include "shpkpart.h"
|
|
#include "sklbpart.h"
|
|
#include "texpart.h"
|
|
|
|
MainWindow::MainWindow(const QString &gamePath, GameData *data)
|
|
: NovusMainWindow()
|
|
, data(data)
|
|
, fileCache(*data)
|
|
{
|
|
setupMenubar();
|
|
setMinimumSize(1280, 720);
|
|
|
|
m_mgr = new QNetworkAccessManager(this);
|
|
|
|
auto dummyWidget = new QSplitter();
|
|
dummyWidget->setChildrenCollapsible(false);
|
|
setCentralWidget(dummyWidget);
|
|
|
|
m_tree = new FileTreeWindow(m_database, gamePath, data);
|
|
connect(m_tree, &FileTreeWindow::extractFile, this, [this, data](const QString &path) {
|
|
const QFileInfo info(path);
|
|
|
|
const QString savePath =
|
|
QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save File"), info.fileName(), QStringLiteral("*.%1").arg(info.completeSuffix()));
|
|
if (!savePath.isEmpty()) {
|
|
qInfo() << "Saving to" << savePath;
|
|
|
|
std::string savePathStd = path.toStdString();
|
|
|
|
auto fileData = physis_gamedata_extract_file(data, savePathStd.c_str());
|
|
QFile file(savePath);
|
|
file.open(QIODevice::WriteOnly);
|
|
file.write(reinterpret_cast<const char *>(fileData.data), fileData.size);
|
|
}
|
|
});
|
|
connect(m_tree, &FileTreeWindow::pathSelected, this, [this](const QString &path) {
|
|
refreshParts(path);
|
|
});
|
|
m_tree->setMaximumWidth(300);
|
|
dummyWidget->addWidget(m_tree);
|
|
|
|
partHolder = new QTabWidget();
|
|
partHolder->setDocumentMode(true); // hide borders
|
|
partHolder->setMinimumHeight(720);
|
|
dummyWidget->addWidget(partHolder);
|
|
|
|
refreshParts({});
|
|
}
|
|
|
|
void MainWindow::refreshParts(const QString &path)
|
|
{
|
|
partHolder->clear();
|
|
|
|
std::string pathStd = path.toStdString();
|
|
if (path.isEmpty() || !physis_gamedata_exists(data, pathStd.c_str())) {
|
|
return;
|
|
}
|
|
|
|
auto file = physis_gamedata_extract_file(data, path.toStdString().c_str());
|
|
|
|
QFileInfo info(path);
|
|
|
|
const FileType type = FileTypes::getFileType(info.completeSuffix());
|
|
switch (type) {
|
|
case FileType::ExcelList: {
|
|
auto exlWidget = new EXLPart(data);
|
|
exlWidget->load(file);
|
|
partHolder->addTab(exlWidget, i18nc("@title:tab", "Excel List"));
|
|
} break;
|
|
case FileType::ExcelHeader: {
|
|
auto exdWidget = new EXDPart(data);
|
|
exdWidget->loadSheet(info.baseName(), file);
|
|
partHolder->addTab(exdWidget, i18nc("@title:tab", "Excel Sheet"));
|
|
} break;
|
|
case FileType::ExcelData: {
|
|
auto exdWidget = new QLabel(i18n("Note: Excel data files cannot be previewed standalone, select the EXH file instead."));
|
|
partHolder->addTab(exdWidget, i18nc("@title:tab", "Note"));
|
|
} break;
|
|
case FileType::Model: {
|
|
auto mdlWidget = new MDLPart(data, fileCache);
|
|
mdlWidget->addModel(physis_mdl_parse(file), false, glm::vec3(), QStringLiteral("mdl"), {}, 0);
|
|
partHolder->addTab(mdlWidget, i18nc("@title:tab", "Model"));
|
|
} break;
|
|
case FileType::Texture: {
|
|
auto texWidget = new TexPart(data);
|
|
texWidget->load(file);
|
|
partHolder->addTab(texWidget, i18nc("@title:tab", "Texture"));
|
|
} break;
|
|
case FileType::ShaderPackage: {
|
|
auto shpkWidget = new SHPKPart(data);
|
|
shpkWidget->load(file);
|
|
partHolder->addTab(shpkWidget, i18nc("@title:tab", "Shader Package"));
|
|
} break;
|
|
case FileType::CharaMakeParams: {
|
|
auto cmpWidget = new CmpPart(data);
|
|
cmpWidget->load(file);
|
|
partHolder->addTab(cmpWidget, i18nc("@title:tab", "Chara Make Params"));
|
|
} break;
|
|
case FileType::Skeleton: {
|
|
auto sklbWidget = new SklbPart();
|
|
sklbWidget->load(physis_parse_skeleton(file));
|
|
partHolder->addTab(sklbWidget, i18nc("@title:tab", "Skeleton"));
|
|
} break;
|
|
case FileType::Dictionary: {
|
|
auto dicWidget = new DicPart();
|
|
dicWidget->load(file);
|
|
partHolder->addTab(dicWidget, i18nc("@title:tab", "Dictionary"));
|
|
} break;
|
|
case FileType::Material: {
|
|
auto mtrlWidget = new MtrlPart(data);
|
|
mtrlWidget->load(physis_material_parse(file));
|
|
partHolder->addTab(mtrlWidget, i18nc("@title:tab", "Material"));
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
auto hexWidget = new HexPart();
|
|
hexWidget->loadFile(file);
|
|
partHolder->addTab(hexWidget, i18nc("@title:tab", "Raw Hex"));
|
|
|
|
auto propertiesWidget = new FilePropertiesWindow(path, file);
|
|
partHolder->addTab(propertiesWidget, i18nc("@title:tab", "Properties"));
|
|
|
|
partHolder->tabBar()->setExpanding(true);
|
|
}
|
|
|
|
void MainWindow::setupFileMenu(QMenu *menu)
|
|
{
|
|
auto openList = menu->addAction(i18nc("@action:inmenu", "Import Path List…"));
|
|
openList->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
|
|
connect(openList, &QAction::triggered, [this] {
|
|
auto fileName = QFileDialog::getOpenFileName(nullptr, i18nc("@title:window", "Open Path List"), QStringLiteral("~"));
|
|
|
|
QMessageBox::warning(this,
|
|
i18nc("@title:window", "Import Warning"),
|
|
i18n("Depending on the size of the import, this process usually takes a few minutes. The program may freeze. Please "
|
|
"keep it open until the operation is finished."),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok);
|
|
|
|
QFile file(fileName);
|
|
file.open(QIODevice::ReadOnly);
|
|
|
|
m_database.importFileList(file.readAll());
|
|
m_tree->refreshModel();
|
|
|
|
QMessageBox::information(this, i18nc("@title:window", "Import Complete"), i18n("Successfully imported path list!"), QMessageBox::Ok, QMessageBox::Ok);
|
|
});
|
|
|
|
auto downloadList = menu->addAction(i18nc("@action:inmenu", "Download Path List…"));
|
|
downloadList->setIcon(QIcon::fromTheme(QStringLiteral("download-symbolic")));
|
|
connect(downloadList, &QAction::triggered, [this] {
|
|
const int ret = QMessageBox::information(this,
|
|
i18nc("@title:window", "Download Confirmation"),
|
|
i18n("This will download the path list from <a "
|
|
"href=\"https://rl2.perchbird.dev/\">ResLogger</a>.this process usually takes a few minutes. The program "
|
|
"may freeze. Please keep it open until the operation is finished.<br><br>Continue?"),
|
|
QMessageBox::Ok | QMessageBox::Cancel,
|
|
QMessageBox::Ok);
|
|
|
|
if (ret != QMessageBox::Ok) {
|
|
return;
|
|
}
|
|
|
|
QUrl url;
|
|
url.setScheme(QStringLiteral("https"));
|
|
url.setHost(QStringLiteral("rl2.perchbird.dev"));
|
|
url.setPath(QStringLiteral("/download/export/CurrentPathListWithHashes.zip"));
|
|
|
|
// TODO: Use Qcoro?
|
|
auto reply = m_mgr->get(QNetworkRequest(url));
|
|
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
|
qInfo() << "Finished downloading path list!";
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
QFile file(tempDir.filePath(QStringLiteral("CurrentPathListWithHashes.zip")));
|
|
file.open(QIODevice::WriteOnly);
|
|
file.write(reply->readAll());
|
|
file.close();
|
|
|
|
KZip archive(file.fileName());
|
|
if (!archive.open(QIODevice::ReadOnly)) {
|
|
// TODO: these should show as message boxes
|
|
qFatal() << "Failed to open path list zip!" << archive.errorString();
|
|
return;
|
|
}
|
|
|
|
const KArchiveFile *root = dynamic_cast<const KArchiveFile *>(archive.directory()->entry(QStringLiteral("CurrentPathListWithHashes.csv")));
|
|
m_database.importFileList(root->data());
|
|
m_tree->refreshModel();
|
|
|
|
archive.close();
|
|
|
|
QMessageBox::information(this,
|
|
i18nc("@title:window", "Import Complete"),
|
|
i18n("Successfully downloaded and imported path list!"),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok);
|
|
});
|
|
});
|
|
}
|