duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cheats.cpp (111567B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "cheats.h"
      5 #include "bus.h"
      6 #include "controller.h"
      7 #include "cpu_core.h"
      8 #include "host.h"
      9 #include "system.h"
     10 
     11 #include "common/file_system.h"
     12 #include "common/log.h"
     13 #include "common/small_string.h"
     14 #include "common/string_util.h"
     15 
     16 #include <cctype>
     17 #include <iomanip>
     18 #include <sstream>
     19 #include <type_traits>
     20 
     21 Log_SetChannel(Cheats);
     22 
     23 static std::array<u32, 256> cht_register; // Used for D7 ,51 & 52 cheat types
     24 
     25 using KeyValuePairVector = std::vector<std::pair<std::string, std::string>>;
     26 
     27 static bool IsValidScanAddress(PhysicalMemoryAddress address)
     28 {
     29   if ((address & CPU::SCRATCHPAD_ADDR_MASK) == CPU::SCRATCHPAD_ADDR &&
     30       (address & CPU::SCRATCHPAD_OFFSET_MASK) < CPU::SCRATCHPAD_SIZE)
     31   {
     32     return true;
     33   }
     34 
     35   address &= CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
     36 
     37   if (address < Bus::RAM_MIRROR_END)
     38     return true;
     39 
     40   if (address >= Bus::BIOS_BASE && address < (Bus::BIOS_BASE + Bus::BIOS_SIZE))
     41     return true;
     42 
     43   return false;
     44 }
     45 
     46 template<typename T>
     47 static T DoMemoryRead(VirtualMemoryAddress address)
     48 {
     49   using UnsignedType = typename std::make_unsigned_t<T>;
     50   static_assert(std::is_same_v<UnsignedType, u8> || std::is_same_v<UnsignedType, u16> ||
     51                 std::is_same_v<UnsignedType, u32>);
     52 
     53   T result;
     54   if constexpr (std::is_same_v<UnsignedType, u8>)
     55     return CPU::SafeReadMemoryByte(address, &result) ? result : static_cast<T>(0);
     56   else if constexpr (std::is_same_v<UnsignedType, u16>)
     57     return CPU::SafeReadMemoryHalfWord(address, &result) ? result : static_cast<T>(0);
     58   else // if constexpr (std::is_same_v<UnsignedType, u32>)
     59     return CPU::SafeReadMemoryWord(address, &result) ? result : static_cast<T>(0);
     60 }
     61 
     62 template<typename T>
     63 static void DoMemoryWrite(PhysicalMemoryAddress address, T value)
     64 {
     65   using UnsignedType = typename std::make_unsigned_t<T>;
     66   static_assert(std::is_same_v<UnsignedType, u8> || std::is_same_v<UnsignedType, u16> ||
     67                 std::is_same_v<UnsignedType, u32>);
     68 
     69   if constexpr (std::is_same_v<UnsignedType, u8>)
     70     CPU::SafeWriteMemoryByte(address, value);
     71   else if constexpr (std::is_same_v<UnsignedType, u16>)
     72     CPU::SafeWriteMemoryHalfWord(address, value);
     73   else // if constexpr (std::is_same_v<UnsignedType, u32>)
     74     CPU::SafeWriteMemoryWord(address, value);
     75 }
     76 
     77 static u32 GetControllerButtonBits()
     78 {
     79   static constexpr std::array<u16, 16> button_mapping = {{
     80     0x0100, // Select
     81     0x0200, // L3
     82     0x0400, // R3
     83     0x0800, // Start
     84     0x1000, // Up
     85     0x2000, // Right
     86     0x4000, // Down
     87     0x8000, // Left
     88     0x0001, // L2
     89     0x0002, // R2
     90     0x0004, // L1
     91     0x0008, // R1
     92     0x0010, // Triangle
     93     0x0020, // Circle
     94     0x0040, // Cross
     95     0x0080, // Square
     96   }};
     97 
     98   u32 bits = 0;
     99   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    100   {
    101     Controller* controller = System::GetController(i);
    102     if (!controller)
    103       continue;
    104 
    105     bits |= controller->GetButtonStateBits();
    106   }
    107 
    108   u32 translated_bits = 0;
    109   for (u32 i = 0, bit = 1; i < static_cast<u32>(button_mapping.size()); i++, bit <<= 1)
    110   {
    111     if (bits & bit)
    112       translated_bits |= button_mapping[i];
    113   }
    114 
    115   return translated_bits;
    116 }
    117 
    118 static u32 GetControllerAnalogBits()
    119 {
    120   // 0x010000 - Right Thumb Up
    121   // 0x020000 - Right Thumb Right
    122   // 0x040000 - Right Thumb Down
    123   // 0x080000 - Right Thumb Left
    124   // 0x100000 - Left Thumb Up
    125   // 0x200000 - Left Thumb Right
    126   // 0x400000 - Left Thumb Down
    127   // 0x800000 - Left Thumb Left
    128 
    129   u32 bits = 0;
    130   u8 l_ypos = 0;
    131   u8 l_xpos = 0;
    132   u8 r_ypos = 0;
    133   u8 r_xpos = 0;
    134 
    135   std::optional<u32> analog = 0;
    136   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    137   {
    138     Controller* controller = System::GetController(i);
    139     if (!controller)
    140       continue;
    141 
    142     analog = controller->GetAnalogInputBytes();
    143     if (analog.has_value())
    144     {
    145       l_ypos = Truncate8((analog.value() & 0xFF000000u) >> 24);
    146       l_xpos = Truncate8((analog.value() & 0x00FF0000u) >> 16);
    147       r_ypos = Truncate8((analog.value() & 0x0000FF00u) >> 8);
    148       r_xpos = Truncate8(analog.value() & 0x000000FFu);
    149       if (l_ypos < 0x50)
    150         bits |= 0x100000;
    151       else if (l_ypos > 0xA0)
    152         bits |= 0x400000;
    153       if (l_xpos < 0x50)
    154         bits |= 0x800000;
    155       else if (l_xpos > 0xA0)
    156         bits |= 0x200000;
    157       if (r_ypos < 0x50)
    158         bits |= 0x10000;
    159       else if (r_ypos > 0xA0)
    160         bits |= 0x40000;
    161       if (r_xpos < 0x50)
    162         bits |= 0x80000;
    163       else if (r_xpos > 0xA0)
    164         bits |= 0x20000;
    165     }
    166   }
    167   return bits;
    168 }
    169 
    170 CheatList::CheatList() = default;
    171 
    172 CheatList::~CheatList() = default;
    173 
    174 static bool IsHexCharacter(char c)
    175 {
    176   return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
    177 }
    178 
    179 static int SignedCharToInt(char ch)
    180 {
    181   return static_cast<int>(static_cast<unsigned char>(ch));
    182 }
    183 
    184 static const std::string* FindKey(const KeyValuePairVector& kvp, const char* search)
    185 {
    186   for (const auto& it : kvp)
    187   {
    188     if (StringUtil::Strcasecmp(it.first.c_str(), search) == 0)
    189       return &it.second;
    190   }
    191 
    192   return nullptr;
    193 }
    194 
    195 bool CheatList::LoadFromPCSXRFile(const char* filename)
    196 {
    197   std::optional<std::string> str = FileSystem::ReadFileToString(filename);
    198   if (!str.has_value() || str->empty())
    199     return false;
    200 
    201   return LoadFromPCSXRString(str.value());
    202 }
    203 
    204 bool CheatList::LoadFromPCSXRString(const std::string& str)
    205 {
    206   std::istringstream iss(str);
    207 
    208   std::string line;
    209   std::string comments;
    210   std::string group;
    211   CheatCode::Type type = CheatCode::Type::Gameshark;
    212   CheatCode::Activation activation = CheatCode::Activation::EndFrame;
    213   CheatCode current_code;
    214   while (std::getline(iss, line))
    215   {
    216     char* start = line.data();
    217     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    218       start++;
    219 
    220     // skip empty lines
    221     if (*start == '\0')
    222       continue;
    223 
    224     char* end = start + std::strlen(start) - 1;
    225     while (end > start && std::isspace(SignedCharToInt(*end)))
    226     {
    227       *end = '\0';
    228       end--;
    229     }
    230 
    231     // DuckStation metadata
    232     if (StringUtil::Strncasecmp(start, "#group=", 7) == 0)
    233     {
    234       group = start + 7;
    235       continue;
    236     }
    237     if (StringUtil::Strncasecmp(start, "#type=", 6) == 0)
    238     {
    239       type = CheatCode::ParseTypeName(start + 6).value_or(CheatCode::Type::Gameshark);
    240       continue;
    241     }
    242     if (StringUtil::Strncasecmp(start, "#activation=", 12) == 0)
    243     {
    244       activation = CheatCode::ParseActivationName(start + 12).value_or(CheatCode::Activation::EndFrame);
    245       continue;
    246     }
    247 
    248     // skip comments and empty line
    249     if (*start == '#' || *start == ';' || *start == '/' || *start == '\"')
    250     {
    251       comments.append(start);
    252       comments += '\n';
    253       continue;
    254     }
    255 
    256     if (*start == '[' && *end == ']')
    257     {
    258       start++;
    259       *end = '\0';
    260 
    261       // new cheat
    262       if (current_code.Valid())
    263         m_codes.push_back(std::move(current_code));
    264 
    265       current_code = CheatCode();
    266       if (group.empty())
    267         group = "Ungrouped";
    268 
    269       current_code.group = std::move(group);
    270       group = std::string();
    271       current_code.comments = std::move(comments);
    272       comments = std::string();
    273       current_code.type = type;
    274       type = CheatCode::Type::Gameshark;
    275       current_code.activation = activation;
    276       activation = CheatCode::Activation::EndFrame;
    277 
    278       if (*start == '*')
    279       {
    280         current_code.enabled = true;
    281         start++;
    282       }
    283 
    284       current_code.description.append(start);
    285       continue;
    286     }
    287 
    288     while (!IsHexCharacter(*start) && start != end)
    289       start++;
    290     if (start == end)
    291       continue;
    292 
    293     char* end_ptr;
    294     CheatCode::Instruction inst;
    295     inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
    296     inst.second = 0;
    297     if (end_ptr)
    298     {
    299       while (!IsHexCharacter(*end_ptr) && end_ptr != end)
    300         end_ptr++;
    301       if (end_ptr != end)
    302         inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
    303     }
    304     current_code.instructions.push_back(inst);
    305   }
    306 
    307   if (current_code.Valid())
    308   {
    309     // technically this isn't the place for end of file
    310     if (!comments.empty())
    311       current_code.comments += comments;
    312     m_codes.push_back(std::move(current_code));
    313   }
    314 
    315   INFO_LOG("Loaded {} cheats (PCSXR format)", m_codes.size());
    316   return !m_codes.empty();
    317 }
    318 
    319 bool CheatList::LoadFromLibretroFile(const char* filename)
    320 {
    321   std::optional<std::string> str = FileSystem::ReadFileToString(filename);
    322   if (!str.has_value() || str->empty())
    323     return false;
    324 
    325   return LoadFromLibretroString(str.value());
    326 }
    327 
    328 bool CheatList::LoadFromLibretroString(const std::string& str)
    329 {
    330   std::istringstream iss(str);
    331   std::string line;
    332   KeyValuePairVector kvp;
    333   while (std::getline(iss, line))
    334   {
    335     char* start = line.data();
    336     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    337       start++;
    338 
    339     // skip empty lines
    340     if (*start == '\0' || *start == '=')
    341       continue;
    342 
    343     char* end = start + std::strlen(start) - 1;
    344     while (end > start && std::isspace(SignedCharToInt(*end)))
    345     {
    346       *end = '\0';
    347       end--;
    348     }
    349 
    350     char* equals = start;
    351     while (*equals != '=' && equals != end)
    352       equals++;
    353     if (equals == end)
    354       continue;
    355 
    356     *equals = '\0';
    357 
    358     char* key_end = equals - 1;
    359     while (key_end > start && std::isspace(SignedCharToInt(*key_end)))
    360     {
    361       *key_end = '\0';
    362       key_end--;
    363     }
    364 
    365     char* value_start = equals + 1;
    366     while (*value_start != '\0' && std::isspace(SignedCharToInt(*value_start)))
    367       value_start++;
    368 
    369     if (*value_start == '\0')
    370       continue;
    371 
    372     char* value_end = value_start + std::strlen(value_start) - 1;
    373     while (value_end > value_start && std::isspace(SignedCharToInt(*value_end)))
    374     {
    375       *value_end = '\0';
    376       value_end--;
    377     }
    378 
    379     if (*value_start == '\"')
    380     {
    381       if (*value_end != '\"')
    382         continue;
    383 
    384       value_start++;
    385       *value_end = '\0';
    386     }
    387 
    388     kvp.emplace_back(start, value_start);
    389   }
    390 
    391   if (kvp.empty())
    392     return false;
    393 
    394   const std::string* num_cheats_value = FindKey(kvp, "cheats");
    395   const u32 num_cheats = num_cheats_value ? StringUtil::FromChars<u32>(*num_cheats_value).value_or(0) : 0;
    396   if (num_cheats == 0)
    397     return false;
    398 
    399   for (u32 i = 0; i < num_cheats; i++)
    400   {
    401     const std::string* desc = FindKey(kvp, TinyString::from_format("cheat{}_desc", i));
    402     const std::string* code = FindKey(kvp, TinyString::from_format("cheat{}_code", i));
    403     const std::string* enable = FindKey(kvp, TinyString::from_format("cheat{}_enable", i));
    404     if (!desc || !code || !enable)
    405     {
    406       WARNING_LOG("Missing desc/code/enable for cheat {}", i);
    407       continue;
    408     }
    409 
    410     CheatCode cc;
    411     cc.group = "Ungrouped";
    412     cc.description = *desc;
    413     cc.enabled = StringUtil::FromChars<bool>(*enable).value_or(false);
    414     if (ParseLibretroCheat(&cc, code->c_str()))
    415       m_codes.push_back(std::move(cc));
    416   }
    417 
    418   INFO_LOG("Loaded {} cheats (libretro format)", m_codes.size());
    419   return !m_codes.empty();
    420 }
    421 
    422 bool CheatList::LoadFromEPSXeString(const std::string& str)
    423 {
    424   std::istringstream iss(str);
    425 
    426   std::string line;
    427   std::string group;
    428   CheatCode::Type type = CheatCode::Type::Gameshark;
    429   CheatCode::Activation activation = CheatCode::Activation::EndFrame;
    430   CheatCode current_code;
    431   while (std::getline(iss, line))
    432   {
    433     char* start = line.data();
    434     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    435       start++;
    436 
    437     // skip empty lines
    438     if (*start == '\0')
    439       continue;
    440 
    441     char* end = start + std::strlen(start) - 1;
    442     while (end > start && std::isspace(SignedCharToInt(*end)))
    443     {
    444       *end = '\0';
    445       end--;
    446     }
    447 
    448     // skip comments and empty line
    449     if (*start == ';' || *start == '\0')
    450       continue;
    451 
    452     if (*start == '#')
    453     {
    454       start++;
    455 
    456       // new cheat
    457       if (current_code.Valid())
    458         m_codes.push_back(std::move(current_code));
    459 
    460       current_code = CheatCode();
    461       if (group.empty())
    462         group = "Ungrouped";
    463 
    464       current_code.group = std::move(group);
    465       group = std::string();
    466       current_code.type = type;
    467       type = CheatCode::Type::Gameshark;
    468       current_code.activation = activation;
    469       activation = CheatCode::Activation::EndFrame;
    470 
    471       char* separator = std::strchr(start, '\\');
    472       if (separator)
    473       {
    474         *separator = 0;
    475         current_code.group = start;
    476         start = separator + 1;
    477       }
    478 
    479       current_code.description.append(start);
    480       continue;
    481     }
    482 
    483     while (!IsHexCharacter(*start) && start != end)
    484       start++;
    485     if (start == end)
    486       continue;
    487 
    488     char* end_ptr;
    489     CheatCode::Instruction inst;
    490     inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
    491     inst.second = 0;
    492     if (end_ptr)
    493     {
    494       while (!IsHexCharacter(*end_ptr) && end_ptr != end)
    495         end_ptr++;
    496       if (end_ptr != end)
    497         inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
    498     }
    499     current_code.instructions.push_back(inst);
    500   }
    501 
    502   if (current_code.Valid())
    503     m_codes.push_back(std::move(current_code));
    504 
    505   INFO_LOG("Loaded {} cheats (EPSXe format)", m_codes.size());
    506   return !m_codes.empty();
    507 }
    508 
    509 static bool IsLibretroSeparator(char ch)
    510 {
    511   return (ch == ' ' || ch == '-' || ch == ':' || ch == '+');
    512 }
    513 
    514 bool CheatList::ParseLibretroCheat(CheatCode* cc, const char* line)
    515 {
    516   const char* current_ptr = line;
    517   while (current_ptr)
    518   {
    519     char* end_ptr;
    520     CheatCode::Instruction inst;
    521     inst.first = static_cast<u32>(std::strtoul(current_ptr, &end_ptr, 16));
    522     current_ptr = end_ptr;
    523     if (end_ptr)
    524     {
    525       if (!IsLibretroSeparator(*end_ptr))
    526       {
    527         WARNING_LOG("Malformed code '{}'", line);
    528         break;
    529       }
    530 
    531       end_ptr++;
    532       inst.second = static_cast<u32>(std::strtoul(current_ptr, &end_ptr, 16));
    533       if (end_ptr && *end_ptr == '\0')
    534         end_ptr = nullptr;
    535 
    536       if (end_ptr && *end_ptr != '\0')
    537       {
    538         if (!IsLibretroSeparator(*end_ptr))
    539         {
    540           WARNING_LOG("Malformed code '{}'", line);
    541           break;
    542         }
    543 
    544         end_ptr++;
    545       }
    546 
    547       current_ptr = end_ptr;
    548       cc->instructions.push_back(inst);
    549     }
    550   }
    551 
    552   return !cc->instructions.empty();
    553 }
    554 
    555 void CheatList::Apply()
    556 {
    557   if (!m_master_enable)
    558     return;
    559 
    560   for (const CheatCode& code : m_codes)
    561   {
    562     if (code.enabled)
    563       code.Apply();
    564   }
    565 }
    566 
    567 void CheatList::AddCode(CheatCode cc)
    568 {
    569   m_codes.push_back(std::move(cc));
    570 }
    571 
    572 void CheatList::SetCode(u32 index, CheatCode cc)
    573 {
    574   if (index > m_codes.size())
    575     return;
    576 
    577   if (index == m_codes.size())
    578   {
    579     m_codes.push_back(std::move(cc));
    580     return;
    581   }
    582 
    583   m_codes[index] = std::move(cc);
    584 }
    585 
    586 void CheatList::RemoveCode(u32 i)
    587 {
    588   m_codes.erase(m_codes.begin() + i);
    589 }
    590 
    591 std::optional<CheatList::Format> CheatList::DetectFileFormat(const char* filename)
    592 {
    593   std::optional<std::string> str = FileSystem::ReadFileToString(filename);
    594   if (!str.has_value() || str->empty())
    595     return std::nullopt;
    596 
    597   return DetectFileFormat(str.value());
    598 }
    599 
    600 CheatList::Format CheatList::DetectFileFormat(const std::string& str)
    601 {
    602   std::istringstream iss(str);
    603   std::string line;
    604   while (std::getline(iss, line))
    605   {
    606     char* start = line.data();
    607     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    608       start++;
    609 
    610     // skip empty lines
    611     if (*start == '\0')
    612       continue;
    613 
    614     char* end = start + std::strlen(start) - 1;
    615     while (end > start && std::isspace(SignedCharToInt(*end)))
    616     {
    617       *end = '\0';
    618       end--;
    619     }
    620 
    621     // eat comments
    622     if (start[0] == '#' || start[0] == ';')
    623       continue;
    624 
    625     if (std::strncmp(line.data(), "cheats", 6) == 0)
    626       return Format::Libretro;
    627 
    628     // pcsxr if we see brackets
    629     if (start[0] == '[')
    630       return Format::PCSXR;
    631 
    632     // otherwise if it's a code, it's probably epsxe
    633     if (std::isdigit(start[0]))
    634       return Format::EPSXe;
    635   }
    636 
    637   return Format::Count;
    638 }
    639 
    640 bool CheatList::LoadFromFile(const char* filename, Format format)
    641 {
    642   if (!FileSystem::FileExists(filename))
    643     return false;
    644 
    645   std::optional<std::string> str = FileSystem::ReadFileToString(filename);
    646   if (!str.has_value())
    647     return false;
    648 
    649   if (str->empty())
    650     return true;
    651 
    652   return LoadFromString(str.value(), format);
    653 }
    654 
    655 bool CheatList::LoadFromString(const std::string& str, Format format)
    656 {
    657   if (format == Format::Autodetect)
    658     format = DetectFileFormat(str);
    659 
    660   if (format == Format::PCSXR)
    661     return LoadFromPCSXRString(str);
    662   else if (format == Format::Libretro)
    663     return LoadFromLibretroString(str);
    664   else if (format == Format::EPSXe)
    665     return LoadFromEPSXeString(str);
    666   else
    667     return false;
    668 }
    669 
    670 bool CheatList::SaveToPCSXRFile(const char* filename)
    671 {
    672   auto fp = FileSystem::OpenManagedCFile(filename, "wb");
    673   if (!fp)
    674     return false;
    675 
    676   for (const CheatCode& cc : m_codes)
    677   {
    678     if (!cc.comments.empty())
    679       std::fputs(cc.comments.c_str(), fp.get());
    680     std::fprintf(fp.get(), "#group=%s\n", cc.group.c_str());
    681     std::fprintf(fp.get(), "#type=%s\n", CheatCode::GetTypeName(cc.type));
    682     std::fprintf(fp.get(), "#activation=%s\n", CheatCode::GetActivationName(cc.activation));
    683     std::fprintf(fp.get(), "[%s%s]\n", cc.enabled ? "*" : "", cc.description.c_str());
    684     for (const CheatCode::Instruction& i : cc.instructions)
    685       std::fprintf(fp.get(), "%08X %04X\n", i.first, i.second);
    686     std::fprintf(fp.get(), "\n");
    687   }
    688 
    689   std::fflush(fp.get());
    690   return (std::ferror(fp.get()) == 0);
    691 }
    692 
    693 bool CheatList::LoadFromPackage(const std::string& serial)
    694 {
    695   const std::optional<std::string> db_string(Host::ReadResourceFileToString("chtdb.txt", false));
    696   if (!db_string.has_value())
    697     return false;
    698 
    699   std::istringstream iss(db_string.value());
    700   std::string line;
    701   while (std::getline(iss, line))
    702   {
    703     char* start = line.data();
    704     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    705       start++;
    706 
    707     // skip empty lines
    708     if (*start == '\0' || *start == ';')
    709       continue;
    710 
    711     char* end = start + std::strlen(start) - 1;
    712     while (end > start && std::isspace(SignedCharToInt(*end)))
    713     {
    714       *end = '\0';
    715       end--;
    716     }
    717 
    718     if (start == end)
    719       continue;
    720 
    721     if (start[0] != ':' || std::strcmp(&start[1], serial.c_str()) != 0)
    722       continue;
    723 
    724     // game code match
    725     CheatCode current_code;
    726     while (std::getline(iss, line))
    727     {
    728       start = line.data();
    729       while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    730         start++;
    731 
    732       // skip empty lines
    733       if (*start == '\0' || *start == ';')
    734         continue;
    735 
    736       end = start + std::strlen(start) - 1;
    737       while (end > start && std::isspace(SignedCharToInt(*end)))
    738       {
    739         *end = '\0';
    740         end--;
    741       }
    742 
    743       if (start == end)
    744         continue;
    745 
    746       // stop adding codes when we hit a different game
    747       if (start[0] == ':' && (!m_codes.empty() || current_code.Valid()))
    748         break;
    749 
    750       if (start[0] == '#')
    751       {
    752         start++;
    753 
    754         if (current_code.Valid())
    755         {
    756           m_codes.push_back(std::move(current_code));
    757           current_code = CheatCode();
    758         }
    759 
    760         // new code
    761         char* slash = std::strrchr(start, '\\');
    762         if (slash)
    763         {
    764           *slash = '\0';
    765           current_code.group = start;
    766           start = slash + 1;
    767         }
    768         if (current_code.group.empty())
    769           current_code.group = "Ungrouped";
    770 
    771         current_code.description = start;
    772         continue;
    773       }
    774 
    775       while (!IsHexCharacter(*start) && start != end)
    776         start++;
    777       if (start == end)
    778         continue;
    779 
    780       char* end_ptr;
    781       CheatCode::Instruction inst;
    782       inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
    783       inst.second = 0;
    784       if (end_ptr)
    785       {
    786         while (!IsHexCharacter(*end_ptr) && end_ptr != end)
    787           end_ptr++;
    788         if (end_ptr != end)
    789           inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
    790       }
    791       current_code.instructions.push_back(inst);
    792     }
    793 
    794     if (current_code.Valid())
    795       m_codes.push_back(std::move(current_code));
    796 
    797     INFO_LOG("Loaded {} codes from package for {}", m_codes.size(), serial);
    798     return !m_codes.empty();
    799   }
    800 
    801   WARNING_LOG("No codes found in package for {}", serial);
    802   return false;
    803 }
    804 
    805 u32 CheatList::GetEnabledCodeCount() const
    806 {
    807   u32 count = 0;
    808   for (const CheatCode& cc : m_codes)
    809   {
    810     if (cc.enabled)
    811       count++;
    812   }
    813 
    814   return count;
    815 }
    816 
    817 std::vector<std::string> CheatList::GetCodeGroups() const
    818 {
    819   std::vector<std::string> groups;
    820   for (const CheatCode& cc : m_codes)
    821   {
    822     if (std::any_of(groups.begin(), groups.end(), [cc](const std::string& group) { return (group == cc.group); }))
    823       continue;
    824 
    825     groups.emplace_back(cc.group);
    826   }
    827 
    828   return groups;
    829 }
    830 
    831 void CheatList::SetCodeEnabled(u32 index, bool state)
    832 {
    833   if (index >= m_codes.size() || m_codes[index].enabled == state)
    834     return;
    835 
    836   m_codes[index].enabled = state;
    837   if (!state)
    838     m_codes[index].ApplyOnDisable();
    839 }
    840 
    841 void CheatList::EnableCode(u32 index)
    842 {
    843   SetCodeEnabled(index, true);
    844 }
    845 
    846 void CheatList::DisableCode(u32 index)
    847 {
    848   SetCodeEnabled(index, false);
    849 }
    850 
    851 void CheatList::ApplyCode(u32 index)
    852 {
    853   if (index >= m_codes.size())
    854     return;
    855 
    856   m_codes[index].Apply();
    857 }
    858 
    859 const CheatCode* CheatList::FindCode(const char* name) const
    860 {
    861   for (const CheatCode& cc : m_codes)
    862   {
    863     if (cc.description == name)
    864       return &cc;
    865   }
    866 
    867   return nullptr;
    868 }
    869 
    870 const CheatCode* CheatList::FindCode(const char* group, const char* name) const
    871 {
    872   for (const CheatCode& cc : m_codes)
    873   {
    874     if (cc.group == group && cc.description == name)
    875       return &cc;
    876   }
    877 
    878   return nullptr;
    879 }
    880 
    881 void CheatList::MergeList(const CheatList& cl)
    882 {
    883   for (const CheatCode& cc : cl.m_codes)
    884   {
    885     if (!FindCode(cc.group.c_str(), cc.description.c_str()))
    886       AddCode(cc);
    887   }
    888 }
    889 
    890 std::string CheatCode::GetInstructionsAsString() const
    891 {
    892   std::stringstream ss;
    893 
    894   for (const Instruction& inst : instructions)
    895   {
    896     ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << inst.first;
    897     ss << " ";
    898     ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << inst.second;
    899     ss << '\n';
    900   }
    901 
    902   return ss.str();
    903 }
    904 
    905 bool CheatCode::SetInstructionsFromString(const std::string& str)
    906 {
    907   std::vector<Instruction> new_instructions;
    908   std::istringstream ss(str);
    909 
    910   for (std::string line; std::getline(ss, line);)
    911   {
    912     char* start = line.data();
    913     while (*start != '\0' && std::isspace(SignedCharToInt(*start)))
    914       start++;
    915 
    916     // skip empty lines
    917     if (*start == '\0')
    918       continue;
    919 
    920     char* end = start + std::strlen(start) - 1;
    921     while (end > start && std::isspace(SignedCharToInt(*end)))
    922     {
    923       *end = '\0';
    924       end--;
    925     }
    926 
    927     // skip comments and empty line
    928     if (*start == '#' || *start == ';' || *start == '/' || *start == '\"')
    929       continue;
    930 
    931     while (!IsHexCharacter(*start) && start != end)
    932       start++;
    933     if (start == end)
    934       continue;
    935 
    936     char* end_ptr;
    937     CheatCode::Instruction inst;
    938     inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
    939     inst.second = 0;
    940     if (end_ptr)
    941     {
    942       while (!IsHexCharacter(*end_ptr) && end_ptr != end)
    943         end_ptr++;
    944       if (end_ptr != end)
    945         inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
    946     }
    947     new_instructions.push_back(inst);
    948   }
    949 
    950   if (new_instructions.empty())
    951     return false;
    952 
    953   instructions = std::move(new_instructions);
    954   return true;
    955 }
    956 
    957 static bool IsConditionalInstruction(CheatCode::InstructionCode code)
    958 {
    959   switch (code)
    960   {
    961     case CheatCode::InstructionCode::CompareEqual16:       // D0
    962     case CheatCode::InstructionCode::CompareNotEqual16:    // D1
    963     case CheatCode::InstructionCode::CompareLess16:        // D2
    964     case CheatCode::InstructionCode::CompareGreater16:     // D3
    965     case CheatCode::InstructionCode::CompareEqual8:        // E0
    966     case CheatCode::InstructionCode::CompareNotEqual8:     // E1
    967     case CheatCode::InstructionCode::CompareLess8:         // E2
    968     case CheatCode::InstructionCode::CompareGreater8:      // E3
    969     case CheatCode::InstructionCode::CompareButtons:       // D4
    970     case CheatCode::InstructionCode::ExtCompareEqual32:    // A0
    971     case CheatCode::InstructionCode::ExtCompareNotEqual32: // A1
    972     case CheatCode::InstructionCode::ExtCompareLess32:     // A2
    973     case CheatCode::InstructionCode::ExtCompareGreater32:  // A3
    974       return true;
    975 
    976     default:
    977       return false;
    978   }
    979 }
    980 
    981 u32 CheatCode::GetNextNonConditionalInstruction(u32 index) const
    982 {
    983   const u32 count = static_cast<u32>(instructions.size());
    984   for (; index < count; index++)
    985   {
    986     if (!IsConditionalInstruction(instructions[index].code))
    987     {
    988       // we've found the first non conditional instruction in the chain, so skip over the instruction following it
    989       return index + 1;
    990     }
    991   }
    992 
    993   return index;
    994 }
    995 
    996 void CheatCode::Apply() const
    997 {
    998   const u32 count = static_cast<u32>(instructions.size());
    999   u32 index = 0;
   1000   for (; index < count;)
   1001   {
   1002     const Instruction& inst = instructions[index];
   1003     switch (inst.code)
   1004     {
   1005       case InstructionCode::Nop:
   1006       {
   1007         index++;
   1008       }
   1009       break;
   1010 
   1011       case InstructionCode::ConstantWrite8:
   1012       {
   1013         DoMemoryWrite<u8>(inst.address, inst.value8);
   1014         index++;
   1015       }
   1016       break;
   1017 
   1018       case InstructionCode::ConstantWrite16:
   1019       {
   1020         DoMemoryWrite<u16>(inst.address, inst.value16);
   1021         index++;
   1022       }
   1023       break;
   1024 
   1025       case InstructionCode::ExtConstantWrite32:
   1026       {
   1027         DoMemoryWrite<u32>(inst.address, inst.value32);
   1028         index++;
   1029       }
   1030       break;
   1031 
   1032       case InstructionCode::ExtConstantBitSet8:
   1033       {
   1034         const u8 value = DoMemoryRead<u8>(inst.address) | inst.value8;
   1035         DoMemoryWrite<u8>(inst.address, value);
   1036         index++;
   1037       }
   1038       break;
   1039 
   1040       case InstructionCode::ExtConstantBitSet16:
   1041       {
   1042         const u16 value = DoMemoryRead<u16>(inst.address) | inst.value16;
   1043         DoMemoryWrite<u16>(inst.address, value);
   1044         index++;
   1045       }
   1046       break;
   1047 
   1048       case InstructionCode::ExtConstantBitSet32:
   1049       {
   1050         const u32 value = DoMemoryRead<u32>(inst.address) | inst.value32;
   1051         DoMemoryWrite<u32>(inst.address, value);
   1052         index++;
   1053       }
   1054       break;
   1055 
   1056       case InstructionCode::ExtConstantBitClear8:
   1057       {
   1058         const u8 value = DoMemoryRead<u8>(inst.address) & ~inst.value8;
   1059         DoMemoryWrite<u8>(inst.address, value);
   1060         index++;
   1061       }
   1062       break;
   1063 
   1064       case InstructionCode::ExtConstantBitClear16:
   1065       {
   1066         const u16 value = DoMemoryRead<u16>(inst.address) & ~inst.value16;
   1067         DoMemoryWrite<u16>(inst.address, value);
   1068         index++;
   1069       }
   1070       break;
   1071 
   1072       case InstructionCode::ExtConstantBitClear32:
   1073       {
   1074         const u32 value = DoMemoryRead<u32>(inst.address) & ~inst.value32;
   1075         DoMemoryWrite<u32>(inst.address, value);
   1076         index++;
   1077       }
   1078       break;
   1079 
   1080       case InstructionCode::ScratchpadWrite16:
   1081       {
   1082         DoMemoryWrite<u16>(CPU::SCRATCHPAD_ADDR | (inst.address & CPU::SCRATCHPAD_OFFSET_MASK), inst.value16);
   1083         index++;
   1084       }
   1085       break;
   1086 
   1087       case InstructionCode::ExtScratchpadWrite32:
   1088       {
   1089         DoMemoryWrite<u32>(CPU::SCRATCHPAD_ADDR | (inst.address & CPU::SCRATCHPAD_OFFSET_MASK), inst.value32);
   1090         index++;
   1091       }
   1092       break;
   1093 
   1094       case InstructionCode::ExtIncrement32:
   1095       {
   1096         const u32 value = DoMemoryRead<u32>(inst.address);
   1097         DoMemoryWrite<u32>(inst.address, value + inst.value32);
   1098         index++;
   1099       }
   1100       break;
   1101 
   1102       case InstructionCode::ExtDecrement32:
   1103       {
   1104         const u32 value = DoMemoryRead<u32>(inst.address);
   1105         DoMemoryWrite<u32>(inst.address, value - inst.value32);
   1106         index++;
   1107       }
   1108       break;
   1109 
   1110       case InstructionCode::Increment16:
   1111       {
   1112         const u16 value = DoMemoryRead<u16>(inst.address);
   1113         DoMemoryWrite<u16>(inst.address, value + inst.value16);
   1114         index++;
   1115       }
   1116       break;
   1117 
   1118       case InstructionCode::Decrement16:
   1119       {
   1120         const u16 value = DoMemoryRead<u16>(inst.address);
   1121         DoMemoryWrite<u16>(inst.address, value - inst.value16);
   1122         index++;
   1123       }
   1124       break;
   1125 
   1126       case InstructionCode::Increment8:
   1127       {
   1128         const u8 value = DoMemoryRead<u8>(inst.address);
   1129         DoMemoryWrite<u8>(inst.address, value + inst.value8);
   1130         index++;
   1131       }
   1132       break;
   1133 
   1134       case InstructionCode::Decrement8:
   1135       {
   1136         const u8 value = DoMemoryRead<u8>(inst.address);
   1137         DoMemoryWrite<u8>(inst.address, value - inst.value8);
   1138         index++;
   1139       }
   1140       break;
   1141 
   1142       case InstructionCode::ExtCompareEqual32:
   1143       {
   1144         const u32 value = DoMemoryRead<u32>(inst.address);
   1145         if (value == inst.value32)
   1146           index++;
   1147         else
   1148           index = GetNextNonConditionalInstruction(index);
   1149       }
   1150       break;
   1151 
   1152       case InstructionCode::ExtCompareNotEqual32:
   1153       {
   1154         const u32 value = DoMemoryRead<u32>(inst.address);
   1155         if (value != inst.value32)
   1156           index++;
   1157         else
   1158           index = GetNextNonConditionalInstruction(index);
   1159       }
   1160       break;
   1161 
   1162       case InstructionCode::ExtCompareLess32:
   1163       {
   1164         const u32 value = DoMemoryRead<u32>(inst.address);
   1165         if (value < inst.value32)
   1166           index++;
   1167         else
   1168           index = GetNextNonConditionalInstruction(index);
   1169       }
   1170       break;
   1171 
   1172       case InstructionCode::ExtCompareGreater32:
   1173       {
   1174         const u32 value = DoMemoryRead<u32>(inst.address);
   1175         if (value > inst.value32)
   1176           index++;
   1177         else
   1178           index = GetNextNonConditionalInstruction(index);
   1179       }
   1180       break;
   1181 
   1182       case InstructionCode::ExtConstantWriteIfMatch16:
   1183       case InstructionCode::ExtConstantWriteIfMatchWithRestore16:
   1184       {
   1185         const u16 value = DoMemoryRead<u16>(inst.address);
   1186         const u16 comparevalue = Truncate16(inst.value32 >> 16);
   1187         const u16 newvalue = Truncate16(inst.value32 & 0xFFFFu);
   1188         if (value == comparevalue)
   1189           DoMemoryWrite<u16>(inst.address, newvalue);
   1190 
   1191         index++;
   1192       }
   1193       break;
   1194 
   1195       case InstructionCode::ExtConstantForceRange8:
   1196       {
   1197         const u8 value = DoMemoryRead<u8>(inst.address);
   1198         const u8 min = Truncate8(inst.value32 & 0x000000FFu);
   1199         const u8 max = Truncate8((inst.value32 & 0x0000FF00u) >> 8);
   1200         const u8 overmin = Truncate8((inst.value32 & 0x00FF0000u) >> 16);
   1201         const u8 overmax = Truncate8((inst.value32 & 0xFF000000u) >> 24);
   1202         if ((value < min) || (value < min && min == 0x00u && max < 0xFEu))
   1203           DoMemoryWrite<u8>(inst.address, overmin); // also handles a min value of 0x00
   1204         else if (value > max)
   1205           DoMemoryWrite<u8>(inst.address, overmax);
   1206         index++;
   1207       }
   1208       break;
   1209 
   1210       case InstructionCode::ExtConstantForceRangeLimits16:
   1211       {
   1212         const u16 value = DoMemoryRead<u16>(inst.address);
   1213         const u16 min = Truncate16(inst.value32 & 0x0000FFFFu);
   1214         const u16 max = Truncate16((inst.value32 & 0xFFFF0000u) >> 16);
   1215         if ((value < min) || (value < min && min == 0x0000u && max < 0xFFFEu))
   1216           DoMemoryWrite<u16>(inst.address, min); // also handles a min value of 0x0000
   1217         else if (value > max)
   1218           DoMemoryWrite<u16>(inst.address, max);
   1219         index++;
   1220       }
   1221       break;
   1222 
   1223       case InstructionCode::ExtConstantForceRangeRollRound16:
   1224       {
   1225         const u16 value = DoMemoryRead<u16>(inst.address);
   1226         const u16 min = Truncate16(inst.value32 & 0x0000FFFFu);
   1227         const u16 max = Truncate16((inst.value32 & 0xFFFF0000u) >> 16);
   1228         if ((value < min) || (value < min && min == 0x0000u && max < 0xFFFEu))
   1229           DoMemoryWrite<u16>(inst.address, max); // also handles a min value of 0x0000
   1230         else if (value > max)
   1231           DoMemoryWrite<u16>(inst.address, min);
   1232         index++;
   1233       }
   1234       break;
   1235 
   1236       case InstructionCode::ExtConstantForceRange16:
   1237       {
   1238         const u16 min = Truncate16(inst.value32 & 0x0000FFFFu);
   1239         const u16 max = Truncate16((inst.value32 & 0xFFFF0000u) >> 16);
   1240         const u16 value = DoMemoryRead<u16>(inst.address);
   1241         const Instruction& inst2 = instructions[index + 1];
   1242         const u16 overmin = Truncate16(inst2.value32 & 0x0000FFFFu);
   1243         const u16 overmax = Truncate16((inst2.value32 & 0xFFFF0000u) >> 16);
   1244 
   1245         if ((value < min) || (value < min && min == 0x0000u && max < 0xFFFEu))
   1246           DoMemoryWrite<u16>(inst.address, overmin); // also handles a min value of 0x0000
   1247         else if (value > max)
   1248           DoMemoryWrite<u16>(inst.address, overmax);
   1249         index += 2;
   1250       }
   1251       break;
   1252 
   1253       case InstructionCode::ExtConstantSwap16:
   1254       {
   1255         const u16 value1 = Truncate16(inst.value32 & 0x0000FFFFu);
   1256         const u16 value2 = Truncate16((inst.value32 & 0xFFFF0000u) >> 16);
   1257         const u16 value = DoMemoryRead<u16>(inst.address);
   1258 
   1259         if (value == value1)
   1260           DoMemoryWrite<u16>(inst.address, value2);
   1261         else if (value == value2)
   1262           DoMemoryWrite<u16>(inst.address, value1);
   1263         index++;
   1264       }
   1265       break;
   1266 
   1267       case InstructionCode::ExtFindAndReplace:
   1268       {
   1269 
   1270         if ((index + 4) >= instructions.size())
   1271         {
   1272           ERROR_LOG("Incomplete find/replace instruction");
   1273           return;
   1274         }
   1275         const Instruction& inst2 = instructions[index + 1];
   1276         const Instruction& inst3 = instructions[index + 2];
   1277         const Instruction& inst4 = instructions[index + 3];
   1278         const Instruction& inst5 = instructions[index + 4];
   1279 
   1280         const u32 offset = Truncate16(inst.value32 & 0x0000FFFFu) << 1;
   1281         const u8 wildcard = Truncate8((inst.value32 & 0x00FF0000u) >> 16);
   1282         const u32 minaddress = inst.address - offset;
   1283         const u32 maxaddress = inst.address + offset;
   1284         const u8 f1 = Truncate8((inst2.first & 0xFF000000u) >> 24);
   1285         const u8 f2 = Truncate8((inst2.first & 0x00FF0000u) >> 16);
   1286         const u8 f3 = Truncate8((inst2.first & 0x0000FF00u) >> 8);
   1287         const u8 f4 = Truncate8(inst2.first & 0x000000FFu);
   1288         const u8 f5 = Truncate8((inst2.value32 & 0xFF000000u) >> 24);
   1289         const u8 f6 = Truncate8((inst2.value32 & 0x00FF0000u) >> 16);
   1290         const u8 f7 = Truncate8((inst2.value32 & 0x0000FF00u) >> 8);
   1291         const u8 f8 = Truncate8(inst2.value32 & 0x000000FFu);
   1292         const u8 f9 = Truncate8((inst3.first & 0xFF000000u) >> 24);
   1293         const u8 f10 = Truncate8((inst3.first & 0x00FF0000u) >> 16);
   1294         const u8 f11 = Truncate8((inst3.first & 0x0000FF00u) >> 8);
   1295         const u8 f12 = Truncate8(inst3.first & 0x000000FFu);
   1296         const u8 f13 = Truncate8((inst3.value32 & 0xFF000000u) >> 24);
   1297         const u8 f14 = Truncate8((inst3.value32 & 0x00FF0000u) >> 16);
   1298         const u8 f15 = Truncate8((inst3.value32 & 0x0000FF00u) >> 8);
   1299         const u8 f16 = Truncate8(inst3.value32 & 0x000000FFu);
   1300         const u8 r1 = Truncate8((inst4.first & 0xFF000000u) >> 24);
   1301         const u8 r2 = Truncate8((inst4.first & 0x00FF0000u) >> 16);
   1302         const u8 r3 = Truncate8((inst4.first & 0x0000FF00u) >> 8);
   1303         const u8 r4 = Truncate8(inst4.first & 0x000000FFu);
   1304         const u8 r5 = Truncate8((inst4.value32 & 0xFF000000u) >> 24);
   1305         const u8 r6 = Truncate8((inst4.value32 & 0x00FF0000u) >> 16);
   1306         const u8 r7 = Truncate8((inst4.value32 & 0x0000FF00u) >> 8);
   1307         const u8 r8 = Truncate8(inst4.value32 & 0x000000FFu);
   1308         const u8 r9 = Truncate8((inst5.first & 0xFF000000u) >> 24);
   1309         const u8 r10 = Truncate8((inst5.first & 0x00FF0000u) >> 16);
   1310         const u8 r11 = Truncate8((inst5.first & 0x0000FF00u) >> 8);
   1311         const u8 r12 = Truncate8(inst5.first & 0x000000FFu);
   1312         const u8 r13 = Truncate8((inst5.value32 & 0xFF000000u) >> 24);
   1313         const u8 r14 = Truncate8((inst5.value32 & 0x00FF0000u) >> 16);
   1314         const u8 r15 = Truncate8((inst5.value32 & 0x0000FF00u) >> 8);
   1315         const u8 r16 = Truncate8(inst5.value32 & 0x000000FFu);
   1316 
   1317         for (u32 address = minaddress; address <= maxaddress; address += 2)
   1318         {
   1319           if ((DoMemoryRead<u8>(address) == f1 || f1 == wildcard) &&
   1320               (DoMemoryRead<u8>(address + 1) == f2 || f2 == wildcard) &&
   1321               (DoMemoryRead<u8>(address + 2) == f3 || f3 == wildcard) &&
   1322               (DoMemoryRead<u8>(address + 3) == f4 || f4 == wildcard) &&
   1323               (DoMemoryRead<u8>(address + 4) == f5 || f5 == wildcard) &&
   1324               (DoMemoryRead<u8>(address + 5) == f6 || f6 == wildcard) &&
   1325               (DoMemoryRead<u8>(address + 6) == f7 || f7 == wildcard) &&
   1326               (DoMemoryRead<u8>(address + 7) == f8 || f8 == wildcard) &&
   1327               (DoMemoryRead<u8>(address + 8) == f9 || f9 == wildcard) &&
   1328               (DoMemoryRead<u8>(address + 9) == f10 || f10 == wildcard) &&
   1329               (DoMemoryRead<u8>(address + 10) == f11 || f11 == wildcard) &&
   1330               (DoMemoryRead<u8>(address + 11) == f12 || f12 == wildcard) &&
   1331               (DoMemoryRead<u8>(address + 12) == f13 || f13 == wildcard) &&
   1332               (DoMemoryRead<u8>(address + 13) == f14 || f14 == wildcard) &&
   1333               (DoMemoryRead<u8>(address + 14) == f15 || f15 == wildcard) &&
   1334               (DoMemoryRead<u8>(address + 15) == f16 || f16 == wildcard))
   1335           {
   1336             if (r1 != wildcard)
   1337               DoMemoryWrite<u8>(address, r1);
   1338             if (r2 != wildcard)
   1339               DoMemoryWrite<u8>(address + 1, r2);
   1340             if (r3 != wildcard)
   1341               DoMemoryWrite<u8>(address + 2, r3);
   1342             if (r4 != wildcard)
   1343               DoMemoryWrite<u8>(address + 3, r4);
   1344             if (r5 != wildcard)
   1345               DoMemoryWrite<u8>(address + 4, r5);
   1346             if (r6 != wildcard)
   1347               DoMemoryWrite<u8>(address + 5, r6);
   1348             if (r7 != wildcard)
   1349               DoMemoryWrite<u8>(address + 6, r7);
   1350             if (r8 != wildcard)
   1351               DoMemoryWrite<u8>(address + 7, r8);
   1352             if (r9 != wildcard)
   1353               DoMemoryWrite<u8>(address + 8, r9);
   1354             if (r10 != wildcard)
   1355               DoMemoryWrite<u8>(address + 9, r10);
   1356             if (r11 != wildcard)
   1357               DoMemoryWrite<u8>(address + 10, r11);
   1358             if (r12 != wildcard)
   1359               DoMemoryWrite<u8>(address + 11, r12);
   1360             if (r13 != wildcard)
   1361               DoMemoryWrite<u8>(address + 12, r13);
   1362             if (r14 != wildcard)
   1363               DoMemoryWrite<u8>(address + 13, r14);
   1364             if (r15 != wildcard)
   1365               DoMemoryWrite<u8>(address + 14, r15);
   1366             if (r16 != wildcard)
   1367               DoMemoryWrite<u8>(address + 15, r16);
   1368             address = address + 15;
   1369           }
   1370         }
   1371         index += 5;
   1372       }
   1373       break;
   1374 
   1375       case InstructionCode::CompareEqual16:
   1376       {
   1377         const u16 value = DoMemoryRead<u16>(inst.address);
   1378         if (value == inst.value16)
   1379           index++;
   1380         else
   1381           index = GetNextNonConditionalInstruction(index);
   1382       }
   1383       break;
   1384 
   1385       case InstructionCode::CompareNotEqual16:
   1386       {
   1387         const u16 value = DoMemoryRead<u16>(inst.address);
   1388         if (value != inst.value16)
   1389           index++;
   1390         else
   1391           index = GetNextNonConditionalInstruction(index);
   1392       }
   1393       break;
   1394 
   1395       case InstructionCode::CompareLess16:
   1396       {
   1397         const u16 value = DoMemoryRead<u16>(inst.address);
   1398         if (value < inst.value16)
   1399           index++;
   1400         else
   1401           index = GetNextNonConditionalInstruction(index);
   1402       }
   1403       break;
   1404 
   1405       case InstructionCode::CompareGreater16:
   1406       {
   1407         const u16 value = DoMemoryRead<u16>(inst.address);
   1408         if (value > inst.value16)
   1409           index++;
   1410         else
   1411           index = GetNextNonConditionalInstruction(index);
   1412       }
   1413       break;
   1414 
   1415       case InstructionCode::CompareEqual8:
   1416       {
   1417         const u8 value = DoMemoryRead<u8>(inst.address);
   1418         if (value == inst.value8)
   1419           index++;
   1420         else
   1421           index = GetNextNonConditionalInstruction(index);
   1422       }
   1423       break;
   1424 
   1425       case InstructionCode::CompareNotEqual8:
   1426       {
   1427         const u8 value = DoMemoryRead<u8>(inst.address);
   1428         if (value != inst.value8)
   1429           index++;
   1430         else
   1431           index = GetNextNonConditionalInstruction(index);
   1432       }
   1433       break;
   1434 
   1435       case InstructionCode::CompareLess8:
   1436       {
   1437         const u8 value = DoMemoryRead<u8>(inst.address);
   1438         if (value < inst.value8)
   1439           index++;
   1440         else
   1441           index = GetNextNonConditionalInstruction(index);
   1442       }
   1443       break;
   1444 
   1445       case InstructionCode::CompareGreater8:
   1446       {
   1447         const u8 value = DoMemoryRead<u8>(inst.address);
   1448         if (value > inst.value8)
   1449           index++;
   1450         else
   1451           index = GetNextNonConditionalInstruction(index);
   1452       }
   1453       break;
   1454 
   1455       case InstructionCode::CompareButtons: // D4
   1456       {
   1457         if (inst.value16 == GetControllerButtonBits())
   1458           index++;
   1459         else
   1460           index = GetNextNonConditionalInstruction(index);
   1461       }
   1462       break;
   1463 
   1464       case InstructionCode::ExtCheatRegisters: // 51
   1465       {
   1466         const u32 poke_value = inst.value32;
   1467         const u8 cht_reg_no1 = Truncate8(inst.address & 0xFFu);
   1468         const u8 cht_reg_no2 = Truncate8((inst.address & 0xFF00u) >> 8);
   1469         const u8 cht_reg_no3 = Truncate8(inst.value32 & 0xFFu);
   1470         const u8 sub_type = Truncate8((inst.address & 0xFF0000u) >> 16);
   1471 
   1472         switch (sub_type)
   1473         {
   1474           case 0x00: // Write the u8 from cht_register[cht_reg_no1] to address
   1475             DoMemoryWrite<u8>(inst.value32, Truncate8(cht_register[cht_reg_no1]) & 0xFFu);
   1476             break;
   1477           case 0x01: // Read the u8 from address to cht_register[cht_reg_no1]
   1478             cht_register[cht_reg_no1] = DoMemoryRead<u8>(inst.value32);
   1479             break;
   1480           case 0x02: // Write the u8 from address field to the address stored in cht_register[cht_reg_no1]
   1481             DoMemoryWrite<u8>(cht_register[cht_reg_no1], Truncate8(poke_value & 0xFFu));
   1482             break;
   1483           case 0x03: // Write the u8 from cht_register[cht_reg_no2] to cht_register[cht_reg_no1]
   1484                      // and add the u8 from the address field to it
   1485             cht_register[cht_reg_no1] = Truncate8(cht_register[cht_reg_no2] & 0xFFu) + Truncate8(poke_value & 0xFFu);
   1486             break;
   1487           case 0x04: // Write the u8 from the value stored in cht_register[cht_reg_no2] + poke_value to the address
   1488                      // stored in cht_register[cht_reg_no1]
   1489             DoMemoryWrite<u8>(cht_register[cht_reg_no1],
   1490                               Truncate8(cht_register[cht_reg_no2] & 0xFFu) + Truncate8(poke_value & 0xFFu));
   1491             break;
   1492           case 0x05: // Write the u8 poke value to cht_register[cht_reg_no1]
   1493             cht_register[cht_reg_no1] = Truncate8(poke_value & 0xFFu);
   1494             break;
   1495           case 0x06: // Read the u8 value from the address (cht_register[cht_reg_no2] + poke_value) to
   1496                      // cht_register[cht_reg_no1]
   1497             cht_register[cht_reg_no1] = DoMemoryRead<u8>(cht_register[cht_reg_no2] + poke_value);
   1498             break;
   1499 
   1500           case 0x40: // Write the u16 from cht_register[cht_reg_no1] to address
   1501             DoMemoryWrite<u16>(inst.value32, Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   1502             break;
   1503           case 0x41: // Read the u16 from address to cht_register[cht_reg_no1]
   1504             cht_register[cht_reg_no1] = DoMemoryRead<u16>(inst.value32);
   1505             break;
   1506           case 0x42: // Write the u16 from address field to the address stored in cht_register[cht_reg_no1]
   1507             DoMemoryWrite<u16>(cht_register[cht_reg_no1], Truncate16(poke_value & 0xFFFFu));
   1508             break;
   1509           case 0x43: // Write the u16 from cht_register[cht_reg_no2] to cht_register[cht_reg_no1]
   1510                      // and add the u16 from the address field to it
   1511             cht_register[cht_reg_no1] =
   1512               Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) + Truncate16(poke_value & 0xFFFFu);
   1513             break;
   1514           case 0x44: // Write the u16 from the value stored in cht_register[cht_reg_no2] + poke_value to the address
   1515                      // stored in cht_register[cht_reg_no1]
   1516             DoMemoryWrite<u16>(cht_register[cht_reg_no1],
   1517                                Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) + Truncate16(poke_value & 0xFFFFu));
   1518             break;
   1519           case 0x45: // Write the u16 poke value to cht_register[cht_reg_no1]
   1520             cht_register[cht_reg_no1] = Truncate16(poke_value & 0xFFFFu);
   1521             break;
   1522           case 0x46: // Read the u16 value from the address (cht_register[cht_reg_no2] + poke_value) to
   1523                      // cht_register[cht_reg_no1]
   1524             cht_register[cht_reg_no1] = DoMemoryRead<u16>(cht_register[cht_reg_no2] + poke_value);
   1525             break;
   1526 
   1527           case 0x80: // Write the u32 from cht_register[cht_reg_no1] to address
   1528             DoMemoryWrite<u32>(inst.value32, cht_register[cht_reg_no1]);
   1529             break;
   1530           case 0x81: // Read the u32 from address to cht_register[cht_reg_no1]
   1531             cht_register[cht_reg_no1] = DoMemoryRead<u32>(inst.value32);
   1532             break;
   1533           case 0x82: // Write the u32 from address field to the address stored in cht_register[cht_reg_no]
   1534             DoMemoryWrite<u32>(cht_register[cht_reg_no1], poke_value);
   1535             break;
   1536           case 0x83: // Write the u32 from cht_register[cht_reg_no2] to cht_register[cht_reg_no1]
   1537                      // and add the u32 from the address field to it
   1538             cht_register[cht_reg_no1] = cht_register[cht_reg_no2] + poke_value;
   1539             break;
   1540           case 0x84: // Write the u32 from the value stored in cht_register[cht_reg_no2] + poke_value to the address
   1541                      // stored in cht_register[cht_reg_no1]
   1542             DoMemoryWrite<u32>(cht_register[cht_reg_no1], cht_register[cht_reg_no2] + poke_value);
   1543             break;
   1544           case 0x85: // Write the u32 poke value to cht_register[cht_reg_no1]
   1545             cht_register[cht_reg_no1] = poke_value;
   1546             break;
   1547           case 0x86: // Read the u32 value from the address (cht_register[cht_reg_no2] + poke_value) to
   1548                      // cht_register[cht_reg_no1]
   1549             cht_register[cht_reg_no1] = DoMemoryRead<u32>(cht_register[cht_reg_no2] + poke_value);
   1550             break;
   1551 
   1552           case 0xC0: // Reg3 = Reg2 + Reg1
   1553             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] + cht_register[cht_reg_no1];
   1554             break;
   1555           case 0xC1: // Reg3 = Reg2 - Reg1
   1556             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] - cht_register[cht_reg_no1];
   1557             break;
   1558           case 0xC2: // Reg3 = Reg2 * Reg1
   1559             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] * cht_register[cht_reg_no1];
   1560             break;
   1561           case 0xC3: // Reg3 = Reg2 / Reg1 with DIV0 handling
   1562             if (cht_register[cht_reg_no1] == 0)
   1563               cht_register[cht_reg_no3] = 0;
   1564             else
   1565               cht_register[cht_reg_no3] = cht_register[cht_reg_no2] / cht_register[cht_reg_no1];
   1566             break;
   1567           case 0xC4: // Reg3 = Reg2 % Reg1 (with DIV0 handling)
   1568             if (cht_register[cht_reg_no1] == 0)
   1569               cht_register[cht_reg_no3] = cht_register[cht_reg_no2];
   1570             else
   1571               cht_register[cht_reg_no3] = cht_register[cht_reg_no2] % cht_register[cht_reg_no1];
   1572             break;
   1573           case 0xC5: // Reg3 = Reg2 & Reg1
   1574             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] & cht_register[cht_reg_no1];
   1575             break;
   1576           case 0xC6: // Reg3 = Reg2 | Reg1
   1577             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] | cht_register[cht_reg_no1];
   1578             break;
   1579           case 0xC7: // Reg3 = Reg2 ^ Reg1
   1580             cht_register[cht_reg_no3] = cht_register[cht_reg_no2] ^ cht_register[cht_reg_no1];
   1581             break;
   1582           case 0xC8: // Reg3 = ~Reg1
   1583             cht_register[cht_reg_no3] = ~cht_register[cht_reg_no1];
   1584             break;
   1585           case 0xC9: // Reg3 = Reg1 << X
   1586             cht_register[cht_reg_no3] = cht_register[cht_reg_no1] << cht_reg_no2;
   1587             break;
   1588           case 0xCA: // Reg3 = Reg1 >> X
   1589             cht_register[cht_reg_no3] = cht_register[cht_reg_no1] >> cht_reg_no2;
   1590             break;
   1591           // Lots of options exist for expanding into this space
   1592           default:
   1593             break;
   1594         }
   1595         index++;
   1596       }
   1597       break;
   1598 
   1599       case InstructionCode::SkipIfNotEqual16:      // C0
   1600       case InstructionCode::ExtSkipIfNotEqual32:   // A4
   1601       case InstructionCode::SkipIfButtonsNotEqual: // D5
   1602       case InstructionCode::SkipIfButtonsEqual:    // D6
   1603       case InstructionCode::ExtSkipIfNotLess8:     // C3
   1604       case InstructionCode::ExtSkipIfNotGreater8:  // C4
   1605       case InstructionCode::ExtSkipIfNotLess16:    // C5
   1606       case InstructionCode::ExtSkipIfNotGreater16: // C6
   1607       case InstructionCode::ExtMultiConditionals:  // F6
   1608       {
   1609         index++;
   1610 
   1611         bool activate_codes;
   1612         switch (inst.code)
   1613         {
   1614           case InstructionCode::SkipIfNotEqual16: // C0
   1615             activate_codes = (DoMemoryRead<u16>(inst.address) == inst.value16);
   1616             break;
   1617           case InstructionCode::ExtSkipIfNotEqual32: // A4
   1618             activate_codes = (DoMemoryRead<u32>(inst.address) == inst.value32);
   1619             break;
   1620           case InstructionCode::SkipIfButtonsNotEqual: // D5
   1621             activate_codes = (GetControllerButtonBits() == inst.value16);
   1622             break;
   1623           case InstructionCode::SkipIfButtonsEqual: // D6
   1624             activate_codes = (GetControllerButtonBits() != inst.value16);
   1625             break;
   1626           case InstructionCode::ExtSkipIfNotLess8: // C3
   1627             activate_codes = (DoMemoryRead<u8>(inst.address) < inst.value8);
   1628             break;
   1629           case InstructionCode::ExtSkipIfNotGreater8: // C4
   1630             activate_codes = (DoMemoryRead<u8>(inst.address) > inst.value8);
   1631             break;
   1632           case InstructionCode::ExtSkipIfNotLess16: // C5
   1633             activate_codes = (DoMemoryRead<u16>(inst.address) < inst.value16);
   1634             break;
   1635           case InstructionCode::ExtSkipIfNotGreater16: // C6
   1636             activate_codes = (DoMemoryRead<u16>(inst.address) > inst.value16);
   1637             break;
   1638           case InstructionCode::ExtMultiConditionals: // F6
   1639           {
   1640             // Ensure any else if or else that are hit outside the if context are skipped
   1641             if ((inst.value32 & 0xFFFFFF00u) != 0x1F000000)
   1642             {
   1643               activate_codes = false;
   1644               break;
   1645             }
   1646             for (;;)
   1647             {
   1648               const u8 totalConds = Truncate8(instructions[index - 1].value32 & 0x000000FFu);
   1649               const u8 conditionType = Truncate8(instructions[index - 1].address & 0x000000FFu);
   1650 
   1651               bool conditions_check;
   1652 
   1653               if (conditionType == 0x00 && totalConds > 0) // AND
   1654               {
   1655                 conditions_check = true;
   1656 
   1657                 for (int i = 1; totalConds >= i; index++, i++)
   1658                 {
   1659                   switch (instructions[index].code)
   1660                   {
   1661                     case InstructionCode::CompareEqual16: // D0
   1662                       conditions_check &=
   1663                         (DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16);
   1664                       break;
   1665                     case InstructionCode::CompareNotEqual16: // D1
   1666                       conditions_check &=
   1667                         (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
   1668                       break;
   1669                     case InstructionCode::CompareLess16: // D2
   1670                       conditions_check &=
   1671                         (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
   1672                       break;
   1673                     case InstructionCode::CompareGreater16: // D3
   1674                       conditions_check &=
   1675                         (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
   1676                       break;
   1677                     case InstructionCode::CompareEqual8: // E0
   1678                       conditions_check &= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
   1679                       break;
   1680                     case InstructionCode::CompareNotEqual8: // E1
   1681                       conditions_check &= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
   1682                       break;
   1683                     case InstructionCode::CompareLess8: // E2
   1684                       conditions_check &= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
   1685                       break;
   1686                     case InstructionCode::CompareGreater8: // E3
   1687                       conditions_check &= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
   1688                       break;
   1689                     case InstructionCode::ExtCompareEqual32: // A0
   1690                       conditions_check &=
   1691                         (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
   1692                       break;
   1693                     case InstructionCode::ExtCompareNotEqual32: // A1
   1694                       conditions_check &=
   1695                         (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
   1696                       break;
   1697                     case InstructionCode::ExtCompareLess32: // A2
   1698                       conditions_check &=
   1699                         (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
   1700                       break;
   1701                     case InstructionCode::ExtCompareGreater32: // A3
   1702                       conditions_check &=
   1703                         (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
   1704                       break;
   1705                     case InstructionCode::ExtCompareBitsSet8: // E4 Internal to F6
   1706                       conditions_check &=
   1707                         (instructions[index].value8 ==
   1708                          (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
   1709                       break;
   1710                     case InstructionCode::ExtCompareBitsClear8: // E5 Internal to F6
   1711                       conditions_check &=
   1712                         ((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0);
   1713                       break;
   1714                     case InstructionCode::ExtBitCompareButtons: // D7
   1715                     {
   1716                       const u32 frame_compare_value = instructions[index].address & 0xFFFFu;
   1717                       const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >> 24);
   1718                       const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >> 20);
   1719                       const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >> 16);
   1720                       const u32 check_value = (instructions[index].value32 & 0xFFFFFFu);
   1721                       const u32 value1 = GetControllerButtonBits();
   1722                       const u32 value2 = GetControllerAnalogBits();
   1723                       u32 value = value1 | value2;
   1724 
   1725                       if ((bit_comparison_type == false && check_value == (value & check_value)) // Check Bits are set
   1726                           ||
   1727                           (bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
   1728                       {
   1729                         cht_register[cht_reg_no] += 1;
   1730                         switch (frame_comparison)
   1731                         {
   1732                           case 0x0: // No comparison on frame count, just do it
   1733                             conditions_check &= true;
   1734                             break;
   1735                           case 0x1: // Check if frame_compare_value == current count
   1736                             conditions_check &= (cht_register[cht_reg_no] == frame_compare_value);
   1737                             break;
   1738                           case 0x2: // Check if frame_compare_value < current count
   1739                             conditions_check &= (cht_register[cht_reg_no] < frame_compare_value);
   1740                             break;
   1741                           case 0x3: // Check if frame_compare_value > current count
   1742                             conditions_check &= (cht_register[cht_reg_no] > frame_compare_value);
   1743                             break;
   1744                           case 0x4: // Check if frame_compare_value != current count
   1745                             conditions_check &= (cht_register[cht_reg_no] != frame_compare_value);
   1746                             break;
   1747                           default:
   1748                             conditions_check &= false;
   1749                             break;
   1750                         }
   1751                       }
   1752                       else
   1753                       {
   1754                         cht_register[cht_reg_no] = 0;
   1755                         conditions_check &= false;
   1756                       }
   1757                       break;
   1758                     }
   1759                     default:
   1760                       ERROR_LOG("Incorrect conditional instruction (see chtdb.txt for supported instructions)");
   1761                       return;
   1762                   }
   1763                 }
   1764               }
   1765               else if (conditionType == 0x01 && totalConds > 0) // OR
   1766               {
   1767                 conditions_check = false;
   1768 
   1769                 for (int i = 1; totalConds >= i; index++, i++)
   1770                 {
   1771                   switch (instructions[index].code)
   1772                   {
   1773                     case InstructionCode::CompareEqual16: // D0
   1774                       conditions_check |=
   1775                         (DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16);
   1776                       break;
   1777                     case InstructionCode::CompareNotEqual16: // D1
   1778                       conditions_check |=
   1779                         (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
   1780                       break;
   1781                     case InstructionCode::CompareLess16: // D2
   1782                       conditions_check |=
   1783                         (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
   1784                       break;
   1785                     case InstructionCode::CompareGreater16: // D3
   1786                       conditions_check |=
   1787                         (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
   1788                       break;
   1789                     case InstructionCode::CompareEqual8: // E0
   1790                       conditions_check |= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
   1791                       break;
   1792                     case InstructionCode::CompareNotEqual8: // E1
   1793                       conditions_check |= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
   1794                       break;
   1795                     case InstructionCode::CompareLess8: // E2
   1796                       conditions_check |= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
   1797                       break;
   1798                     case InstructionCode::CompareGreater8: // E3
   1799                       conditions_check |= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
   1800                       break;
   1801                     case InstructionCode::ExtCompareEqual32: // A0
   1802                       conditions_check |=
   1803                         (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
   1804                       break;
   1805                     case InstructionCode::ExtCompareNotEqual32: // A1
   1806                       conditions_check |=
   1807                         (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
   1808                       break;
   1809                     case InstructionCode::ExtCompareLess32: // A2
   1810                       conditions_check |=
   1811                         (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
   1812                       break;
   1813                     case InstructionCode::ExtCompareGreater32: // A3
   1814                       conditions_check |=
   1815                         (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
   1816                       break;
   1817                     case InstructionCode::ExtCompareBitsSet8: // E4 Internal to F6
   1818                       conditions_check |=
   1819                         (instructions[index].value8 ==
   1820                          (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
   1821                       break;
   1822                     case InstructionCode::ExtCompareBitsClear8: // E5 Internal to F6
   1823                       conditions_check |=
   1824                         ((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0);
   1825                       break;
   1826                     case InstructionCode::ExtBitCompareButtons: // D7
   1827                     {
   1828                       const u32 frame_compare_value = instructions[index].address & 0xFFFFu;
   1829                       const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >> 24);
   1830                       const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >> 20);
   1831                       const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >> 16);
   1832                       const u32 check_value = (instructions[index].value32 & 0xFFFFFFu);
   1833                       const u32 value1 = GetControllerButtonBits();
   1834                       const u32 value2 = GetControllerAnalogBits();
   1835                       u32 value = value1 | value2;
   1836 
   1837                       if ((bit_comparison_type == false && check_value == (value & check_value)) // Check Bits are set
   1838                           ||
   1839                           (bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
   1840                       {
   1841                         cht_register[cht_reg_no] += 1;
   1842                         switch (frame_comparison)
   1843                         {
   1844                           case 0x0: // No comparison on frame count, just do it
   1845                             conditions_check |= true;
   1846                             break;
   1847                           case 0x1: // Check if frame_compare_value == current count
   1848                             conditions_check |= (cht_register[cht_reg_no] == frame_compare_value);
   1849                             break;
   1850                           case 0x2: // Check if frame_compare_value < current count
   1851                             conditions_check |= (cht_register[cht_reg_no] < frame_compare_value);
   1852                             break;
   1853                           case 0x3: // Check if frame_compare_value > current count
   1854                             conditions_check |= (cht_register[cht_reg_no] > frame_compare_value);
   1855                             break;
   1856                           case 0x4: // Check if frame_compare_value != current count
   1857                             conditions_check |= (cht_register[cht_reg_no] != frame_compare_value);
   1858                             break;
   1859                           default:
   1860                             conditions_check |= false;
   1861                             break;
   1862                         }
   1863                       }
   1864                       else
   1865                       {
   1866                         cht_register[cht_reg_no] = 0;
   1867                         conditions_check |= false;
   1868                       }
   1869                       break;
   1870                     }
   1871                     default:
   1872                       ERROR_LOG("Incorrect conditional instruction (see chtdb.txt for supported instructions)");
   1873                       return;
   1874                   }
   1875                 }
   1876               }
   1877               else
   1878               {
   1879                 ERROR_LOG("Incomplete multi conditional instruction");
   1880                 return;
   1881               }
   1882               if (conditions_check == true)
   1883               {
   1884                 activate_codes = true;
   1885                 break;
   1886               }
   1887               else
   1888               { // parse through to 00000000 FFFF and peek if next line is a F6 type associated with a ELSE
   1889                 activate_codes = false;
   1890                 // skip to the next separator (00000000 FFFF), or end
   1891                 constexpr u64 separator_value = UINT64_C(0x000000000000FFFF);
   1892                 constexpr u64 else_value = UINT64_C(0x00000000E15E0000);
   1893                 constexpr u64 elseif_value = UINT64_C(0x00000000E15E1F00);
   1894                 while (index < count)
   1895                 {
   1896                   const u64 bits = instructions[index++].bits;
   1897                   if (bits == separator_value)
   1898                   {
   1899                     const u64 bits_ahead = instructions[index].bits;
   1900                     if ((bits_ahead & 0xFFFFFF00u) == elseif_value)
   1901                     {
   1902                       break;
   1903                     }
   1904                     if ((bits_ahead & 0xFFFF0000u) == else_value)
   1905                     {
   1906                       // index++;
   1907                       activate_codes = true;
   1908                       break;
   1909                     }
   1910                     index--;
   1911                     break;
   1912                   }
   1913                   if ((bits & 0xFFFFFF00u) == elseif_value)
   1914                   {
   1915                     // index--;
   1916                     break;
   1917                   }
   1918                   if ((bits & 0xFFFFFFFFu) == else_value)
   1919                   {
   1920                     // index++;
   1921                     activate_codes = true;
   1922                     break;
   1923                   }
   1924                 }
   1925                 if (activate_codes == true)
   1926                   break;
   1927               }
   1928             }
   1929             break;
   1930           }
   1931           default:
   1932             activate_codes = false;
   1933             break;
   1934         }
   1935 
   1936         if (activate_codes)
   1937         {
   1938           // execute following instructions
   1939           continue;
   1940         }
   1941 
   1942         // skip to the next separator (00000000 FFFF), or end
   1943         constexpr u64 separator_value = UINT64_C(0x000000000000FFFF);
   1944         while (index < count)
   1945         {
   1946           // we don't want to execute the separator instruction
   1947           const u64 bits = instructions[index++].bits;
   1948           if (bits == separator_value)
   1949             break;
   1950         }
   1951       }
   1952       break;
   1953 
   1954       case InstructionCode::ExtBitCompareButtons: // D7
   1955       {
   1956         index++;
   1957         bool activate_codes;
   1958         const u32 frame_compare_value = inst.address & 0xFFFFu;
   1959         const u8 cht_reg_no = Truncate8((inst.value32 & 0xFF000000u) >> 24);
   1960         const bool bit_comparison_type = ((inst.address & 0x100000u) >> 20);
   1961         const u8 frame_comparison = Truncate8((inst.address & 0xF0000u) >> 16);
   1962         const u32 check_value = (inst.value32 & 0xFFFFFFu);
   1963         const u32 value1 = GetControllerButtonBits();
   1964         const u32 value2 = GetControllerAnalogBits();
   1965         u32 value = value1 | value2;
   1966 
   1967         if ((bit_comparison_type == false && check_value == (value & check_value))    // Check Bits are set
   1968             || (bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
   1969         {
   1970           cht_register[cht_reg_no] += 1;
   1971           switch (frame_comparison)
   1972           {
   1973             case 0x0: // No comparison on frame count, just do it
   1974               activate_codes = true;
   1975               break;
   1976             case 0x1: // Check if frame_compare_value == current count
   1977               activate_codes = (cht_register[cht_reg_no] == frame_compare_value);
   1978               break;
   1979             case 0x2: // Check if frame_compare_value < current count
   1980               activate_codes = (cht_register[cht_reg_no] < frame_compare_value);
   1981               break;
   1982             case 0x3: // Check if frame_compare_value > current count
   1983               activate_codes = (cht_register[cht_reg_no] > frame_compare_value);
   1984               break;
   1985             case 0x4: // Check if frame_compare_value != current count
   1986               activate_codes = (cht_register[cht_reg_no] != frame_compare_value);
   1987               break;
   1988             default:
   1989               activate_codes = false;
   1990               break;
   1991           }
   1992         }
   1993         else
   1994         {
   1995           cht_register[cht_reg_no] = 0;
   1996           activate_codes = false;
   1997         }
   1998 
   1999         if (activate_codes)
   2000         {
   2001           // execute following instructions
   2002           continue;
   2003         }
   2004 
   2005         // skip to the next separator (00000000 FFFF), or end
   2006         constexpr u64 separator_value = UINT64_C(0x000000000000FFFF);
   2007         while (index < count)
   2008         {
   2009           // we don't want to execute the separator instruction
   2010           const u64 bits = instructions[index++].bits;
   2011           if (bits == separator_value)
   2012             break;
   2013         }
   2014       }
   2015       break;
   2016 
   2017       case InstructionCode::ExtCheatRegistersCompare: // 52
   2018       {
   2019         index++;
   2020         bool activate_codes = false;
   2021         const u8 cht_reg_no1 = Truncate8(inst.address & 0xFFu);
   2022         const u8 cht_reg_no2 = Truncate8((inst.address & 0xFF00u) >> 8);
   2023         const u8 sub_type = Truncate8((inst.first & 0xFF0000u) >> 16);
   2024 
   2025         switch (sub_type)
   2026         {
   2027           case 0x00:
   2028             activate_codes =
   2029               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) == Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2030             break;
   2031           case 0x01:
   2032             activate_codes =
   2033               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) != Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2034             break;
   2035           case 0x02:
   2036             activate_codes =
   2037               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) > Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2038             break;
   2039           case 0x03:
   2040             activate_codes =
   2041               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) >= Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2042             break;
   2043           case 0x04:
   2044             activate_codes =
   2045               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) < Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2046             break;
   2047           case 0x05:
   2048             activate_codes =
   2049               (Truncate8(cht_register[cht_reg_no2] & 0xFFu) <= Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2050             break;
   2051           case 0x06:
   2052             activate_codes =
   2053               ((Truncate8(cht_register[cht_reg_no2] & 0xFFu) & Truncate8(cht_register[cht_reg_no1] & 0xFFu)) ==
   2054                (Truncate8(cht_register[cht_reg_no1] & 0xFFu)));
   2055             break;
   2056           case 0x07:
   2057             activate_codes =
   2058               ((Truncate8(cht_register[cht_reg_no2] & 0xFFu) & Truncate8(cht_register[cht_reg_no1] & 0xFFu)) !=
   2059                (Truncate8(cht_register[cht_reg_no1] & 0xFFu)));
   2060             break;
   2061           case 0x0A:
   2062             activate_codes =
   2063               ((Truncate8(cht_register[cht_reg_no2] & 0xFFu) & Truncate8(cht_register[cht_reg_no1] & 0xFFu)) ==
   2064                (Truncate8(cht_register[cht_reg_no2] & 0xFFu)));
   2065             break;
   2066           case 0x0B:
   2067             activate_codes =
   2068               ((Truncate8(cht_register[cht_reg_no2] & 0xFFu) & Truncate8(cht_register[cht_reg_no1] & 0xFFu)) !=
   2069                (Truncate8(cht_register[cht_reg_no2] & 0xFFu)));
   2070             break;
   2071           case 0x10:
   2072             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) == inst.value8);
   2073             break;
   2074           case 0x11:
   2075             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) != inst.value8);
   2076             break;
   2077           case 0x12:
   2078             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) > inst.value8);
   2079             break;
   2080           case 0x13:
   2081             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) >= inst.value8);
   2082             break;
   2083           case 0x14:
   2084             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) < inst.value8);
   2085             break;
   2086           case 0x15:
   2087             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) <= inst.value8);
   2088             break;
   2089           case 0x16:
   2090             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & inst.value8) == inst.value8);
   2091             break;
   2092           case 0x17:
   2093             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & inst.value8) != inst.value8);
   2094             break;
   2095           case 0x18:
   2096             activate_codes =
   2097               ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) > inst.value8) &&
   2098                (Truncate8(cht_register[cht_reg_no1] & 0xFFu) < Truncate8((inst.value32 & 0xFF0000u) >> 16)));
   2099             break;
   2100           case 0x19:
   2101             activate_codes =
   2102               ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) >= inst.value8) &&
   2103                (Truncate8(cht_register[cht_reg_no1] & 0xFFu) <= Truncate8((inst.value32 & 0xFF0000u) >> 16)));
   2104             break;
   2105           case 0x1A:
   2106             activate_codes = ((Truncate8(cht_register[cht_reg_no2] & 0xFFu) & inst.value8) ==
   2107                               Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2108             break;
   2109           case 0x1B:
   2110             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & inst.value8) !=
   2111                               Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2112             break;
   2113           case 0x20:
   2114             activate_codes =
   2115               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) == DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2116             break;
   2117           case 0x21:
   2118             activate_codes =
   2119               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) != DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2120             break;
   2121           case 0x22:
   2122             activate_codes =
   2123               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) > DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2124             break;
   2125           case 0x23:
   2126             activate_codes =
   2127               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) >= DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2128             break;
   2129           case 0x24:
   2130             activate_codes =
   2131               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) < DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2132             break;
   2133           case 0x25:
   2134             activate_codes =
   2135               (DoMemoryRead<u8>(cht_register[cht_reg_no2]) <= DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2136             break;
   2137           case 0x26:
   2138             activate_codes = ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) & inst.value8) == inst.value8);
   2139             break;
   2140           case 0x27:
   2141             activate_codes = ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) & inst.value8) != inst.value8);
   2142             break;
   2143           case 0x28:
   2144             activate_codes =
   2145               ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) > inst.value8) &&
   2146                (DoMemoryRead<u8>(cht_register[cht_reg_no1]) < Truncate8((inst.value32 & 0xFF0000u) >> 16)));
   2147             break;
   2148           case 0x29:
   2149             activate_codes =
   2150               ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) >= inst.value8) &&
   2151                (DoMemoryRead<u8>(cht_register[cht_reg_no1]) <= Truncate8((inst.value32 & 0xFF0000u) >> 16)));
   2152             break;
   2153           case 0x2A:
   2154             activate_codes = ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) & inst.value8) ==
   2155                               DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2156             break;
   2157           case 0x2B:
   2158             activate_codes = ((DoMemoryRead<u8>(cht_register[cht_reg_no1]) & inst.value8) !=
   2159                               DoMemoryRead<u8>(cht_register[cht_reg_no1]));
   2160             break;
   2161           case 0x30:
   2162             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) == DoMemoryRead<u8>(inst.value32));
   2163             break;
   2164           case 0x31:
   2165             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) != DoMemoryRead<u8>(inst.value32));
   2166             break;
   2167           case 0x32:
   2168             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) > DoMemoryRead<u8>(inst.value32));
   2169             break;
   2170           case 0x33:
   2171             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) >= DoMemoryRead<u8>(inst.value32));
   2172             break;
   2173           case 0x34:
   2174             activate_codes = (Truncate8(cht_register[cht_reg_no1] & 0xFFu) < DoMemoryRead<u8>(inst.value32));
   2175             break;
   2176           case 0x36:
   2177             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & DoMemoryRead<u8>(inst.value32)) ==
   2178                               DoMemoryRead<u8>(inst.value32));
   2179             break;
   2180           case 0x37:
   2181             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & DoMemoryRead<u8>(inst.value32)) !=
   2182                               DoMemoryRead<u8>(inst.value32));
   2183             break;
   2184           case 0x3A:
   2185             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & DoMemoryRead<u8>(inst.value32)) ==
   2186                               Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2187             break;
   2188           case 0x3B:
   2189             activate_codes = ((Truncate8(cht_register[cht_reg_no1] & 0xFFu) & DoMemoryRead<u8>(inst.value32)) !=
   2190                               Truncate8(cht_register[cht_reg_no1] & 0xFFu));
   2191             break;
   2192           case 0x40:
   2193             activate_codes =
   2194               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) == Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2195             break;
   2196           case 0x41:
   2197             activate_codes =
   2198               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) != Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2199             break;
   2200           case 0x42:
   2201             activate_codes =
   2202               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) > Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2203             break;
   2204           case 0x43:
   2205             activate_codes =
   2206               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) >= Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2207             break;
   2208           case 0x44:
   2209             activate_codes =
   2210               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) < Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2211             break;
   2212           case 0x45:
   2213             activate_codes =
   2214               (Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) <= Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2215             break;
   2216           case 0x46:
   2217             activate_codes =
   2218               ((Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) & Truncate16(cht_register[cht_reg_no1] & 0xFFFFu)) ==
   2219                Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2220             break;
   2221           case 0x47:
   2222             activate_codes =
   2223               ((Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) & Truncate16(cht_register[cht_reg_no1] & 0xFFFFu)) !=
   2224                Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2225             break;
   2226           case 0x4A:
   2227             activate_codes =
   2228               ((Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) & Truncate16(cht_register[cht_reg_no1] & 0xFFFFu)) ==
   2229                Truncate16(cht_register[cht_reg_no2] & 0xFFFFu));
   2230             break;
   2231           case 0x4B:
   2232             activate_codes =
   2233               ((Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) & Truncate16(cht_register[cht_reg_no1] & 0xFFFFu)) !=
   2234                Truncate16(cht_register[cht_reg_no2] & 0xFFFFu));
   2235             break;
   2236           case 0x50:
   2237             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) == inst.value16);
   2238             break;
   2239           case 0x51:
   2240             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) != inst.value16);
   2241             break;
   2242           case 0x52:
   2243             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) > inst.value16);
   2244             break;
   2245           case 0x53:
   2246             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) >= inst.value16);
   2247             break;
   2248           case 0x54:
   2249             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) < inst.value16);
   2250             break;
   2251           case 0x55:
   2252             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) <= inst.value16);
   2253             break;
   2254           case 0x56:
   2255             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & inst.value16) == inst.value16);
   2256             break;
   2257           case 0x57:
   2258             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & inst.value16) != inst.value16);
   2259             break;
   2260           case 0x58:
   2261             activate_codes =
   2262               ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) > inst.value16) &&
   2263                (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) < Truncate16((inst.value32 & 0xFFFF0000u) >> 16)));
   2264             break;
   2265           case 0x59:
   2266             activate_codes =
   2267               ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) >= inst.value16) &&
   2268                (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) <= Truncate16((inst.value32 & 0xFFFF0000u) >> 16)));
   2269             break;
   2270           case 0x5A:
   2271             activate_codes = ((Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) & inst.value16) ==
   2272                               Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2273             break;
   2274           case 0x5B:
   2275             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & inst.value16) !=
   2276                               Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2277             break;
   2278           case 0x60:
   2279             activate_codes =
   2280               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) == DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2281             break;
   2282           case 0x61:
   2283             activate_codes =
   2284               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) != DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2285             break;
   2286           case 0x62:
   2287             activate_codes =
   2288               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) > DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2289             break;
   2290           case 0x63:
   2291             activate_codes =
   2292               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) >= DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2293             break;
   2294           case 0x64:
   2295             activate_codes =
   2296               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) < DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2297             break;
   2298           case 0x65:
   2299             activate_codes =
   2300               (DoMemoryRead<u16>(cht_register[cht_reg_no2]) <= DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2301             break;
   2302           case 0x66:
   2303             activate_codes = ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) & inst.value16) == inst.value16);
   2304             break;
   2305           case 0x67:
   2306             activate_codes = ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) & inst.value16) != inst.value16);
   2307             break;
   2308           case 0x68:
   2309             activate_codes =
   2310               ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) > inst.value16) &&
   2311                (DoMemoryRead<u16>(cht_register[cht_reg_no1]) < Truncate16((inst.value32 & 0xFFFF0000u) >> 16)));
   2312             break;
   2313           case 0x69:
   2314             activate_codes =
   2315               ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) >= inst.value16) &&
   2316                (DoMemoryRead<u16>(cht_register[cht_reg_no1]) <= Truncate16((inst.value32 & 0xFFFF0000u) >> 16)));
   2317             break;
   2318           case 0x6A:
   2319             activate_codes = ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) & inst.value16) ==
   2320                               DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2321             break;
   2322           case 0x6B:
   2323             activate_codes = ((DoMemoryRead<u16>(cht_register[cht_reg_no1]) & inst.value16) !=
   2324                               DoMemoryRead<u16>(cht_register[cht_reg_no1]));
   2325             break;
   2326           case 0x70:
   2327             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) == DoMemoryRead<u16>(inst.value32));
   2328             break;
   2329           case 0x71:
   2330             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) != DoMemoryRead<u16>(inst.value32));
   2331             break;
   2332           case 0x72:
   2333             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) > DoMemoryRead<u16>(inst.value32));
   2334             break;
   2335           case 0x73:
   2336             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) >= DoMemoryRead<u16>(inst.value32));
   2337             break;
   2338           case 0x74:
   2339             activate_codes = (Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) < DoMemoryRead<u16>(inst.value32));
   2340             break;
   2341           case 0x76:
   2342             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & DoMemoryRead<u16>(inst.value32)) ==
   2343                               DoMemoryRead<u16>(inst.value32));
   2344             break;
   2345           case 0x77:
   2346             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & DoMemoryRead<u16>(inst.value32)) !=
   2347                               DoMemoryRead<u16>(inst.value32));
   2348             break;
   2349           case 0x7A:
   2350             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & DoMemoryRead<u16>(inst.value32)) ==
   2351                               Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2352             break;
   2353           case 0x7B:
   2354             activate_codes = ((Truncate16(cht_register[cht_reg_no1] & 0xFFFFu) & DoMemoryRead<u16>(inst.value32)) !=
   2355                               Truncate16(cht_register[cht_reg_no1] & 0xFFFFu));
   2356             break;
   2357           case 0x80:
   2358             activate_codes = (cht_register[cht_reg_no2] == cht_register[cht_reg_no1]);
   2359             break;
   2360           case 0x81:
   2361             activate_codes = (cht_register[cht_reg_no2] != cht_register[cht_reg_no1]);
   2362             break;
   2363           case 0x82:
   2364             activate_codes = (cht_register[cht_reg_no2] > cht_register[cht_reg_no1]);
   2365             break;
   2366           case 0x83:
   2367             activate_codes = (cht_register[cht_reg_no2] >= cht_register[cht_reg_no1]);
   2368             break;
   2369           case 0x84:
   2370             activate_codes = (cht_register[cht_reg_no2] < cht_register[cht_reg_no1]);
   2371             break;
   2372           case 0x85:
   2373             activate_codes = (cht_register[cht_reg_no2] <= cht_register[cht_reg_no1]);
   2374             break;
   2375           case 0x86:
   2376             activate_codes = ((cht_register[cht_reg_no2] & cht_register[cht_reg_no1]) == cht_register[cht_reg_no1]);
   2377             break;
   2378           case 0x87:
   2379             activate_codes = ((cht_register[cht_reg_no2] & cht_register[cht_reg_no1]) != cht_register[cht_reg_no1]);
   2380             break;
   2381           case 0x8A:
   2382             activate_codes = ((cht_register[cht_reg_no2] & cht_register[cht_reg_no1]) == cht_register[cht_reg_no2]);
   2383             break;
   2384           case 0x8B:
   2385             activate_codes = ((cht_register[cht_reg_no2] & cht_register[cht_reg_no1]) != cht_register[cht_reg_no2]);
   2386             break;
   2387           case 0x90:
   2388             activate_codes = (cht_register[cht_reg_no1] == inst.value32);
   2389             break;
   2390           case 0x91:
   2391             activate_codes = (cht_register[cht_reg_no1] != inst.value32);
   2392             break;
   2393           case 0x92:
   2394             activate_codes = (cht_register[cht_reg_no1] > inst.value32);
   2395             break;
   2396           case 0x93:
   2397             activate_codes = (cht_register[cht_reg_no1] >= inst.value32);
   2398             break;
   2399           case 0x94:
   2400             activate_codes = (cht_register[cht_reg_no1] < inst.value32);
   2401             break;
   2402           case 0x95:
   2403             activate_codes = (cht_register[cht_reg_no1] <= inst.value32);
   2404             break;
   2405           case 0x96:
   2406             activate_codes = ((cht_register[cht_reg_no1] & inst.value32) == inst.value32);
   2407             break;
   2408           case 0x97:
   2409             activate_codes = ((cht_register[cht_reg_no1] & inst.value32) != inst.value32);
   2410             break;
   2411           case 0x9A:
   2412             activate_codes = ((cht_register[cht_reg_no2] & inst.value32) == cht_register[cht_reg_no1]);
   2413             break;
   2414           case 0x9B:
   2415             activate_codes = ((cht_register[cht_reg_no1] & inst.value32) != cht_register[cht_reg_no1]);
   2416             break;
   2417           case 0xA0:
   2418             activate_codes =
   2419               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) == DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2420             break;
   2421           case 0xA1:
   2422             activate_codes =
   2423               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) != DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2424             break;
   2425           case 0xA2:
   2426             activate_codes =
   2427               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) > DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2428             break;
   2429           case 0xA3:
   2430             activate_codes =
   2431               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) >= DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2432             break;
   2433           case 0xA4:
   2434             activate_codes =
   2435               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) < DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2436             break;
   2437           case 0xA5:
   2438             activate_codes =
   2439               (DoMemoryRead<u32>(cht_register[cht_reg_no2]) <= DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2440             break;
   2441           case 0xA6:
   2442             activate_codes = ((DoMemoryRead<u32>(cht_register[cht_reg_no1]) & inst.value32) == inst.value32);
   2443             break;
   2444           case 0xA7:
   2445             activate_codes = ((DoMemoryRead<u32>(cht_register[cht_reg_no1]) & inst.value32) != inst.value32);
   2446             break;
   2447           case 0xAA:
   2448             activate_codes = ((DoMemoryRead<u32>(cht_register[cht_reg_no1]) & inst.value32) ==
   2449                               DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2450             break;
   2451           case 0xAB:
   2452             activate_codes = ((DoMemoryRead<u32>(cht_register[cht_reg_no1]) & inst.value32) !=
   2453                               DoMemoryRead<u32>(cht_register[cht_reg_no1]));
   2454             break;
   2455           case 0xB0:
   2456             activate_codes = (cht_register[cht_reg_no1] == DoMemoryRead<u32>(inst.value32));
   2457             break;
   2458           case 0xB1:
   2459             activate_codes = (cht_register[cht_reg_no1] != DoMemoryRead<u32>(inst.value32));
   2460             break;
   2461           case 0xB2:
   2462             activate_codes = (cht_register[cht_reg_no1] > DoMemoryRead<u32>(inst.value32));
   2463             break;
   2464           case 0xB3:
   2465             activate_codes = (cht_register[cht_reg_no1] >= DoMemoryRead<u32>(inst.value32));
   2466             break;
   2467           case 0xB4:
   2468             activate_codes = (cht_register[cht_reg_no1] < DoMemoryRead<u32>(inst.value32));
   2469             break;
   2470           case 0xB6:
   2471             activate_codes =
   2472               ((cht_register[cht_reg_no1] & DoMemoryRead<u32>(inst.value32)) == DoMemoryRead<u32>(inst.value32));
   2473             break;
   2474           case 0xB7:
   2475             activate_codes =
   2476               ((cht_register[cht_reg_no1] & DoMemoryRead<u32>(inst.value32)) != DoMemoryRead<u32>(inst.value32));
   2477             break;
   2478           case 0xBA:
   2479             activate_codes =
   2480               ((cht_register[cht_reg_no1] & DoMemoryRead<u32>(inst.value32)) == cht_register[cht_reg_no1]);
   2481             break;
   2482           case 0xBB:
   2483             activate_codes =
   2484               ((cht_register[cht_reg_no1] & DoMemoryRead<u32>(inst.value32)) != cht_register[cht_reg_no1]);
   2485             break;
   2486           default:
   2487             activate_codes = false;
   2488             break;
   2489         }
   2490         if (activate_codes)
   2491         {
   2492           // execute following instructions
   2493           continue;
   2494         }
   2495 
   2496         // skip to the next separator (00000000 FFFF), or end
   2497         constexpr u64 separator_value = UINT64_C(0x000000000000FFFF);
   2498         while (index < count)
   2499         {
   2500           // we don't want to execute the separator instruction
   2501           const u64 bits = instructions[index++].bits;
   2502           if (bits == separator_value)
   2503             break;
   2504         }
   2505       }
   2506       break;
   2507 
   2508       case InstructionCode::DelayActivation: // C1
   2509       {
   2510         // A value of around 4000 or 5000 will usually give you a good 20-30 second delay before codes are activated.
   2511         // Frame number * 0.3 -> (20 * 60) * 10 / 3 => 4000
   2512         const u32 comp_value = (System::GetFrameNumber() * 10) / 3;
   2513         if (comp_value < inst.value16)
   2514           index = count;
   2515         else
   2516           index++;
   2517       }
   2518       break;
   2519 
   2520       case InstructionCode::Slide:
   2521       {
   2522         if ((index + 1) >= instructions.size())
   2523         {
   2524           ERROR_LOG("Incomplete slide instruction");
   2525           return;
   2526         }
   2527 
   2528         const u32 slide_count = (inst.first >> 8) & 0xFFu;
   2529         const u32 address_increment = inst.first & 0xFFu;
   2530         const u16 value_increment = Truncate16(inst.second);
   2531         const Instruction& inst2 = instructions[index + 1];
   2532         const InstructionCode write_type = inst2.code;
   2533         u32 address = inst2.address;
   2534         u16 value = inst2.value16;
   2535 
   2536         if (write_type == InstructionCode::ConstantWrite8)
   2537         {
   2538           for (u32 i = 0; i < slide_count; i++)
   2539           {
   2540             DoMemoryWrite<u8>(address, Truncate8(value));
   2541             address += address_increment;
   2542             value += value_increment;
   2543           }
   2544         }
   2545         else if (write_type == InstructionCode::ConstantWrite16)
   2546         {
   2547           for (u32 i = 0; i < slide_count; i++)
   2548           {
   2549             DoMemoryWrite<u16>(address, value);
   2550             address += address_increment;
   2551             value += value_increment;
   2552           }
   2553         }
   2554         else
   2555         {
   2556           ERROR_LOG("Invalid command in second slide parameter 0x{:02X}", static_cast<unsigned>(write_type));
   2557         }
   2558 
   2559         index += 2;
   2560       }
   2561       break;
   2562 
   2563       case InstructionCode::ExtImprovedSlide:
   2564       {
   2565         if ((index + 1) >= instructions.size())
   2566         {
   2567           ERROR_LOG("Incomplete slide instruction");
   2568           return;
   2569         }
   2570 
   2571         const u32 slide_count = inst.first & 0xFFFFu;
   2572         const u32 address_change = (inst.second >> 16) & 0xFFFFu;
   2573         const u16 value_change = Truncate16(inst.second);
   2574         const Instruction& inst2 = instructions[index + 1];
   2575         const InstructionCode write_type = inst2.code;
   2576         const bool address_change_negative = (inst.first >> 20) & 0x1u;
   2577         const bool value_change_negative = (inst.first >> 16) & 0x1u;
   2578         u32 address = inst2.address;
   2579         u32 value = inst2.value32;
   2580 
   2581         if (write_type == InstructionCode::ConstantWrite8)
   2582         {
   2583           for (u32 i = 0; i < slide_count; i++)
   2584           {
   2585             DoMemoryWrite<u8>(address, Truncate8(value));
   2586             if (address_change_negative)
   2587               address -= address_change;
   2588             else
   2589               address += address_change;
   2590             if (value_change_negative)
   2591               value -= value_change;
   2592             else
   2593               value += value_change;
   2594           }
   2595         }
   2596         else if (write_type == InstructionCode::ConstantWrite16)
   2597         {
   2598           for (u32 i = 0; i < slide_count; i++)
   2599           {
   2600             DoMemoryWrite<u16>(address, Truncate16(value));
   2601             if (address_change_negative)
   2602               address -= address_change;
   2603             else
   2604               address += address_change;
   2605             if (value_change_negative)
   2606               value -= value_change;
   2607             else
   2608               value += value_change;
   2609           }
   2610         }
   2611         else if (write_type == InstructionCode::ExtConstantWrite32)
   2612         {
   2613           for (u32 i = 0; i < slide_count; i++)
   2614           {
   2615             DoMemoryWrite<u32>(address, value);
   2616             if (address_change_negative)
   2617               address -= address_change;
   2618             else
   2619               address += address_change;
   2620             if (value_change_negative)
   2621               value -= value_change;
   2622             else
   2623               value += value_change;
   2624           }
   2625         }
   2626         else
   2627         {
   2628           ERROR_LOG("Invalid command in second slide parameter 0x{:02X}", static_cast<unsigned>(write_type));
   2629         }
   2630 
   2631         index += 2;
   2632       }
   2633       break;
   2634 
   2635       case InstructionCode::MemoryCopy:
   2636       {
   2637         if ((index + 1) >= instructions.size())
   2638         {
   2639           ERROR_LOG("Incomplete memory copy instruction");
   2640           return;
   2641         }
   2642 
   2643         const Instruction& inst2 = instructions[index + 1];
   2644         const u32 byte_count = inst.value16;
   2645         u32 src_address = inst.address;
   2646         u32 dst_address = inst2.address;
   2647 
   2648         for (u32 i = 0; i < byte_count; i++)
   2649         {
   2650           u8 value = DoMemoryRead<u8>(src_address);
   2651           DoMemoryWrite<u8>(dst_address, value);
   2652           src_address++;
   2653           dst_address++;
   2654         }
   2655 
   2656         index += 2;
   2657       }
   2658       break;
   2659 
   2660       default:
   2661       {
   2662         ERROR_LOG("Unhandled instruction code 0x{:02X} ({:08X} {:08X})", static_cast<u8>(inst.code.GetValue()),
   2663                   inst.first, inst.second);
   2664         index++;
   2665       }
   2666       break;
   2667     }
   2668   }
   2669 }
   2670 
   2671 void CheatCode::ApplyOnDisable() const
   2672 {
   2673   const u32 count = static_cast<u32>(instructions.size());
   2674   u32 index = 0;
   2675   for (; index < count;)
   2676   {
   2677     const Instruction& inst = instructions[index];
   2678     switch (inst.code)
   2679     {
   2680       case InstructionCode::Nop:
   2681       case InstructionCode::ConstantWrite8:
   2682       case InstructionCode::ConstantWrite16:
   2683       case InstructionCode::ExtConstantWrite32:
   2684       case InstructionCode::ExtConstantBitSet8:
   2685       case InstructionCode::ExtConstantBitSet16:
   2686       case InstructionCode::ExtConstantBitSet32:
   2687       case InstructionCode::ExtConstantBitClear8:
   2688       case InstructionCode::ExtConstantBitClear16:
   2689       case InstructionCode::ExtConstantBitClear32:
   2690       case InstructionCode::ScratchpadWrite16:
   2691       case InstructionCode::ExtScratchpadWrite32:
   2692       case InstructionCode::ExtIncrement32:
   2693       case InstructionCode::ExtDecrement32:
   2694       case InstructionCode::Increment16:
   2695       case InstructionCode::Decrement16:
   2696       case InstructionCode::Increment8:
   2697       case InstructionCode::Decrement8:
   2698       case InstructionCode::ExtConstantForceRange8:
   2699       case InstructionCode::ExtConstantForceRangeLimits16:
   2700       case InstructionCode::ExtConstantForceRangeRollRound16:
   2701       case InstructionCode::ExtConstantSwap16:
   2702       case InstructionCode::DelayActivation: // C1
   2703       case InstructionCode::ExtConstantWriteIfMatch16:
   2704       case InstructionCode::ExtCheatRegisters:
   2705         index++;
   2706         break;
   2707 
   2708       case InstructionCode::ExtConstantForceRange16:
   2709       case InstructionCode::Slide:
   2710       case InstructionCode::ExtImprovedSlide:
   2711       case InstructionCode::MemoryCopy:
   2712         index += 2;
   2713         break;
   2714       case InstructionCode::ExtFindAndReplace:
   2715         index += 5;
   2716         break;
   2717       // for conditionals, we don't want to skip over in case it changed at some point
   2718       case InstructionCode::ExtCompareEqual32:
   2719       case InstructionCode::ExtCompareNotEqual32:
   2720       case InstructionCode::ExtCompareLess32:
   2721       case InstructionCode::ExtCompareGreater32:
   2722       case InstructionCode::CompareEqual16:
   2723       case InstructionCode::CompareNotEqual16:
   2724       case InstructionCode::CompareLess16:
   2725       case InstructionCode::CompareGreater16:
   2726       case InstructionCode::CompareEqual8:
   2727       case InstructionCode::CompareNotEqual8:
   2728       case InstructionCode::CompareLess8:
   2729       case InstructionCode::CompareGreater8:
   2730       case InstructionCode::CompareButtons: // D4
   2731         index++;
   2732         break;
   2733 
   2734       // same deal for block conditionals
   2735       case InstructionCode::SkipIfNotEqual16:         // C0
   2736       case InstructionCode::ExtSkipIfNotEqual32:      // A4
   2737       case InstructionCode::SkipIfButtonsNotEqual:    // D5
   2738       case InstructionCode::SkipIfButtonsEqual:       // D6
   2739       case InstructionCode::ExtBitCompareButtons:     // D7
   2740       case InstructionCode::ExtSkipIfNotLess8:        // C3
   2741       case InstructionCode::ExtSkipIfNotGreater8:     // C4
   2742       case InstructionCode::ExtSkipIfNotLess16:       // C5
   2743       case InstructionCode::ExtSkipIfNotGreater16:    // C6
   2744       case InstructionCode::ExtMultiConditionals:     // F6
   2745       case InstructionCode::ExtCheatRegistersCompare: // 52
   2746         index++;
   2747         break;
   2748 
   2749       case InstructionCode::ExtConstantWriteIfMatchWithRestore16:
   2750       {
   2751         const u16 value = DoMemoryRead<u16>(inst.address);
   2752         const u16 comparevalue = Truncate16(inst.value32 >> 16);
   2753         const u16 newvalue = Truncate16(inst.value32 & 0xFFFFu);
   2754         if (value == newvalue)
   2755           DoMemoryWrite<u16>(inst.address, comparevalue);
   2756 
   2757         index++;
   2758       }
   2759       break;
   2760 
   2761         [[unlikely]] default:
   2762         {
   2763           ERROR_LOG("Unhandled instruction code 0x{:02X} ({:08X} {:08X})", static_cast<u8>(inst.code.GetValue()),
   2764                     inst.first, inst.second);
   2765           index++;
   2766         }
   2767         break;
   2768     }
   2769   }
   2770 }
   2771 
   2772 static std::array<const char*, 1> s_cheat_code_type_names = {{"Gameshark"}};
   2773 static std::array<const char*, 1> s_cheat_code_type_display_names{{TRANSLATE_NOOP("Cheats", "Gameshark")}};
   2774 
   2775 const char* CheatCode::GetTypeName(Type type)
   2776 {
   2777   return s_cheat_code_type_names[static_cast<u32>(type)];
   2778 }
   2779 
   2780 const char* CheatCode::GetTypeDisplayName(Type type)
   2781 {
   2782   return s_cheat_code_type_display_names[static_cast<u32>(type)];
   2783 }
   2784 
   2785 std::optional<CheatCode::Type> CheatCode::ParseTypeName(const char* str)
   2786 {
   2787   for (size_t i = 0; i < s_cheat_code_type_names.size(); i++)
   2788   {
   2789     if (std::strcmp(s_cheat_code_type_names[i], str) == 0)
   2790       return static_cast<Type>(i);
   2791   }
   2792 
   2793   return std::nullopt;
   2794 }
   2795 
   2796 static std::array<const char*, 2> s_cheat_code_activation_names = {{"Manual", "EndFrame"}};
   2797 static std::array<const char*, 2> s_cheat_code_activation_display_names{
   2798   {TRANSLATE_NOOP("Cheats", "Manual"), TRANSLATE_NOOP("Cheats", "Automatic (Frame End)")}};
   2799 
   2800 const char* CheatCode::GetActivationName(Activation activation)
   2801 {
   2802   return s_cheat_code_activation_names[static_cast<u32>(activation)];
   2803 }
   2804 
   2805 const char* CheatCode::GetActivationDisplayName(Activation activation)
   2806 {
   2807   return s_cheat_code_activation_display_names[static_cast<u32>(activation)];
   2808 }
   2809 
   2810 std::optional<CheatCode::Activation> CheatCode::ParseActivationName(const char* str)
   2811 {
   2812   for (u32 i = 0; i < static_cast<u32>(s_cheat_code_activation_names.size()); i++)
   2813   {
   2814     if (std::strcmp(s_cheat_code_activation_names[i], str) == 0)
   2815       return static_cast<Activation>(i);
   2816   }
   2817 
   2818   return std::nullopt;
   2819 }
   2820 
   2821 MemoryScan::MemoryScan() = default;
   2822 
   2823 MemoryScan::~MemoryScan() = default;
   2824 
   2825 void MemoryScan::ResetSearch()
   2826 {
   2827   m_results.clear();
   2828 }
   2829 
   2830 void MemoryScan::Search()
   2831 {
   2832   m_results.clear();
   2833 
   2834   switch (m_size)
   2835   {
   2836     case MemoryAccessSize::Byte:
   2837       SearchBytes();
   2838       break;
   2839 
   2840     case MemoryAccessSize::HalfWord:
   2841       SearchHalfwords();
   2842       break;
   2843 
   2844     case MemoryAccessSize::Word:
   2845       SearchWords();
   2846       break;
   2847 
   2848     default:
   2849       break;
   2850   }
   2851 }
   2852 
   2853 void MemoryScan::SearchBytes()
   2854 {
   2855   for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address++)
   2856   {
   2857     if (!IsValidScanAddress(address))
   2858       continue;
   2859 
   2860     const u8 bvalue = DoMemoryRead<u8>(address);
   2861 
   2862     Result res;
   2863     res.address = address;
   2864     res.value = m_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   2865     res.last_value = res.value;
   2866     res.value_changed = false;
   2867 
   2868     if (res.Filter(m_operator, m_value, m_signed))
   2869       m_results.push_back(res);
   2870   }
   2871 }
   2872 
   2873 void MemoryScan::SearchHalfwords()
   2874 {
   2875   for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address += 2)
   2876   {
   2877     if (!IsValidScanAddress(address))
   2878       continue;
   2879 
   2880     const u16 bvalue = DoMemoryRead<u16>(address);
   2881 
   2882     Result res;
   2883     res.address = address;
   2884     res.value = m_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   2885     res.last_value = res.value;
   2886     res.value_changed = false;
   2887 
   2888     if (res.Filter(m_operator, m_value, m_signed))
   2889       m_results.push_back(res);
   2890   }
   2891 }
   2892 
   2893 void MemoryScan::SearchWords()
   2894 {
   2895   for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address += 4)
   2896   {
   2897     if (!IsValidScanAddress(address))
   2898       continue;
   2899 
   2900     Result res;
   2901     res.address = address;
   2902     res.value = DoMemoryRead<u32>(address);
   2903     res.last_value = res.value;
   2904     res.value_changed = false;
   2905 
   2906     if (res.Filter(m_operator, m_value, m_signed))
   2907       m_results.push_back(res);
   2908   }
   2909 }
   2910 
   2911 void MemoryScan::SearchAgain()
   2912 {
   2913   ResultVector new_results;
   2914   new_results.reserve(m_results.size());
   2915   for (Result& res : m_results)
   2916   {
   2917     res.UpdateValue(m_size, m_signed);
   2918 
   2919     if (res.Filter(m_operator, m_value, m_signed))
   2920     {
   2921       res.last_value = res.value;
   2922       new_results.push_back(res);
   2923     }
   2924   }
   2925 
   2926   m_results.swap(new_results);
   2927 }
   2928 
   2929 void MemoryScan::UpdateResultsValues()
   2930 {
   2931   for (Result& res : m_results)
   2932     res.UpdateValue(m_size, m_signed);
   2933 }
   2934 
   2935 void MemoryScan::SetResultValue(u32 index, u32 value)
   2936 {
   2937   if (index >= m_results.size())
   2938     return;
   2939 
   2940   Result& res = m_results[index];
   2941   if (res.value == value)
   2942     return;
   2943 
   2944   switch (m_size)
   2945   {
   2946     case MemoryAccessSize::Byte:
   2947       DoMemoryWrite<u8>(res.address, Truncate8(value));
   2948       break;
   2949 
   2950     case MemoryAccessSize::HalfWord:
   2951       DoMemoryWrite<u16>(res.address, Truncate16(value));
   2952       break;
   2953 
   2954     case MemoryAccessSize::Word:
   2955       CPU::SafeWriteMemoryWord(res.address, value);
   2956       break;
   2957   }
   2958 
   2959   res.value = value;
   2960   res.value_changed = true;
   2961 }
   2962 
   2963 bool MemoryScan::Result::Filter(Operator op, u32 comp_value, bool is_signed) const
   2964 {
   2965   switch (op)
   2966   {
   2967     case Operator::Equal:
   2968     {
   2969       return (value == comp_value);
   2970     }
   2971 
   2972     case Operator::NotEqual:
   2973     {
   2974       return (value != comp_value);
   2975     }
   2976 
   2977     case Operator::GreaterThan:
   2978     {
   2979       return is_signed ? (static_cast<s32>(value) > static_cast<s32>(comp_value)) : (value > comp_value);
   2980     }
   2981 
   2982     case Operator::GreaterEqual:
   2983     {
   2984       return is_signed ? (static_cast<s32>(value) >= static_cast<s32>(comp_value)) : (value >= comp_value);
   2985     }
   2986 
   2987     case Operator::LessThan:
   2988     {
   2989       return is_signed ? (static_cast<s32>(value) < static_cast<s32>(comp_value)) : (value < comp_value);
   2990     }
   2991 
   2992     case Operator::LessEqual:
   2993     {
   2994       return is_signed ? (static_cast<s32>(value) <= static_cast<s32>(comp_value)) : (value <= comp_value);
   2995     }
   2996 
   2997     case Operator::IncreasedBy:
   2998     {
   2999       return is_signed ? ((static_cast<s32>(value) - static_cast<s32>(last_value)) == static_cast<s32>(comp_value)) :
   3000                          ((value - last_value) == comp_value);
   3001     }
   3002 
   3003     case Operator::DecreasedBy:
   3004     {
   3005       return is_signed ? ((static_cast<s32>(last_value) - static_cast<s32>(value)) == static_cast<s32>(comp_value)) :
   3006                          ((last_value - value) == comp_value);
   3007     }
   3008 
   3009     case Operator::ChangedBy:
   3010     {
   3011       if (is_signed)
   3012         return (std::abs(static_cast<s32>(last_value) - static_cast<s32>(value)) == static_cast<s32>(comp_value));
   3013       else
   3014         return ((last_value > value) ? (last_value - value) : (value - last_value)) == comp_value;
   3015     }
   3016 
   3017     case Operator::EqualLast:
   3018     {
   3019       return (value == last_value);
   3020     }
   3021 
   3022     case Operator::NotEqualLast:
   3023     {
   3024       return (value != last_value);
   3025     }
   3026 
   3027     case Operator::GreaterThanLast:
   3028     {
   3029       return is_signed ? (static_cast<s32>(value) > static_cast<s32>(last_value)) : (value > last_value);
   3030     }
   3031 
   3032     case Operator::GreaterEqualLast:
   3033     {
   3034       return is_signed ? (static_cast<s32>(value) >= static_cast<s32>(last_value)) : (value >= last_value);
   3035     }
   3036 
   3037     case Operator::LessThanLast:
   3038     {
   3039       return is_signed ? (static_cast<s32>(value) < static_cast<s32>(last_value)) : (value < last_value);
   3040     }
   3041 
   3042     case Operator::LessEqualLast:
   3043     {
   3044       return is_signed ? (static_cast<s32>(value) <= static_cast<s32>(last_value)) : (value <= last_value);
   3045     }
   3046 
   3047     case Operator::Any:
   3048       return true;
   3049 
   3050     default:
   3051       return false;
   3052   }
   3053 }
   3054 
   3055 void MemoryScan::Result::UpdateValue(MemoryAccessSize size, bool is_signed)
   3056 {
   3057   const u32 old_value = value;
   3058 
   3059   switch (size)
   3060   {
   3061     case MemoryAccessSize::Byte:
   3062     {
   3063       u8 bvalue = DoMemoryRead<u8>(address);
   3064       value = is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   3065     }
   3066     break;
   3067 
   3068     case MemoryAccessSize::HalfWord:
   3069     {
   3070       u16 bvalue = DoMemoryRead<u16>(address);
   3071       value = is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   3072     }
   3073     break;
   3074 
   3075     case MemoryAccessSize::Word:
   3076     {
   3077       CPU::SafeReadMemoryWord(address, &value);
   3078     }
   3079     break;
   3080   }
   3081 
   3082   value_changed = (value != old_value);
   3083 }
   3084 
   3085 MemoryWatchList::MemoryWatchList() = default;
   3086 
   3087 MemoryWatchList::~MemoryWatchList() = default;
   3088 
   3089 const MemoryWatchList::Entry* MemoryWatchList::GetEntryByAddress(u32 address) const
   3090 {
   3091   for (const Entry& entry : m_entries)
   3092   {
   3093     if (entry.address == address)
   3094       return &entry;
   3095   }
   3096 
   3097   return nullptr;
   3098 }
   3099 
   3100 bool MemoryWatchList::AddEntry(std::string description, u32 address, MemoryAccessSize size, bool is_signed, bool freeze)
   3101 {
   3102   if (GetEntryByAddress(address))
   3103     return false;
   3104 
   3105   Entry entry;
   3106   entry.description = std::move(description);
   3107   entry.address = address;
   3108   entry.size = size;
   3109   entry.is_signed = is_signed;
   3110   entry.freeze = false;
   3111 
   3112   UpdateEntryValue(&entry);
   3113 
   3114   entry.changed = false;
   3115   entry.freeze = freeze;
   3116 
   3117   m_entries.push_back(std::move(entry));
   3118   return true;
   3119 }
   3120 
   3121 void MemoryWatchList::RemoveEntry(u32 index)
   3122 {
   3123   if (index >= m_entries.size())
   3124     return;
   3125 
   3126   m_entries.erase(m_entries.begin() + index);
   3127 }
   3128 
   3129 bool MemoryWatchList::RemoveEntryByAddress(u32 address)
   3130 {
   3131   for (auto it = m_entries.begin(); it != m_entries.end(); ++it)
   3132   {
   3133     if (it->address == address)
   3134     {
   3135       m_entries.erase(it);
   3136       return true;
   3137     }
   3138   }
   3139 
   3140   return false;
   3141 }
   3142 
   3143 void MemoryWatchList::SetEntryDescription(u32 index, std::string description)
   3144 {
   3145   if (index >= m_entries.size())
   3146     return;
   3147 
   3148   Entry& entry = m_entries[index];
   3149   entry.description = std::move(description);
   3150 }
   3151 
   3152 void MemoryWatchList::SetEntryFreeze(u32 index, bool freeze)
   3153 {
   3154   if (index >= m_entries.size())
   3155     return;
   3156 
   3157   Entry& entry = m_entries[index];
   3158   entry.freeze = freeze;
   3159 }
   3160 
   3161 void MemoryWatchList::SetEntryValue(u32 index, u32 value)
   3162 {
   3163   if (index >= m_entries.size())
   3164     return;
   3165 
   3166   Entry& entry = m_entries[index];
   3167   if (entry.value == value)
   3168     return;
   3169 
   3170   SetEntryValue(&entry, value);
   3171 }
   3172 
   3173 bool MemoryWatchList::RemoveEntryByDescription(const char* description)
   3174 {
   3175   bool result = false;
   3176   for (auto it = m_entries.begin(); it != m_entries.end();)
   3177   {
   3178     if (it->description == description)
   3179     {
   3180       it = m_entries.erase(it);
   3181       result = true;
   3182       continue;
   3183     }
   3184 
   3185     ++it;
   3186   }
   3187 
   3188   return result;
   3189 }
   3190 
   3191 void MemoryWatchList::UpdateValues()
   3192 {
   3193   for (Entry& entry : m_entries)
   3194     UpdateEntryValue(&entry);
   3195 }
   3196 
   3197 void MemoryWatchList::SetEntryValue(Entry* entry, u32 value)
   3198 {
   3199   switch (entry->size)
   3200   {
   3201     case MemoryAccessSize::Byte:
   3202       DoMemoryWrite<u8>(entry->address, Truncate8(value));
   3203       break;
   3204 
   3205     case MemoryAccessSize::HalfWord:
   3206       DoMemoryWrite<u16>(entry->address, Truncate16(value));
   3207       break;
   3208 
   3209     case MemoryAccessSize::Word:
   3210       DoMemoryWrite<u32>(entry->address, value);
   3211       break;
   3212   }
   3213 
   3214   entry->changed = (entry->value != value);
   3215   entry->value = value;
   3216 }
   3217 
   3218 void MemoryWatchList::UpdateEntryValue(Entry* entry)
   3219 {
   3220   const u32 old_value = entry->value;
   3221 
   3222   switch (entry->size)
   3223   {
   3224     case MemoryAccessSize::Byte:
   3225     {
   3226       u8 bvalue = DoMemoryRead<u8>(entry->address);
   3227       entry->value = entry->is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   3228     }
   3229     break;
   3230 
   3231     case MemoryAccessSize::HalfWord:
   3232     {
   3233       u16 bvalue = DoMemoryRead<u16>(entry->address);
   3234       entry->value = entry->is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
   3235     }
   3236     break;
   3237 
   3238     case MemoryAccessSize::Word:
   3239     {
   3240       entry->value = DoMemoryRead<u32>(entry->address);
   3241     }
   3242     break;
   3243   }
   3244 
   3245   entry->changed = (old_value != entry->value);
   3246 
   3247   if (entry->freeze && entry->changed)
   3248     SetEntryValue(entry, old_value);
   3249 }