summaryrefslogtreecommitdiff
path: root/src/core/memory
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/memory')
-rw-r--r--src/core/memory/cheat_engine.cpp234
-rw-r--r--src/core/memory/cheat_engine.h86
2 files changed, 320 insertions, 0 deletions
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
new file mode 100644
index 000000000..ea5c76fc0
--- /dev/null
+++ b/src/core/memory/cheat_engine.cpp
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <locale>
6#include "common/hex_util.h"
7#include "common/microprofile.h"
8#include "common/swap.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/service/hid/controllers/npad.h"
14#include "core/hle/service/hid/hid.h"
15#include "core/hle/service/sm/sm.h"
16#include "core/memory/cheat_engine.h"
17
18namespace Memory {
19
20constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
21constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
22
23StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
24 const CheatProcessMetadata& metadata)
25 : system(system), metadata(metadata) {}
26
27StandardVmCallbacks::~StandardVmCallbacks() = default;
28
29void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
30 ReadBlock(SanitizeAddress(address), data, size);
31}
32
33void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
34 WriteBlock(SanitizeAddress(address), data, size);
35}
36
37u64 StandardVmCallbacks::HidKeysDown() {
38 const auto applet_resource =
39 system.ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
40 if (applet_resource == nullptr) {
41 LOG_WARNING(CheatEngine,
42 "Attempted to read input state, but applet resource is not initialized!");
43 return false;
44 }
45
46 const auto press_state =
47 applet_resource
48 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
49 .GetAndResetPressState();
50 return press_state & KEYPAD_BITMASK;
51}
52
53void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
54 LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
55}
56
57void StandardVmCallbacks::CommandLog(std::string_view data) {
58 LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
59 data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
60}
61
62VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
63 if ((in < metadata.main_nso_extents.base ||
64 in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
65 (in < metadata.heap_extents.base ||
66 in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
67 LOG_ERROR(CheatEngine,
68 "Cheat attempting to access memory at invalid address={:016X}, if this "
69 "persists, "
70 "the cheat may be incorrect. However, this may be normal early in execution if "
71 "the game has not properly set up yet.",
72 in);
73 return 0; ///< Invalid addresses will hard crash
74 }
75
76 return in;
77}
78
79CheatParser::~CheatParser() = default;
80
81TextCheatParser::~TextCheatParser() = default;
82
83namespace {
84template <char match>
85std::string_view ExtractName(std::string_view data, std::size_t start_index) {
86 auto end_index = start_index;
87 while (data[end_index] != match) {
88 ++end_index;
89 if (end_index > data.size() ||
90 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
91 return {};
92 }
93 }
94
95 return data.substr(start_index, end_index - start_index);
96}
97} // Anonymous namespace
98
99std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
100 std::string_view data) const {
101 std::vector<CheatEntry> out(1);
102 std::optional<u64> current_entry = std::nullopt;
103
104 for (std::size_t i = 0; i < data.size(); ++i) {
105 if (std::isspace(data[i])) {
106 continue;
107 }
108
109 if (data[i] == '{') {
110 current_entry = 0;
111
112 if (out[*current_entry].definition.num_opcodes > 0) {
113 return {};
114 }
115
116 const auto name = ExtractName<'}'>(data, i + 1);
117 if (name.empty()) {
118 return {};
119 }
120
121 std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
122 std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
123 name.size()));
124 out[*current_entry]
125 .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
126 '\0';
127
128 i += name.length() + 1;
129 } else if (data[i] == '[') {
130 current_entry = out.size();
131 out.emplace_back();
132
133 const auto name = ExtractName<']'>(data, i + 1);
134 if (name.empty()) {
135 return {};
136 }
137
138 std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
139 std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
140 name.size()));
141 out[*current_entry]
142 .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
143 '\0';
144
145 i += name.length() + 1;
146 } else if (std::isxdigit(data[i])) {
147 if (!current_entry || out[*current_entry].definition.num_opcodes >=
148 out[*current_entry].definition.opcodes.size()) {
149 return {};
150 }
151
152 const auto hex = std::string(data.substr(i, 8));
153 if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
154 return {};
155 }
156
157 out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
158 std::stoul(hex, nullptr, 0x10);
159
160 i += 8;
161 } else {
162 return {};
163 }
164 }
165
166 out[0].enabled = out[0].definition.num_opcodes > 0;
167 out[0].cheat_id = 0;
168
169 for (u32 i = 1; i < out.size(); ++i) {
170 out[i].enabled = out[i].definition.num_opcodes > 0;
171 out[i].cheat_id = i;
172 }
173
174 return out;
175}
176
177CheatEngine::CheatEngine(Core::System& system, std::vector<CheatEntry> cheats,
178 const std::array<u8, 0x20>& build_id)
179 : system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique<StandardVmCallbacks>(
180 system, metadata)},
181 cheats(std::move(cheats)) {
182 metadata.main_nso_build_id = build_id;
183}
184
185CheatEngine::~CheatEngine() {
186 core_timing.UnscheduleEvent(event, 0);
187}
188
189void CheatEngine::Initialize() {
190 event = core_timing.RegisterEvent(
191 "CheatEngine::FrameCallback::" + Common::HexArrayToString(metadata.main_nso_build_id),
192 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
193 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
194
195 metadata.process_id = system.CurrentProcess()->GetProcessID();
196 metadata.title_id = system.CurrentProcess()->GetTitleID();
197
198 const auto& vm_manager = system.CurrentProcess()->VMManager();
199 metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()};
200 metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(),
201 vm_manager.GetAddressSpaceSize()};
202 metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()};
203
204 is_pending_reload.exchange(true);
205}
206
207void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
208 metadata.main_nso_extents = {main_region_begin, main_region_size};
209}
210
211void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
212 this->cheats = std::move(cheats);
213 is_pending_reload.exchange(true);
214}
215
216MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
217
218void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
219 if (is_pending_reload.exchange(false)) {
220 vm.LoadProgram(cheats);
221 }
222
223 if (vm.GetProgramSize() == 0) {
224 return;
225 }
226
227 MICROPROFILE_SCOPE(Cheat_Engine);
228
229 vm.Execute(metadata);
230
231 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
232}
233
234} // namespace Memory
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
new file mode 100644
index 000000000..0f012e9b5
--- /dev/null
+++ b/src/core/memory/cheat_engine.h
@@ -0,0 +1,86 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <vector>
9#include "common/common_types.h"
10#include "core/memory/dmnt_cheat_types.h"
11#include "core/memory/dmnt_cheat_vm.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18class CoreTiming;
19struct EventType;
20} // namespace Core::Timing
21
22namespace Memory {
23
24class StandardVmCallbacks : public DmntCheatVm::Callbacks {
25public:
26 StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
27 ~StandardVmCallbacks() override;
28
29 void MemoryRead(VAddr address, void* data, u64 size) override;
30 void MemoryWrite(VAddr address, const void* data, u64 size) override;
31 u64 HidKeysDown() override;
32 void DebugLog(u8 id, u64 value) override;
33 void CommandLog(std::string_view data) override;
34
35private:
36 VAddr SanitizeAddress(VAddr address) const;
37
38 const CheatProcessMetadata& metadata;
39 const Core::System& system;
40};
41
42// Intermediary class that parses a text file or other disk format for storing cheats into a
43// CheatList object, that can be used for execution.
44class CheatParser {
45public:
46 virtual ~CheatParser();
47
48 virtual std::vector<CheatEntry> Parse(const Core::System& system,
49 std::string_view data) const = 0;
50};
51
52// CheatParser implementation that parses text files
53class TextCheatParser final : public CheatParser {
54public:
55 ~TextCheatParser() override;
56
57 std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override;
58};
59
60// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
61class CheatEngine final {
62public:
63 CheatEngine(Core::System& system_, std::vector<CheatEntry> cheats_,
64 const std::array<u8, 0x20>& build_id);
65 ~CheatEngine();
66
67 void Initialize();
68 void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
69
70 void Reload(std::vector<CheatEntry> cheats);
71
72private:
73 void FrameCallback(u64 userdata, s64 cycles_late);
74
75 DmntCheatVm vm;
76 CheatProcessMetadata metadata;
77
78 std::vector<CheatEntry> cheats;
79 std::atomic_bool is_pending_reload{false};
80
81 Core::Timing::EventType* event{};
82 Core::Timing::CoreTiming& core_timing;
83 Core::System& system;
84};
85
86} // namespace Memory