cheat_engine.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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/core.h"
  8. #include "core/core_timing.h"
  9. #include "core/hle/kernel/k_page_table.h"
  10. #include "core/hle/kernel/k_process.h"
  11. #include "core/hle/service/hid/controllers/npad.h"
  12. #include "core/hle/service/hid/hid.h"
  13. #include "core/hle/service/sm/sm.h"
  14. #include "core/memory.h"
  15. #include "core/memory/cheat_engine.h"
  16. namespace Core::Memory {
  17. namespace {
  18. constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
  19. std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
  20. auto end_index = start_index;
  21. while (data[end_index] != match) {
  22. ++end_index;
  23. if (end_index > data.size() ||
  24. (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
  25. return {};
  26. }
  27. }
  28. return data.substr(start_index, end_index - start_index);
  29. }
  30. } // Anonymous namespace
  31. StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_)
  32. : metadata{metadata_}, system{system_} {}
  33. StandardVmCallbacks::~StandardVmCallbacks() = default;
  34. void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
  35. system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size);
  36. }
  37. void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
  38. system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size);
  39. }
  40. u64 StandardVmCallbacks::HidKeysDown() {
  41. const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid");
  42. if (hid == nullptr) {
  43. LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
  44. return 0;
  45. }
  46. const auto applet_resource = hid->GetAppletResource();
  47. if (applet_resource == nullptr) {
  48. LOG_WARNING(CheatEngine,
  49. "Attempted to read input state, but applet resource is not initialized!");
  50. return 0;
  51. }
  52. const auto press_state =
  53. applet_resource
  54. ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
  55. .GetAndResetPressState();
  56. return static_cast<u64>(press_state & HID::NpadButton::All);
  57. }
  58. void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
  59. LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
  60. }
  61. void StandardVmCallbacks::CommandLog(std::string_view data) {
  62. LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
  63. data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
  64. }
  65. VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
  66. if ((in < metadata.main_nso_extents.base ||
  67. in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
  68. (in < metadata.heap_extents.base ||
  69. in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
  70. LOG_ERROR(CheatEngine,
  71. "Cheat attempting to access memory at invalid address={:016X}, if this "
  72. "persists, "
  73. "the cheat may be incorrect. However, this may be normal early in execution if "
  74. "the game has not properly set up yet.",
  75. in);
  76. return 0; ///< Invalid addresses will hard crash
  77. }
  78. return in;
  79. }
  80. CheatParser::~CheatParser() = default;
  81. TextCheatParser::~TextCheatParser() = default;
  82. std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
  83. std::vector<CheatEntry> out(1);
  84. std::optional<u64> current_entry;
  85. for (std::size_t i = 0; i < data.size(); ++i) {
  86. if (::isspace(data[i])) {
  87. continue;
  88. }
  89. if (data[i] == '{') {
  90. current_entry = 0;
  91. if (out[*current_entry].definition.num_opcodes > 0) {
  92. return {};
  93. }
  94. const auto name = ExtractName(data, i + 1, '}');
  95. if (name.empty()) {
  96. return {};
  97. }
  98. std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
  99. std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
  100. name.size()));
  101. out[*current_entry]
  102. .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
  103. '\0';
  104. i += name.length() + 1;
  105. } else if (data[i] == '[') {
  106. current_entry = out.size();
  107. out.emplace_back();
  108. const auto name = ExtractName(data, i + 1, ']');
  109. if (name.empty()) {
  110. return {};
  111. }
  112. std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
  113. std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
  114. name.size()));
  115. out[*current_entry]
  116. .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
  117. '\0';
  118. i += name.length() + 1;
  119. } else if (::isxdigit(data[i])) {
  120. if (!current_entry || out[*current_entry].definition.num_opcodes >=
  121. out[*current_entry].definition.opcodes.size()) {
  122. return {};
  123. }
  124. const auto hex = std::string(data.substr(i, 8));
  125. if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
  126. return {};
  127. }
  128. const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
  129. out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
  130. value;
  131. i += 8;
  132. } else {
  133. return {};
  134. }
  135. }
  136. out[0].enabled = out[0].definition.num_opcodes > 0;
  137. out[0].cheat_id = 0;
  138. for (u32 i = 1; i < out.size(); ++i) {
  139. out[i].enabled = out[i].definition.num_opcodes > 0;
  140. out[i].cheat_id = i;
  141. }
  142. return out;
  143. }
  144. CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
  145. const std::array<u8, 0x20>& build_id_)
  146. : vm{std::make_unique<StandardVmCallbacks>(system_, metadata)},
  147. cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} {
  148. metadata.main_nso_build_id = build_id_;
  149. }
  150. CheatEngine::~CheatEngine() {
  151. core_timing.UnscheduleEvent(event, 0);
  152. }
  153. void CheatEngine::Initialize() {
  154. event = Core::Timing::CreateEvent(
  155. "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
  156. [this](std::uintptr_t user_data, s64 time,
  157. std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
  158. FrameCallback(user_data, ns_late);
  159. return std::nullopt;
  160. });
  161. core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
  162. metadata.process_id = system.ApplicationProcess()->GetProcessId();
  163. metadata.title_id = system.GetApplicationProcessProgramID();
  164. const auto& page_table = system.ApplicationProcess()->GetPageTable();
  165. metadata.heap_extents = {
  166. .base = GetInteger(page_table.GetHeapRegionStart()),
  167. .size = page_table.GetHeapRegionSize(),
  168. };
  169. metadata.address_space_extents = {
  170. .base = GetInteger(page_table.GetAddressSpaceStart()),
  171. .size = page_table.GetAddressSpaceSize(),
  172. };
  173. metadata.alias_extents = {
  174. .base = GetInteger(page_table.GetAliasCodeRegionStart()),
  175. .size = page_table.GetAliasCodeRegionSize(),
  176. };
  177. is_pending_reload.exchange(true);
  178. }
  179. void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
  180. metadata.main_nso_extents = {
  181. .base = main_region_begin,
  182. .size = main_region_size,
  183. };
  184. }
  185. void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
  186. cheats = std::move(reload_cheats);
  187. is_pending_reload.exchange(true);
  188. }
  189. MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
  190. void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
  191. if (is_pending_reload.exchange(false)) {
  192. vm.LoadProgram(cheats);
  193. }
  194. if (vm.GetProgramSize() == 0) {
  195. return;
  196. }
  197. MICROPROFILE_SCOPE(Cheat_Engine);
  198. vm.Execute(metadata);
  199. }
  200. } // namespace Core::Memory