From 6e814cbb8f3ede208952e161059f10f9a4c5168c Mon Sep 17 00:00:00 2001
From: "Admiral H. Curtiss" <pikachu025@gmail.com>
Date: Thu, 28 Oct 2021 02:00:38 +0200
Subject: [PATCH] Qt/CheatSearchWidget: Add a checkbox to force parsing a value
 as hexadecimal.

---
 Source/Core/Core/CheatSearch.cpp            | 31 +++++++++++++++++----
 Source/Core/Core/CheatSearch.h              | 10 +++++--
 Source/Core/DolphinQt/CheatSearchWidget.cpp | 20 ++++++++-----
 Source/Core/DolphinQt/CheatSearchWidget.h   |  3 +-
 4 files changed, 49 insertions(+), 15 deletions(-)

diff --git a/Source/Core/Core/CheatSearch.cpp b/Source/Core/Core/CheatSearch.cpp
index 9710f31624..66882911ba 100644
--- a/Source/Core/Core/CheatSearch.cpp
+++ b/Source/Core/Core/CheatSearch.cpp
@@ -340,22 +340,31 @@ void Cheats::CheatSearchSession<T>::SetFilterType(FilterType filter_type)
 }
 
 template <typename T>
-static std::optional<T> ParseValue(const std::string& str)
+static std::optional<T> 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<T>)
+  {
+    if (TryParse(str, &tmp, force_parse_as_hex ? 16 : 0))
+      return tmp;
+  }
+  else
+  {
+    if (TryParse(str, &tmp))
+      return tmp;
+  }
 
   return std::nullopt;
 }
 
 template <typename T>
-bool Cheats::CheatSearchSession<T>::SetValueFromString(const std::string& value_as_string)
+bool Cheats::CheatSearchSession<T>::SetValueFromString(const std::string& value_as_string,
+                                                       bool force_parse_as_hex)
 {
-  m_value = ParseValue<T>(value_as_string);
+  m_value = ParseValue<T>(value_as_string, force_parse_as_hex);
   return m_value.has_value();
 }
 
@@ -498,6 +507,18 @@ bool Cheats::CheatSearchSession<T>::GetAligned()
   return m_aligned;
 }
 
+template <typename T>
+bool Cheats::CheatSearchSession<T>::IsIntegerType() const
+{
+  return std::is_integral_v<T>;
+}
+
+template <typename T>
+bool Cheats::CheatSearchSession<T>::IsFloatingType() const
+{
+  return std::is_floating_point_v<T>;
+}
+
 template <typename T>
 size_t Cheats::CheatSearchSession<T>::GetResultCount() const
 {
diff --git a/Source/Core/Core/CheatSearch.h b/Source/Core/Core/CheatSearch.h
index 5b777f50f8..b03a7f8adc 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;
@@ -146,6 +146,9 @@ public:
   virtual DataType GetDataType() = 0;
   virtual bool GetAligned() = 0;
 
+  virtual bool IsIntegerType() const = 0;
+  virtual bool IsFloatingType() const = 0;
+
   virtual size_t GetResultCount() const = 0;
   virtual size_t GetValidValueCount() const = 0;
   virtual u32 GetResultAddress(size_t index) const = 0;
@@ -178,7 +181,7 @@ 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;
@@ -189,6 +192,9 @@ public:
   DataType GetDataType() override;
   bool GetAligned() override;
 
+  bool IsIntegerType() const override;
+  bool IsFloatingType() const override;
+
   size_t GetResultCount() const override;
   size_t GetValidValueCount() const override;
   u32 GetResultAddress(size_t index) const override;
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_ptr<Cheats::CheatSearchSessionB
   setAttribute(Qt::WA_DeleteOnClose);
   CreateWidgets();
   ConnectWidgets();
+  OnValueSourceChanged();
   UpdateGuiTable();
 }
 
@@ -157,16 +158,14 @@ void CheatSearchWidget::CreateWidgets()
     session_info_label->setText(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<Cheats::CompareType>());
   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<Cheats::FilterType>();
-  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;
 };