diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 2517e3f22..4ce02109b 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -30,20 +30,34 @@ std::string toHex(u64 value, size_t byteSize) { return ss.str(); } +static bool isHexSym(const std::string& s) { + return (s.size() >= 2 && (s[0] == '$' || s[0] == '#')); +} + +static bool isHex0x(const std::string& s) { + return (s.size() >= 3 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')); +} + +// possible prefixes == (s == `0x` || s == `#` || s == `$`) will be interpet as hex value, else is +// decimal +static int convertNumBase(const std::string& s) { + return (isHex0x(s) || isHexSym(s)) ? 16 : 10; +} + std::string convertValueToHex(const std::string type, const std::string valueStr) { std::string result; if (type == "byte") { - const u32 value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, convertNumBase(valueStr)); result = toHex(value, 1); } else if (type == "bytes16") { - const u32 value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, convertNumBase(valueStr)); result = toHex(value, 2); } else if (type == "bytes32") { - const u32 value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, convertNumBase(valueStr)); result = toHex(value, 4); } else if (type == "bytes64") { - const u64 value = std::stoull(valueStr, nullptr, 16); + const u64 value = std::stoull(valueStr, nullptr, convertNumBase(valueStr)); result = toHex(value, 8); } else if (type == "float32") { union { @@ -170,9 +184,18 @@ void ApplyPatchesFromXML(std::filesystem::path path) { maskOffsetValue = std::stoi(maskOffsetStr, 0, 10); } - MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, targetStr, - sizeStr, false, littleEndian, patchMask, - maskOffsetValue); + const patchInfo patch = { + .gameSerial = "*", + .modNameStr = currentPatchName, + .offsetStr = address, + .valueStr = patchValue, + .targetStr = targetStr, + .sizeStr = sizeStr, + .littleEndian = littleEndian, + .patchMask = patchMask, + .maskOffset = maskOffsetValue, + }; + MemoryPatcher::PatchMemory(patch); } } } @@ -214,11 +237,9 @@ void OnGameLoaded() { ApplyPendingPatches(); } -void AddPatchToQueue(patchInfo patchToAdd) { +void AddPatchToQueue(const patchInfo& patchToAdd) { if (patches_applied) { - PatchMemory(patchToAdd.modNameStr, patchToAdd.offsetStr, patchToAdd.valueStr, - patchToAdd.targetStr, patchToAdd.sizeStr, patchToAdd.isOffset, - patchToAdd.littleEndian, patchToAdd.patchMask, patchToAdd.maskOffset); + PatchMemory(patchToAdd); return; } pending_patches.push_back(patchToAdd); @@ -232,35 +253,32 @@ void ApplyPendingPatches() { if (currentPatch.gameSerial != "*" && currentPatch.gameSerial != g_game_serial) continue; - PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr, - currentPatch.targetStr, currentPatch.sizeStr, currentPatch.isOffset, - currentPatch.littleEndian, currentPatch.patchMask, currentPatch.maskOffset); + PatchMemory(currentPatch); } pending_patches.clear(); } -void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, - std::string targetStr, std::string sizeStr, bool isOffset, bool littleEndian, - PatchMask patchMask, int maskOffset) { +void PatchMemory(const patchInfo& patch) { // Send a request to modify the process memory. void* cheatAddress = nullptr; - if (patchMask == PatchMask::None) { - if (isOffset) { - cheatAddress = reinterpret_cast(g_eboot_address + std::stoi(offsetStr, 0, 16)); - } else { + if (patch.patchMask == PatchMask::None) { + if (patch.isOffset) { cheatAddress = - reinterpret_cast(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000)); + reinterpret_cast(g_eboot_address + std::stoi(patch.offsetStr, 0, 16)); + } else { + cheatAddress = reinterpret_cast(g_eboot_address + + (std::stoi(patch.offsetStr, 0, 16) - 0x400000)); } } - if (patchMask == PatchMask::Mask) { - cheatAddress = reinterpret_cast(PatternScan(offsetStr) + maskOffset); + if (patch.patchMask == PatchMask::Mask) { + cheatAddress = reinterpret_cast(PatternScan(patch.offsetStr) + patch.maskOffset); } - if (patchMask == PatchMask::Mask_Jump32) { - int jumpSize = std::stoi(sizeStr); + if (patch.patchMask == PatchMask::Mask_Jump32) { + int jumpSize = std::stoi(patch.sizeStr); constexpr int MAX_PATTERN_LENGTH = 256; if (jumpSize < 5) { @@ -273,35 +291,35 @@ void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valu } // Find the base address using "Address" - uintptr_t baseAddress = PatternScan(offsetStr); + uintptr_t baseAddress = PatternScan(patch.offsetStr); if (baseAddress == 0) { - LOG_ERROR(Loader, "PatternScan failed for mask_jump32 with pattern: {}", offsetStr); + LOG_ERROR(Loader, "PatternScan failed for mask_jump32 with pattern: {}", + patch.offsetStr); return; } - uintptr_t patchAddress = baseAddress + maskOffset; + uintptr_t patchAddress = baseAddress + patch.maskOffset; // Fills the original region (jumpSize bytes) with NOPs std::vector nopBytes(jumpSize, 0x90); std::memcpy(reinterpret_cast(patchAddress), nopBytes.data(), nopBytes.size()); // Use "Target" to locate the start of the code cave - uintptr_t jump_target = PatternScan(targetStr); + uintptr_t jump_target = PatternScan(patch.targetStr); if (jump_target == 0) { - LOG_ERROR(Loader, "PatternScan failed to Target with pattern: {}", targetStr); + LOG_ERROR(Loader, "PatternScan failed to Target with pattern: {}", patch.targetStr); return; } // Converts the Value attribute to a byte array (payload) std::vector payload; - for (size_t i = 0; i < valueStr.length(); i += 2) { - - std::string tempStr = valueStr.substr(i, 2); + for (size_t i = 0; i < patch.valueStr.length(); i += 2) { + std::string tempStr = patch.valueStr.substr(i, 2); const char* byteStr = tempStr.c_str(); char* endPtr; unsigned int byteVal = std::strtoul(byteStr, &endPtr, 16); if (endPtr != byteStr + 2) { - LOG_ERROR(Loader, "Invalid byte in Value: {}", valueStr.substr(i, 2)); + LOG_ERROR(Loader, "Invalid byte in Value: {}", patch.valueStr.substr(i, 2)); return; } payload.push_back(static_cast(byteVal)); @@ -333,32 +351,32 @@ void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valu LOG_INFO(Loader, "Applied Patch mask_jump32: {}, PatchAddress: {:#x}, JumpTarget: {:#x}, " "CodeCaveEnd: {:#x}, JumpSize: {}", - modNameStr, patchAddress, jump_target, code_cave_end, jumpSize); + patch.modNameStr, patchAddress, jump_target, code_cave_end, jumpSize); return; } if (cheatAddress == nullptr) { - LOG_ERROR(Loader, "Failed to get address for patch {}", modNameStr); + LOG_ERROR(Loader, "Failed to get address for patch {}", patch.modNameStr); return; } std::vector bytePatch; - for (size_t i = 0; i < valueStr.length(); i += 2) { - unsigned char byte = - static_cast(std::strtol(valueStr.substr(i, 2).c_str(), nullptr, 16)); + for (size_t i = 0; i < patch.valueStr.length(); i += 2) { + unsigned char byte = static_cast( + std::strtol(patch.valueStr.substr(i, 2).c_str(), nullptr, 16)); bytePatch.push_back(byte); } - if (littleEndian) { + if (patch.littleEndian) { std::reverse(bytePatch.begin(), bytePatch.end()); } std::memcpy(cheatAddress, bytePatch.data(), bytePatch.size()); - LOG_INFO(Loader, "Applied patch: {}, Offset: {}, Value: {}", modNameStr, - (uintptr_t)cheatAddress, valueStr); + LOG_INFO(Loader, "Applied patch: {}, Offset: {:#x}, Value: {}", patch.modNameStr, + (uintptr_t)cheatAddress, patch.valueStr); } static std::vector PatternToByte(const std::string& pattern) { diff --git a/src/common/memory_patcher.h b/src/common/memory_patcher.h index 5a67beec5..5306839bf 100644 --- a/src/common/memory_patcher.h +++ b/src/common/memory_patcher.h @@ -26,12 +26,12 @@ enum PatchMask : uint8_t { }; struct patchInfo { - std::string gameSerial; - std::string modNameStr; - std::string offsetStr; - std::string valueStr; - std::string targetStr; - std::string sizeStr; + const std::string& gameSerial; + const std::string& modNameStr; + const std::string& offsetStr; + const std::string& valueStr; + const std::string& targetStr; + const std::string& sizeStr; bool isOffset; bool littleEndian; PatchMask patchMask; @@ -41,11 +41,9 @@ struct patchInfo { std::string convertValueToHex(const std::string type, const std::string valueStr); void OnGameLoaded(); -void AddPatchToQueue(patchInfo patchToAdd); +void AddPatchToQueue(const patchInfo& patchToAdd); -void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, - std::string targetStr, std::string sizeStr, bool isOffset, bool littleEndian, - PatchMask patchMask = PatchMask::None, int maskOffset = 0); +void PatchMemory(const patchInfo& patch); static std::vector PatternToByte(const std::string& pattern); uintptr_t PatternScan(const std::string& signature); diff --git a/src/core/ipc/ipc.cpp b/src/core/ipc/ipc.cpp index 16e901a03..c29cb93b5 100644 --- a/src/core/ipc/ipc.cpp +++ b/src/core/ipc/ipc.cpp @@ -124,17 +124,18 @@ void IPC::InputLoop() { } else if (cmd == "START") { start_semaphore.release(); } else if (cmd == "PATCH_MEMORY") { - MemoryPatcher::patchInfo entry; - entry.gameSerial = "*"; - entry.modNameStr = next_str(); - entry.offsetStr = next_str(); - entry.valueStr = next_str(); - entry.targetStr = next_str(); - entry.sizeStr = next_str(); - entry.isOffset = next_u64() != 0; - entry.littleEndian = next_u64() != 0; - entry.patchMask = static_cast(next_u64()); - entry.maskOffset = static_cast(next_u64()); + const MemoryPatcher::patchInfo entry = { + .gameSerial = "*", + .modNameStr = next_str(), + .offsetStr = next_str(), + .valueStr = next_str(), + .targetStr = next_str(), + .sizeStr = next_str(), + .isOffset = next_u64() != 0, + .littleEndian = next_u64() != 0, + .patchMask = static_cast(next_u64()), + .maskOffset = static_cast(next_u64()), + }; MemoryPatcher::AddPatchToQueue(entry); } else if (cmd == "PAUSE") { DebugState.PauseGuestThreads();