cheat_engine.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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/file_sys/cheat_engine.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. namespace FileSys {
  17. constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
  18. constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
  19. u64 Cheat::Address() const {
  20. u64 out;
  21. std::memcpy(&out, raw.data(), sizeof(u64));
  22. return Common::swap64(out) & 0xFFFFFFFFFF;
  23. }
  24. u64 Cheat::ValueWidth(u64 offset) const {
  25. return Value(offset, width);
  26. }
  27. u64 Cheat::Value(u64 offset, u64 width) const {
  28. u64 out;
  29. std::memcpy(&out, raw.data() + offset, sizeof(u64));
  30. out = Common::swap64(out);
  31. if (width == 8)
  32. return out;
  33. return out & ((1ull << (width * CHAR_BIT)) - 1);
  34. }
  35. u32 Cheat::KeypadValue() const {
  36. u32 out;
  37. std::memcpy(&out, raw.data(), sizeof(u32));
  38. return Common::swap32(out) & 0x0FFFFFFF;
  39. }
  40. void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
  41. VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
  42. this->main_region_begin = main_begin;
  43. this->main_region_end = main_end;
  44. this->heap_region_begin = heap_begin;
  45. this->heap_region_end = heap_end;
  46. this->writer = writer;
  47. this->reader = reader;
  48. }
  49. MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
  50. void CheatList::Execute() {
  51. MICROPROFILE_SCOPE(Cheat_Engine);
  52. std::fill(scratch.begin(), scratch.end(), 0);
  53. in_standard = false;
  54. for (std::size_t i = 0; i < master_list.size(); ++i) {
  55. LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
  56. current_block = i;
  57. ExecuteBlock(master_list[i].second);
  58. }
  59. in_standard = true;
  60. for (std::size_t i = 0; i < standard_list.size(); ++i) {
  61. LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
  62. current_block = i;
  63. ExecuteBlock(standard_list[i].second);
  64. }
  65. }
  66. CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
  67. : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
  68. bool CheatList::EvaluateConditional(const Cheat& cheat) const {
  69. using ComparisonFunction = bool (*)(u64, u64);
  70. constexpr std::array<ComparisonFunction, 6> comparison_functions{
  71. [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
  72. [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
  73. [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
  74. };
  75. if (cheat.type == CodeType::ConditionalInput) {
  76. const auto applet_resource =
  77. system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
  78. if (applet_resource == nullptr) {
  79. LOG_WARNING(
  80. Common_Filesystem,
  81. "Attempted to evaluate input conditional, but applet resource is not initialized!");
  82. return false;
  83. }
  84. const auto press_state =
  85. applet_resource
  86. ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
  87. .GetAndResetPressState();
  88. return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
  89. }
  90. ASSERT(cheat.type == CodeType::Conditional);
  91. const auto offset =
  92. cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
  93. ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
  94. auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
  95. const auto addr = cheat.Address() + offset;
  96. return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
  97. }
  98. void CheatList::ProcessBlockPairs(const Block& block) {
  99. block_pairs.clear();
  100. u64 scope = 0;
  101. std::map<u64, u64> pairs;
  102. for (std::size_t i = 0; i < block.size(); ++i) {
  103. const auto& cheat = block[i];
  104. switch (cheat.type) {
  105. case CodeType::Conditional:
  106. case CodeType::ConditionalInput:
  107. pairs.insert_or_assign(scope, i);
  108. ++scope;
  109. break;
  110. case CodeType::EndConditional: {
  111. --scope;
  112. const auto idx = pairs.at(scope);
  113. block_pairs.insert_or_assign(idx, i);
  114. break;
  115. }
  116. case CodeType::Loop: {
  117. if (cheat.end_of_loop) {
  118. --scope;
  119. const auto idx = pairs.at(scope);
  120. block_pairs.insert_or_assign(idx, i);
  121. } else {
  122. pairs.insert_or_assign(scope, i);
  123. ++scope;
  124. }
  125. break;
  126. }
  127. }
  128. }
  129. }
  130. void CheatList::WriteImmediate(const Cheat& cheat) {
  131. const auto offset =
  132. cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
  133. const auto& register_3 = scratch.at(cheat.register_3);
  134. const auto addr = cheat.Address() + offset + register_3;
  135. LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
  136. cheat.Value(8, cheat.width));
  137. writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
  138. }
  139. void CheatList::BeginConditional(const Cheat& cheat) {
  140. if (EvaluateConditional(cheat)) {
  141. return;
  142. }
  143. const auto iter = block_pairs.find(current_index);
  144. ASSERT(iter != block_pairs.end());
  145. current_index = iter->second - 1;
  146. }
  147. void CheatList::EndConditional(const Cheat& cheat) {
  148. LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
  149. }
  150. void CheatList::Loop(const Cheat& cheat) {
  151. if (cheat.end_of_loop.Value())
  152. ASSERT(!cheat.end_of_loop.Value());
  153. auto& register_3 = scratch.at(cheat.register_3);
  154. const auto iter = block_pairs.find(current_index);
  155. ASSERT(iter != block_pairs.end());
  156. ASSERT(iter->first < iter->second);
  157. const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
  158. for (s32 i = initial_value; i >= 0; --i) {
  159. register_3 = static_cast<u64>(i);
  160. for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
  161. current_index = c;
  162. ExecuteSingleCheat(
  163. (in_standard ? standard_list : master_list)[current_block].second[c]);
  164. }
  165. }
  166. current_index = iter->second;
  167. }
  168. void CheatList::LoadImmediate(const Cheat& cheat) {
  169. auto& register_3 = scratch.at(cheat.register_3);
  170. LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
  171. cheat.Value(4, 8));
  172. register_3 = cheat.Value(4, 8);
  173. }
  174. void CheatList::LoadIndexed(const Cheat& cheat) {
  175. const auto offset =
  176. cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
  177. auto& register_3 = scratch.at(cheat.register_3);
  178. const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
  179. LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
  180. cheat.register_3, addr);
  181. register_3 = reader(cheat.width, SanitizeAddress(addr));
  182. }
  183. void CheatList::StoreIndexed(const Cheat& cheat) {
  184. const auto& register_3 = scratch.at(cheat.register_3);
  185. const auto addr =
  186. register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
  187. LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
  188. cheat.Value(4, cheat.width), addr);
  189. writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
  190. }
  191. void CheatList::RegisterArithmetic(const Cheat& cheat) {
  192. using ArithmeticFunction = u64 (*)(u64, u64);
  193. constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
  194. [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
  195. [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
  196. [](u64 a, u64 b) { return a >> b; },
  197. };
  198. using ArithmeticOverflowCheck = bool (*)(u64, u64);
  199. constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
  200. [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
  201. [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
  202. [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
  203. [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
  204. [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
  205. };
  206. static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
  207. "Missing or have extra arithmetic overflow checks compared to functions!");
  208. auto& register_3 = scratch.at(cheat.register_3);
  209. ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
  210. auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
  211. auto* overflow_function =
  212. arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
  213. LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
  214. cheat.register_3, cheat.ValueWidth(4));
  215. if (overflow_function(register_3, cheat.ValueWidth(4))) {
  216. LOG_WARNING(Common_Filesystem,
  217. "overflow will occur when performing arithmetic operation={:02X} with operands "
  218. "a={:016X}, b={:016X}!",
  219. static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
  220. }
  221. register_3 = function(register_3, cheat.ValueWidth(4));
  222. }
  223. void CheatList::BeginConditionalInput(const Cheat& cheat) {
  224. if (EvaluateConditional(cheat))
  225. return;
  226. const auto iter = block_pairs.find(current_index);
  227. ASSERT(iter != block_pairs.end());
  228. current_index = iter->second - 1;
  229. }
  230. VAddr CheatList::SanitizeAddress(VAddr in) const {
  231. if ((in < main_region_begin || in >= main_region_end) &&
  232. (in < heap_region_begin || in >= heap_region_end)) {
  233. LOG_ERROR(Common_Filesystem,
  234. "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
  235. "the cheat may be incorrect. However, this may be normal early in execution if "
  236. "the game has not properly set up yet.",
  237. in);
  238. return 0; ///< Invalid addresses will hard crash
  239. }
  240. return in;
  241. }
  242. void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
  243. using CheatOperationFunction = void (CheatList::*)(const Cheat&);
  244. constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
  245. &CheatList::WriteImmediate, &CheatList::BeginConditional,
  246. &CheatList::EndConditional, &CheatList::Loop,
  247. &CheatList::LoadImmediate, &CheatList::LoadIndexed,
  248. &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
  249. &CheatList::BeginConditionalInput,
  250. };
  251. const auto index = static_cast<u8>(cheat.type.Value());
  252. ASSERT(index < sizeof(cheat_operation_functions));
  253. const auto op = cheat_operation_functions[index];
  254. (this->*op)(cheat);
  255. }
  256. void CheatList::ExecuteBlock(const Block& block) {
  257. encountered_loops.clear();
  258. ProcessBlockPairs(block);
  259. for (std::size_t i = 0; i < block.size(); ++i) {
  260. current_index = i;
  261. ExecuteSingleCheat(block[i]);
  262. i = current_index;
  263. }
  264. }
  265. CheatParser::~CheatParser() = default;
  266. CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
  267. CheatList::ProgramSegment standard) const {
  268. return {system, std::move(master), std::move(standard)};
  269. }
  270. TextCheatParser::~TextCheatParser() = default;
  271. CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
  272. std::stringstream ss;
  273. ss.write(reinterpret_cast<const char*>(data.data()), data.size());
  274. std::vector<std::string> lines;
  275. std::string stream_line;
  276. while (std::getline(ss, stream_line)) {
  277. // Remove a trailing \r
  278. if (!stream_line.empty() && stream_line.back() == '\r')
  279. stream_line.pop_back();
  280. lines.push_back(std::move(stream_line));
  281. }
  282. CheatList::ProgramSegment master_list;
  283. CheatList::ProgramSegment standard_list;
  284. for (std::size_t i = 0; i < lines.size(); ++i) {
  285. auto line = lines[i];
  286. if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
  287. const auto master = line[0] == '{';
  288. const auto begin = master ? line.find('{') : line.find('[');
  289. const auto end = master ? line.rfind('}') : line.rfind(']');
  290. ASSERT(begin != std::string::npos && end != std::string::npos);
  291. const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
  292. CheatList::Block block{};
  293. while (i < lines.size() - 1) {
  294. line = lines[++i];
  295. if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
  296. --i;
  297. break;
  298. }
  299. if (line.size() < 8)
  300. continue;
  301. Cheat out{};
  302. out.raw = ParseSingleLineCheat(line);
  303. block.push_back(out);
  304. }
  305. (master ? master_list : standard_list).emplace_back(patch_name, block);
  306. }
  307. }
  308. return MakeCheatList(system, master_list, standard_list);
  309. }
  310. std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
  311. std::array<u8, 16> out{};
  312. if (line.size() < 8)
  313. return out;
  314. const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
  315. std::memcpy(out.data(), word1.data(), sizeof(u32));
  316. if (line.size() < 17 || line[8] != ' ')
  317. return out;
  318. const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
  319. std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
  320. if (line.size() < 26 || line[17] != ' ') {
  321. // Perform shifting in case value is truncated early.
  322. const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
  323. if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
  324. type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
  325. std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
  326. std::memset(out.data() + 4, 0, sizeof(u32));
  327. }
  328. return out;
  329. }
  330. const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
  331. std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
  332. if (line.size() < 35 || line[26] != ' ') {
  333. // Perform shifting in case value is truncated early.
  334. const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
  335. if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
  336. std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
  337. std::memset(out.data() + 8, 0, sizeof(u32));
  338. }
  339. return out;
  340. }
  341. const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
  342. std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
  343. return out;
  344. }
  345. u64 MemoryReadImpl(u32 width, VAddr addr) {
  346. switch (width) {
  347. case 1:
  348. return Memory::Read8(addr);
  349. case 2:
  350. return Memory::Read16(addr);
  351. case 4:
  352. return Memory::Read32(addr);
  353. case 8:
  354. return Memory::Read64(addr);
  355. default:
  356. UNREACHABLE();
  357. return 0;
  358. }
  359. }
  360. void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
  361. switch (width) {
  362. case 1:
  363. Memory::Write8(addr, static_cast<u8>(value));
  364. break;
  365. case 2:
  366. Memory::Write16(addr, static_cast<u16>(value));
  367. break;
  368. case 4:
  369. Memory::Write32(addr, static_cast<u32>(value));
  370. break;
  371. case 8:
  372. Memory::Write64(addr, value);
  373. break;
  374. default:
  375. UNREACHABLE();
  376. }
  377. }
  378. CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
  379. const std::string& build_id, VAddr code_region_start,
  380. VAddr code_region_end)
  381. : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
  382. event = core_timing.RegisterEvent(
  383. "CheatEngine::FrameCallback::" + build_id,
  384. [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
  385. core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
  386. const auto& vm_manager = system.CurrentProcess()->VMManager();
  387. for (auto& list : this->cheats) {
  388. list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
  389. code_region_end, vm_manager.GetHeapRegionEndAddress(),
  390. &MemoryWriteImpl, &MemoryReadImpl);
  391. }
  392. }
  393. CheatEngine::~CheatEngine() {
  394. core_timing.UnscheduleEvent(event, 0);
  395. }
  396. void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
  397. for (auto& list : cheats) {
  398. list.Execute();
  399. }
  400. core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
  401. }
  402. } // namespace FileSys