Repository: RetroAchievements/RAIntegration
Pull request #937 (2022-11-01 14:24:03)

add XOR (^) as selectable operator for modifying conditions
everywhere bitwise AND (&) is currently selectable: AddSource, SubSource, AddAddress


Patch:
diff --git a/data/editor_theme.json b/data/editor_theme.json
index ac2a1449..8d7eabeb 100644
--- a/data/editor_theme.json
+++ b/data/editor_theme.json
@@ -15,11 +15,17 @@
       "HeaderSelected": "#FF8080"
     }
   },
+  "CodeNotes": {
+    "Colors": {
+      "Normal": "#000000",
+      "Modified": "#C04040"
+    }
+  },
   "TriggerColors": {
     "IsTrue": "#C0FFC0",
     "WasTrue": "#C0C0FF",
     "BecomingTrue": "#C0FFFF",
     "ResetTrue": "#FFFFC0",
-    "PauseTrue": "#FFC0C0",
+    "PauseTrue": "#FFC0C0"
   }
 }
\ No newline at end of file
diff --git a/rcheevos b/rcheevos
index 46619259..165d1a3b 160000
--- a/rcheevos
+++ b/rcheevos
@@ -1,1 +1,1 @@
-Subproject commit 46619259a07fb947b34ab3fa38139244fa0ca55a
+Subproject commit 165d1a3b189c472c4547ac385e59be2f527e688e
diff --git a/src/ui/EditorTheme.hh b/src/ui/EditorTheme.hh
index 1e471685..4a249c41 100644
--- a/src/ui/EditorTheme.hh
+++ b/src/ui/EditorTheme.hh
@@ -37,7 +37,8 @@ public:
     Color ColorHeaderSelected() const noexcept { return m_colorHeaderSelected; }
 
     // ===== code notes =====
-    Color ColorModified() const noexcept { return m_colorModified; }
+    Color ColorNoteNormal() const noexcept { return m_colorNoteNormal; }
+    Color ColorNoteModified() const noexcept { return m_colorNoteModified; }
 
     // ===== triggers =====
 
@@ -67,7 +68,8 @@ private:
     Color m_colorHeader{ 255, 128, 128, 128 };
     Color m_colorHeaderSelected{ 255, 255, 96, 96 };
 
-    Color m_colorModified { 255, 192, 64, 64 };
+    Color m_colorNoteNormal{ 255, 0, 0, 0 };
+    Color m_colorNoteModified { 255, 192, 64, 64 };
 
     Color m_colorTriggerIsTrue{ 255, 192, 255, 192 };
     Color m_colorTriggerWasTrue{ 255, 192, 192, 255 };
diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp
index 4ac72065..4e3b797e 100644
--- a/src/ui/Theme.cpp
+++ b/src/ui/Theme.cpp
@@ -199,7 +199,8 @@ void EditorTheme::LoadFromFile()
         {
             const rapidjson::Value& colors = codeNotes["Colors"];
 
-            ReadColor(m_colorModified, colors, "Modified");
+            ReadColor(m_colorNoteNormal, colors, "Normal");
+            ReadColor(m_colorNoteModified, colors, "Modified");
         }
     }
 
diff --git a/src/ui/viewmodels/CodeNotesViewModel.cpp b/src/ui/viewmodels/CodeNotesViewModel.cpp
index 75f35804..7583d049 100644
--- a/src/ui/viewmodels/CodeNotesViewModel.cpp
+++ b/src/ui/viewmodels/CodeNotesViewModel.cpp
@@ -26,7 +26,7 @@ const IntModelProperty CodeNotesViewModel::CodeNoteViewModel::BookmarkColorPrope
 void CodeNotesViewModel::CodeNoteViewModel::SetModified(bool bValue)
 {
     const auto& pEditorTheme = ra::services::ServiceLocator::Get<ra::ui::EditorTheme>();
-    SetBookmarkColor(bValue ? pEditorTheme.ColorModified() : pEditorTheme.ColorNormal());
+    SetBookmarkColor(bValue ? pEditorTheme.ColorNoteModified() : pEditorTheme.ColorNoteNormal());
 }
 
 CodeNotesViewModel::CodeNotesViewModel() noexcept
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.cpp b/src/ui/viewmodels/TriggerConditionViewModel.cpp
index 522ae809..42cdb324 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.cpp
+++ b/src/ui/viewmodels/TriggerConditionViewModel.cpp
@@ -129,6 +129,10 @@ void TriggerConditionViewModel::SerializeAppend(std::string& sBuffer) const
                 sBuffer.push_back('&');
                 break;
 
+            case TriggerOperatorType::BitwiseXor:
+                sBuffer.push_back('^');
+                break;
+
             default:
                 assert(!"Unknown comparison");
                 break;
@@ -820,6 +824,7 @@ bool TriggerConditionViewModel::IsComparisonVisible(const ViewModelBase& vmItem,
         case TriggerOperatorType::Multiply:
         case TriggerOperatorType::Divide:
         case TriggerOperatorType::BitwiseAnd:
+        case TriggerOperatorType::BitwiseXor:
             return vmCondition->IsModifying();
 
         default:
diff --git a/src/ui/viewmodels/TriggerConditionViewModel.hh b/src/ui/viewmodels/TriggerConditionViewModel.hh
index 29c1167a..63c0ce98 100644
--- a/src/ui/viewmodels/TriggerConditionViewModel.hh
+++ b/src/ui/viewmodels/TriggerConditionViewModel.hh
@@ -55,7 +55,8 @@ enum class TriggerOperatorType : uint8_t
     None = RC_OPERATOR_NONE,
     Multiply = RC_OPERATOR_MULT,
     Divide = RC_OPERATOR_DIV,
-    BitwiseAnd = RC_OPERATOR_AND
+    BitwiseAnd = RC_OPERATOR_AND,
+    BitwiseXor = RC_OPERATOR_XOR
 };
 
 class TriggerConditionViewModel : public ViewModelBase
diff --git a/src/ui/viewmodels/TriggerViewModel.cpp b/src/ui/viewmodels/TriggerViewModel.cpp
index 1ac6cf69..b19394ef 100644
--- a/src/ui/viewmodels/TriggerViewModel.cpp
+++ b/src/ui/viewmodels/TriggerViewModel.cpp
@@ -82,6 +82,7 @@ TriggerViewModel::TriggerViewModel() noexcept
     m_vOperatorTypes.Add(ra::etoi(TriggerOperatorType::Multiply), L"*");
     m_vOperatorTypes.Add(ra::etoi(TriggerOperatorType::Divide), L"/");
     m_vOperatorTypes.Add(ra::etoi(TriggerOperatorType::BitwiseAnd), L"&");
+    m_vOperatorTypes.Add(ra::etoi(TriggerOperatorType::BitwiseXor), L"^");
 }
 
 std::string TriggerViewModel::Serialize() const


Test patch:
diff --git a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
index 459ff7f8..9d877f76 100644
--- a/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/TriggerConditionViewModel_Tests.cpp
@@ -1063,6 +1063,7 @@ public:
         Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Multiply)));
         Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Divide)));
         Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseAnd)));
+        Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseXor)));
 
         condition.SetType(TriggerConditionType::AddAddress);
         Assert::IsFalse(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Equals)));
@@ -1075,6 +1076,7 @@ public:
         Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Multiply)));
         Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::Divide)));
         Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseAnd)));
+        Assert::IsTrue(TriggerConditionViewModel::IsComparisonVisible(condition, ra::etoi(TriggerOperatorType::BitwiseXor)));
     }
 
     TEST_METHOD(TestIsComparisonVisibleMeasured)
diff --git a/tests/ui/viewmodels/TriggerViewModel_Tests.cpp b/tests/ui/viewmodels/TriggerViewModel_Tests.cpp
index fd947adf..a5dfe33b 100644
--- a/tests/ui/viewmodels/TriggerViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/TriggerViewModel_Tests.cpp
@@ -208,7 +208,7 @@ public:
         Assert::AreEqual((int)MemSize::MBF32LE, vmTrigger.OperandSizes().GetItemAt(20)->GetId());
         Assert::AreEqual(std::wstring(L"MBF32 LE"), vmTrigger.OperandSizes().GetItemAt(20)->GetLabel());
 
-        Assert::AreEqual({ 10U }, vmTrigger.OperatorTypes().Count());
+        Assert::AreEqual({ 11U }, vmTrigger.OperatorTypes().Count());
         Assert::AreEqual((int)TriggerOperatorType::Equals, vmTrigger.OperatorTypes().GetItemAt(0)->GetId());
         Assert::AreEqual(std::wstring(L"="), vmTrigger.OperatorTypes().GetItemAt(0)->GetLabel());
         Assert::AreEqual((int)TriggerOperatorType::LessThan, vmTrigger.OperatorTypes().GetItemAt(1)->GetId());
@@ -229,6 +229,8 @@ public:
         Assert::AreEqual(std::wstring(L"/"), vmTrigger.OperatorTypes().GetItemAt(8)->GetLabel());
         Assert::AreEqual((int)TriggerOperatorType::BitwiseAnd, vmTrigger.OperatorTypes().GetItemAt(9)->GetId());
         Assert::AreEqual(std::wstring(L"&"), vmTrigger.OperatorTypes().GetItemAt(9)->GetLabel());
+        Assert::AreEqual((int)TriggerOperatorType::BitwiseXor, vmTrigger.OperatorTypes().GetItemAt(10)->GetId());
+        Assert::AreEqual(std::wstring(L"^"), vmTrigger.OperatorTypes().GetItemAt(10)->GetLabel());
     }
 
     TEST_METHOD(TestParseAndRegenerateCoreOnly)
@@ -258,6 +260,8 @@ public:
         ParseAndRegenerateValue("M:0xH1234$I:0x 1234_A:0xH2345_M:0xH7777"); // addsource/addaddress chain
         ParseAndRegenerateValue("M:0xS2345"); // bit6
         ParseAndRegenerateValue("M:0xS2345$M:0xS2346"); // bit6 in alts
+        ParseAndRegenerateValue("A:0xH1234&31_M:0"); // bitwise and
+        ParseAndRegenerateValue("A:0xH1234^129_M:0"); // bitwise or
     }
 
     TEST_METHOD(TestTriggerUpdateConditionModified)