mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #9886 from AdmiralCurtiss/cheat-search
Reimplement Cheat Search.
This commit is contained in:
@@ -15,6 +15,10 @@ add_library(core
|
||||
BootManager.cpp
|
||||
BootManager.h
|
||||
CheatCodes.h
|
||||
CheatGeneration.cpp
|
||||
CheatGeneration.h
|
||||
CheatSearch.cpp
|
||||
CheatSearch.h
|
||||
CommonTitles.h
|
||||
Config/DefaultLocale.cpp
|
||||
Config/DefaultLocale.h
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Core/CheatGeneration.h"
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Result.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/CheatSearch.h"
|
||||
|
||||
constexpr int AR_SET_BYTE_CMD = 0x00;
|
||||
constexpr int AR_SET_SHORT_CMD = 0x02;
|
||||
constexpr int AR_SET_INT_CMD = 0x04;
|
||||
|
||||
static std::vector<ActionReplay::AREntry> ResultToAREntries(u32 addr, const Cheats::SearchValue& sv)
|
||||
{
|
||||
std::vector<ActionReplay::AREntry> codes;
|
||||
std::vector<u8> data = Cheats::GetValueAsByteVector(sv);
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
const u32 address = (addr + i) & 0x01ff'ffffu;
|
||||
if (Common::AlignUp(address, 4) == address && i + 3 < data.size())
|
||||
{
|
||||
const u8 cmd = AR_SET_INT_CMD;
|
||||
const u32 val = Common::swap32(&data[i]);
|
||||
codes.emplace_back((cmd << 24) | address, val);
|
||||
i += 3;
|
||||
}
|
||||
else if (Common::AlignUp(address, 2) == address && i + 1 < data.size())
|
||||
{
|
||||
const u8 cmd = AR_SET_SHORT_CMD;
|
||||
const u32 val = Common::swap16(&data[i]);
|
||||
codes.emplace_back((cmd << 24) | address, val);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u8 cmd = AR_SET_BYTE_CMD;
|
||||
const u32 val = data[i];
|
||||
codes.emplace_back((cmd << 24) | address, val);
|
||||
}
|
||||
}
|
||||
|
||||
return codes;
|
||||
}
|
||||
|
||||
Common::Result<Cheats::GenerateActionReplayCodeErrorCode, ActionReplay::ARCode>
|
||||
Cheats::GenerateActionReplayCode(const Cheats::CheatSearchSessionBase& session, size_t index)
|
||||
{
|
||||
if (index >= session.GetResultCount())
|
||||
return Cheats::GenerateActionReplayCodeErrorCode::IndexOutOfRange;
|
||||
|
||||
if (session.GetResultValueState(index) != Cheats::SearchResultValueState::ValueFromVirtualMemory)
|
||||
return Cheats::GenerateActionReplayCodeErrorCode::NotVirtualMemory;
|
||||
|
||||
u32 address = session.GetResultAddress(index);
|
||||
|
||||
// check if the address is actually addressable by the ActionReplay system
|
||||
if (((address & 0x01ff'ffffu) | 0x8000'0000u) != address)
|
||||
return Cheats::GenerateActionReplayCodeErrorCode::InvalidAddress;
|
||||
|
||||
ActionReplay::ARCode ar_code;
|
||||
ar_code.enabled = true;
|
||||
ar_code.user_defined = true;
|
||||
ar_code.name = fmt::format("Generated by Cheat Search (Address 0x{:08x})", address);
|
||||
ar_code.ops = ResultToAREntries(address, session.GetResultValueAsSearchValue(index));
|
||||
|
||||
return ar_code;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/Result.h"
|
||||
|
||||
#include "Core/ActionReplay.h"
|
||||
|
||||
namespace Cheats
|
||||
{
|
||||
class CheatSearchSessionBase;
|
||||
|
||||
enum class GenerateActionReplayCodeErrorCode
|
||||
{
|
||||
IndexOutOfRange,
|
||||
NotVirtualMemory,
|
||||
InvalidAddress,
|
||||
};
|
||||
|
||||
Common::Result<GenerateActionReplayCodeErrorCode, ActionReplay::ARCode>
|
||||
GenerateActionReplayCode(const Cheats::CheatSearchSessionBase& session, size_t index);
|
||||
} // namespace Cheats
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,219 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Result.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
||||
namespace Cheats
|
||||
{
|
||||
enum class CompareType
|
||||
{
|
||||
Equal,
|
||||
NotEqual,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
};
|
||||
|
||||
enum class FilterType
|
||||
{
|
||||
CompareAgainstSpecificValue,
|
||||
CompareAgainstLastValue,
|
||||
DoNotFilter,
|
||||
};
|
||||
|
||||
enum class DataType
|
||||
{
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
S8,
|
||||
S16,
|
||||
S32,
|
||||
S64,
|
||||
F32,
|
||||
F64,
|
||||
};
|
||||
|
||||
struct SearchValue
|
||||
{
|
||||
std::variant<u8, u16, u32, u64, s8, s16, s32, s64, float, double> m_value;
|
||||
};
|
||||
|
||||
enum class SearchResultValueState : u8
|
||||
{
|
||||
ValueFromPhysicalMemory,
|
||||
ValueFromVirtualMemory,
|
||||
AddressNotAccessible,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct SearchResult
|
||||
{
|
||||
T m_value;
|
||||
SearchResultValueState m_value_state;
|
||||
u32 m_address;
|
||||
|
||||
bool IsValueValid() const
|
||||
{
|
||||
return m_value_state == SearchResultValueState::ValueFromPhysicalMemory ||
|
||||
m_value_state == SearchResultValueState::ValueFromVirtualMemory;
|
||||
}
|
||||
};
|
||||
|
||||
struct MemoryRange
|
||||
{
|
||||
u32 m_start;
|
||||
u64 m_length;
|
||||
|
||||
MemoryRange(u32 start, u64 length) : m_start(start), m_length(length) {}
|
||||
};
|
||||
|
||||
enum class SearchErrorCode
|
||||
{
|
||||
Success,
|
||||
|
||||
// No emulation is currently active.
|
||||
NoEmulationActive,
|
||||
|
||||
// The parameter set given to the search function is bogus.
|
||||
InvalidParameters,
|
||||
|
||||
// This is returned if PowerPC::RequestedAddressSpace::Virtual is given but the MSR.DR flag is
|
||||
// currently off in the emulated game.
|
||||
VirtualAddressesCurrentlyNotAccessible,
|
||||
};
|
||||
|
||||
// Returns the corresponding DataType enum for the value currently held by the given SearchValue.
|
||||
DataType GetDataType(const SearchValue& value);
|
||||
|
||||
// Converts the given value to a std::vector<u8>, regardless of the actual stored value type.
|
||||
// Returned array is in big endian byte order, so it can be copied directly into emulated memory for
|
||||
// patches or action replay codes.
|
||||
std::vector<u8> GetValueAsByteVector(const SearchValue& value);
|
||||
|
||||
// Do a new search across the given memory region in the given address space, only keeping values
|
||||
// for which the given validator returns true.
|
||||
template <typename T>
|
||||
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>>
|
||||
NewSearch(const std::vector<MemoryRange>& memory_ranges,
|
||||
PowerPC::RequestedAddressSpace address_space, bool aligned,
|
||||
const std::function<bool(const T& value)>& validator);
|
||||
|
||||
// Refresh the values for the given results in the given address space, only keeping values for
|
||||
// which the given validator returns true.
|
||||
template <typename T>
|
||||
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>>
|
||||
NextSearch(const std::vector<SearchResult<T>>& previous_results,
|
||||
PowerPC::RequestedAddressSpace address_space,
|
||||
const std::function<bool(const T& new_value, const T& old_value)>& validator);
|
||||
|
||||
class CheatSearchSessionBase
|
||||
{
|
||||
public:
|
||||
virtual ~CheatSearchSessionBase();
|
||||
|
||||
// Set the value comparison operation used by subsequent searches.
|
||||
virtual void SetCompareType(CompareType compare_type) = 0;
|
||||
|
||||
// Set the filtering value target used by subsequent searches.
|
||||
virtual void SetFilterType(FilterType filter_type) = 0;
|
||||
|
||||
// Set the value of the CompareAgainstSpecificValue filter used by subsequent searches.
|
||||
virtual bool SetValueFromString(const std::string& value_as_string) = 0;
|
||||
|
||||
// Resets the search results, causing the next search to act as a new search.
|
||||
virtual void ResetResults() = 0;
|
||||
|
||||
// Run either a new search or a next search based on the current state of this session.
|
||||
virtual SearchErrorCode RunSearch() = 0;
|
||||
|
||||
virtual size_t GetMemoryRangeCount() = 0;
|
||||
virtual MemoryRange GetMemoryRange(size_t index) = 0;
|
||||
virtual PowerPC::RequestedAddressSpace GetAddressSpace() = 0;
|
||||
virtual DataType GetDataType() = 0;
|
||||
virtual bool GetAligned() = 0;
|
||||
|
||||
virtual size_t GetResultCount() const = 0;
|
||||
virtual size_t GetValidValueCount() const = 0;
|
||||
virtual u32 GetResultAddress(size_t index) const = 0;
|
||||
virtual SearchValue GetResultValueAsSearchValue(size_t index) const = 0;
|
||||
virtual std::string GetResultValueAsString(size_t index, bool hex) const = 0;
|
||||
virtual SearchResultValueState GetResultValueState(size_t index) const = 0;
|
||||
virtual bool WasFirstSearchDone() const = 0;
|
||||
|
||||
// Create a complete copy of this search session.
|
||||
virtual std::unique_ptr<CheatSearchSessionBase> Clone() const = 0;
|
||||
|
||||
// Create a partial copy of this search session. Only the results with the passed indices are
|
||||
// copied. This is useful if you want to run a next search on only partial result data of a
|
||||
// previous search.
|
||||
virtual std::unique_ptr<CheatSearchSessionBase>
|
||||
ClonePartial(const std::vector<size_t>& result_indices) const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CheatSearchSession : public CheatSearchSessionBase
|
||||
{
|
||||
public:
|
||||
CheatSearchSession(std::vector<MemoryRange> memory_ranges,
|
||||
PowerPC::RequestedAddressSpace address_space, bool aligned);
|
||||
CheatSearchSession(const CheatSearchSession& session);
|
||||
CheatSearchSession(CheatSearchSession&& session);
|
||||
CheatSearchSession& operator=(const CheatSearchSession& session);
|
||||
CheatSearchSession& operator=(CheatSearchSession&& session);
|
||||
~CheatSearchSession() override;
|
||||
|
||||
void SetCompareType(CompareType compare_type) override;
|
||||
void SetFilterType(FilterType filter_type) override;
|
||||
bool SetValueFromString(const std::string& value_as_string) override;
|
||||
|
||||
void ResetResults() override;
|
||||
SearchErrorCode RunSearch() override;
|
||||
|
||||
size_t GetMemoryRangeCount() override;
|
||||
MemoryRange GetMemoryRange(size_t index) override;
|
||||
PowerPC::RequestedAddressSpace GetAddressSpace() override;
|
||||
DataType GetDataType() override;
|
||||
bool GetAligned() override;
|
||||
|
||||
size_t GetResultCount() const override;
|
||||
size_t GetValidValueCount() const override;
|
||||
u32 GetResultAddress(size_t index) const override;
|
||||
T GetResultValue(size_t index) const;
|
||||
SearchValue GetResultValueAsSearchValue(size_t index) const override;
|
||||
std::string GetResultValueAsString(size_t index, bool hex) const override;
|
||||
SearchResultValueState GetResultValueState(size_t index) const override;
|
||||
bool WasFirstSearchDone() const override;
|
||||
|
||||
std::unique_ptr<CheatSearchSessionBase> Clone() const override;
|
||||
std::unique_ptr<CheatSearchSessionBase>
|
||||
ClonePartial(const std::vector<size_t>& result_indices) const override;
|
||||
|
||||
private:
|
||||
std::vector<SearchResult<T>> m_search_results;
|
||||
std::vector<MemoryRange> m_memory_ranges;
|
||||
PowerPC::RequestedAddressSpace m_address_space;
|
||||
CompareType m_compare_type = CompareType::Equal;
|
||||
FilterType m_filter_type = FilterType::DoNotFilter;
|
||||
std::optional<T> m_value = std::nullopt;
|
||||
bool m_aligned;
|
||||
bool m_first_search_done = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<CheatSearchSessionBase> MakeSession(std::vector<MemoryRange> memory_ranges,
|
||||
PowerPC::RequestedAddressSpace address_space,
|
||||
bool aligned, DataType data_type);
|
||||
} // namespace Cheats
|
||||
@@ -163,6 +163,8 @@
|
||||
<ClInclude Include="Core\Boot\ElfTypes.h" />
|
||||
<ClInclude Include="Core\BootManager.h" />
|
||||
<ClInclude Include="Core\CheatCodes.h" />
|
||||
<ClInclude Include="Core\CheatGeneration.h" />
|
||||
<ClInclude Include="Core\CheatSearch.h" />
|
||||
<ClInclude Include="Core\CommonTitles.h" />
|
||||
<ClInclude Include="Core\Config\DefaultLocale.h" />
|
||||
<ClInclude Include="Core\Config\FreeLookSettings.h" />
|
||||
@@ -743,6 +745,8 @@
|
||||
<ClCompile Include="Core\Boot\DolReader.cpp" />
|
||||
<ClCompile Include="Core\Boot\ElfReader.cpp" />
|
||||
<ClCompile Include="Core\BootManager.cpp" />
|
||||
<ClCompile Include="Core\CheatGeneration.cpp" />
|
||||
<ClCompile Include="Core\CheatSearch.cpp" />
|
||||
<ClCompile Include="Core\Config\DefaultLocale.cpp" />
|
||||
<ClCompile Include="Core\Config\FreeLookSettings.cpp" />
|
||||
<ClCompile Include="Core\Config\GraphicsSettings.cpp" />
|
||||
|
||||
@@ -16,6 +16,10 @@ set(CMAKE_AUTOMOC ON)
|
||||
add_executable(dolphin-emu
|
||||
AboutDialog.cpp
|
||||
AboutDialog.h
|
||||
CheatSearchFactoryWidget.cpp
|
||||
CheatSearchFactoryWidget.h
|
||||
CheatSearchWidget.cpp
|
||||
CheatSearchWidget.h
|
||||
CheatsManager.cpp
|
||||
CheatsManager.h
|
||||
ConvertDialog.cpp
|
||||
@@ -269,6 +273,8 @@ add_executable(dolphin-emu
|
||||
QtUtils/ModalMessageBox.cpp
|
||||
QtUtils/ModalMessageBox.h
|
||||
QtUtils/ParallelProgressDialog.h
|
||||
QtUtils/PartiallyClosableTabWidget.cpp
|
||||
QtUtils/PartiallyClosableTabWidget.h
|
||||
QtUtils/ImageConverter.cpp
|
||||
QtUtils/ImageConverter.h
|
||||
QtUtils/UTF8CodePointCountValidator.cpp
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/CheatSearchFactoryWidget.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/CheatSearch.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
||||
CheatSearchFactoryWidget::CheatSearchFactoryWidget()
|
||||
{
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
RefreshGui();
|
||||
}
|
||||
|
||||
CheatSearchFactoryWidget::~CheatSearchFactoryWidget() = default;
|
||||
|
||||
Q_DECLARE_METATYPE(Cheats::DataType);
|
||||
|
||||
void CheatSearchFactoryWidget::CreateWidgets()
|
||||
{
|
||||
auto* layout = new QVBoxLayout();
|
||||
|
||||
auto* address_space_group = new QGroupBox(tr("Address Space"));
|
||||
auto* address_space_layout = new QVBoxLayout();
|
||||
address_space_group->setLayout(address_space_layout);
|
||||
|
||||
m_standard_address_space = new QRadioButton(tr("Typical GameCube/Wii Address Space"));
|
||||
m_standard_address_space->setChecked(true);
|
||||
m_custom_address_space = new QRadioButton(tr("Custom Address Space"));
|
||||
|
||||
QLabel* label_standard_address_space =
|
||||
new QLabel(tr("Sets up the search using standard MEM1 and (on Wii) MEM2 mappings in virtual "
|
||||
"address space. This will work for the vast majority of games."));
|
||||
label_standard_address_space->setWordWrap(true);
|
||||
|
||||
auto* custom_address_space_layout = new QVBoxLayout();
|
||||
custom_address_space_layout->setMargin(6);
|
||||
auto* custom_address_space_button_group = new QButtonGroup();
|
||||
m_custom_virtual_address_space = new QRadioButton(tr("Use virtual addresses when possible"));
|
||||
m_custom_virtual_address_space->setChecked(true);
|
||||
m_custom_physical_address_space = new QRadioButton(tr("Use physical addresses"));
|
||||
m_custom_effective_address_space =
|
||||
new QRadioButton(tr("Use memory mapper configuration at time of scan"));
|
||||
custom_address_space_button_group->addButton(m_custom_virtual_address_space);
|
||||
custom_address_space_button_group->addButton(m_custom_physical_address_space);
|
||||
custom_address_space_button_group->addButton(m_custom_effective_address_space);
|
||||
custom_address_space_layout->addWidget(m_custom_virtual_address_space);
|
||||
custom_address_space_layout->addWidget(m_custom_physical_address_space);
|
||||
custom_address_space_layout->addWidget(m_custom_effective_address_space);
|
||||
|
||||
QLabel* label_range_start = new QLabel(tr("Range Start: "));
|
||||
m_custom_address_start = new QLineEdit(QStringLiteral("0x80000000"));
|
||||
QLabel* label_range_end = new QLabel(tr("Range End: "));
|
||||
m_custom_address_end = new QLineEdit(QStringLiteral("0x81800000"));
|
||||
custom_address_space_layout->addWidget(label_range_start);
|
||||
custom_address_space_layout->addWidget(m_custom_address_start);
|
||||
custom_address_space_layout->addWidget(label_range_end);
|
||||
custom_address_space_layout->addWidget(m_custom_address_end);
|
||||
|
||||
address_space_layout->addWidget(m_standard_address_space);
|
||||
address_space_layout->addWidget(label_standard_address_space);
|
||||
address_space_layout->addWidget(m_custom_address_space);
|
||||
address_space_layout->addLayout(custom_address_space_layout);
|
||||
|
||||
layout->addWidget(address_space_group);
|
||||
|
||||
auto* data_type_group = new QGroupBox(tr("Data Type"));
|
||||
auto* data_type_layout = new QVBoxLayout();
|
||||
data_type_group->setLayout(data_type_layout);
|
||||
|
||||
m_data_type_dropdown = new QComboBox();
|
||||
m_data_type_dropdown->addItem(tr("8-bit Unsigned Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::U8));
|
||||
m_data_type_dropdown->addItem(tr("16-bit Unsigned Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::U16));
|
||||
m_data_type_dropdown->addItem(tr("32-bit Unsigned Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::U32));
|
||||
m_data_type_dropdown->addItem(tr("64-bit Unsigned Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::U64));
|
||||
m_data_type_dropdown->addItem(tr("8-bit Signed Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::S8));
|
||||
m_data_type_dropdown->addItem(tr("16-bit Signed Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::S16));
|
||||
m_data_type_dropdown->addItem(tr("32-bit Signed Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::S32));
|
||||
m_data_type_dropdown->addItem(tr("64-bit Signed Integer"),
|
||||
QVariant::fromValue(Cheats::DataType::S64));
|
||||
m_data_type_dropdown->addItem(tr("32-bit Float"), QVariant::fromValue(Cheats::DataType::F32));
|
||||
m_data_type_dropdown->addItem(tr("64-bit Float"), QVariant::fromValue(Cheats::DataType::F64));
|
||||
m_data_type_dropdown->setCurrentIndex(6); // select 32bit signed int by default
|
||||
|
||||
data_type_layout->addWidget(m_data_type_dropdown);
|
||||
|
||||
m_data_type_aligned = new QCheckBox(tr("Aligned to data type length"));
|
||||
m_data_type_aligned->setChecked(true);
|
||||
|
||||
data_type_layout->addWidget(m_data_type_aligned);
|
||||
|
||||
layout->addWidget(data_type_group);
|
||||
|
||||
m_new_search = new QPushButton(tr("New Search"));
|
||||
layout->addWidget(m_new_search);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void CheatSearchFactoryWidget::ConnectWidgets()
|
||||
{
|
||||
connect(m_new_search, &QPushButton::clicked, this, &CheatSearchFactoryWidget::OnNewSearchClicked);
|
||||
connect(m_standard_address_space, &QPushButton::toggled, this,
|
||||
&CheatSearchFactoryWidget::OnAddressSpaceRadioChanged);
|
||||
connect(m_custom_address_space, &QRadioButton::toggled, this,
|
||||
&CheatSearchFactoryWidget::OnAddressSpaceRadioChanged);
|
||||
}
|
||||
|
||||
void CheatSearchFactoryWidget::RefreshGui()
|
||||
{
|
||||
bool enable_custom = m_custom_address_space->isChecked();
|
||||
m_custom_virtual_address_space->setEnabled(enable_custom);
|
||||
m_custom_physical_address_space->setEnabled(enable_custom);
|
||||
m_custom_effective_address_space->setEnabled(enable_custom);
|
||||
m_custom_address_start->setEnabled(enable_custom);
|
||||
m_custom_address_end->setEnabled(enable_custom);
|
||||
}
|
||||
|
||||
void CheatSearchFactoryWidget::OnAddressSpaceRadioChanged()
|
||||
{
|
||||
RefreshGui();
|
||||
}
|
||||
|
||||
void CheatSearchFactoryWidget::OnNewSearchClicked()
|
||||
{
|
||||
std::vector<Cheats::MemoryRange> memory_ranges;
|
||||
PowerPC::RequestedAddressSpace address_space;
|
||||
if (m_standard_address_space->isChecked())
|
||||
{
|
||||
memory_ranges.emplace_back(0x80000000, Memory::GetRamSizeReal());
|
||||
if (SConfig::GetInstance().bWii)
|
||||
memory_ranges.emplace_back(0x90000000, Memory::GetExRamSizeReal());
|
||||
address_space = PowerPC::RequestedAddressSpace::Virtual;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string address_start_str = m_custom_address_start->text().toStdString();
|
||||
const std::string address_end_str = m_custom_address_end->text().toStdString();
|
||||
|
||||
u64 address_start;
|
||||
u64 address_end;
|
||||
if (!TryParse(address_start_str, &address_start) || !TryParse(address_end_str, &address_end))
|
||||
return;
|
||||
if (address_end <= address_start || address_end > 0x1'0000'0000)
|
||||
return;
|
||||
|
||||
memory_ranges.emplace_back(static_cast<u32>(address_start), address_end - address_start);
|
||||
|
||||
if (m_custom_virtual_address_space->isChecked())
|
||||
address_space = PowerPC::RequestedAddressSpace::Virtual;
|
||||
else if (m_custom_physical_address_space->isChecked())
|
||||
address_space = PowerPC::RequestedAddressSpace::Physical;
|
||||
else
|
||||
address_space = PowerPC::RequestedAddressSpace::Effective;
|
||||
}
|
||||
|
||||
bool aligned = m_data_type_aligned->isChecked();
|
||||
auto data_type = m_data_type_dropdown->currentData().value<Cheats::DataType>();
|
||||
auto session = Cheats::MakeSession(std::move(memory_ranges), address_space, aligned, data_type);
|
||||
if (session)
|
||||
emit NewSessionCreated(*session);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "Core/CheatSearch.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QRadioButton;
|
||||
|
||||
class CheatSearchFactoryWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheatSearchFactoryWidget();
|
||||
~CheatSearchFactoryWidget() override;
|
||||
|
||||
signals:
|
||||
void NewSessionCreated(const Cheats::CheatSearchSessionBase& session);
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
|
||||
void RefreshGui();
|
||||
|
||||
void OnAddressSpaceRadioChanged();
|
||||
void OnNewSearchClicked();
|
||||
|
||||
QRadioButton* m_standard_address_space;
|
||||
QRadioButton* m_custom_address_space;
|
||||
|
||||
QRadioButton* m_custom_virtual_address_space;
|
||||
QRadioButton* m_custom_physical_address_space;
|
||||
QRadioButton* m_custom_effective_address_space;
|
||||
|
||||
QLineEdit* m_custom_address_start;
|
||||
QLineEdit* m_custom_address_end;
|
||||
|
||||
QComboBox* m_data_type_dropdown;
|
||||
QCheckBox* m_data_type_aligned;
|
||||
|
||||
QPushButton* m_new_search;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/CheatSearch.h"
|
||||
|
||||
namespace ActionReplay
|
||||
{
|
||||
struct ARCode;
|
||||
}
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QTableWidget;
|
||||
class QTableWidgetItem;
|
||||
|
||||
struct CheatSearchTableUserData
|
||||
{
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
class CheatSearchWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionBase> session);
|
||||
~CheatSearchWidget() override;
|
||||
|
||||
signals:
|
||||
void ActionReplayCodeGenerated(const ActionReplay::ARCode& ar_code);
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
|
||||
void OnNextScanClicked();
|
||||
void OnRefreshClicked();
|
||||
void OnResetClicked();
|
||||
void OnAddressTableItemChanged(QTableWidgetItem* item);
|
||||
void OnAddressTableContextMenu();
|
||||
void OnValueSourceChanged();
|
||||
void OnHexCheckboxStateChanged();
|
||||
|
||||
bool RefreshValues();
|
||||
void UpdateGuiTable();
|
||||
void GenerateARCode();
|
||||
|
||||
std::unique_ptr<Cheats::CheatSearchSessionBase> m_session;
|
||||
|
||||
// storage for the 'Current Value' column's data
|
||||
std::unordered_map<u32, std::string> m_address_table_current_values;
|
||||
|
||||
// storage for user-entered metadata such as text descriptions for memory addresses
|
||||
// this is intentionally NOT cleared when updating values or resetting or similar
|
||||
std::unordered_map<u32, CheatSearchTableUserData> m_address_table_user_data;
|
||||
|
||||
QComboBox* m_compare_type_dropdown;
|
||||
QComboBox* m_value_source_dropdown;
|
||||
QLineEdit* m_given_value_text;
|
||||
QLabel* m_info_label_1;
|
||||
QLabel* m_info_label_2;
|
||||
QPushButton* m_next_scan_button;
|
||||
QPushButton* m_refresh_values_button;
|
||||
QPushButton* m_reset_button;
|
||||
QCheckBox* m_display_values_in_hex_checkbox;
|
||||
QTableWidget* m_address_table;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <QDialog>
|
||||
@@ -12,29 +13,19 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "DolphinQt/GameList/GameListModel.h"
|
||||
|
||||
#include "Core/CheatSearch.h"
|
||||
|
||||
class ARCodeWidget;
|
||||
class QComboBox;
|
||||
class GeckoCodeWidget;
|
||||
class CheatSearchFactoryWidget;
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QRadioButton;
|
||||
class QSplitter;
|
||||
class QTabWidget;
|
||||
class QTableWidget;
|
||||
class QTableWidgetItem;
|
||||
struct Result;
|
||||
class PartiallyClosableTabWidget;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
enum class State;
|
||||
}
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
class GameFile;
|
||||
}
|
||||
|
||||
class CheatsManager : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -43,51 +34,20 @@ public:
|
||||
~CheatsManager();
|
||||
|
||||
private:
|
||||
QWidget* CreateCheatSearch();
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
void OnStateChanged(Core::State state);
|
||||
|
||||
size_t GetTypeSize() const;
|
||||
std::function<bool(u32)> CreateMatchFunction();
|
||||
|
||||
void Reset();
|
||||
void NewSearch();
|
||||
void NextSearch();
|
||||
void Update();
|
||||
void GenerateARCode();
|
||||
|
||||
void OnWatchContextMenu();
|
||||
void OnMatchContextMenu();
|
||||
void OnWatchItemChanged(QTableWidgetItem* item);
|
||||
void OnNewSessionCreated(const Cheats::CheatSearchSessionBase& session);
|
||||
void OnTabCloseRequested(int index);
|
||||
|
||||
std::string m_game_id;
|
||||
std::string m_game_tdb_id;
|
||||
u16 m_revision = 0;
|
||||
|
||||
std::vector<Result> m_results;
|
||||
std::vector<Result> m_watch;
|
||||
QDialogButtonBox* m_button_box;
|
||||
QTabWidget* m_tab_widget = nullptr;
|
||||
PartiallyClosableTabWidget* m_tab_widget = nullptr;
|
||||
|
||||
QWidget* m_cheat_search;
|
||||
ARCodeWidget* m_ar_code = nullptr;
|
||||
|
||||
QLabel* m_result_label;
|
||||
QTableWidget* m_match_table;
|
||||
QTableWidget* m_watch_table;
|
||||
QSplitter* m_option_splitter;
|
||||
QSplitter* m_table_splitter;
|
||||
QComboBox* m_match_length;
|
||||
QComboBox* m_match_operation;
|
||||
QLineEdit* m_match_value;
|
||||
QPushButton* m_match_new;
|
||||
QPushButton* m_match_next;
|
||||
QPushButton* m_match_refresh;
|
||||
QPushButton* m_match_reset;
|
||||
|
||||
QRadioButton* m_match_decimal;
|
||||
QRadioButton* m_match_hexadecimal;
|
||||
QRadioButton* m_match_octal;
|
||||
bool m_updating = false;
|
||||
GeckoCodeWidget* m_gecko_code = nullptr;
|
||||
CheatSearchFactoryWidget* m_cheat_search_new = nullptr;
|
||||
};
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AboutDialog.cpp" />
|
||||
<ClCompile Include="CheatSearchFactoryWidget.cpp" />
|
||||
<ClCompile Include="CheatSearchWidget.cpp" />
|
||||
<ClCompile Include="CheatsManager.cpp" />
|
||||
<ClCompile Include="Config\ARCodeWidget.cpp" />
|
||||
<ClCompile Include="Config\CheatCodeEditor.cpp" />
|
||||
@@ -168,6 +170,7 @@
|
||||
<ClCompile Include="QtUtils\FlowLayout.cpp" />
|
||||
<ClCompile Include="QtUtils\ImageConverter.cpp" />
|
||||
<ClCompile Include="QtUtils\ModalMessageBox.cpp" />
|
||||
<ClCompile Include="QtUtils\PartiallyClosableTabWidget.cpp" />
|
||||
<ClCompile Include="QtUtils\UTF8CodePointCountValidator.cpp" />
|
||||
<ClCompile Include="QtUtils\WindowActivationEventFilter.cpp" />
|
||||
<ClCompile Include="QtUtils\WinIconHelper.cpp" />
|
||||
@@ -227,6 +230,8 @@
|
||||
<ClInclude Include="Translation.h" />
|
||||
<ClInclude Include="WiiUpdate.h" />
|
||||
<QtMoc Include="AboutDialog.h" />
|
||||
<QtMoc Include="CheatSearchFactoryWidget.h" />
|
||||
<QtMoc Include="CheatSearchWidget.h" />
|
||||
<QtMoc Include="CheatsManager.h" />
|
||||
<QtMoc Include="Config\ARCodeWidget.h" />
|
||||
<QtMoc Include="Config\CheatWarningWidget.h" />
|
||||
@@ -340,6 +345,7 @@
|
||||
<QtMoc Include="QtUtils\ElidedButton.h" />
|
||||
<QtMoc Include="QtUtils\FileOpenEventFilter.h" />
|
||||
<QtMoc Include="QtUtils\ParallelProgressDialog.h" />
|
||||
<QtMoc Include="QtUtils\PartiallyClosableTabWidget.h" />
|
||||
<QtMoc Include="QtUtils\UTF8CodePointCountValidator.h" />
|
||||
<QtMoc Include="QtUtils\WindowActivationEventFilter.h" />
|
||||
<QtMoc Include="RenderWidget.h" />
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/QtUtils/PartiallyClosableTabWidget.h"
|
||||
|
||||
#include <QStyle>
|
||||
#include <QTabBar>
|
||||
|
||||
PartiallyClosableTabWidget::PartiallyClosableTabWidget(QWidget* parent) : QTabWidget(parent)
|
||||
{
|
||||
setTabsClosable(true);
|
||||
}
|
||||
|
||||
void PartiallyClosableTabWidget::setTabUnclosable(int index)
|
||||
{
|
||||
QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)style()->styleHint(
|
||||
QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
|
||||
tabBar()->setTabButton(index, closeSide, nullptr);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
class QEvent;
|
||||
|
||||
class PartiallyClosableTabWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PartiallyClosableTabWidget(QWidget* parent = nullptr);
|
||||
|
||||
void setTabUnclosable(int index);
|
||||
};
|
||||
Reference in New Issue
Block a user