cheat_engine.cpp 17 KB

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