diff --git a/CMakeLists.txt b/CMakeLists.txt index 30136128..3781b5be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,8 @@ set(COMMON src/common/logging/backend.cpp src/common/enum.h src/common/io_file.cpp src/common/io_file.h + src/common/memory_patcher.cpp + src/common/memory_patcher.h src/common/error.cpp src/common/error.h src/common/scope_exit.h diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp new file mode 100644 index 00000000..af8b425c --- /dev/null +++ b/src/common/memory_patcher.cpp @@ -0,0 +1,51 @@ +#include "common/logging/log.h" +#include "memory_patcher.h" + +namespace MemoryPatcher { + +uintptr_t g_eboot_address; + +std::vector pending_patches; + +void AddPatchToQueue(patchInfo patchToAdd) { + pending_patches.push_back(patchToAdd); +} + +void ApplyPendingPatches() { + + for (size_t i = 0; i < pending_patches.size(); ++i) { + patchInfo currentPatch = pending_patches[i]; + PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr, + currentPatch.isOffset); + } + + pending_patches.clear(); +} + +void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, + bool isOffset) { + // Send a request to modify the process memory. + void* cheatAddress = nullptr; + + if (isOffset) { + cheatAddress = reinterpret_cast(g_eboot_address + std::stoi(offsetStr, 0, 16)); + } else { + cheatAddress = + reinterpret_cast(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000)); + } + + 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)); + + bytePatch.push_back(byte); + } + std::memcpy(cheatAddress, bytePatch.data(), bytePatch.size()); + + LOG_INFO(Loader, "Applied patch:{}, Offset:{}, Value:{}", modNameStr, (uintptr_t)cheatAddress, + valueStr); +} + +} // namespace MemoryPatcher \ No newline at end of file diff --git a/src/common/memory_patcher.h b/src/common/memory_patcher.h new file mode 100644 index 00000000..f9f76a32 --- /dev/null +++ b/src/common/memory_patcher.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include + +namespace MemoryPatcher { + +extern uintptr_t g_eboot_address; + +struct patchInfo { + std::string modNameStr; + std::string offsetStr; + std::string valueStr; + bool isOffset; +}; + +extern std::vector pending_patches; + +void AddPatchToQueue(patchInfo patchToAdd); +void ApplyPendingPatches(); + +void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, + bool isOffset); + +} // namespace MemoryPatcher \ No newline at end of file diff --git a/src/core/module.cpp b/src/core/module.cpp index 369931ff..2f121244 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -5,6 +5,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/memory_patcher.h" #include "common/string_util.h" #include "core/aerolib/aerolib.h" #include "core/cpu_patches.h" @@ -19,8 +20,6 @@ using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, void* para static u64 LoadOffset = CODE_BASE_OFFSET; static constexpr u64 CODE_BASE_INCR = 0x010000000u; -uintptr_t g_eboot_address; - static u64 GetAlignedSize(const elf_program_header& phdr) { return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) : phdr.p_memsz); @@ -92,12 +91,6 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { LoadOffset += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); - if (g_eboot_address == 0) { - if (name == "eboot") { - g_eboot_address = base_virtual_addr; - } - } - // Initialize trampoline generator. void* trampoline_addr = std::bit_cast(base_virtual_addr + aligned_base_size); Xbyak::CodeGenerator c(TrampolineSize, trampoline_addr); @@ -200,6 +193,13 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { const VAddr entry_addr = base_virtual_addr + elf.GetElfEntry(); LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); + + if (MemoryPatcher::g_eboot_address == 0) { + if (name == "eboot") { + MemoryPatcher::g_eboot_address = base_virtual_addr; + MemoryPatcher::ApplyPendingPatches(); + } + } } void Module::LoadDynamicInfo() { diff --git a/src/core/module.h b/src/core/module.h index c0048855..007501f0 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -243,6 +243,4 @@ public: std::vector rela_bits; }; -extern uintptr_t g_eboot_address; - } // namespace Core diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index bc1b0c84..32eea09f 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -26,8 +26,10 @@ #include #include #include "cheats_patches.h" +#include "common/memory_patcher.h" #include "common/path_util.h" #include "core/module.h" + using namespace Common::FS; CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, @@ -578,6 +580,8 @@ void CheatsPatches::addPatchToLayout(const QString& name, const QString& author, // Hook checkbox hover events patchCheckBox->installEventFilter(this); + + connect(patchCheckBox, &QCheckBox::toggled, [=](bool checked) { applyPatch(name, checked); }); } void CheatsPatches::updateNoteTextEdit(const QString& patchName) { @@ -625,16 +629,6 @@ void CheatsPatches::applyCheat(const QString& modName, bool enabled) { if (!m_cheats.contains(modName)) return; - if (Core::g_eboot_address == 0) { - if (showErrorMessage) { - QMessageBox::warning(this, "Game Not Started", - "Cheat cannot be applied until the game has started."); - showErrorMessage = false; - } - uncheckAllCheatCheckBoxes(); - return; - } - Cheat cheat = m_cheats[modName]; for (const MemoryMod& memoryMod : cheat.memoryMods) { @@ -644,21 +638,45 @@ void CheatsPatches::applyCheat(const QString& modName, bool enabled) { std::string offsetStr = memoryMod.offset.toStdString(); std::string valueStr = value.toStdString(); - LOG_INFO(Loader, "Cheat applied:{}, Offset:{}, Value:{}", modNameStr, offsetStr, valueStr); + if (MemoryPatcher::g_eboot_address == 0) { + MemoryPatcher::patchInfo addingPatch; + addingPatch.modNameStr = modNameStr; + addingPatch.offsetStr = offsetStr; + addingPatch.valueStr = valueStr; + addingPatch.isOffset = true; - // Send a request to modify the process memory. - void* cheatAddress = - reinterpret_cast(Core::g_eboot_address + std::stoi(offsetStr, 0, 16)); - - 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)); - - bytePatch.push_back(byte); + MemoryPatcher::AddPatchToQueue(addingPatch); + continue; + } + + MemoryPatcher::PatchMemory(modNameStr, offsetStr, valueStr, true); + } +} + +void CheatsPatches::applyPatch(const QString& patchName, bool enabled) { + if (m_patchInfos.contains(patchName)) { + const PatchInfo& patchInfo = m_patchInfos[patchName]; + + foreach (const QJsonValue& value, patchInfo.linesArray) { + QJsonObject lineObject = value.toObject(); + QString type = lineObject["Type"].toString(); + QString address = lineObject["Address"].toString(); + QString patchValue = lineObject["Value"].toString(); + + if (MemoryPatcher::g_eboot_address == 0) { + MemoryPatcher::patchInfo addingPatch; + addingPatch.modNameStr = patchName.toStdString(); + addingPatch.offsetStr = address.toStdString(); + addingPatch.valueStr = patchValue.toStdString(); + addingPatch.isOffset = false; + + MemoryPatcher::AddPatchToQueue(addingPatch); + continue; + } + + MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(), + patchValue.toStdString(), false); } - std::memcpy(cheatAddress, bytePatch.data(), bytePatch.size()); } } diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h index d9cb7227..ad0c0e91 100644 --- a/src/qt_gui/cheats_patches.h +++ b/src/qt_gui/cheats_patches.h @@ -50,6 +50,7 @@ private: void createFilesJson(); void uncheckAllCheatCheckBoxes(); void applyCheat(const QString& modName, bool enabled); + void applyPatch(const QString& patchName, bool enabled); // Event Filtering bool eventFilter(QObject* obj, QEvent* event);