diff --git a/Source/Core/Core/CheatSearch.cpp b/Source/Core/Core/CheatSearch.cpp index 9710f31624..9d2d288555 100644 --- a/Source/Core/Core/CheatSearch.cpp +++ b/Source/Core/Core/CheatSearch.cpp @@ -340,22 +340,31 @@ void Cheats::CheatSearchSession::SetFilterType(FilterType filter_type) } template -static std::optional ParseValue(const std::string& str) +static std::optional ParseValue(const std::string& str, bool force_parse_as_hex) { if (str.empty()) return std::nullopt; T tmp; - if (TryParse(str, &tmp)) - return tmp; + if constexpr (std::is_integral_v) + { + if (TryParse(str, &tmp, force_parse_as_hex ? 16 : 0)) + return tmp; + } + else + { + if (TryParse(str, &tmp)) + return tmp; + } return std::nullopt; } template -bool Cheats::CheatSearchSession::SetValueFromString(const std::string& value_as_string) +bool Cheats::CheatSearchSession::SetValueFromString(const std::string& value_as_string, + bool force_parse_as_hex) { - m_value = ParseValue(value_as_string); + m_value = ParseValue(value_as_string, force_parse_as_hex); return m_value.has_value(); } @@ -469,35 +478,47 @@ Cheats::SearchErrorCode Cheats::CheatSearchSession::RunSearch() } template -size_t Cheats::CheatSearchSession::GetMemoryRangeCount() +size_t Cheats::CheatSearchSession::GetMemoryRangeCount() const { return m_memory_ranges.size(); } template -Cheats::MemoryRange Cheats::CheatSearchSession::GetMemoryRange(size_t index) +Cheats::MemoryRange Cheats::CheatSearchSession::GetMemoryRange(size_t index) const { return m_memory_ranges[index]; } template -PowerPC::RequestedAddressSpace Cheats::CheatSearchSession::GetAddressSpace() +PowerPC::RequestedAddressSpace Cheats::CheatSearchSession::GetAddressSpace() const { return m_address_space; } template -Cheats::DataType Cheats::CheatSearchSession::GetDataType() +Cheats::DataType Cheats::CheatSearchSession::GetDataType() const { return Cheats::GetDataType(Cheats::SearchValue{T(0)}); } template -bool Cheats::CheatSearchSession::GetAligned() +bool Cheats::CheatSearchSession::GetAligned() const { return m_aligned; } +template +bool Cheats::CheatSearchSession::IsIntegerType() const +{ + return std::is_integral_v; +} + +template +bool Cheats::CheatSearchSession::IsFloatingType() const +{ + return std::is_floating_point_v; +} + template size_t Cheats::CheatSearchSession::GetResultCount() const { diff --git a/Source/Core/Core/CheatSearch.h b/Source/Core/Core/CheatSearch.h index 5b777f50f8..8876d2ad86 100644 --- a/Source/Core/Core/CheatSearch.h +++ b/Source/Core/Core/CheatSearch.h @@ -132,7 +132,7 @@ public: 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; + virtual bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) = 0; // Resets the search results, causing the next search to act as a new search. virtual void ResetResults() = 0; @@ -140,11 +140,14 @@ public: // 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 GetMemoryRangeCount() const = 0; + virtual MemoryRange GetMemoryRange(size_t index) const = 0; + virtual PowerPC::RequestedAddressSpace GetAddressSpace() const = 0; + virtual DataType GetDataType() const = 0; + virtual bool GetAligned() const = 0; + + virtual bool IsIntegerType() const = 0; + virtual bool IsFloatingType() const = 0; virtual size_t GetResultCount() const = 0; virtual size_t GetValidValueCount() const = 0; @@ -165,7 +168,7 @@ public: }; template -class CheatSearchSession : public CheatSearchSessionBase +class CheatSearchSession final : public CheatSearchSessionBase { public: CheatSearchSession(std::vector memory_ranges, @@ -178,16 +181,19 @@ public: void SetCompareType(CompareType compare_type) override; void SetFilterType(FilterType filter_type) override; - bool SetValueFromString(const std::string& value_as_string) override; + bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) 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 GetMemoryRangeCount() const override; + MemoryRange GetMemoryRange(size_t index) const override; + PowerPC::RequestedAddressSpace GetAddressSpace() const override; + DataType GetDataType() const override; + bool GetAligned() const override; + + bool IsIntegerType() const override; + bool IsFloatingType() const override; size_t GetResultCount() const override; size_t GetValidValueCount() const override; diff --git a/Source/Core/DolphinQt/CheatSearchFactoryWidget.cpp b/Source/Core/DolphinQt/CheatSearchFactoryWidget.cpp index 4e0f8f2f1b..6e566600f8 100644 --- a/Source/Core/DolphinQt/CheatSearchFactoryWidget.cpp +++ b/Source/Core/DolphinQt/CheatSearchFactoryWidget.cpp @@ -20,8 +20,10 @@ #include "Core/CheatSearch.h" #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "Core/HW/Memmap.h" #include "Core/PowerPC/MMU.h" +#include "DolphinQt/QtUtils/ModalMessageBox.h" CheatSearchFactoryWidget::CheatSearchFactoryWidget() { @@ -152,6 +154,15 @@ void CheatSearchFactoryWidget::OnNewSearchClicked() PowerPC::RequestedAddressSpace address_space; if (m_standard_address_space->isChecked()) { + const Core::State core_state = Core::GetState(); + if (core_state != Core::State::Running && core_state != Core::State::Paused) + { + ModalMessageBox::warning( + this, tr("No game running."), + tr("Please start a game before starting a search with standard memory regions.")); + return; + } + memory_ranges.emplace_back(0x80000000, Memory::GetRamSizeReal()); if (SConfig::GetInstance().bWii) memory_ranges.emplace_back(0x90000000, Memory::GetExRamSizeReal()); diff --git a/Source/Core/DolphinQt/CheatSearchWidget.cpp b/Source/Core/DolphinQt/CheatSearchWidget.cpp index eed1c2a058..07409c6ad5 100644 --- a/Source/Core/DolphinQt/CheatSearchWidget.cpp +++ b/Source/Core/DolphinQt/CheatSearchWidget.cpp @@ -57,6 +57,7 @@ CheatSearchWidget::CheatSearchWidget(std::unique_ptrsetText(tr("%1, %2, %3, %4").arg(ranges).arg(space).arg(type).arg(aligned)); } - auto* value_layout = new QHBoxLayout(); - // i18n: This label is followed by a dropdown where the user can select things like "is equal to" // or "is less than or equal to", followed by another dropdown where the user can select "any // value", "last value", or "this value:". These three UI elements are intended to form a sentence // together. Because the UI elements can't be reordered by a translation, you may have to give // up on the idea of having them form a sentence depending on the grammar of your target language. auto* instructions_label = new QLabel(tr("Keep addresses where value in memory")); - value_layout->addWidget(instructions_label); + auto* value_layout = new QHBoxLayout(); m_compare_type_dropdown = new QComboBox(); m_compare_type_dropdown->addItem(tr("is equal to"), QVariant::fromValue(Cheats::CompareType::Equal)); @@ -194,6 +193,9 @@ void CheatSearchWidget::CreateWidgets() m_given_value_text = new QLineEdit(); value_layout->addWidget(m_given_value_text); + m_parse_values_as_hex_checkbox = new QCheckBox(tr("Parse as Hex")); + value_layout->addWidget(m_parse_values_as_hex_checkbox); + auto* button_layout = new QHBoxLayout(); m_next_scan_button = new QPushButton(tr("Search and Filter")); button_layout->addWidget(m_next_scan_button); @@ -212,6 +214,7 @@ void CheatSearchWidget::CreateWidgets() QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget(session_info_label); + layout->addWidget(instructions_label); layout->addLayout(value_layout); layout->addLayout(button_layout); layout->addWidget(m_display_values_in_hex_checkbox); @@ -234,7 +237,7 @@ void CheatSearchWidget::ConnectWidgets() connect(m_value_source_dropdown, &QComboBox::currentTextChanged, this, &CheatSearchWidget::OnValueSourceChanged); connect(m_display_values_in_hex_checkbox, &QCheckBox::toggled, this, - &CheatSearchWidget::OnHexCheckboxStateChanged); + &CheatSearchWidget::OnDisplayHexCheckboxStateChanged); } void CheatSearchWidget::OnNextScanClicked() @@ -253,7 +256,8 @@ void CheatSearchWidget::OnNextScanClicked() m_session->SetCompareType(m_compare_type_dropdown->currentData().value()); if (filter_type == Cheats::FilterType::CompareAgainstSpecificValue) { - if (!m_session->SetValueFromString(m_given_value_text->text().toStdString())) + if (!m_session->SetValueFromString(m_given_value_text->text().toStdString(), + m_parse_values_as_hex_checkbox->isChecked())) { m_info_label_1->setText(tr("Failed to parse given value into target data type.")); return; @@ -431,10 +435,12 @@ void CheatSearchWidget::OnAddressTableContextMenu() void CheatSearchWidget::OnValueSourceChanged() { const auto filter_type = m_value_source_dropdown->currentData().value(); - m_given_value_text->setEnabled(filter_type == Cheats::FilterType::CompareAgainstSpecificValue); + const bool is_value_search = filter_type == Cheats::FilterType::CompareAgainstSpecificValue; + m_given_value_text->setEnabled(is_value_search); + m_parse_values_as_hex_checkbox->setEnabled(is_value_search && m_session->IsIntegerType()); } -void CheatSearchWidget::OnHexCheckboxStateChanged() +void CheatSearchWidget::OnDisplayHexCheckboxStateChanged() { if (!m_session->WasFirstSearchDone()) return; diff --git a/Source/Core/DolphinQt/CheatSearchWidget.h b/Source/Core/DolphinQt/CheatSearchWidget.h index 74ee2fc2d7..9fa1581247 100644 --- a/Source/Core/DolphinQt/CheatSearchWidget.h +++ b/Source/Core/DolphinQt/CheatSearchWidget.h @@ -52,7 +52,7 @@ private: void OnAddressTableItemChanged(QTableWidgetItem* item); void OnAddressTableContextMenu(); void OnValueSourceChanged(); - void OnHexCheckboxStateChanged(); + void OnDisplayHexCheckboxStateChanged(); bool RefreshValues(); void UpdateGuiTable(); @@ -75,6 +75,7 @@ private: QPushButton* m_next_scan_button; QPushButton* m_refresh_values_button; QPushButton* m_reset_button; + QCheckBox* m_parse_values_as_hex_checkbox; QCheckBox* m_display_values_in_hex_checkbox; QTableWidget* m_address_table; }; diff --git a/Source/Core/DolphinQt/CheatsManager.cpp b/Source/Core/DolphinQt/CheatsManager.cpp index b5fffd771c..44620378f9 100644 --- a/Source/Core/DolphinQt/CheatsManager.cpp +++ b/Source/Core/DolphinQt/CheatsManager.cpp @@ -34,9 +34,16 @@ CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent) ConnectWidgets(); RefreshCodeTabs(Core::GetState(), true); + + auto& settings = Settings::GetQSettings(); + restoreGeometry(settings.value(QStringLiteral("cheatsmanager/geometry")).toByteArray()); } -CheatsManager::~CheatsManager() = default; +CheatsManager::~CheatsManager() +{ + auto& settings = Settings::GetQSettings(); + settings.setValue(QStringLiteral("cheatsmanager/geometry"), saveGeometry()); +} void CheatsManager::OnStateChanged(Core::State state) {