diff options
Diffstat (limited to 'src/core/file_sys/cheat_engine.cpp')
-rw-r--r-- | src/core/file_sys/cheat_engine.cpp | 492 |
1 files changed, 0 insertions, 492 deletions
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp deleted file mode 100644 index b06c2f20a..000000000 --- a/src/core/file_sys/cheat_engine.cpp +++ /dev/null @@ -1,492 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <locale> -#include "common/hex_util.h" -#include "common/microprofile.h" -#include "common/swap.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/core_timing_util.h" -#include "core/file_sys/cheat_engine.h" -#include "core/hle/kernel/process.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" -#include "core/hle/service/sm/sm.h" - -namespace FileSys { - -constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); -constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; - -u64 Cheat::Address() const { - u64 out; - std::memcpy(&out, raw.data(), sizeof(u64)); - return Common::swap64(out) & 0xFFFFFFFFFF; -} - -u64 Cheat::ValueWidth(u64 offset) const { - return Value(offset, width); -} - -u64 Cheat::Value(u64 offset, u64 width) const { - u64 out; - std::memcpy(&out, raw.data() + offset, sizeof(u64)); - out = Common::swap64(out); - if (width == 8) - return out; - return out & ((1ull << (width * CHAR_BIT)) - 1); -} - -u32 Cheat::KeypadValue() const { - u32 out; - std::memcpy(&out, raw.data(), sizeof(u32)); - return Common::swap32(out) & 0x0FFFFFFF; -} - -void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, - VAddr heap_end, MemoryWriter writer, MemoryReader reader) { - this->main_region_begin = main_begin; - this->main_region_end = main_end; - this->heap_region_begin = heap_begin; - this->heap_region_end = heap_end; - this->writer = writer; - this->reader = reader; -} - -MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); - -void CheatList::Execute() { - MICROPROFILE_SCOPE(Cheat_Engine); - - std::fill(scratch.begin(), scratch.end(), 0); - in_standard = false; - for (std::size_t i = 0; i < master_list.size(); ++i) { - LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first); - current_block = i; - ExecuteBlock(master_list[i].second); - } - - in_standard = true; - for (std::size_t i = 0; i < standard_list.size(); ++i) { - LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first); - current_block = i; - ExecuteBlock(standard_list[i].second); - } -} - -CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard) - : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {} - -bool CheatList::EvaluateConditional(const Cheat& cheat) const { - using ComparisonFunction = bool (*)(u64, u64); - constexpr std::array<ComparisonFunction, 6> comparison_functions{ - [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; }, - [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; }, - [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; }, - }; - - if (cheat.type == CodeType::ConditionalInput) { - const auto applet_resource = - system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource(); - if (applet_resource == nullptr) { - LOG_WARNING( - Common_Filesystem, - "Attempted to evaluate input conditional, but applet resource is not initialized!"); - return false; - } - - const auto press_state = - applet_resource - ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) - .GetAndResetPressState(); - return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0; - } - - ASSERT(cheat.type == CodeType::Conditional); - - const auto offset = - cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; - ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6); - auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())]; - const auto addr = cheat.Address() + offset; - - return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8)); -} - -void CheatList::ProcessBlockPairs(const Block& block) { - block_pairs.clear(); - - u64 scope = 0; - std::map<u64, u64> pairs; - - for (std::size_t i = 0; i < block.size(); ++i) { - const auto& cheat = block[i]; - - switch (cheat.type) { - case CodeType::Conditional: - case CodeType::ConditionalInput: - pairs.insert_or_assign(scope, i); - ++scope; - break; - case CodeType::EndConditional: { - --scope; - const auto idx = pairs.at(scope); - block_pairs.insert_or_assign(idx, i); - break; - } - case CodeType::Loop: { - if (cheat.end_of_loop) { - --scope; - const auto idx = pairs.at(scope); - block_pairs.insert_or_assign(idx, i); - } else { - pairs.insert_or_assign(scope, i); - ++scope; - } - break; - } - } - } -} - -void CheatList::WriteImmediate(const Cheat& cheat) { - const auto offset = - cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; - const auto& register_3 = scratch.at(cheat.register_3); - - const auto addr = cheat.Address() + offset + register_3; - LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr, - cheat.Value(8, cheat.width)); - writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8)); -} - -void CheatList::BeginConditional(const Cheat& cheat) { - if (EvaluateConditional(cheat)) { - return; - } - - const auto iter = block_pairs.find(current_index); - ASSERT(iter != block_pairs.end()); - current_index = iter->second - 1; -} - -void CheatList::EndConditional(const Cheat& cheat) { - LOG_DEBUG(Common_Filesystem, "Ending conditional block."); -} - -void CheatList::Loop(const Cheat& cheat) { - if (cheat.end_of_loop.Value()) - ASSERT(!cheat.end_of_loop.Value()); - - auto& register_3 = scratch.at(cheat.register_3); - const auto iter = block_pairs.find(current_index); - ASSERT(iter != block_pairs.end()); - ASSERT(iter->first < iter->second); - - const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32))); - for (s32 i = initial_value; i >= 0; --i) { - register_3 = static_cast<u64>(i); - for (std::size_t c = iter->first + 1; c < iter->second; ++c) { - current_index = c; - ExecuteSingleCheat( - (in_standard ? standard_list : master_list)[current_block].second[c]); - } - } - - current_index = iter->second; -} - -void CheatList::LoadImmediate(const Cheat& cheat) { - auto& register_3 = scratch.at(cheat.register_3); - - LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3, - cheat.Value(4, 8)); - register_3 = cheat.Value(4, 8); -} - -void CheatList::LoadIndexed(const Cheat& cheat) { - const auto offset = - cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; - auto& register_3 = scratch.at(cheat.register_3); - - const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address(); - LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}", - cheat.register_3, addr); - register_3 = reader(cheat.width, SanitizeAddress(addr)); -} - -void CheatList::StoreIndexed(const Cheat& cheat) { - const auto& register_3 = scratch.at(cheat.register_3); - - const auto addr = - register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0); - LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", - cheat.Value(4, cheat.width), addr); - writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4)); -} - -void CheatList::RegisterArithmetic(const Cheat& cheat) { - using ArithmeticFunction = u64 (*)(u64, u64); - constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{ - [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; }, - [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; }, - [](u64 a, u64 b) { return a >> b; }, - }; - - using ArithmeticOverflowCheck = bool (*)(u64, u64); - constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{ - [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b - [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b - [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b - [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b - [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b - }; - - static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks), - "Missing or have extra arithmetic overflow checks compared to functions!"); - - auto& register_3 = scratch.at(cheat.register_3); - - ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5); - auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())]; - auto* overflow_function = - arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())]; - LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}", - cheat.register_3, cheat.ValueWidth(4)); - - if (overflow_function(register_3, cheat.ValueWidth(4))) { - LOG_WARNING(Common_Filesystem, - "overflow will occur when performing arithmetic operation={:02X} with operands " - "a={:016X}, b={:016X}!", - static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4)); - } - - register_3 = function(register_3, cheat.ValueWidth(4)); -} - -void CheatList::BeginConditionalInput(const Cheat& cheat) { - if (EvaluateConditional(cheat)) - return; - - const auto iter = block_pairs.find(current_index); - ASSERT(iter != block_pairs.end()); - current_index = iter->second - 1; -} - -VAddr CheatList::SanitizeAddress(VAddr in) const { - if ((in < main_region_begin || in >= main_region_end) && - (in < heap_region_begin || in >= heap_region_end)) { - LOG_ERROR(Common_Filesystem, - "Cheat attempting to access memory at invalid address={:016X}, if this persists, " - "the cheat may be incorrect. However, this may be normal early in execution if " - "the game has not properly set up yet.", - in); - return 0; ///< Invalid addresses will hard crash - } - - return in; -} - -void CheatList::ExecuteSingleCheat(const Cheat& cheat) { - using CheatOperationFunction = void (CheatList::*)(const Cheat&); - constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{ - &CheatList::WriteImmediate, &CheatList::BeginConditional, - &CheatList::EndConditional, &CheatList::Loop, - &CheatList::LoadImmediate, &CheatList::LoadIndexed, - &CheatList::StoreIndexed, &CheatList::RegisterArithmetic, - &CheatList::BeginConditionalInput, - }; - - const auto index = static_cast<u8>(cheat.type.Value()); - ASSERT(index < sizeof(cheat_operation_functions)); - const auto op = cheat_operation_functions[index]; - (this->*op)(cheat); -} - -void CheatList::ExecuteBlock(const Block& block) { - encountered_loops.clear(); - - ProcessBlockPairs(block); - for (std::size_t i = 0; i < block.size(); ++i) { - current_index = i; - ExecuteSingleCheat(block[i]); - i = current_index; - } -} - -CheatParser::~CheatParser() = default; - -CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master, - CheatList::ProgramSegment standard) const { - return {system, std::move(master), std::move(standard)}; -} - -TextCheatParser::~TextCheatParser() = default; - -CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const { - std::stringstream ss; - ss.write(reinterpret_cast<const char*>(data.data()), data.size()); - - std::vector<std::string> lines; - std::string stream_line; - while (std::getline(ss, stream_line)) { - // Remove a trailing \r - if (!stream_line.empty() && stream_line.back() == '\r') - stream_line.pop_back(); - lines.push_back(std::move(stream_line)); - } - - CheatList::ProgramSegment master_list; - CheatList::ProgramSegment standard_list; - - for (std::size_t i = 0; i < lines.size(); ++i) { - auto line = lines[i]; - - if (!line.empty() && (line[0] == '[' || line[0] == '{')) { - const auto master = line[0] == '{'; - const auto begin = master ? line.find('{') : line.find('['); - const auto end = master ? line.rfind('}') : line.rfind(']'); - - ASSERT(begin != std::string::npos && end != std::string::npos); - - const std::string patch_name{line.begin() + begin + 1, line.begin() + end}; - CheatList::Block block{}; - - while (i < lines.size() - 1) { - line = lines[++i]; - if (!line.empty() && (line[0] == '[' || line[0] == '{')) { - --i; - break; - } - - if (line.size() < 8) - continue; - - Cheat out{}; - out.raw = ParseSingleLineCheat(line); - block.push_back(out); - } - - (master ? master_list : standard_list).emplace_back(patch_name, block); - } - } - - return MakeCheatList(system, master_list, standard_list); -} - -std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const { - std::array<u8, 16> out{}; - - if (line.size() < 8) - return out; - - const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8}); - std::memcpy(out.data(), word1.data(), sizeof(u32)); - - if (line.size() < 17 || line[8] != ' ') - return out; - - const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8}); - std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32)); - - if (line.size() < 26 || line[17] != ' ') { - // Perform shifting in case value is truncated early. - const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); - if (type == CodeType::Loop || type == CodeType::LoadImmediate || - type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) { - std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32)); - std::memset(out.data() + 4, 0, sizeof(u32)); - } - - return out; - } - - const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8}); - std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32)); - - if (line.size() < 35 || line[26] != ' ') { - // Perform shifting in case value is truncated early. - const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); - if (type == CodeType::WriteImmediate || type == CodeType::Conditional) { - std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32)); - std::memset(out.data() + 8, 0, sizeof(u32)); - } - - return out; - } - - const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8}); - std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32)); - - return out; -} - -namespace { -u64 MemoryReadImpl(u32 width, VAddr addr) { - switch (width) { - case 1: - return Memory::Read8(addr); - case 2: - return Memory::Read16(addr); - case 4: - return Memory::Read32(addr); - case 8: - return Memory::Read64(addr); - default: - UNREACHABLE(); - return 0; - } -} - -void MemoryWriteImpl(u32 width, VAddr addr, u64 value) { - switch (width) { - case 1: - Memory::Write8(addr, static_cast<u8>(value)); - break; - case 2: - Memory::Write16(addr, static_cast<u16>(value)); - break; - case 4: - Memory::Write32(addr, static_cast<u32>(value)); - break; - case 8: - Memory::Write64(addr, value); - break; - default: - UNREACHABLE(); - } -} -} // Anonymous namespace - -CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_, - const std::string& build_id, VAddr code_region_start, - VAddr code_region_end) - : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} { - event = core_timing.RegisterEvent( - "CheatEngine::FrameCallback::" + build_id, - [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); - core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); - - const auto& vm_manager = system.CurrentProcess()->VMManager(); - for (auto& list : this->cheats) { - list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(), - code_region_end, vm_manager.GetHeapRegionEndAddress(), - &MemoryWriteImpl, &MemoryReadImpl); - } -} - -CheatEngine::~CheatEngine() { - core_timing.UnscheduleEvent(event, 0); -} - -void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { - for (auto& list : cheats) { - list.Execute(); - } - - core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); -} - -} // namespace FileSys |