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 }