From 9d1ab766a0d953a307313bd1812b5416b1e913b9 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:27:44 -0500
Subject: [PATCH 1/8] controllers/npad: Add accessor for current press state
 Allows frontend/features to access pressed buttons conveniently as possible

---
 src/core/hle/service/hid/hid.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 7cc58db4c..e39cd6b51 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #pragma once
+#include "core/hle/service/service.h"
 
 #include "controllers/controller_base.h"
 #include "core/hle/service/service.h"

From c5091bfe00c241b6432367aeaea020d4e3d40d28 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:28:56 -0500
Subject: [PATCH 2/8] patch_manager: Add support for loading cheats lists Uses
 load/<title_id>/<mod_name>/cheats as root dir, file name is all upper or
 lower hex first 8 bytes build ID.

---
 src/core/file_sys/patch_manager.cpp | 52 +++++++++++++++++++++++++++++
 src/core/file_sys/patch_manager.h   |  4 +++
 2 files changed, 56 insertions(+)

diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 61706966e..f0fb28e2f 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -7,6 +7,7 @@
 #include <cstddef>
 #include <cstring>
 
+#include "common/file_util.h"
 #include "common/hex_util.h"
 #include "common/logging/log.h"
 #include "core/file_sys/content_archive.h"
@@ -232,6 +233,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
     return !CollectPatches(patch_dirs, build_id).empty();
 }
 
+static std::optional<CheatList> ReadCheatFileFromFolder(u64 title_id,
+                                                        const std::array<u8, 0x20>& build_id_,
+                                                        const VirtualDir& base_path, bool upper) {
+    const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
+    const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
+    const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
+
+    if (file == nullptr) {
+        LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
+                 title_id, build_id);
+        return std::nullopt;
+    }
+
+    std::vector<u8> data(file->GetSize());
+    if (file->Read(data.data(), data.size()) != data.size()) {
+        LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
+                 title_id, build_id);
+        return std::nullopt;
+    }
+
+    TextCheatParser parser;
+    return parser.Parse(data);
+}
+
+std::vector<CheatList> PatchManager::CreateCheatList(const std::array<u8, 32>& build_id_) const {
+    std::vector<CheatList> out;
+
+    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    auto patch_dirs = load_dir->GetSubdirectories();
+    std::sort(patch_dirs.begin(), patch_dirs.end(),
+              [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
+
+    out.reserve(patch_dirs.size());
+    for (const auto& subdir : patch_dirs) {
+        auto cheats_dir = subdir->GetSubdirectory("cheats");
+        if (cheats_dir != nullptr) {
+            auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true);
+            if (res.has_value()) {
+                out.push_back(std::move(*res));
+                continue;
+            }
+
+            res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false);
+            if (res.has_value())
+                out.push_back(std::move(*res));
+        }
+    }
+
+    return out;
+}
+
 static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
     if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b8a1652fd..3e3ac6aca 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 #include "common/common_types.h"
+#include "core/file_sys/cheat_engine.h"
 #include "core/file_sys/nca_metadata.h"
 #include "core/file_sys/vfs.h"
 
@@ -45,6 +46,9 @@ public:
     // Used to prevent expensive copies in NSO loader.
     bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
 
+    // Creates a CheatList object with all
+    std::vector<CheatList> CreateCheatList(const std::array<u8, 0x20>& build_id) const;
+
     // Currently tracked RomFS patches:
     // - Game Updates
     // - LayeredFS

From 4495bf5706d989e2dddaa80c3435b20698e6c11a Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:29:15 -0500
Subject: [PATCH 3/8] patch_manager: Display cheats in game list add-ons

---
 src/core/file_sys/patch_manager.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index f0fb28e2f..2b09e5d35 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -455,6 +455,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
             }
             if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
                 AppendCommaIfNotEmpty(types, "LayeredFS");
+            if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
+                AppendCommaIfNotEmpty(types, "Cheats");
 
             if (types.empty())
                 continue;

From b952a3055575224d3f873b4a24498714f8eb2820 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:29:52 -0500
Subject: [PATCH 4/8] vm_manager: Add support for storing and getting main code
 region Used as root for one region of cheats, set by loader

---
 src/core/hle/kernel/vm_manager.cpp | 17 +++++++++++++++++
 src/core/hle/kernel/vm_manager.h   | 11 +++++++++++
 2 files changed, 28 insertions(+)

diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 10ad94aa6..161a83660 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -786,6 +786,23 @@ u64 VMManager::GetNewMapRegionSize() const {
     return new_map_region_end - new_map_region_base;
 }
 
+void VMManager::SetMainCodeRegion(VAddr begin, VAddr end) {
+    main_code_region_base = begin;
+    main_code_region_end = end;
+}
+
+VAddr VMManager::GetMainCodeRegionBaseAddress() const {
+    return main_code_region_base;
+}
+
+VAddr VMManager::GetMainCodeRegionEndAddress() const {
+    return main_code_region_end;
+}
+
+u64 VMManager::GetMainCodeRegionSize() const {
+    return main_code_region_end - main_code_region_base;
+}
+
 VAddr VMManager::GetTLSIORegionBaseAddress() const {
     return tls_io_region_base;
 }
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6091533bc..728318f4b 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -480,6 +480,14 @@ public:
     /// Gets the total size of the new map region in bytes.
     u64 GetNewMapRegionSize() const;
 
+    void SetMainCodeRegion(VAddr begin, VAddr end);
+
+    VAddr GetMainCodeRegionBaseAddress() const;
+
+    VAddr GetMainCodeRegionEndAddress() const;
+
+    u64 GetMainCodeRegionSize() const;
+
     /// Gets the base address of the TLS IO region.
     VAddr GetTLSIORegionBaseAddress() const;
 
@@ -598,6 +606,9 @@ private:
     VAddr new_map_region_base = 0;
     VAddr new_map_region_end = 0;
 
+    VAddr main_code_region_base = 0;
+    VAddr main_code_region_end = 0;
+
     VAddr tls_io_region_base = 0;
     VAddr tls_io_region_end = 0;
 

From c100a4b8d45d09494d79b5f5e3ece7dee9f841e9 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:31:07 -0500
Subject: [PATCH 5/8] loader/nso: Set main code region in VMManager For rom
 directories (and by extension, XCI/NSP/NAX/NCA) this is for the NSO with name
 'main', for regular NSOs, this is the NSO.

---
 .../loader/deconstructed_rom_directory.cpp     |  4 +++-
 src/core/loader/nso.cpp                        | 18 +++++++++++++++++-
 src/core/loader/nso.h                          |  1 +
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd..ef9a577e3 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -147,8 +147,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
 
         const VAddr load_addr = next_load_addr;
         const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
+        const bool should_register_data_segment = std::strcmp(module, "main") == 0;
         const auto tentative_next_load_addr =
-            AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
+            AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments,
+                                      should_register_data_segment, pm);
         if (!tentative_next_load_addr) {
             return ResultStatus::ErrorLoadingNSO;
         }
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 6ded0b707..2721d85b1 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -7,8 +7,10 @@
 #include <lz4.h>
 #include "common/common_funcs.h"
 #include "common/file_util.h"
+#include "common/hex_util.h"
 #include "common/logging/log.h"
 #include "common/swap.h"
+#include "core/core.h"
 #include "core/file_sys/patch_manager.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/hle/kernel/process.h"
@@ -95,6 +97,7 @@ static constexpr u32 PageAlignSize(u32 size) {
 std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
                                                const FileSys::VfsFile& file, VAddr load_base,
                                                bool should_pass_arguments,
+                                               bool should_register_data_region,
                                                std::optional<FileSys::PatchManager> pm) {
     if (file.GetSize() < sizeof(NsoHeader))
         return {};
@@ -153,6 +156,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
     const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
     program_image.resize(image_size);
 
+    if (should_register_data_region) {
+        process.VMManager().SetMainCodeRegion(load_base, load_base + program_image.size());
+    }
+
     // Apply patches if necessary
     if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
         std::vector<u8> pi_header(program_image.size() + 0x100);
@@ -164,6 +171,15 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
         std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
     }
 
+    // Apply cheats if they exist and the program has a valid title ID
+    if (pm) {
+        const auto cheats = pm->CreateCheatList(nso_header.build_id);
+        if (!cheats.empty()) {
+            Core::System::GetInstance().RegisterCheatList(
+                cheats, Common::HexArrayToString(nso_header.build_id));
+        }
+    }
+
     // Load codeset for current process
     codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
     process.LoadModule(std::move(codeset), load_base);
@@ -181,7 +197,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
 
     // Load module
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
-    if (!LoadModule(process, *file, base_address, true)) {
+    if (!LoadModule(process, *file, base_address, true, true)) {
         return ResultStatus::ErrorLoadingNSO;
     }
     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..858e346c6 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -43,6 +43,7 @@ public:
 
     static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file,
                                            VAddr load_base, bool should_pass_arguments,
+                                           bool should_register_data_segment,
                                            std::optional<FileSys::PatchManager> pm = {});
 
     ResultStatus Load(Kernel::Process& process) override;

From 769b3466823d988d262b13325c6eb926770d0868 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:31:38 -0500
Subject: [PATCH 6/8] cheat_engine: Add parser and interpreter for game cheats

---
 src/core/CMakeLists.txt            |   2 +
 src/core/file_sys/cheat_engine.cpp | 487 +++++++++++++++++++++++++++++
 src/core/file_sys/cheat_engine.h   | 226 +++++++++++++
 3 files changed, 715 insertions(+)
 create mode 100644 src/core/file_sys/cheat_engine.cpp
 create mode 100644 src/core/file_sys/cheat_engine.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 988356c65..199a0c2e3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,6 +31,8 @@ add_library(core STATIC
     file_sys/bis_factory.h
     file_sys/card_image.cpp
     file_sys/card_image.h
+    file_sys/cheat_engine.cpp
+    file_sys/cheat_engine.h
     file_sys/content_archive.cpp
     file_sys/content_archive.h
     file_sys/control_metadata.cpp
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
new file mode 100644
index 000000000..e4383d8c1
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -0,0 +1,487 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <locale>
+#include "common/hex_util.h"
+#include "common/microprofile.h"
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/file_sys/cheat_engine.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace FileSys {
+
+constexpr u64 CHEAT_ENGINE_TICKS = CoreTiming::BASE_CLOCK_RATE / 60;
+constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
+
+u64 Cheat::Address() const {
+    u64 out;
+    std::memcpy(&out, raw.data(), sizeof(u64));
+    return Common::swap64(out) & 0xFFFFFFFFFF;
+}
+
+u64 Cheat::ValueWidth(u64 offset) const {
+    return Value(offset, width);
+}
+
+u64 Cheat::Value(u64 offset, u64 width) const {
+    u64 out;
+    std::memcpy(&out, raw.data() + offset, sizeof(u64));
+    out = Common::swap64(out);
+    if (width == 8)
+        return out;
+    return out & ((1ull << (width * CHAR_BIT)) - 1);
+}
+
+u32 Cheat::KeypadValue() const {
+    u32 out;
+    std::memcpy(&out, raw.data(), sizeof(u32));
+    return Common::swap32(out) & 0x0FFFFFFF;
+}
+
+void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
+                                    VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
+    this->main_region_begin = main_begin;
+    this->main_region_end = main_end;
+    this->heap_region_begin = heap_begin;
+    this->heap_region_end = heap_end;
+    this->writer = writer;
+    this->reader = reader;
+}
+
+MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
+
+void CheatList::Execute() {
+    MICROPROFILE_SCOPE(Cheat_Engine);
+
+    std::fill(scratch.begin(), scratch.end(), 0);
+    in_standard = false;
+    for (std::size_t i = 0; i < master_list.size(); ++i) {
+        LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
+        current_block = i;
+        ExecuteBlock(master_list[i].second);
+    }
+
+    in_standard = true;
+    for (std::size_t i = 0; i < standard_list.size(); ++i) {
+        LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
+        current_block = i;
+        ExecuteBlock(standard_list[i].second);
+    }
+}
+
+CheatList::CheatList(ProgramSegment master, ProgramSegment standard)
+    : master_list(master), standard_list(standard) {}
+
+bool CheatList::EvaluateConditional(const Cheat& cheat) const {
+    using ComparisonFunction = bool (*)(u64, u64);
+    constexpr ComparisonFunction comparison_functions[] = {
+        [](u64 a, u64 b) { return a > b; },  [](u64 a, u64 b) { return a >= b; },
+        [](u64 a, u64 b) { return a < b; },  [](u64 a, u64 b) { return a <= b; },
+        [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
+    };
+
+    if (cheat.type == CodeType::ConditionalInput) {
+        const auto applet_resource = Core::System::GetInstance()
+                                         .ServiceManager()
+                                         .GetService<Service::HID::Hid>("hid")
+                                         ->GetAppletResource();
+        if (applet_resource == nullptr) {
+            LOG_WARNING(
+                Common_Filesystem,
+                "Attempted to evaluate input conditional, but applet resource is not initialized!");
+            return false;
+        }
+
+        const auto press_state =
+            applet_resource
+                ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
+                .GetPressState();
+        return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
+    }
+
+    ASSERT(cheat.type == CodeType::Conditional);
+
+    const auto offset =
+        cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+    ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
+    const auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
+    const auto addr = cheat.Address() + offset;
+
+    return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
+}
+
+void CheatList::ProcessBlockPairs(const Block& block) {
+    block_pairs.clear();
+
+    u64 scope = 0;
+    std::map<u64, u64> pairs;
+
+    for (std::size_t i = 0; i < block.size(); ++i) {
+        const auto& cheat = block[i];
+
+        switch (cheat.type) {
+        case CodeType::Conditional:
+        case CodeType::ConditionalInput:
+            pairs.insert_or_assign(scope, i);
+            ++scope;
+            break;
+        case CodeType::EndConditional: {
+            --scope;
+            const auto idx = pairs.at(scope);
+            block_pairs.insert_or_assign(idx, i);
+            break;
+        }
+        case CodeType::Loop: {
+            if (cheat.end_of_loop) {
+                --scope;
+                const auto idx = pairs.at(scope);
+                block_pairs.insert_or_assign(idx, i);
+            } else {
+                pairs.insert_or_assign(scope, i);
+                ++scope;
+            }
+            break;
+        }
+        }
+    }
+}
+
+void CheatList::WriteImmediate(const Cheat& cheat) {
+    const auto offset =
+        cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+    auto& register_3 = scratch.at(cheat.register_3);
+
+    const auto addr = cheat.Address() + offset + register_3;
+    LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
+              cheat.Value(8, cheat.width));
+    writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
+}
+
+void CheatList::BeginConditional(const Cheat& cheat) {
+    if (!EvaluateConditional(cheat)) {
+        const auto iter = block_pairs.find(current_index);
+        ASSERT(iter != block_pairs.end());
+        current_index = iter->second - 1;
+    }
+}
+
+void CheatList::EndConditional(const Cheat& cheat) {
+    LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
+}
+
+void CheatList::Loop(const Cheat& cheat) {
+    if (cheat.end_of_loop.Value())
+        ASSERT(!cheat.end_of_loop.Value());
+
+    auto& register_3 = scratch.at(cheat.register_3);
+    const auto iter = block_pairs.find(current_index);
+    ASSERT(iter != block_pairs.end());
+    ASSERT(iter->first < iter->second);
+
+    for (int i = cheat.Value(4, 4); i >= 0; --i) {
+        register_3 = i;
+        for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
+            current_index = c;
+            ExecuteSingleCheat(
+                (in_standard ? standard_list : master_list)[current_block].second[c]);
+        }
+    }
+
+    current_index = iter->second;
+}
+
+void CheatList::LoadImmediate(const Cheat& cheat) {
+    auto& register_3 = scratch.at(cheat.register_3);
+
+    LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
+              cheat.Value(4, 8));
+    register_3 = cheat.Value(4, 8);
+}
+
+void CheatList::LoadIndexed(const Cheat& cheat) {
+    const auto offset =
+        cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+    auto& register_3 = scratch.at(cheat.register_3);
+
+    const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
+    LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
+              cheat.register_3, addr);
+    register_3 = reader(cheat.width, SanitizeAddress(addr));
+}
+
+void CheatList::StoreIndexed(const Cheat& cheat) {
+    auto& register_3 = scratch.at(cheat.register_3);
+
+    const auto addr =
+        register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
+    LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
+              cheat.Value(4, cheat.width), addr);
+    writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
+}
+
+void CheatList::RegisterArithmetic(const Cheat& cheat) {
+    using ArithmeticFunction = u64 (*)(u64, u64);
+    constexpr ArithmeticFunction arithmetic_functions[] = {
+        [](u64 a, u64 b) { return a + b; },  [](u64 a, u64 b) { return a - b; },
+        [](u64 a, u64 b) { return a * b; },  [](u64 a, u64 b) { return a << b; },
+        [](u64 a, u64 b) { return a >> b; },
+    };
+
+    using ArithmeticOverflowCheck = bool (*)(u64, u64);
+    constexpr ArithmeticOverflowCheck arithmetic_overflow_checks[] = {
+        [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); },       // a + b
+        [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); },       // a - b
+        [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); },       // a * b
+        [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
+        [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; },         // a >> b
+    };
+
+    static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
+                  "Missing or have extra arithmetic overflow checks compared to functions!");
+
+    auto& register_3 = scratch.at(cheat.register_3);
+
+    ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
+    const auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
+    const auto* overflow_function =
+        arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
+    LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
+              cheat.register_3, cheat.ValueWidth(4));
+
+    if (overflow_function(register_3, cheat.ValueWidth(4))) {
+        LOG_WARNING(Common_Filesystem,
+                    "overflow will occur when performing arithmetic operation={:02X} with operands "
+                    "a={:016X}, b={:016X}!",
+                    static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
+    }
+
+    register_3 = function(register_3, cheat.ValueWidth(4));
+}
+
+void CheatList::BeginConditionalInput(const Cheat& cheat) {
+    if (!EvaluateConditional(cheat)) {
+        const auto iter = block_pairs.find(current_index);
+        ASSERT(iter != block_pairs.end());
+        current_index = iter->second - 1;
+    }
+}
+
+VAddr CheatList::SanitizeAddress(VAddr in) const {
+    if ((in < main_region_begin || in >= main_region_end) &&
+        (in < heap_region_begin || in >= heap_region_end)) {
+        LOG_ERROR(Common_Filesystem,
+                  "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
+                  "the cheat may be incorrect. However, this may be normal early in execution if "
+                  "the game has not properly set up yet.",
+                  in);
+        return 0; ///< Invalid addresses will hard crash
+    }
+
+    return in;
+}
+
+void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
+    using CheatOperationFunction = void (CheatList::*)(const Cheat&);
+    constexpr CheatOperationFunction cheat_operation_functions[] = {
+        &CheatList::WriteImmediate,        &CheatList::BeginConditional,
+        &CheatList::EndConditional,        &CheatList::Loop,
+        &CheatList::LoadImmediate,         &CheatList::LoadIndexed,
+        &CheatList::StoreIndexed,          &CheatList::RegisterArithmetic,
+        &CheatList::BeginConditionalInput,
+    };
+
+    const auto index = static_cast<u8>(cheat.type.Value());
+    ASSERT(index < sizeof(cheat_operation_functions));
+    const auto op = cheat_operation_functions[index];
+    (this->*op)(cheat);
+}
+
+void CheatList::ExecuteBlock(const Block& block) {
+    encountered_loops.clear();
+
+    ProcessBlockPairs(block);
+    for (std::size_t i = 0; i < block.size(); ++i) {
+        current_index = i;
+        ExecuteSingleCheat(block[i]);
+        i = current_index;
+    }
+}
+
+CheatParser::~CheatParser() = default;
+
+CheatList CheatParser::MakeCheatList(CheatList::ProgramSegment master,
+                                     CheatList::ProgramSegment standard) const {
+    return {master, standard};
+}
+
+TextCheatParser::~TextCheatParser() = default;
+
+CheatList TextCheatParser::Parse(const std::vector<u8>& data) const {
+    std::stringstream ss;
+    ss.write(reinterpret_cast<const char*>(data.data()), data.size());
+
+    std::vector<std::string> lines;
+    std::string stream_line;
+    while (std::getline(ss, stream_line)) {
+        // Remove a trailing \r
+        if (!stream_line.empty() && stream_line.back() == '\r')
+            stream_line.pop_back();
+        lines.push_back(std::move(stream_line));
+    }
+
+    CheatList::ProgramSegment master_list;
+    CheatList::ProgramSegment standard_list;
+
+    for (std::size_t i = 0; i < lines.size(); ++i) {
+        auto line = lines[i];
+
+        if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
+            const auto master = line[0] == '{';
+            const auto begin = master ? line.find('{') : line.find('[');
+            const auto end = master ? line.find_last_of('}') : line.find_last_of(']');
+
+            ASSERT(begin != std::string::npos && end != std::string::npos);
+
+            const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
+            CheatList::Block block{};
+
+            while (i < lines.size() - 1) {
+                line = lines[++i];
+                if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
+                    --i;
+                    break;
+                }
+
+                if (line.size() < 8)
+                    continue;
+
+                Cheat out{};
+                out.raw = ParseSingleLineCheat(line);
+                block.push_back(out);
+            }
+
+            (master ? master_list : standard_list).emplace_back(patch_name, block);
+        }
+    }
+
+    return MakeCheatList(master_list, standard_list);
+}
+
+std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
+    std::array<u8, 16> out{};
+
+    if (line.size() < 8)
+        return out;
+
+    const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
+    std::memcpy(out.data(), word1.data(), sizeof(u32));
+
+    if (line.size() < 17 || line[8] != ' ')
+        return out;
+
+    const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
+    std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
+
+    if (line.size() < 26 || line[17] != ' ') {
+        // Perform shifting in case value is truncated early.
+        const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
+        if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
+            type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
+            std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
+            std::memset(out.data() + 4, 0, sizeof(u32));
+        }
+
+        return out;
+    }
+
+    const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
+    std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
+
+    if (line.size() < 35 || line[26] != ' ') {
+        // Perform shifting in case value is truncated early.
+        const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
+        if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
+            std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
+            std::memset(out.data() + 8, 0, sizeof(u32));
+        }
+
+        return out;
+    }
+
+    const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
+    std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
+
+    return out;
+}
+
+u64 MemoryReadImpl(u8 width, VAddr addr) {
+    switch (width) {
+    case 1:
+        return Memory::Read8(addr);
+    case 2:
+        return Memory::Read16(addr);
+    case 4:
+        return Memory::Read32(addr);
+    case 8:
+        return Memory::Read64(addr);
+    default:
+        UNREACHABLE();
+        return 0;
+    }
+}
+
+void MemoryWriteImpl(u8 width, VAddr addr, u64 value) {
+    switch (width) {
+    case 1:
+        Memory::Write8(addr, static_cast<u8>(value));
+        break;
+    case 2:
+        Memory::Write16(addr, static_cast<u16>(value));
+        break;
+    case 4:
+        Memory::Write32(addr, static_cast<u32>(value));
+        break;
+    case 8:
+        Memory::Write64(addr, value);
+        break;
+    default:
+        UNREACHABLE();
+    }
+}
+
+CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id)
+    : cheats(std::move(cheats)) {
+    event = CoreTiming::RegisterEvent(
+        "CheatEngine::FrameCallback::" + build_id,
+        [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
+    CoreTiming::ScheduleEvent(CHEAT_ENGINE_TICKS, event);
+
+    const auto& vm_manager = Core::System::GetInstance().CurrentProcess()->VMManager();
+    for (auto& list : this->cheats) {
+        list.SetMemoryParameters(
+            vm_manager.GetMainCodeRegionBaseAddress(), vm_manager.GetHeapRegionBaseAddress(),
+            vm_manager.GetMainCodeRegionEndAddress(), vm_manager.GetHeapRegionEndAddress(),
+            &MemoryWriteImpl, &MemoryReadImpl);
+    }
+}
+
+CheatEngine::~CheatEngine() {
+    CoreTiming::UnscheduleEvent(event, 0);
+}
+
+void CheatEngine::FrameCallback(u64 userdata, int cycles_late) {
+    for (auto& list : cheats)
+        list.Execute();
+
+    CoreTiming::ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
new file mode 100644
index 000000000..d7a8654b7
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.h
@@ -0,0 +1,226 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+#include <queue>
+#include "common/bit_field.h"
+#include "common/common_types.h"
+
+namespace CoreTiming {
+struct EventType;
+}
+
+namespace FileSys {
+
+enum class CodeType : u32 {
+    // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
+    // Writes a T sized value Y to the address A added to the value of register R in memory domain M
+    WriteImmediate = 0,
+
+    // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
+    // Compares the T sized value Y to the value at address A in memory domain M using the
+    // conditional function C. If success, continues execution. If failure, jumps to the matching
+    // EndConditional statement.
+    Conditional = 1,
+
+    // 20000000
+    // Terminates a Conditional or ConditionalInput block.
+    EndConditional = 2,
+
+    // 300R0000 VVVVVVVV
+    // Starts looping V times, storing the current count in register R.
+    // Loop block is terminated with a matching 310R0000.
+    Loop = 3,
+
+    // 400R0000 VVVVVVVV VVVVVVVV
+    // Sets the value of register R to the value V.
+    LoadImmediate = 4,
+
+    // 5TMRI0AA AAAAAAAA
+    // Sets the value of register R to the value of width T at address A in memory domain M, with
+    // the current value of R added to the address if I == 1.
+    LoadIndexed = 5,
+
+    // 6T0RIFG0 VVVVVVVV VVVVVVVV
+    // Writes the value V of width T to the memory address stored in register R. Adds the value of
+    // register G to the final calculation if F is nonzero. Increments the value of register R by T
+    // after operation if I is nonzero.
+    StoreIndexed = 6,
+
+    // 7T0RA000 VVVVVVVV
+    // Performs the arithmetic operation A on the value in register R and the value V of width T,
+    // storing the result in register R.
+    RegisterArithmetic = 7,
+
+    // 8KKKKKKK
+    // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
+    // execution continues. If none are, execution skips to the next EndConditional command.
+    ConditionalInput = 8,
+};
+
+enum class MemoryType : u32 {
+    // Addressed relative to start of main NSO
+    MainNSO = 0,
+
+    // Addressed relative to start of heap
+    Heap = 1,
+};
+
+enum class ArithmeticOp : u32 {
+    Add = 0,
+    Sub = 1,
+    Mult = 2,
+    LShift = 3,
+    RShift = 4,
+};
+
+enum class ComparisonOp : u32 {
+    GreaterThan = 1,
+    GreaterThanEqual = 2,
+    LessThan = 3,
+    LessThanEqual = 4,
+    Equal = 5,
+    Inequal = 6,
+};
+
+union Cheat {
+    std::array<u8, 16> raw;
+
+    BitField<4, 4, CodeType> type;
+    BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
+    BitField<0, 4, u32> end_of_loop;
+    BitField<12, 4, MemoryType> memory_type;
+    BitField<8, 4, u32> register_3;
+    BitField<8, 4, ComparisonOp> comparison_op;
+    BitField<20, 4, u32> load_from_register;
+    BitField<20, 4, u32> increment_register;
+    BitField<20, 4, ArithmeticOp> arithmetic_op;
+    BitField<16, 4, u32> add_additional_register;
+    BitField<28, 4, u32> register_6;
+
+    u64 Address() const;
+    u64 ValueWidth(u64 offset) const;
+    u64 Value(u64 offset, u64 width) const;
+    u32 KeypadValue() const;
+};
+
+class CheatParser;
+
+// Represents a full collection of cheats for a game. The Execute function should be called every
+// interval that all cheats should be executed. Clients should not directly instantiate this class
+// (hence private constructor), they should instead receive an instance from CheatParser, which
+// guarantees the list is always in an acceptable state.
+class CheatList {
+public:
+    friend class CheatParser;
+
+    using Block = std::vector<Cheat>;
+    using ProgramSegment = std::vector<std::pair<std::string, Block>>;
+
+    // (width in bytes, address, value)
+    using MemoryWriter = void (*)(u8, VAddr, u64);
+    // (width in bytes, address) -> value
+    using MemoryReader = u64 (*)(u8, VAddr);
+
+    void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
+                             MemoryWriter writer, MemoryReader reader);
+
+    void Execute();
+
+private:
+    CheatList(ProgramSegment master, ProgramSegment standard);
+
+    void ProcessBlockPairs(const Block& block);
+    void ExecuteSingleCheat(const Cheat& cheat);
+
+    void ExecuteBlock(const Block& block);
+
+    bool EvaluateConditional(const Cheat& cheat) const;
+
+    // Individual cheat operations
+    void WriteImmediate(const Cheat& cheat);
+    void BeginConditional(const Cheat& cheat);
+    void EndConditional(const Cheat& cheat);
+    void Loop(const Cheat& cheat);
+    void LoadImmediate(const Cheat& cheat);
+    void LoadIndexed(const Cheat& cheat);
+    void StoreIndexed(const Cheat& cheat);
+    void RegisterArithmetic(const Cheat& cheat);
+    void BeginConditionalInput(const Cheat& cheat);
+
+    VAddr SanitizeAddress(VAddr in) const;
+
+    // Master Codes are defined as codes that cannot be disabled and are run prior to all
+    // others.
+    ProgramSegment master_list;
+    // All other codes
+    ProgramSegment standard_list;
+
+    bool in_standard = false;
+
+    // 16 (0x0-0xF) scratch registers that can be used by cheats
+    std::array<u64, 16> scratch{};
+
+    MemoryWriter writer = nullptr;
+    MemoryReader reader = nullptr;
+
+    u64 main_region_begin{};
+    u64 heap_region_begin{};
+    u64 main_region_end{};
+    u64 heap_region_end{};
+
+    u64 current_block{};
+    // The current index of the cheat within the current Block
+    u64 current_index{};
+
+    // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
+    // pushed onto this queue. When a end block is encountered, the condition is checked.
+    std::map<u64, u64> block_pairs;
+
+    std::set<u64> encountered_loops;
+};
+
+// Intermediary class that parses a text file or other disk format for storing cheats into a
+// CheatList object, that can be used for execution.
+class CheatParser {
+public:
+    virtual ~CheatParser();
+
+    virtual CheatList Parse(const std::vector<u8>& data) const = 0;
+
+protected:
+    CheatList MakeCheatList(CheatList::ProgramSegment master,
+                            CheatList::ProgramSegment standard) const;
+};
+
+// CheatParser implementation that parses text files
+class TextCheatParser final : public CheatParser {
+public:
+    ~TextCheatParser() override;
+
+    CheatList Parse(const std::vector<u8>& data) const override;
+
+private:
+    std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
+};
+
+// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
+class CheatEngine final {
+public:
+    CheatEngine(std::vector<CheatList> cheats, const std::string& build_id);
+    ~CheatEngine();
+
+private:
+    void FrameCallback(u64 userdata, int cycles_late);
+
+    CoreTiming::EventType* event;
+
+    std::vector<CheatList> cheats;
+};
+
+} // namespace FileSys

From 70535466877748ed765c11170c573767f61bd744 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 22 Dec 2018 21:32:05 -0500
Subject: [PATCH 7/8] core: Add support for registering and controlling
 ownership of CheatEngine

---
 src/core/core.cpp | 9 +++++++++
 src/core/core.h   | 4 ++++
 2 files changed, 13 insertions(+)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index ab7181a05..ce53ef758 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -32,6 +32,7 @@
 #include "core/perf_stats.h"
 #include "core/settings.h"
 #include "core/telemetry_session.h"
+#include "file_sys/cheat_engine.h"
 #include "frontend/applets/profile_select.h"
 #include "frontend/applets/software_keyboard.h"
 #include "frontend/applets/web_browser.h"
@@ -197,6 +198,7 @@ struct System::Impl {
         GDBStub::Shutdown();
         Service::Shutdown();
         service_manager.reset();
+        cheat_engine.reset();
         telemetry_session.reset();
         gpu_core.reset();
 
@@ -247,6 +249,8 @@ struct System::Impl {
     CpuCoreManager cpu_core_manager;
     bool is_powered_on = false;
 
+    std::unique_ptr<FileSys::CheatEngine> cheat_engine;
+
     /// Frontend applets
     std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
     std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -445,6 +449,11 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
     return impl->debug_context.get();
 }
 
+void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
+                               const std::string& build_id) {
+    impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(list, build_id);
+}
+
 void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
     impl->virtual_filesystem = std::move(vfs);
 }
diff --git a/src/core/core.h b/src/core/core.h
index d720013f7..bfb75631e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -20,6 +20,7 @@ class WebBrowserApplet;
 } // namespace Core::Frontend
 
 namespace FileSys {
+class CheatList;
 class VfsFilesystem;
 } // namespace FileSys
 
@@ -253,6 +254,9 @@ public:
 
     std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
 
+    void RegisterCheatList(const std::vector<FileSys::CheatList>& list,
+                           const std::string& build_id);
+
     void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
 
     const Frontend::ProfileSelectApplet& GetProfileSelector() const;

From 52ac6419dafb84b10369226d3746b3b5b761d33b Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 5 Mar 2019 10:09:27 -0500
Subject: [PATCH 8/8] vm_manager: Remove cheat-specific ranges from VMManager

---
 src/core/core.cpp                             |  6 +-
 src/core/core.h                               |  4 +-
 src/core/file_sys/cheat_engine.cpp            | 70 ++++++++++---------
 src/core/file_sys/cheat_engine.h              | 11 +--
 src/core/hle/kernel/vm_manager.cpp            | 17 -----
 src/core/hle/kernel/vm_manager.h              |  8 ---
 src/core/hle/service/hid/hid.h                |  2 +
 .../loader/deconstructed_rom_directory.cpp    |  4 +-
 src/core/loader/nso.cpp                       | 10 +--
 src/core/loader/nso.h                         |  1 -
 10 files changed, 56 insertions(+), 77 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index ce53ef758..32c5b112d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -450,8 +450,10 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
 }
 
 void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
-                               const std::string& build_id) {
-    impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(list, build_id);
+                               const std::string& build_id, VAddr code_region_start,
+                               VAddr code_region_end) {
+    impl->cheat_engine =
+        std::make_unique<FileSys::CheatEngine>(list, build_id, code_region_start, code_region_end);
 }
 
 void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
diff --git a/src/core/core.h b/src/core/core.h
index bfb75631e..a6cfda405 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -254,8 +254,8 @@ public:
 
     std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
 
-    void RegisterCheatList(const std::vector<FileSys::CheatList>& list,
-                           const std::string& build_id);
+    void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
+                           VAddr code_region_start, VAddr code_region_end);
 
     void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
 
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
index e4383d8c1..09ca9d705 100644
--- a/src/core/file_sys/cheat_engine.cpp
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -18,7 +18,7 @@
 
 namespace FileSys {
 
-constexpr u64 CHEAT_ENGINE_TICKS = CoreTiming::BASE_CLOCK_RATE / 60;
+constexpr u64 CHEAT_ENGINE_TICKS = Core::Timing::BASE_CLOCK_RATE / 60;
 constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
 
 u64 Cheat::Address() const {
@@ -82,7 +82,7 @@ CheatList::CheatList(ProgramSegment master, ProgramSegment standard)
 
 bool CheatList::EvaluateConditional(const Cheat& cheat) const {
     using ComparisonFunction = bool (*)(u64, u64);
-    constexpr ComparisonFunction comparison_functions[] = {
+    constexpr std::array<ComparisonFunction, 6> comparison_functions{
         [](u64 a, u64 b) { return a > b; },  [](u64 a, u64 b) { return a >= b; },
         [](u64 a, u64 b) { return a < b; },  [](u64 a, u64 b) { return a <= b; },
         [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
@@ -103,7 +103,7 @@ bool CheatList::EvaluateConditional(const Cheat& cheat) const {
         const auto press_state =
             applet_resource
                 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
-                .GetPressState();
+                .GetAndResetPressState();
         return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
     }
 
@@ -112,7 +112,7 @@ bool CheatList::EvaluateConditional(const Cheat& cheat) const {
     const auto offset =
         cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
     ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
-    const auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
+    auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
     const auto addr = cheat.Address() + offset;
 
     return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
@@ -157,7 +157,7 @@ void CheatList::ProcessBlockPairs(const Block& block) {
 void CheatList::WriteImmediate(const Cheat& cheat) {
     const auto offset =
         cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
-    auto& register_3 = scratch.at(cheat.register_3);
+    const auto& register_3 = scratch.at(cheat.register_3);
 
     const auto addr = cheat.Address() + offset + register_3;
     LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
@@ -166,11 +166,13 @@ void CheatList::WriteImmediate(const Cheat& cheat) {
 }
 
 void CheatList::BeginConditional(const Cheat& cheat) {
-    if (!EvaluateConditional(cheat)) {
-        const auto iter = block_pairs.find(current_index);
-        ASSERT(iter != block_pairs.end());
-        current_index = iter->second - 1;
+    if (EvaluateConditional(cheat)) {
+        return;
     }
+
+    const auto iter = block_pairs.find(current_index);
+    ASSERT(iter != block_pairs.end());
+    current_index = iter->second - 1;
 }
 
 void CheatList::EndConditional(const Cheat& cheat) {
@@ -218,7 +220,7 @@ void CheatList::LoadIndexed(const Cheat& cheat) {
 }
 
 void CheatList::StoreIndexed(const Cheat& cheat) {
-    auto& register_3 = scratch.at(cheat.register_3);
+    const auto& register_3 = scratch.at(cheat.register_3);
 
     const auto addr =
         register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
@@ -229,14 +231,14 @@ void CheatList::StoreIndexed(const Cheat& cheat) {
 
 void CheatList::RegisterArithmetic(const Cheat& cheat) {
     using ArithmeticFunction = u64 (*)(u64, u64);
-    constexpr ArithmeticFunction arithmetic_functions[] = {
+    constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
         [](u64 a, u64 b) { return a + b; },  [](u64 a, u64 b) { return a - b; },
         [](u64 a, u64 b) { return a * b; },  [](u64 a, u64 b) { return a << b; },
         [](u64 a, u64 b) { return a >> b; },
     };
 
     using ArithmeticOverflowCheck = bool (*)(u64, u64);
-    constexpr ArithmeticOverflowCheck arithmetic_overflow_checks[] = {
+    constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
         [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); },       // a + b
         [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); },       // a - b
         [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); },       // a * b
@@ -250,8 +252,8 @@ void CheatList::RegisterArithmetic(const Cheat& cheat) {
     auto& register_3 = scratch.at(cheat.register_3);
 
     ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
-    const auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
-    const auto* overflow_function =
+    auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
+    auto* overflow_function =
         arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
     LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
               cheat.register_3, cheat.ValueWidth(4));
@@ -267,11 +269,12 @@ void CheatList::RegisterArithmetic(const Cheat& cheat) {
 }
 
 void CheatList::BeginConditionalInput(const Cheat& cheat) {
-    if (!EvaluateConditional(cheat)) {
-        const auto iter = block_pairs.find(current_index);
-        ASSERT(iter != block_pairs.end());
-        current_index = iter->second - 1;
-    }
+    if (EvaluateConditional(cheat))
+        return;
+
+    const auto iter = block_pairs.find(current_index);
+    ASSERT(iter != block_pairs.end());
+    current_index = iter->second - 1;
 }
 
 VAddr CheatList::SanitizeAddress(VAddr in) const {
@@ -290,7 +293,7 @@ VAddr CheatList::SanitizeAddress(VAddr in) const {
 
 void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
     using CheatOperationFunction = void (CheatList::*)(const Cheat&);
-    constexpr CheatOperationFunction cheat_operation_functions[] = {
+    constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
         &CheatList::WriteImmediate,        &CheatList::BeginConditional,
         &CheatList::EndConditional,        &CheatList::Loop,
         &CheatList::LoadImmediate,         &CheatList::LoadIndexed,
@@ -346,7 +349,7 @@ CheatList TextCheatParser::Parse(const std::vector<u8>& data) const {
         if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
             const auto master = line[0] == '{';
             const auto begin = master ? line.find('{') : line.find('[');
-            const auto end = master ? line.find_last_of('}') : line.find_last_of(']');
+            const auto end = master ? line.rfind('}') : line.rfind(']');
 
             ASSERT(begin != std::string::npos && end != std::string::npos);
 
@@ -422,7 +425,7 @@ std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line
     return out;
 }
 
-u64 MemoryReadImpl(u8 width, VAddr addr) {
+u64 MemoryReadImpl(u32 width, VAddr addr) {
     switch (width) {
     case 1:
         return Memory::Read8(addr);
@@ -438,7 +441,7 @@ u64 MemoryReadImpl(u8 width, VAddr addr) {
     }
 }
 
-void MemoryWriteImpl(u8 width, VAddr addr, u64 value) {
+void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
     switch (width) {
     case 1:
         Memory::Write8(addr, static_cast<u8>(value));
@@ -457,31 +460,34 @@ void MemoryWriteImpl(u8 width, VAddr addr, u64 value) {
     }
 }
 
-CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id)
+CheatEngine::CheatEngine(std::vector<CheatList> cheats, const std::string& build_id,
+                         VAddr code_region_start, VAddr code_region_end)
     : cheats(std::move(cheats)) {
-    event = CoreTiming::RegisterEvent(
+    auto& core_timing{Core::System::GetInstance().CoreTiming()};
+    event = core_timing.RegisterEvent(
         "CheatEngine::FrameCallback::" + build_id,
         [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
-    CoreTiming::ScheduleEvent(CHEAT_ENGINE_TICKS, event);
+    core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
 
     const auto& vm_manager = Core::System::GetInstance().CurrentProcess()->VMManager();
     for (auto& list : this->cheats) {
-        list.SetMemoryParameters(
-            vm_manager.GetMainCodeRegionBaseAddress(), vm_manager.GetHeapRegionBaseAddress(),
-            vm_manager.GetMainCodeRegionEndAddress(), vm_manager.GetHeapRegionEndAddress(),
-            &MemoryWriteImpl, &MemoryReadImpl);
+        list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
+                                 code_region_end, vm_manager.GetHeapRegionEndAddress(),
+                                 &MemoryWriteImpl, &MemoryReadImpl);
     }
 }
 
 CheatEngine::~CheatEngine() {
-    CoreTiming::UnscheduleEvent(event, 0);
+    auto& core_timing{Core::System::GetInstance().CoreTiming()};
+    core_timing.UnscheduleEvent(event, 0);
 }
 
 void CheatEngine::FrameCallback(u64 userdata, int cycles_late) {
     for (auto& list : cheats)
         list.Execute();
 
-    CoreTiming::ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
+    auto& core_timing{Core::System::GetInstance().CoreTiming()};
+    core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
 }
 
 } // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
index d7a8654b7..7ed69a2c8 100644
--- a/src/core/file_sys/cheat_engine.h
+++ b/src/core/file_sys/cheat_engine.h
@@ -11,7 +11,7 @@
 #include "common/bit_field.h"
 #include "common/common_types.h"
 
-namespace CoreTiming {
+namespace Core::Timing {
 struct EventType;
 }
 
@@ -123,9 +123,9 @@ public:
     using ProgramSegment = std::vector<std::pair<std::string, Block>>;
 
     // (width in bytes, address, value)
-    using MemoryWriter = void (*)(u8, VAddr, u64);
+    using MemoryWriter = void (*)(u32, VAddr, u64);
     // (width in bytes, address) -> value
-    using MemoryReader = u64 (*)(u8, VAddr);
+    using MemoryReader = u64 (*)(u32, VAddr);
 
     void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
                              MemoryWriter writer, MemoryReader reader);
@@ -212,13 +212,14 @@ private:
 // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
 class CheatEngine final {
 public:
-    CheatEngine(std::vector<CheatList> cheats, const std::string& build_id);
+    CheatEngine(std::vector<CheatList> cheats, const std::string& build_id, VAddr code_region_start,
+                VAddr code_region_end);
     ~CheatEngine();
 
 private:
     void FrameCallback(u64 userdata, int cycles_late);
 
-    CoreTiming::EventType* event;
+    Core::Timing::EventType* event;
 
     std::vector<CheatList> cheats;
 };
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 161a83660..10ad94aa6 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -786,23 +786,6 @@ u64 VMManager::GetNewMapRegionSize() const {
     return new_map_region_end - new_map_region_base;
 }
 
-void VMManager::SetMainCodeRegion(VAddr begin, VAddr end) {
-    main_code_region_base = begin;
-    main_code_region_end = end;
-}
-
-VAddr VMManager::GetMainCodeRegionBaseAddress() const {
-    return main_code_region_base;
-}
-
-VAddr VMManager::GetMainCodeRegionEndAddress() const {
-    return main_code_region_end;
-}
-
-u64 VMManager::GetMainCodeRegionSize() const {
-    return main_code_region_end - main_code_region_base;
-}
-
 VAddr VMManager::GetTLSIORegionBaseAddress() const {
     return tls_io_region_base;
 }
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 728318f4b..d83a63e07 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -480,14 +480,6 @@ public:
     /// Gets the total size of the new map region in bytes.
     u64 GetNewMapRegionSize() const;
 
-    void SetMainCodeRegion(VAddr begin, VAddr end);
-
-    VAddr GetMainCodeRegionBaseAddress() const;
-
-    VAddr GetMainCodeRegionEndAddress() const;
-
-    u64 GetMainCodeRegionSize() const;
-
     /// Gets the base address of the TLS IO region.
     VAddr GetTLSIORegionBaseAddress() const;
 
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index e39cd6b51..498602de5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -3,6 +3,8 @@
 // Refer to the license.txt file included.
 
 #pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
 #include "core/hle/service/service.h"
 
 #include "controllers/controller_base.h"
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index ef9a577e3..07aa7a1cd 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -147,10 +147,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
 
         const VAddr load_addr = next_load_addr;
         const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
-        const bool should_register_data_segment = std::strcmp(module, "main") == 0;
         const auto tentative_next_load_addr =
-            AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments,
-                                      should_register_data_segment, pm);
+            AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
         if (!tentative_next_load_addr) {
             return ResultStatus::ErrorLoadingNSO;
         }
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 2721d85b1..5f6a6c0cf 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -97,7 +97,6 @@ static constexpr u32 PageAlignSize(u32 size) {
 std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
                                                const FileSys::VfsFile& file, VAddr load_base,
                                                bool should_pass_arguments,
-                                               bool should_register_data_region,
                                                std::optional<FileSys::PatchManager> pm) {
     if (file.GetSize() < sizeof(NsoHeader))
         return {};
@@ -156,10 +155,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
     const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
     program_image.resize(image_size);
 
-    if (should_register_data_region) {
-        process.VMManager().SetMainCodeRegion(load_base, load_base + program_image.size());
-    }
-
     // Apply patches if necessary
     if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
         std::vector<u8> pi_header(program_image.size() + 0x100);
@@ -176,7 +171,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
         const auto cheats = pm->CreateCheatList(nso_header.build_id);
         if (!cheats.empty()) {
             Core::System::GetInstance().RegisterCheatList(
-                cheats, Common::HexArrayToString(nso_header.build_id));
+                cheats, Common::HexArrayToString(nso_header.build_id), load_base,
+                load_base + program_image.size());
         }
     }
 
@@ -197,7 +193,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
 
     // Load module
     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
-    if (!LoadModule(process, *file, base_address, true, true)) {
+    if (!LoadModule(process, *file, base_address, true)) {
         return ResultStatus::ErrorLoadingNSO;
     }
     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 858e346c6..135b6ea5a 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -43,7 +43,6 @@ public:
 
     static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file,
                                            VAddr load_base, bool should_pass_arguments,
-                                           bool should_register_data_segment,
                                            std::optional<FileSys::PatchManager> pm = {});
 
     ResultStatus Load(Kernel::Process& process) override;