mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 06:05:40 +12:00
Qt: Add file patcher
This commit is contained in:
parent
70f443b06e
commit
66bcf384f3
9 changed files with 206 additions and 2 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -70,3 +70,6 @@
|
|||
[submodule "third_party/capstone"]
|
||||
path = third_party/capstone
|
||||
url = https://github.com/capstone-engine/capstone
|
||||
[submodule "third_party/hips"]
|
||||
path = third_party/hips
|
||||
url = https://github.com/wheremyfoodat/Hips
|
||||
|
|
|
@ -52,6 +52,7 @@ include_directories(${PROJECT_SOURCE_DIR}/include/kernel)
|
|||
include_directories(${FMT_INCLUDE_DIR})
|
||||
include_directories(third_party/boost/)
|
||||
include_directories(third_party/elfio/)
|
||||
include_directories(third_party/hips/include/)
|
||||
include_directories(third_party/imgui/)
|
||||
include_directories(third_party/dynarmic/src)
|
||||
include_directories(third_party/cryptopp/)
|
||||
|
@ -448,9 +449,11 @@ if(NOT BUILD_HYDRA_CORE)
|
|||
|
||||
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
|
||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
|
||||
)
|
||||
src/panda_qt/patch_window.cpp src/panda_qt/ellided_label.cpp
|
||||
)
|
||||
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
||||
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
|
||||
include/panda_qt/patch_window.hpp include/panda_qt/ellided_label.hpp
|
||||
)
|
||||
|
||||
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
|
||||
|
|
21
include/panda_qt/ellided_label.hpp
Normal file
21
include/panda_qt/ellided_label.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include <QFontMetrics>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class EllidedLabel : public QLabel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EllidedLabel(Qt::TextElideMode elideMode = Qt::ElideLeft, QWidget* parent = nullptr);
|
||||
explicit EllidedLabel(QString text, Qt::TextElideMode elideMode = Qt::ElideLeft, QWidget* parent = nullptr);
|
||||
void setText(QString text);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
|
||||
private:
|
||||
void updateText();
|
||||
QString m_text;
|
||||
Qt::TextElideMode m_elideMode;
|
||||
};
|
|
@ -17,6 +17,7 @@
|
|||
#include "panda_qt/about_window.hpp"
|
||||
#include "panda_qt/cheats_window.hpp"
|
||||
#include "panda_qt/config_window.hpp"
|
||||
#include "panda_qt/patch_window.hpp"
|
||||
#include "panda_qt/screen.hpp"
|
||||
#include "panda_qt/text_editor.hpp"
|
||||
#include "services/hid.hpp"
|
||||
|
@ -90,13 +91,14 @@ class MainWindow : public QMainWindow {
|
|||
std::mutex messageQueueMutex;
|
||||
std::vector<EmulatorMessage> messageQueue;
|
||||
|
||||
QMenuBar* menuBar = nullptr;
|
||||
InputMappings keyboardMappings;
|
||||
ScreenWidget screen;
|
||||
AboutWindow* aboutWindow;
|
||||
ConfigWindow* configWindow;
|
||||
CheatsWindow* cheatsEditor;
|
||||
TextEditorWindow* luaEditor;
|
||||
QMenuBar* menuBar = nullptr;
|
||||
PatchWindow* patchWindow;
|
||||
|
||||
// We use SDL's game controller API since it's the sanest API that supports as many controllers as possible
|
||||
SDL_GameController* gameController = nullptr;
|
||||
|
@ -110,6 +112,7 @@ class MainWindow : public QMainWindow {
|
|||
void dumpRomFS();
|
||||
void openLuaEditor();
|
||||
void openCheatsEditor();
|
||||
void openPatchWindow();
|
||||
void showAboutMenu();
|
||||
void initControllers();
|
||||
void pollControllers();
|
||||
|
|
21
include/panda_qt/patch_window.hpp
Normal file
21
include/panda_qt/patch_window.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
#include <filesystem>
|
||||
|
||||
#include "panda_qt/ellided_label.hpp"
|
||||
|
||||
class PatchWindow final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PatchWindow(QWidget* parent = nullptr);
|
||||
~PatchWindow() = default;
|
||||
|
||||
private:
|
||||
std::filesystem::path inputPath = "";
|
||||
std::filesystem::path patchPath = "";
|
||||
|
||||
EllidedLabel* inputPathLabel = nullptr;
|
||||
EllidedLabel* patchPathLabel = nullptr;
|
||||
};
|
25
src/panda_qt/ellided_label.cpp
Normal file
25
src/panda_qt/ellided_label.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "panda_qt/ellided_label.hpp"
|
||||
|
||||
// Based on https://stackoverflow.com/questions/7381100/text-overflow-for-a-qlabel-s-text-rendering-in-qt
|
||||
EllidedLabel::EllidedLabel(Qt::TextElideMode elideMode, QWidget* parent) : EllidedLabel("", elideMode, parent) {}
|
||||
|
||||
EllidedLabel::EllidedLabel(QString text, Qt::TextElideMode elideMode, QWidget* parent) : QLabel(parent) {
|
||||
m_elideMode = elideMode;
|
||||
setText(text);
|
||||
}
|
||||
|
||||
void EllidedLabel::setText(QString text) {
|
||||
m_text = text;
|
||||
updateText();
|
||||
}
|
||||
|
||||
void EllidedLabel::resizeEvent(QResizeEvent* event) {
|
||||
QLabel::resizeEvent(event);
|
||||
updateText();
|
||||
}
|
||||
|
||||
void EllidedLabel::updateText() {
|
||||
QFontMetrics metrics(font());
|
||||
QString elided = metrics.elidedText(m_text, m_elideMode, width());
|
||||
QLabel::setText(elided);
|
||||
}
|
|
@ -54,11 +54,13 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
auto dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS"));
|
||||
auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor"));
|
||||
auto cheatsEditorAction = toolsMenu->addAction(tr("Open Cheats Editor"));
|
||||
auto patchWindowAction = toolsMenu->addAction(tr("Open Patch Window"));
|
||||
auto dumpDspFirmware = toolsMenu->addAction(tr("Dump loaded DSP firmware"));
|
||||
|
||||
connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS);
|
||||
connect(luaEditorAction, &QAction::triggered, this, &MainWindow::openLuaEditor);
|
||||
connect(cheatsEditorAction, &QAction::triggered, this, &MainWindow::openCheatsEditor);
|
||||
connect(patchWindowAction, &QAction::triggered, this, &MainWindow::openPatchWindow);
|
||||
connect(dumpDspFirmware, &QAction::triggered, this, &MainWindow::dumpDspFirmware);
|
||||
|
||||
auto aboutAction = aboutMenu->addAction(tr("About Panda3DS"));
|
||||
|
@ -71,6 +73,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
aboutWindow = new AboutWindow(nullptr);
|
||||
configWindow = new ConfigWindow(this);
|
||||
cheatsEditor = new CheatsWindow(emu, {}, this);
|
||||
patchWindow = new PatchWindow(this);
|
||||
luaEditor = new TextEditorWindow(this, "script.lua", "");
|
||||
|
||||
auto args = QCoreApplication::arguments();
|
||||
|
@ -293,6 +296,7 @@ void MainWindow::showAboutMenu() {
|
|||
|
||||
void MainWindow::openLuaEditor() { luaEditor->show(); }
|
||||
void MainWindow::openCheatsEditor() { cheatsEditor->show(); }
|
||||
void MainWindow::openPatchWindow() { patchWindow->show(); }
|
||||
|
||||
void MainWindow::dispatchMessage(const EmulatorMessage& message) {
|
||||
switch (message.type) {
|
||||
|
|
123
src/panda_qt/patch_window.cpp
Normal file
123
src/panda_qt/patch_window.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "panda_qt/patch_window.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <memory>
|
||||
|
||||
#include "hips.hpp"
|
||||
#include "io_file.hpp"
|
||||
|
||||
PatchWindow::PatchWindow(QWidget* parent) : QWidget(parent, Qt::Window) {
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(6, 6, 6, 6);
|
||||
setLayout(layout);
|
||||
|
||||
QWidget* inputBox = new QWidget;
|
||||
QHBoxLayout* inputLayout = new QHBoxLayout;
|
||||
QLabel* inputText = new QLabel(tr("Select input file"));
|
||||
QPushButton* inputButton = new QPushButton(tr("Select"));
|
||||
inputPathLabel = new EllidedLabel("");
|
||||
inputPathLabel->setFixedWidth(200);
|
||||
|
||||
inputLayout->addWidget(inputText);
|
||||
inputLayout->addWidget(inputButton);
|
||||
inputLayout->addWidget(inputPathLabel);
|
||||
inputBox->setLayout(inputLayout);
|
||||
|
||||
QWidget* patchBox = new QWidget;
|
||||
QHBoxLayout* patchLayout = new QHBoxLayout;
|
||||
QLabel* patchText = new QLabel(tr("Select patch file"));
|
||||
QPushButton* patchButton = new QPushButton(tr("Select"));
|
||||
patchPathLabel = new EllidedLabel("");
|
||||
patchPathLabel->setFixedWidth(200);
|
||||
|
||||
patchLayout->addWidget(patchText);
|
||||
patchLayout->addWidget(patchButton);
|
||||
patchLayout->addWidget(patchPathLabel);
|
||||
patchBox->setLayout(patchLayout);
|
||||
|
||||
QWidget* actionBox = new QWidget;
|
||||
QHBoxLayout* actionLayout = new QHBoxLayout;
|
||||
QPushButton* applyPatchButton = new QPushButton(tr("Apply patch"));
|
||||
actionLayout->addWidget(applyPatchButton);
|
||||
actionBox->setLayout(actionLayout);
|
||||
|
||||
layout->addWidget(inputBox);
|
||||
layout->addWidget(patchBox);
|
||||
layout->addWidget(actionBox);
|
||||
|
||||
connect(inputButton, &QPushButton::clicked, this, [this]() {
|
||||
auto path = QFileDialog::getOpenFileName(this, tr("Select file to patch"), "", tr("All files (*.*)"));
|
||||
inputPath = std::filesystem::path(path.toStdU16String());
|
||||
|
||||
inputPathLabel->setText(path);
|
||||
});
|
||||
|
||||
connect(patchButton, &QPushButton::clicked, this, [this]() {
|
||||
auto path = QFileDialog::getOpenFileName(this, tr("Select patch file"), "", tr("Patch files (*.ips *.ups *.bps)"));
|
||||
patchPath = std::filesystem::path(path.toStdU16String());
|
||||
|
||||
patchPathLabel->setText(path);
|
||||
});
|
||||
|
||||
connect(applyPatchButton, &QPushButton::clicked, this, [this]() {
|
||||
if (inputPath.empty() || patchPath.empty()) {
|
||||
printf("Pls set paths properly");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = QFileDialog::getSaveFileName(this, tr("Select file"), QString::fromStdU16String(inputPath.u16string()), tr("All files (*.*)"));
|
||||
std::filesystem::path outputPath = std::filesystem::path(path.toStdU16String());
|
||||
|
||||
if (outputPath.empty()) {
|
||||
printf("Pls set paths properly");
|
||||
return;
|
||||
}
|
||||
|
||||
Hips::PatchType patchType;
|
||||
auto extension = patchPath.extension();
|
||||
|
||||
// Figure out what sort of patch we're dealing with
|
||||
if (extension == ".ips") {
|
||||
patchType = Hips::PatchType::IPS;
|
||||
} else if (extension == ".ups") {
|
||||
patchType = Hips::PatchType::UPS;
|
||||
} else if (extension == ".bps") {
|
||||
patchType = Hips::PatchType::BPS;
|
||||
} else {
|
||||
printf("Unknown patch format\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read input and patch files into buffers
|
||||
IOFile input(inputPath, "rb");
|
||||
IOFile patch(patchPath, "rb");
|
||||
|
||||
if (!input.isOpen() || !patch.isOpen()) {
|
||||
printf("Failed to open input or patch file.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the files into arrays
|
||||
const auto inputSize = *input.size();
|
||||
const auto patchSize = *patch.size();
|
||||
|
||||
std::unique_ptr<uint8_t[]> inputData(new uint8_t[inputSize]);
|
||||
std::unique_ptr<uint8_t[]> patchData(new uint8_t[patchSize]);
|
||||
|
||||
input.rewind();
|
||||
patch.rewind();
|
||||
input.readBytes(inputData.get(), inputSize);
|
||||
patch.readBytes(patchData.get(), patchSize);
|
||||
|
||||
auto [bytes, result] = Hips::patch(inputData.get(), inputSize, patchData.get(), patchSize, patchType);
|
||||
|
||||
// Write patched file
|
||||
if (!bytes.empty()) {
|
||||
IOFile output(outputPath, "wb");
|
||||
output.writeBytes(bytes.data(), bytes.size());
|
||||
}
|
||||
});
|
||||
}
|
1
third_party/hips
vendored
Submodule
1
third_party/hips
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit bbe8faf149c4e10aaa45e2454fdb386e4cabf0cb
|
Loading…
Add table
Reference in a new issue