mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
DolphinQt: Add ability to start a game with Riivolution patches from the GUI.
This commit is contained in:
parent
783b180dc8
commit
175f225ac1
@ -57,6 +57,7 @@ namespace fs = std::filesystem;
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/RiivolutionPatcher.h"
|
||||
#include "DiscIO/VolumeDisc.h"
|
||||
#include "DiscIO/VolumeWad.h"
|
||||
|
||||
@ -547,6 +548,8 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
|
||||
if (!std::visit(BootTitle(), boot->parameters))
|
||||
return false;
|
||||
|
||||
DiscIO::Riivolution::ApplyPatchesToMemory(boot->riivolution_patches);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "Core/IOS/IOSC.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
#include "DiscIO/RiivolutionParser.h"
|
||||
#include "DiscIO/VolumeDisc.h"
|
||||
#include "DiscIO/VolumeWad.h"
|
||||
|
||||
@ -78,6 +79,7 @@ struct BootParameters
|
||||
BootParameters(Parameters&& parameters_, const std::optional<std::string>& savestate_path_ = {});
|
||||
|
||||
Parameters parameters;
|
||||
std::vector<DiscIO::Riivolution::Patch> riivolution_patches;
|
||||
std::optional<std::string> savestate_path;
|
||||
bool delete_savestate = false;
|
||||
};
|
||||
|
@ -291,6 +291,8 @@ add_executable(dolphin-emu
|
||||
QtUtils/AspectRatioWidget.h
|
||||
ResourcePackManager.cpp
|
||||
ResourcePackManager.h
|
||||
RiivolutionBootWidget.cpp
|
||||
RiivolutionBootWidget.h
|
||||
Settings/AdvancedPane.cpp
|
||||
Settings/AdvancedPane.h
|
||||
Settings/AudioPane.cpp
|
||||
|
@ -179,6 +179,7 @@
|
||||
<ClCompile Include="RenderWidget.cpp" />
|
||||
<ClCompile Include="ResourcePackManager.cpp" />
|
||||
<ClCompile Include="Resources.cpp" />
|
||||
<ClCompile Include="RiivolutionBootWidget.cpp" />
|
||||
<ClCompile Include="SearchBar.cpp" />
|
||||
<ClCompile Include="Settings.cpp" />
|
||||
<ClCompile Include="Settings\AdvancedPane.cpp" />
|
||||
@ -351,6 +352,7 @@
|
||||
<QtMoc Include="QtUtils\UTF8CodePointCountValidator.h" />
|
||||
<QtMoc Include="QtUtils\WindowActivationEventFilter.h" />
|
||||
<QtMoc Include="RenderWidget.h" />
|
||||
<QtMoc Include="RiivolutionBootWidget.h" />
|
||||
<QtMoc Include="SearchBar.h" />
|
||||
<QtMoc Include="Settings.h" />
|
||||
<QtMoc Include="Settings\AdvancedPane.h" />
|
||||
|
@ -353,6 +353,11 @@ void GameList::ShowContextMenu(const QPoint&)
|
||||
|
||||
if (DiscIO::IsDisc(platform))
|
||||
{
|
||||
menu->addAction(tr("Start with Riivolution Patches..."), this,
|
||||
&GameList::StartWithRiivolution);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction(tr("Set as &Default ISO"), this, &GameList::SetDefaultISO);
|
||||
|
||||
if (game->ShouldAllowConversion())
|
||||
@ -587,6 +592,15 @@ void GameList::UninstallWAD()
|
||||
result_dialog.exec();
|
||||
}
|
||||
|
||||
void GameList::StartWithRiivolution()
|
||||
{
|
||||
const auto game = GetSelectedGame();
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
emit OnStartWithRiivolution(*game);
|
||||
}
|
||||
|
||||
void GameList::SetDefaultISO()
|
||||
{
|
||||
const auto game = GetSelectedGame();
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
|
||||
signals:
|
||||
void GameSelected();
|
||||
void OnStartWithRiivolution(const UICommon::GameFile& game);
|
||||
void NetPlayHost(const UICommon::GameFile& game);
|
||||
void SelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
|
||||
void OpenGeneralSettings();
|
||||
@ -62,6 +63,7 @@ private:
|
||||
void OpenWiiSaveFolder();
|
||||
void OpenGCSaveFolder();
|
||||
void OpenWiki();
|
||||
void StartWithRiivolution();
|
||||
void SetDefaultISO();
|
||||
void DeleteFile();
|
||||
#ifdef _WIN32
|
||||
|
@ -58,7 +58,9 @@
|
||||
#include "Core/State.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
|
||||
#include "DiscIO/DirectoryBlob.h"
|
||||
#include "DiscIO/NANDImporter.h"
|
||||
#include "DiscIO/RiivolutionPatcher.h"
|
||||
|
||||
#include "DolphinQt/AboutDialog.h"
|
||||
#include "DolphinQt/CheatsManager.h"
|
||||
@ -100,6 +102,7 @@
|
||||
#include "DolphinQt/RenderWidget.h"
|
||||
#include "DolphinQt/ResourcePackManager.h"
|
||||
#include "DolphinQt/Resources.h"
|
||||
#include "DolphinQt/RiivolutionBootWidget.h"
|
||||
#include "DolphinQt/SearchBar.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
#include "DolphinQt/TAS/GCTASInputWindow.h"
|
||||
@ -643,6 +646,8 @@ void MainWindow::ConnectGameList()
|
||||
{
|
||||
connect(m_game_list, &GameList::GameSelected, this, [this]() { Play(); });
|
||||
connect(m_game_list, &GameList::NetPlayHost, this, &MainWindow::NetPlayHost);
|
||||
connect(m_game_list, &GameList::OnStartWithRiivolution, this,
|
||||
&MainWindow::ShowRiivolutionBootWidget);
|
||||
|
||||
connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow);
|
||||
}
|
||||
@ -1807,6 +1812,39 @@ void MainWindow::ShowCheatsManager()
|
||||
m_cheats_manager->show();
|
||||
}
|
||||
|
||||
void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game)
|
||||
{
|
||||
auto second_game = m_game_list->FindSecondDisc(game);
|
||||
std::vector<std::string> paths = {game.GetFilePath()};
|
||||
if (second_game != nullptr)
|
||||
paths.push_back(second_game->GetFilePath());
|
||||
std::unique_ptr<BootParameters> boot_params =
|
||||
BootParameters::GenerateFromFile(paths, std::nullopt);
|
||||
if (!boot_params)
|
||||
return;
|
||||
if (!std::holds_alternative<BootParameters::Disc>(boot_params->parameters))
|
||||
return;
|
||||
|
||||
auto& disc = std::get<BootParameters::Disc>(boot_params->parameters);
|
||||
RiivolutionBootWidget w(disc.volume->GetGameID(), disc.volume->GetRevision(),
|
||||
disc.volume->GetDiscNumber(), this);
|
||||
w.exec();
|
||||
if (!w.ShouldBoot())
|
||||
return;
|
||||
|
||||
if (!w.GetPatches().empty())
|
||||
{
|
||||
disc.volume = DiscIO::CreateDisc(DiscIO::DirectoryBlobReader::Create(
|
||||
std::move(disc.volume),
|
||||
[&](std::vector<DiscIO::FSTBuilderNode>* fst, DiscIO::FSTBuilderNode* dol_node) {
|
||||
DiscIO::Riivolution::ApplyPatchesToFiles(w.GetPatches(), fst, dol_node);
|
||||
}));
|
||||
boot_params->riivolution_patches = std::move(w.GetPatches());
|
||||
}
|
||||
|
||||
StartGame(std::move(boot_params));
|
||||
}
|
||||
|
||||
void MainWindow::Show()
|
||||
{
|
||||
if (!Settings::Instance().IsBatchModeEnabled())
|
||||
|
@ -157,6 +157,7 @@ private:
|
||||
void ShowMemcardManager();
|
||||
void ShowResourcePackManager();
|
||||
void ShowCheatsManager();
|
||||
void ShowRiivolutionBootWidget(const UICommon::GameFile& game);
|
||||
|
||||
void NetPlayInit();
|
||||
bool NetPlayJoin();
|
||||
|
195
Source/Core/DolphinQt/RiivolutionBootWidget.cpp
Normal file
195
Source/Core/DolphinQt/RiivolutionBootWidget.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/RiivolutionBootWidget.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QMetaType>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "DiscIO/RiivolutionParser.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
|
||||
struct GuiRiivolutionPatchIndex
|
||||
{
|
||||
size_t m_disc_index;
|
||||
size_t m_section_index;
|
||||
size_t m_option_index;
|
||||
size_t m_choice_index;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(GuiRiivolutionPatchIndex);
|
||||
|
||||
RiivolutionBootWidget::RiivolutionBootWidget(std::string game_id, std::optional<u16> revision,
|
||||
std::optional<u8> disc, QWidget* parent)
|
||||
: QDialog(parent), m_game_id(std::move(game_id)), m_revision(revision), m_disc_number(disc)
|
||||
{
|
||||
setWindowTitle(tr("Start with Riivolution Patches"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateWidgets();
|
||||
LoadMatchingXMLs();
|
||||
|
||||
resize(QSize(400, 600));
|
||||
}
|
||||
|
||||
RiivolutionBootWidget::~RiivolutionBootWidget() = default;
|
||||
|
||||
void RiivolutionBootWidget::CreateWidgets()
|
||||
{
|
||||
auto* open_xml_button = new QPushButton(tr("Open Riivolution XML..."));
|
||||
auto* boot_game_button = new QPushButton(tr("Start"));
|
||||
boot_game_button->setDefault(true);
|
||||
auto* group_box = new QGroupBox();
|
||||
auto* scroll_area = new QScrollArea();
|
||||
|
||||
auto* stretch_helper = new QVBoxLayout();
|
||||
m_patch_section_layout = new QVBoxLayout();
|
||||
stretch_helper->addLayout(m_patch_section_layout);
|
||||
stretch_helper->addStretch();
|
||||
group_box->setLayout(stretch_helper);
|
||||
scroll_area->setWidget(group_box);
|
||||
scroll_area->setWidgetResizable(true);
|
||||
|
||||
auto* button_layout = new QHBoxLayout();
|
||||
button_layout->addStretch();
|
||||
button_layout->addWidget(open_xml_button, 0, Qt::AlignRight);
|
||||
button_layout->addWidget(boot_game_button, 0, Qt::AlignRight);
|
||||
|
||||
auto* layout = new QVBoxLayout();
|
||||
layout->addWidget(scroll_area);
|
||||
layout->addLayout(button_layout);
|
||||
setLayout(layout);
|
||||
|
||||
connect(open_xml_button, &QPushButton::clicked, this, &RiivolutionBootWidget::OpenXML);
|
||||
connect(boot_game_button, &QPushButton::clicked, this, &RiivolutionBootWidget::BootGame);
|
||||
}
|
||||
|
||||
void RiivolutionBootWidget::LoadMatchingXMLs()
|
||||
{
|
||||
const std::string& riivolution_dir = File::GetUserPath(D_RIIVOLUTION_IDX);
|
||||
for (const std::string& path : Common::DoFileSearch({riivolution_dir + "riivolution"}, {".xml"}))
|
||||
{
|
||||
auto parsed = DiscIO::Riivolution::ParseFile(path);
|
||||
if (!parsed || !parsed->IsValidForGame(m_game_id, m_revision, m_disc_number))
|
||||
continue;
|
||||
MakeGUIForParsedFile(path, *parsed);
|
||||
}
|
||||
}
|
||||
|
||||
void RiivolutionBootWidget::OpenXML()
|
||||
{
|
||||
const std::string& riivolution_dir = File::GetUserPath(D_RIIVOLUTION_IDX);
|
||||
QStringList paths = QFileDialog::getOpenFileNames(
|
||||
this, tr("Select Riivolution XML file"), QString::fromStdString(riivolution_dir),
|
||||
QStringLiteral("%1 (*.xml);;%2 (*)").arg(tr("Riivolution XML files")).arg(tr("All Files")));
|
||||
if (paths.isEmpty())
|
||||
return;
|
||||
|
||||
for (const QString& path : paths)
|
||||
{
|
||||
std::string p = path.toStdString();
|
||||
auto parsed = DiscIO::Riivolution::ParseFile(p);
|
||||
if (!parsed)
|
||||
{
|
||||
ModalMessageBox::warning(
|
||||
this, tr("Failed loading XML."),
|
||||
tr("Did not recognize %1 as a valid Riivolution XML file.").arg(path));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parsed->IsValidForGame(m_game_id, m_revision, m_disc_number))
|
||||
{
|
||||
ModalMessageBox::warning(
|
||||
this, tr("Invalid game."),
|
||||
tr("The patches in %1 are not for the selected game or game revision.").arg(path));
|
||||
continue;
|
||||
}
|
||||
|
||||
MakeGUIForParsedFile(p, *parsed);
|
||||
}
|
||||
}
|
||||
|
||||
void RiivolutionBootWidget::MakeGUIForParsedFile(const std::string& path,
|
||||
DiscIO::Riivolution::Disc input_disc)
|
||||
{
|
||||
const size_t disc_index = m_discs.size();
|
||||
const auto& disc = m_discs.emplace_back(std::move(input_disc));
|
||||
|
||||
for (size_t section_index = 0; section_index < disc.m_sections.size(); ++section_index)
|
||||
{
|
||||
const auto& section = disc.m_sections[section_index];
|
||||
auto* group_box = new QGroupBox(QString::fromStdString(section.m_name));
|
||||
auto* grid_layout = new QGridLayout();
|
||||
group_box->setLayout(grid_layout);
|
||||
|
||||
int row = 0;
|
||||
for (size_t option_index = 0; option_index < section.m_options.size(); ++option_index)
|
||||
{
|
||||
const auto& option = section.m_options[option_index];
|
||||
auto* label = new QLabel(QString::fromStdString(option.m_name));
|
||||
auto* selection = new QComboBox();
|
||||
const GuiRiivolutionPatchIndex gui_disabled_index{disc_index, section_index, option_index, 0};
|
||||
selection->addItem(tr("Disabled"), QVariant::fromValue(gui_disabled_index));
|
||||
for (size_t choice_index = 0; choice_index < option.m_choices.size(); ++choice_index)
|
||||
{
|
||||
const auto& choice = option.m_choices[choice_index];
|
||||
const GuiRiivolutionPatchIndex gui_index{disc_index, section_index, option_index,
|
||||
choice_index + 1};
|
||||
selection->addItem(QString::fromStdString(choice.m_name), QVariant::fromValue(gui_index));
|
||||
}
|
||||
if (option.m_selected_choice <= option.m_choices.size())
|
||||
selection->setCurrentIndex(static_cast<int>(option.m_selected_choice));
|
||||
|
||||
connect(selection, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this, selection](int idx) {
|
||||
const auto gui_index = selection->currentData().value<GuiRiivolutionPatchIndex>();
|
||||
auto& disc = m_discs[gui_index.m_disc_index];
|
||||
auto& section = disc.m_sections[gui_index.m_section_index];
|
||||
auto& option = section.m_options[gui_index.m_option_index];
|
||||
option.m_selected_choice = static_cast<u32>(gui_index.m_choice_index);
|
||||
});
|
||||
|
||||
grid_layout->addWidget(label, row, 0, 1, 1);
|
||||
grid_layout->addWidget(selection, row, 1, 1, 1);
|
||||
++row;
|
||||
}
|
||||
|
||||
m_patch_section_layout->addWidget(group_box);
|
||||
}
|
||||
}
|
||||
|
||||
void RiivolutionBootWidget::BootGame()
|
||||
{
|
||||
const std::string& riivolution_dir = File::GetUserPath(D_RIIVOLUTION_IDX);
|
||||
|
||||
m_patches.clear();
|
||||
for (const auto& disc : m_discs)
|
||||
{
|
||||
auto patches = disc.GeneratePatches(m_game_id);
|
||||
|
||||
// set the root path for each patch
|
||||
for (auto& patch : patches)
|
||||
{
|
||||
if (patch.m_root.empty())
|
||||
SplitPath(disc.m_xml_path, &patch.m_root, nullptr, nullptr);
|
||||
else
|
||||
patch.m_root = riivolution_dir + "/" + patch.m_root;
|
||||
}
|
||||
|
||||
m_patches.insert(m_patches.end(), patches.begin(), patches.end());
|
||||
}
|
||||
|
||||
m_should_boot = true;
|
||||
close();
|
||||
}
|
45
Source/Core/DolphinQt/RiivolutionBootWidget.h
Normal file
45
Source/Core/DolphinQt/RiivolutionBootWidget.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "DiscIO/RiivolutionParser.h"
|
||||
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
|
||||
class RiivolutionBootWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RiivolutionBootWidget(std::string game_id, std::optional<u16> revision,
|
||||
std::optional<u8> disc, QWidget* parent = nullptr);
|
||||
~RiivolutionBootWidget();
|
||||
|
||||
bool ShouldBoot() const { return m_should_boot; }
|
||||
std::vector<DiscIO::Riivolution::Patch>& GetPatches() { return m_patches; }
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
|
||||
void LoadMatchingXMLs();
|
||||
void OpenXML();
|
||||
void MakeGUIForParsedFile(const std::string& path, DiscIO::Riivolution::Disc input_disc);
|
||||
void BootGame();
|
||||
|
||||
std::string m_game_id;
|
||||
std::optional<u16> m_revision;
|
||||
std::optional<u8> m_disc_number;
|
||||
|
||||
bool m_should_boot = false;
|
||||
std::vector<DiscIO::Riivolution::Disc> m_discs;
|
||||
std::vector<DiscIO::Riivolution::Patch> m_patches;
|
||||
|
||||
QVBoxLayout* m_patch_section_layout;
|
||||
};
|
Loading…
Reference in New Issue
Block a user