mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-01-14 02:45:14 +00:00
Enable patches on cli builds (#897)
Some checks are pending
Reuse / reuse (push) Waiting to run
Clang Format / clang-format (push) Waiting to run
Linux-Qt / build (push) Waiting to run
Linux / build (push) Waiting to run
macOS-Qt / build (push) Waiting to run
macOS / build (push) Waiting to run
Windows-Qt / build (push) Waiting to run
Windows / build (push) Waiting to run
Some checks are pending
Reuse / reuse (push) Waiting to run
Clang Format / clang-format (push) Waiting to run
Linux-Qt / build (push) Waiting to run
Linux / build (push) Waiting to run
macOS-Qt / build (push) Waiting to run
macOS / build (push) Waiting to run
Windows-Qt / build (push) Waiting to run
Windows / build (push) Waiting to run
* patch support for cli * fix mac build * format
This commit is contained in:
parent
de183d3b80
commit
ab201398b2
|
@ -376,6 +376,8 @@ set(COMMON src/common/logging/backend.cpp
|
|||
src/common/version.h
|
||||
src/common/ntapi.h
|
||||
src/common/ntapi.cpp
|
||||
src/common/memory_patcher.h
|
||||
src/common/memory_patcher.cpp
|
||||
src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
)
|
||||
|
@ -628,8 +630,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
|||
src/qt_gui/about_dialog.ui
|
||||
src/qt_gui/cheats_patches.cpp
|
||||
src/qt_gui/cheats_patches.h
|
||||
src/qt_gui/memory_patcher.cpp
|
||||
src/qt_gui/memory_patcher.h
|
||||
src/qt_gui/main_window_ui.h
|
||||
src/qt_gui/main_window.cpp
|
||||
src/qt_gui/main_window.h
|
||||
|
|
|
@ -2,14 +2,20 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <pugixml.hpp>
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QListView>
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
#include <QXmlStreamReader>
|
||||
#endif
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "memory_patcher.h"
|
||||
|
@ -17,78 +23,169 @@
|
|||
namespace MemoryPatcher {
|
||||
|
||||
uintptr_t g_eboot_address;
|
||||
u64 g_eboot_image_size;
|
||||
uint64_t g_eboot_image_size;
|
||||
std::string g_game_serial;
|
||||
std::string patchFile;
|
||||
std::vector<patchInfo> pending_patches;
|
||||
|
||||
QString toHex(unsigned long long value, size_t byteSize) {
|
||||
std::string toHex(unsigned long long value, size_t byteSize) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value;
|
||||
return QString::fromStdString(ss.str());
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
QString convertValueToHex(const QString& type, const QString& valueStr) {
|
||||
QString result;
|
||||
std::string typeStr = type.toStdString();
|
||||
std::string valueStrStd = valueStr.toStdString();
|
||||
std::string convertValueToHex(const std::string type, const std::string valueStr) {
|
||||
std::string result;
|
||||
|
||||
if (typeStr == "byte") {
|
||||
unsigned int value = std::stoul(valueStrStd, nullptr, 16);
|
||||
if (type == "byte") {
|
||||
unsigned int value = std::stoul(valueStr, nullptr, 16);
|
||||
result = toHex(value, 1);
|
||||
} else if (typeStr == "bytes16") {
|
||||
unsigned int value = std::stoul(valueStrStd, nullptr, 16);
|
||||
} else if (type == "bytes16") {
|
||||
unsigned int value = std::stoul(valueStr, nullptr, 16);
|
||||
result = toHex(value, 2);
|
||||
} else if (typeStr == "bytes32") {
|
||||
unsigned long value = std::stoul(valueStrStd, nullptr, 16);
|
||||
} else if (type == "bytes32") {
|
||||
unsigned long value = std::stoul(valueStr, nullptr, 16);
|
||||
result = toHex(value, 4);
|
||||
} else if (typeStr == "bytes64") {
|
||||
unsigned long long value = std::stoull(valueStrStd, nullptr, 16);
|
||||
} else if (type == "bytes64") {
|
||||
unsigned long long value = std::stoull(valueStr, nullptr, 16);
|
||||
result = toHex(value, 8);
|
||||
} else if (typeStr == "float32") {
|
||||
} else if (type == "float32") {
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} floatUnion;
|
||||
floatUnion.f = std::stof(valueStrStd);
|
||||
floatUnion.f = std::stof(valueStr);
|
||||
result = toHex(floatUnion.i, sizeof(floatUnion.i));
|
||||
} else if (typeStr == "float64") {
|
||||
} else if (type == "float64") {
|
||||
union {
|
||||
double d;
|
||||
uint64_t i;
|
||||
} doubleUnion;
|
||||
doubleUnion.d = std::stod(valueStrStd);
|
||||
doubleUnion.d = std::stod(valueStr);
|
||||
result = toHex(doubleUnion.i, sizeof(doubleUnion.i));
|
||||
} else if (typeStr == "utf8") {
|
||||
QByteArray byteArray = QString::fromStdString(valueStrStd).toUtf8();
|
||||
byteArray.append('\0');
|
||||
} else if (type == "utf8") {
|
||||
std::vector<unsigned char> byteArray =
|
||||
std::vector<unsigned char>(valueStr.begin(), valueStr.end());
|
||||
byteArray.push_back('\0');
|
||||
std::stringstream ss;
|
||||
for (unsigned char c : byteArray) {
|
||||
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(c);
|
||||
}
|
||||
result = QString::fromStdString(ss.str());
|
||||
} else if (typeStr == "utf16") {
|
||||
QByteArray byteArray(
|
||||
reinterpret_cast<const char*>(QString::fromStdString(valueStrStd).utf16()),
|
||||
QString::fromStdString(valueStrStd).size() * 2);
|
||||
byteArray.append('\0');
|
||||
byteArray.append('\0');
|
||||
std::stringstream ss;
|
||||
for (unsigned char c : byteArray) {
|
||||
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(c);
|
||||
result = ss.str();
|
||||
} else if (type == "utf16") {
|
||||
std::wstring wide_str(valueStr.size(), L'\0');
|
||||
std::mbstowcs(&wide_str[0], valueStr.c_str(), valueStr.size());
|
||||
wide_str.resize(std::wcslen(wide_str.c_str()));
|
||||
|
||||
std::u16string valueStringU16;
|
||||
|
||||
for (wchar_t wc : wide_str) {
|
||||
if (wc <= 0xFFFF) {
|
||||
valueStringU16.push_back(static_cast<char16_t>(wc));
|
||||
} else {
|
||||
wc -= 0x10000;
|
||||
valueStringU16.push_back(static_cast<char16_t>(0xD800 | (wc >> 10)));
|
||||
valueStringU16.push_back(static_cast<char16_t>(0xDC00 | (wc & 0x3FF)));
|
||||
}
|
||||
}
|
||||
result = QString::fromStdString(ss.str());
|
||||
} else if (typeStr == "bytes") {
|
||||
|
||||
std::vector<unsigned char> byteArray;
|
||||
// convert to little endian
|
||||
for (char16_t ch : valueStringU16) {
|
||||
unsigned char low_byte = static_cast<unsigned char>(ch & 0x00FF);
|
||||
unsigned char high_byte = static_cast<unsigned char>((ch >> 8) & 0x00FF);
|
||||
|
||||
byteArray.push_back(low_byte);
|
||||
byteArray.push_back(high_byte);
|
||||
}
|
||||
byteArray.push_back('\0');
|
||||
byteArray.push_back('\0');
|
||||
std::stringstream ss;
|
||||
|
||||
for (unsigned char ch : byteArray) {
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(ch);
|
||||
}
|
||||
result = ss.str();
|
||||
} else if (type == "bytes") {
|
||||
result = valueStr;
|
||||
} else if (typeStr == "mask" || typeStr == "mask_jump32") {
|
||||
} else if (type == "mask" || type == "mask_jump32") {
|
||||
result = valueStr;
|
||||
} else {
|
||||
LOG_INFO(Loader, "Error applying Patch, unknown type: {}", typeStr);
|
||||
LOG_INFO(Loader, "Error applying Patch, unknown type: {}", type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnGameLoaded() {
|
||||
|
||||
if (!patchFile.empty()) {
|
||||
std::string patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string();
|
||||
|
||||
std::string filePath = patchDir + "/" + patchFile;
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(filePath.c_str());
|
||||
|
||||
if (result) {
|
||||
auto patchXML = doc.child("Patch");
|
||||
for (pugi::xml_node_iterator it = patchXML.children().begin();
|
||||
it != patchXML.children().end(); ++it) {
|
||||
|
||||
if (std::string(it->name()) == "Metadata") {
|
||||
if (std::string(it->attribute("isEnabled").value()) == "true") {
|
||||
auto patchList = it->first_child();
|
||||
|
||||
std::string currentPatchName = it->attribute("Name").value();
|
||||
|
||||
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
|
||||
patchLineIt != patchList.children().end(); ++patchLineIt) {
|
||||
|
||||
std::string type = patchLineIt->attribute("Type").value();
|
||||
std::string address = patchLineIt->attribute("Address").value();
|
||||
std::string patchValue = patchLineIt->attribute("Value").value();
|
||||
std::string maskOffsetStr = patchLineIt->attribute("type").value();
|
||||
|
||||
patchValue = convertValueToHex(type, patchValue);
|
||||
|
||||
bool littleEndian = false;
|
||||
|
||||
if (type == "bytes16") {
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes32") {
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes64") {
|
||||
littleEndian = true;
|
||||
}
|
||||
|
||||
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
|
||||
int maskOffsetValue = 0;
|
||||
|
||||
if (type == "mask") {
|
||||
patchMask = MemoryPatcher::PatchMask::Mask;
|
||||
|
||||
// im not sure if this works, there is no games to test the mask
|
||||
// offset on yet
|
||||
if (!maskOffsetStr.empty())
|
||||
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
|
||||
}
|
||||
|
||||
if (type == "mask_jump32")
|
||||
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
|
||||
|
||||
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, false,
|
||||
littleEndian, patchMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
LOG_ERROR(Loader, "couldnt patch parse xml : {}", result.description());
|
||||
|
||||
ApplyPendingPatches();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
|
||||
QString patchDir =
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string());
|
||||
|
@ -190,7 +287,8 @@ void OnGameLoaded() {
|
|||
QString patchValue = lineObject["Value"].toString();
|
||||
QString maskOffsetStr = lineObject["Offset"].toString();
|
||||
|
||||
patchValue = convertValueToHex(type, patchValue);
|
||||
patchValue = QString::fromStdString(
|
||||
convertValueToHex(type.toStdString(), patchValue.toStdString()));
|
||||
|
||||
bool littleEndian = false;
|
||||
|
||||
|
@ -233,6 +331,7 @@ void OnGameLoaded() {
|
|||
}
|
||||
ApplyPendingPatches();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddPatchToQueue(patchInfo patchToAdd) {
|
|
@ -5,13 +5,13 @@
|
|||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
|
||||
namespace MemoryPatcher {
|
||||
|
||||
extern uintptr_t g_eboot_address;
|
||||
extern u64 g_eboot_image_size;
|
||||
extern uint64_t g_eboot_image_size;
|
||||
extern std::string g_game_serial;
|
||||
extern std::string patchFile;
|
||||
|
||||
enum PatchMask : uint8_t {
|
||||
None,
|
||||
|
@ -32,7 +32,7 @@ struct patchInfo {
|
|||
|
||||
extern std::vector<patchInfo> pending_patches;
|
||||
|
||||
QString convertValueToHex(const QString& type, const QString& valueStr);
|
||||
std::string convertValueToHex(const std::string type, const std::string valueStr);
|
||||
|
||||
void OnGameLoaded();
|
||||
void AddPatchToQueue(patchInfo patchToAdd);
|
|
@ -6,9 +6,7 @@
|
|||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include "qt_gui/memory_patcher.h"
|
||||
#endif
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/aerolib/aerolib.h"
|
||||
#include "core/cpu_patches.h"
|
||||
|
@ -199,7 +197,6 @@ 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);
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
if (MemoryPatcher::g_eboot_address == 0) {
|
||||
if (name == "eboot") {
|
||||
MemoryPatcher::g_eboot_address = base_virtual_addr;
|
||||
|
@ -207,7 +204,6 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
|||
MemoryPatcher::OnGameLoaded();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Module::LoadDynamicInfo() {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include "qt_gui/memory_patcher.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#endif
|
||||
#include "common/ntapi.h"
|
||||
#include "common/path_util.h"
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include "common/memory_patcher.h"
|
||||
#include "emulator.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
@ -10,7 +11,16 @@ int main(int argc, char* argv[]) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::string curArg = argv[i];
|
||||
if (curArg == "-p") {
|
||||
std::string patchFile = argv[i + 1];
|
||||
MemoryPatcher::patchFile = patchFile;
|
||||
}
|
||||
}
|
||||
|
||||
Core::Emulator emulator;
|
||||
emulator.Run(argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
#include <QXmlStreamReader>
|
||||
#include <common/logging/log.h>
|
||||
#include "cheats_patches.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/module.h"
|
||||
#include "qt_gui/memory_patcher.h"
|
||||
|
||||
using namespace Common::FS;
|
||||
|
||||
|
@ -1178,7 +1178,8 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
|
|||
QString patchValue = lineObject["Value"].toString();
|
||||
QString maskOffsetStr = lineObject["Offset"].toString();
|
||||
|
||||
patchValue = MemoryPatcher::convertValueToHex(type, patchValue);
|
||||
patchValue = QString::fromStdString(
|
||||
MemoryPatcher::convertValueToHex(type.toStdString(), patchValue.toStdString()));
|
||||
|
||||
bool littleEndian = false;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "emulator.h"
|
||||
#include "game_install_dialog.h"
|
||||
|
@ -36,6 +37,13 @@ int main(int argc, char* argv[]) {
|
|||
// Check for command line arguments
|
||||
if (has_command_line_argument) {
|
||||
Core::Emulator emulator;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::string curArg = argv[i];
|
||||
if (curArg == "-p") {
|
||||
std::string patchFile = argv[i + 1];
|
||||
MemoryPatcher::patchFile = patchFile;
|
||||
}
|
||||
}
|
||||
emulator.Run(argv[1]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue