dolphin/Source/Core/DolphinQt2/Config/PatchesWidget.cpp
JosJuice 1f1dae367d Unify ISOFile (wx) with GameFile (Qt) and put it in UICommon
The original reason I wanted to do this was so that we can replace
the Android-specific code with this in the future, but of course,
just deduplicating between DolphinWX and DolphinQt2 is nice too.

Fixes:

- DolphinQt2 showing the wrong size for split WBFS disc images.

- DolphinQt2 being case sensitive when checking if a file is a DOL/ELF.

- DolphinQt2 not detecting when a Wii banner has become available
after the game list cache was created.

Removes:

- DolphinWX's ability to load PNGs as custom banners. But it was
already rather broken (see https://bugs.dolphin-emu.org/issues/10365
and https://bugs.dolphin-emu.org/issues/10366). The reason I removed
this was because PNG decoding relied on wx code and we don't have any
good non-wx/Qt code for loading PNG files right now (let's not use
SOIL), but we should be able to use libpng directly to implement PNG
loading in the future.

- DolphinQt2's ability to ignore a cached game if the last modified
time differs. We currently don't have a non-wx/Qt way to get the time.
2018-03-09 13:08:38 +01:00

180 lines
4.6 KiB
C++

// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt2/Config/PatchesWidget.h"
#include <QGridLayout>
#include <QListWidget>
#include <QPushButton>
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/StringUtil.h"
#include "Core/ConfigManager.h"
#include "DolphinQt2/Config/NewPatchDialog.h"
PatchesWidget::PatchesWidget(const UICommon::GameFile& game)
: m_game(game), m_game_id(game.GetGameID()), m_game_revision(game.GetRevision())
{
IniFile game_ini_local;
game_ini_local.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
IniFile game_ini_default = SConfig::GetInstance().LoadDefaultGameIni(m_game_id, m_game_revision);
PatchEngine::LoadPatchSection("OnFrame", m_patches, game_ini_default, game_ini_local);
CreateWidgets();
ConnectWidgets();
Update();
UpdateActions();
}
void PatchesWidget::CreateWidgets()
{
m_list = new QListWidget;
m_add_button = new QPushButton(tr("&Add..."));
m_edit_button = new QPushButton();
m_remove_button = new QPushButton(tr("&Remove"));
auto* layout = new QGridLayout;
layout->addWidget(m_list, 0, 0, 1, -1);
layout->addWidget(m_add_button, 1, 0);
layout->addWidget(m_edit_button, 1, 2);
layout->addWidget(m_remove_button, 1, 1);
setLayout(layout);
}
void PatchesWidget::ConnectWidgets()
{
connect(m_list, &QListWidget::itemSelectionChanged, this, &PatchesWidget::UpdateActions);
connect(m_list, &QListWidget::itemChanged, this, &PatchesWidget::OnItemChanged);
connect(m_remove_button, &QPushButton::pressed, this, &PatchesWidget::OnRemove);
connect(m_add_button, &QPushButton::pressed, this, &PatchesWidget::OnAdd);
connect(m_edit_button, &QPushButton::pressed, this, &PatchesWidget::OnEdit);
}
void PatchesWidget::OnItemChanged(QListWidgetItem* item)
{
m_patches[m_list->row(item)].active = (item->checkState() == Qt::Checked);
SavePatches();
}
void PatchesWidget::OnAdd()
{
PatchEngine::Patch patch;
patch.user_defined = true;
if (NewPatchDialog(patch).exec())
{
m_patches.push_back(patch);
SavePatches();
Update();
}
}
void PatchesWidget::OnEdit()
{
if (m_list->selectedItems().isEmpty())
return;
auto* item = m_list->selectedItems()[0];
auto patch = m_patches[m_list->row(item)];
if (!patch.user_defined)
{
// i18n: If there is a pre-defined patch with the name %1 and the user wants to edit it,
// a copy of it gets created with this name
patch.name = tr("%1 (Copy)").arg(QString::fromStdString(patch.name)).toStdString();
}
if (NewPatchDialog(patch).exec())
{
if (patch.user_defined)
{
m_patches[m_list->row(item)] = patch;
}
else
{
patch.user_defined = true;
m_patches.push_back(patch);
}
SavePatches();
Update();
}
}
void PatchesWidget::OnRemove()
{
if (m_list->selectedItems().isEmpty())
return;
m_patches.erase(m_patches.begin() + m_list->row(m_list->selectedItems()[0]));
SavePatches();
Update();
}
void PatchesWidget::SavePatches()
{
std::vector<std::string> lines;
std::vector<std::string> lines_enabled;
for (const auto& patch : m_patches)
{
if (patch.active)
lines_enabled.push_back("$" + patch.name);
if (!patch.user_defined)
continue;
lines.push_back("$" + patch.name);
for (const auto& entry : patch.entries)
{
lines.push_back(StringFromFormat("0x%08X:%s:0x%08X", entry.address,
PatchEngine::PatchTypeStrings[entry.type], entry.value));
}
}
IniFile game_ini_local;
game_ini_local.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
game_ini_local.SetLines("OnFrame_Enabled", lines_enabled);
game_ini_local.SetLines("OnFrame", lines);
game_ini_local.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
}
void PatchesWidget::Update()
{
m_list->clear();
for (const auto& patch : m_patches)
{
auto* item = new QListWidgetItem(QString::fromStdString(patch.name));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(patch.active ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, patch.user_defined);
m_list->addItem(item);
}
}
void PatchesWidget::UpdateActions()
{
bool selected = !m_list->selectedItems().isEmpty();
auto* item = m_list->selectedItems()[0];
bool user_defined = selected ? item->data(Qt::UserRole).toBool() : true;
m_edit_button->setEnabled(selected);
m_edit_button->setText(user_defined ? tr("&Edit...") : tr("&Clone..."));
m_remove_button->setEnabled(selected && user_defined);
}