cheat_engine.cpp 8.2 KB

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