cheat_engine.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <locale>
  4. #include "common/hex_util.h"
  5. #include "common/microprofile.h"
  6. #include "common/swap.h"
  7. #include "core/arm/debug.h"
  8. #include "core/core.h"
  9. #include "core/core_timing.h"
  10. #include "core/hle/kernel/k_page_table.h"
  11. #include "core/hle/kernel/k_process.h"
  12. #include "core/hle/kernel/k_process_page_table.h"
  13. #include "core/hle/kernel/svc_types.h"
  14. #include "core/hle/service/hid/hid_server.h"
  15. #include "core/hle/service/sm/sm.h"
  16. #include "core/memory.h"
  17. #include "core/memory/cheat_engine.h"
  18. #include "hid_core/resource_manager.h"
  19. #include "hid_core/resources/npad/npad.h"
  20. namespace Core::Memory {
  21. namespace {
  22. constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
  23. std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
  24. std::size_t start_index, char match) {
  25. auto end_index = start_index;
  26. while (data[end_index] != match) {
  27. ++end_index;
  28. if (end_index > data.size()) {
  29. return {};
  30. }
  31. }
  32. out_name_size = end_index - start_index;
  33. // Clamp name if it's too big
  34. if (out_name_size > sizeof(CheatDefinition::readable_name)) {
  35. end_index = start_index + sizeof(CheatDefinition::readable_name);
  36. }
  37. return data.substr(start_index, end_index - start_index);
  38. }
  39. } // Anonymous namespace
  40. StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_)
  41. : metadata{metadata_}, system{system_} {}
  42. StandardVmCallbacks::~StandardVmCallbacks() = default;
  43. void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
  44. // Return zero on invalid address
  45. if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
  46. std::memset(data, 0, size);
  47. return;
  48. }
  49. system.ApplicationMemory().ReadBlock(address, data, size);
  50. }
  51. void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
  52. // Skip invalid memory write address
  53. if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
  54. return;
  55. }
  56. if (system.ApplicationMemory().WriteBlock(address, data, size)) {
  57. Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
  58. }
  59. }
  60. u64 StandardVmCallbacks::HidKeysDown() {
  61. const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
  62. if (hid == nullptr) {
  63. LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
  64. return 0;
  65. }
  66. const auto applet_resource = hid->GetResourceManager();
  67. if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
  68. LOG_WARNING(CheatEngine,
  69. "Attempted to read input state, but applet resource is not initialized!");
  70. return 0;
  71. }
  72. const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
  73. return static_cast<u64>(press_state & HID::NpadButton::All);
  74. }
  75. void StandardVmCallbacks::PauseProcess() {
  76. if (system.ApplicationProcess()->IsSuspended()) {
  77. return;
  78. }
  79. system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
  80. }
  81. void StandardVmCallbacks::ResumeProcess() {
  82. if (!system.ApplicationProcess()->IsSuspended()) {
  83. return;
  84. }
  85. system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
  86. }
  87. void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
  88. LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
  89. }
  90. void StandardVmCallbacks::CommandLog(std::string_view data) {
  91. LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
  92. data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
  93. }
  94. bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
  95. if ((in < metadata.main_nso_extents.base ||
  96. in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
  97. (in < metadata.heap_extents.base ||
  98. in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
  99. (in < metadata.alias_extents.base ||
  100. in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
  101. (in < metadata.aslr_extents.base ||
  102. in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
  103. LOG_DEBUG(CheatEngine,
  104. "Cheat attempting to access memory at invalid address={:016X}, if this "
  105. "persists, "
  106. "the cheat may be incorrect. However, this may be normal early in execution if "
  107. "the game has not properly set up yet.",
  108. in);
  109. return false; ///< Invalid addresses will hard crash
  110. }
  111. return true;
  112. }
  113. CheatParser::~CheatParser() = default;
  114. TextCheatParser::~TextCheatParser() = default;
  115. std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
  116. std::vector<CheatEntry> out(1);
  117. std::optional<u64> current_entry;
  118. for (std::size_t i = 0; i < data.size(); ++i) {
  119. if (::isspace(data[i])) {
  120. continue;
  121. }
  122. if (data[i] == '{') {
  123. current_entry = 0;
  124. if (out[*current_entry].definition.num_opcodes > 0) {
  125. return {};
  126. }
  127. std::size_t name_size{};
  128. const auto name = ExtractName(name_size, data, i + 1, '}');
  129. if (name.empty()) {
  130. return {};
  131. }
  132. std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
  133. std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
  134. name.size()));
  135. out[*current_entry]
  136. .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
  137. '\0';
  138. i += name_size + 1;
  139. } else if (data[i] == '[') {
  140. current_entry = out.size();
  141. out.emplace_back();
  142. std::size_t name_size{};
  143. const auto name = ExtractName(name_size, data, i + 1, ']');
  144. if (name.empty()) {
  145. return {};
  146. }
  147. std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
  148. std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
  149. name.size()));
  150. out[*current_entry]
  151. .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
  152. '\0';
  153. i += name_size + 1;
  154. } else if (::isxdigit(data[i])) {
  155. if (!current_entry || out[*current_entry].definition.num_opcodes >=
  156. out[*current_entry].definition.opcodes.size()) {
  157. return {};
  158. }
  159. const auto hex = std::string(data.substr(i, 8));
  160. if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
  161. return {};
  162. }
  163. const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
  164. out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
  165. value;
  166. i += 8;
  167. } else {
  168. return {};
  169. }
  170. }
  171. out[0].enabled = out[0].definition.num_opcodes > 0;
  172. out[0].cheat_id = 0;
  173. for (u32 i = 1; i < out.size(); ++i) {
  174. out[i].enabled = out[i].definition.num_opcodes > 0;
  175. out[i].cheat_id = i;
  176. }
  177. return out;
  178. }
  179. CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
  180. const std::array<u8, 0x20>& build_id_)
  181. : vm{std::make_unique<StandardVmCallbacks>(system_, metadata)},
  182. cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} {
  183. metadata.main_nso_build_id = build_id_;
  184. }
  185. CheatEngine::~CheatEngine() {
  186. core_timing.UnscheduleEvent(event);
  187. }
  188. void CheatEngine::Initialize() {
  189. event = Core::Timing::CreateEvent(
  190. "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
  191. [this](s64 time,
  192. std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
  193. FrameCallback(ns_late);
  194. return std::nullopt;
  195. });
  196. core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
  197. metadata.process_id = system.ApplicationProcess()->GetProcessId();
  198. metadata.title_id = system.GetApplicationProcessProgramID();
  199. const auto& page_table = system.ApplicationProcess()->GetPageTable();
  200. metadata.heap_extents = {
  201. .base = GetInteger(page_table.GetHeapRegionStart()),
  202. .size = page_table.GetHeapRegionSize(),
  203. };
  204. metadata.aslr_extents = {
  205. .base = GetInteger(page_table.GetAliasCodeRegionStart()),
  206. .size = page_table.GetAliasCodeRegionSize(),
  207. };
  208. metadata.alias_extents = {
  209. .base = GetInteger(page_table.GetAliasRegionStart()),
  210. .size = page_table.GetAliasRegionSize(),
  211. };
  212. is_pending_reload.exchange(true);
  213. }
  214. void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
  215. metadata.main_nso_extents = {
  216. .base = main_region_begin,
  217. .size = main_region_size,
  218. };
  219. }
  220. void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
  221. cheats = std::move(reload_cheats);
  222. is_pending_reload.exchange(true);
  223. }
  224. MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
  225. void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
  226. if (is_pending_reload.exchange(false)) {
  227. vm.LoadProgram(cheats);
  228. }
  229. if (vm.GetProgramSize() == 0) {
  230. return;
  231. }
  232. MICROPROFILE_SCOPE(Cheat_Engine);
  233. vm.Execute(metadata);
  234. }
  235. } // namespace Core::Memory