Additional dead code cleanup. (#63)

This commit is contained in:
squidbus
2025-09-23 21:46:29 -07:00
committed by GitHub
parent 8bbe34beae
commit 4c20045a50
20 changed files with 102 additions and 1384 deletions

View File

@@ -277,7 +277,6 @@ set(COMMON src/common/logging/backend.cpp
src/common/aes.h src/common/aes.h
src/common/assert.cpp src/common/assert.cpp
src/common/assert.h src/common/assert.h
src/common/bit_field.h
src/common/bounded_threadsafe_queue.h src/common/bounded_threadsafe_queue.h
src/common/config.cpp src/common/config.cpp
src/common/config.h src/common/config.h
@@ -287,7 +286,6 @@ set(COMMON src/common/logging/backend.cpp
src/common/io_file.h src/common/io_file.h
src/common/path_util.cpp src/common/path_util.cpp
src/common/path_util.h src/common/path_util.h
src/common/singleton.h
src/common/types.h src/common/types.h
src/common/memory_patcher.h src/common/memory_patcher.h
src/common/memory_patcher.cpp src/common/memory_patcher.cpp
@@ -295,19 +293,16 @@ set(COMMON src/common/logging/backend.cpp
src/common/scm_rev.h src/common/scm_rev.h
) )
set(CORE src/core/file_format/psf.cpp set(CORE src/core/file_format/elf.cpp
src/core/file_format/elf.h
src/core/file_format/psf.cpp
src/core/file_format/psf.h src/core/file_format/psf.h
src/core/file_format/trp.cpp src/core/file_format/trp.cpp
src/core/file_format/trp.h src/core/file_format/trp.h
src/core/loader/elf.cpp
src/core/loader/elf.h
src/core/memory_patcher.cpp
src/core/memory_patcher.h
) )
set(INPUT src/input/controller.cpp set(INPUT src/input/controller.cpp
src/input/controller.h src/input/controller.h
src/input/input_handler.h
) )
qt_add_resources(RESOURCE_FILES src/shadps4.qrc) qt_add_resources(RESOURCE_FILES src/shadps4.qrc)

View File

@@ -1,11 +0,0 @@
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,167 +0,0 @@
// SPDX-FileCopyrightText: 2014 Tony Wasserka
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
// SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <limits>
#include <type_traits>
/*
* Abstract bitfield class
*
* Allows endianness-independent access to individual bitfields within some raw
* integer value. The assembly generated by this class is identical to the
* usage of raw bitfields, so it's a perfectly fine replacement.
*
* For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
* raw value, Y is the length in bits of the bitfield. Z is an integer type
* which determines the sign of the bitfield. Z must have the same size as the
* raw integer.
*
*
* General usage:
*
* Create a new union with the raw integer value as a member.
* Then for each bitfield you want to expose, add a BitField member
* in the union. The template parameters are the bit offset and the number
* of desired bits.
*
* Changes in the bitfield members will then get reflected in the raw integer
* value and vice-versa.
*
*
* Sample usage:
*
* union SomeRegister
* {
* u32 hex;
*
* BitField<0,7,u32> first_seven_bits; // unsigned
* BitField<7,8,u32> next_eight_bits; // unsigned
* BitField<3,15,s32> some_signed_fields; // signed
* };
*
* This is equivalent to the little-endian specific code:
*
* union SomeRegister
* {
* u32 hex;
*
* struct
* {
* u32 first_seven_bits : 7;
* u32 next_eight_bits : 8;
* };
* struct
* {
* u32 : 3; // padding
* s32 some_signed_fields : 15;
* };
* };
*
*
* Caveats:
*
* 1)
* BitField provides automatic casting from and to the storage type where
* appropriate. However, when using non-typesafe functions like printf, an
* explicit cast must be performed on the BitField object to make sure it gets
* passed correctly, e.g.:
* printf("Value: %d", (s32)some_register.some_signed_fields);
*
* 2)
* Not really a caveat, but potentially irritating: This class is used in some
* packed structures that do not guarantee proper alignment. Therefore we have
* to use #pragma pack here not to pack the members of the class, but instead
* to break GCC's assumption that the members of the class are aligned on
* sizeof(StorageType).
*/
#pragma pack(1)
template <std::size_t Position, std::size_t Bits, typename T>
struct BitField {
using Type = T;
// UnderlyingType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
std::enable_if<true, T>>::type;
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
using StorageType = std::make_unsigned_t<UnderlyingType>;
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
static constexpr std::size_t bits = Bits;
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
/**
* Formats a value by masking and shifting it according to the field parameters. A value
* containing several bitfields can be assembled by formatting each of their values and ORing
* the results together.
*/
[[nodiscard]] static constexpr StorageType FormatValue(const T& value) {
return (static_cast<StorageType>(value) << position) & mask;
}
/**
* Extracts a value from the passed storage. In most situations prefer use the member functions
* (such as Value() or operator T), but this can be used to extract a value from a bitfield
* union in a constexpr context.
*/
[[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
shift);
} else {
return static_cast<T>((storage & mask) >> position);
}
}
// This constructor and assignment operator might be considered ambiguous:
// Would they initialize the storage or just the bitfield?
// Hence, delete them. Use the Assign method to set bitfield values!
BitField(T val) = delete;
BitField& operator=(T val) = delete;
constexpr BitField() noexcept = default;
constexpr BitField(const BitField&) noexcept = default;
constexpr BitField& operator=(const BitField&) noexcept = default;
constexpr BitField(BitField&&) noexcept = default;
constexpr BitField& operator=(BitField&&) noexcept = default;
[[nodiscard]] constexpr operator T() const {
return Value();
}
constexpr void Assign(const T& value) {
storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
}
[[nodiscard]] constexpr T Value() const {
return ExtractValue(storage);
}
[[nodiscard]] constexpr explicit operator bool() const {
return Value() != 0;
}
private:
StorageType storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
// And, you know, just in case people specify something stupid like bits=position=0x80000000
static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits");
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
};
#pragma pack()

View File

@@ -10,26 +10,17 @@
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QListView>
#include <QMessageBox> #include <QMessageBox>
#include <QString> #include <QString>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <pugixml.hpp> #include <pugixml.hpp>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "common/singleton.h"
#include "core/file_format/psf.h" #include "core/file_format/psf.h"
#include "memory_patcher.h" #include "memory_patcher.h"
namespace MemoryPatcher { namespace MemoryPatcher {
EXPORT uintptr_t g_eboot_address;
uint64_t g_eboot_image_size;
std::string g_game_serial;
std::string patchFile;
bool patches_applied = false;
std::vector<patchInfo> pending_patches;
std::string toHex(u64 value, size_t byteSize) { std::string toHex(u64 value, size_t byteSize) {
std::stringstream ss; std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value; ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value;
@@ -118,93 +109,9 @@ std::string convertValueToHex(const std::string type, const std::string valueStr
return result; return result;
} }
void ApplyPendingPatches(); std::vector<PendingPatch> readPatches(std::string gameSerial, std::string appVersion) {
std::vector<PendingPatch> pending;
void OnGameLoaded() {
if (!patchFile.empty()) {
std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
auto filePath = (patchDir / patchFile).native();
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(filePath.c_str());
auto* param_sfo = Common::Singleton<PSF>::Instance();
auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
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") {
std::string currentPatchName = it->attribute("Name").value();
std::string metadataAppVer = it->attribute("AppVer").value();
bool versionMatches = metadataAppVer == app_version;
auto patchList = it->first_child();
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
patchLineIt != patchList.children().end(); ++patchLineIt) {
std::string type = patchLineIt->attribute("Type").value();
if (!versionMatches && type != "mask" && type != "mask_jump32")
continue;
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("Offset").value();
std::string targetStr = "";
std::string sizeStr = "";
if (type == "mask_jump32") {
targetStr = patchLineIt->attribute("Target").value();
sizeStr = patchLineIt->attribute("Size").value();
} else {
patchValue = convertValueToHex(type, patchValue);
}
bool littleEndian = false;
if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
littleEndian = true;
}
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask")
patchMask = MemoryPatcher::PatchMask::Mask;
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") &&
!maskOffsetStr.empty()) {
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
}
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue,
targetStr, sizeStr, false, littleEndian,
patchMask, maskOffsetValue);
}
}
}
}
}
} else {
LOG_ERROR(Loader, "couldnt patch parse xml : {}", result.description());
}
}
ApplyPendingPatches();
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
QString patchDir; QString patchDir;
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QDir dir(patchDir); QDir dir(patchDir);
@@ -219,20 +126,18 @@ void OnGameLoaded() {
folder.toStdString()); folder.toStdString());
continue; continue;
} }
const QByteArray jsonData = jsonFile.readAll();
QByteArray jsonData = jsonFile.readAll();
jsonFile.close(); jsonFile.close();
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
QJsonObject jsonObject = jsonDoc.object(); const QJsonObject jsonObject = jsonDoc.object();
QString selectedFileName; QString selectedFileName;
QString serial = QString::fromStdString(g_game_serial); const QString serial = QString::fromStdString(gameSerial);
for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) { for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
QString filePath = it.key(); const QString filePath = it.key();
QJsonArray idsArray = it.value().toArray(); const QJsonArray idsArray = it.value().toArray();
if (idsArray.contains(QJsonValue(serial))) { if (idsArray.contains(QJsonValue(serial))) {
selectedFileName = filePath; selectedFileName = filePath;
break; break;
@@ -245,321 +150,105 @@ void OnGameLoaded() {
continue; continue;
} }
QString filePath = patchDir + "/" + folder + "/" + selectedFileName; const QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
QFile file(filePath); QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_ERROR(Loader, "Unable to open the file for reading."); LOG_ERROR(Loader, "Unable to open the file for reading.");
continue; continue;
} }
const QByteArray xmlData = file.readAll();
QByteArray xmlData = file.readAll();
file.close(); file.close();
QString newXmlData;
QXmlStreamReader xmlReader(xmlData); QXmlStreamReader xmlReader(xmlData);
bool isEnabled = false; bool isEnabled = false;
std::string currentPatchName; std::string currentPatchName;
auto* param_sfo = Common::Singleton<PSF>::Instance();
auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
bool versionMatches = true; bool versionMatches = true;
while (!xmlReader.atEnd()) { while (!xmlReader.atEnd()) {
xmlReader.readNext(); xmlReader.readNext();
if (xmlReader.isStartElement()) { if (!xmlReader.isStartElement()) {
QJsonArray patchLines; continue;
if (xmlReader.name() == QStringLiteral("Metadata")) { }
QString name = xmlReader.attributes().value("Name").toString();
currentPatchName = name.toStdString();
QString appVer = xmlReader.attributes().value("AppVer").toString(); if (xmlReader.name() == QStringLiteral("Metadata")) {
QString name = xmlReader.attributes().value("Name").toString();
currentPatchName = name.toStdString();
// Check and update the isEnabled attribute const QString appVer = xmlReader.attributes().value("AppVer").toString();
isEnabled = false;
for (const QXmlStreamAttribute& attr : xmlReader.attributes()) { isEnabled = false;
if (attr.name() == QStringLiteral("isEnabled")) { for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
isEnabled = (attr.value().toString() == "true"); if (attr.name() == QStringLiteral("isEnabled")) {
} isEnabled = (attr.value().toString() == "true");
} }
versionMatches = (appVer.toStdString() == app_version); }
versionMatches = (appVer.toStdString() == appVersion);
} else if (xmlReader.name() == QStringLiteral("PatchList")) {
while (!xmlReader.atEnd() &&
!(xmlReader.tokenType() == QXmlStreamReader::EndElement &&
xmlReader.name() == QStringLiteral("PatchList"))) {
} else if (xmlReader.name() == QStringLiteral("PatchList")) { xmlReader.readNext();
QJsonArray linesArray;
while (!xmlReader.atEnd() && if (xmlReader.tokenType() != QXmlStreamReader::StartElement ||
!(xmlReader.tokenType() == QXmlStreamReader::EndElement && xmlReader.name() != QStringLiteral("Line")) {
xmlReader.name() == QStringLiteral("PatchList"))) { continue;
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::StartElement &&
xmlReader.name() == QStringLiteral("Line")) {
QXmlStreamAttributes attributes = xmlReader.attributes();
QJsonObject lineObject;
lineObject["Type"] = attributes.value("Type").toString();
lineObject["Address"] = attributes.value("Address").toString();
lineObject["Value"] = attributes.value("Value").toString();
lineObject["Offset"] = attributes.value("Offset").toString();
if (lineObject["Type"].toString() == "mask_jump32") {
lineObject["Target"] = attributes.value("Target").toString();
lineObject["Size"] = attributes.value("Size").toString();
}
linesArray.append(lineObject);
}
} }
patchLines = linesArray; const QXmlStreamAttributes a = xmlReader.attributes();
if (isEnabled) { const QString type = a.value("Type").toString();
foreach (const QJsonValue& value, patchLines) { const QString addr = a.value("Address").toString();
QJsonObject lineObject = value.toObject(); QString val = a.value("Value").toString();
QString type = lineObject["Type"].toString(); const QString offStr = a.value("Offset").toString();
const QString tgt =
(type == "mask_jump32") ? a.value("Target").toString() : QString{};
const QString sz =
(type == "mask_jump32") ? a.value("Size").toString() : QString{};
if ((type != "mask" && type != "mask_jump32") && !versionMatches) if (!isEnabled) {
continue; continue;
QString address = lineObject["Address"].toString();
QString patchValue = lineObject["Value"].toString();
QString maskOffsetStr = lineObject["Offset"].toString();
QString targetStr;
QString sizeStr;
if (type == "mask_jump32") {
targetStr = lineObject["Target"].toString();
sizeStr = lineObject["Size"].toString();
} else {
patchValue = QString::fromStdString(convertValueToHex(
type.toStdString(), patchValue.toStdString()));
}
bool littleEndian = false;
if (type == "bytes16" || type == "bytes32" || type == "bytes64")
littleEndian = true;
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask")
patchMask = MemoryPatcher::PatchMask::Mask;
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") &&
!maskOffsetStr.toStdString().empty()) {
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
}
MemoryPatcher::PatchMemory(
currentPatchName, address.toStdString(), patchValue.toStdString(),
targetStr.toStdString(), sizeStr.toStdString(), false, littleEndian,
patchMask, maskOffsetValue);
}
} }
if ((type != "mask" && type != "mask_jump32") && !versionMatches) {
continue;
}
PendingPatch pp;
pp.modName = currentPatchName;
pp.address = addr.toStdString();
if (type == "mask" || type == "mask_jump32") {
if (!offStr.toStdString().empty()) {
pp.maskOffset = std::stoi(offStr.toStdString(), nullptr, 10);
}
pp.mask = (type == "mask") ? MemoryPatcher::PatchMask::Mask
: MemoryPatcher::PatchMask::Mask_Jump32;
pp.value = val.toStdString();
pp.target = tgt.toStdString();
pp.size = sz.toStdString();
} else {
pp.value = convertValueToHex(type.toStdString(), val.toStdString());
pp.target.clear();
pp.size.clear();
pp.mask = MemoryPatcher::PatchMask::None;
pp.maskOffset = 0;
}
pp.littleEndian = (type == "bytes16" || type == "bytes32" || type == "bytes64");
pending.emplace_back(std::move(pp));
} }
} }
} }
if (xmlReader.hasError()) { if (xmlReader.hasError()) {
LOG_ERROR(Loader, "Failed to parse XML for {}", g_game_serial); LOG_ERROR(Loader, "Failed to parse XML for {}", gameSerial);
} else { } else {
LOG_INFO(Loader, "Patches loaded successfully, repository: {}", folder.toStdString()); LOG_INFO(Loader, "Patches parsed successfully, repository: {}", folder.toStdString());
}
ApplyPendingPatches();
}
}
void AddPatchToQueue(patchInfo patchToAdd) {
if (patches_applied) {
PatchMemory(patchToAdd.modNameStr, patchToAdd.offsetStr, patchToAdd.valueStr,
patchToAdd.targetStr, patchToAdd.sizeStr, patchToAdd.isOffset,
patchToAdd.littleEndian, patchToAdd.patchMask, patchToAdd.maskOffset);
return;
}
pending_patches.push_back(patchToAdd);
}
void ApplyPendingPatches() {
patches_applied = true;
for (size_t i = 0; i < pending_patches.size(); ++i) {
const patchInfo& currentPatch = pending_patches[i];
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);
}
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) {
// Send a request to modify the process memory.
void* cheatAddress = nullptr;
if (patchMask == PatchMask::None) {
if (isOffset) {
cheatAddress = reinterpret_cast<void*>(g_eboot_address + std::stoi(offsetStr, 0, 16));
} else {
cheatAddress =
reinterpret_cast<void*>(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000));
} }
} }
return pending;
if (patchMask == PatchMask::Mask) {
cheatAddress = reinterpret_cast<void*>(PatternScan(offsetStr) + maskOffset);
}
if (patchMask == PatchMask::Mask_Jump32) {
int jumpSize = std::stoi(sizeStr);
constexpr int MAX_PATTERN_LENGTH = 256;
if (jumpSize < 5) {
LOG_ERROR(Loader, "Jump size must be at least 5 bytes");
return;
}
if (jumpSize > MAX_PATTERN_LENGTH) {
LOG_ERROR(Loader, "Jump size must be no more than {} bytes.", MAX_PATTERN_LENGTH);
return;
}
// Find the base address using "Address"
uintptr_t baseAddress = PatternScan(offsetStr);
if (baseAddress == 0) {
LOG_ERROR(Loader, "PatternScan failed for mask_jump32 with pattern: {}", offsetStr);
return;
}
uintptr_t patchAddress = baseAddress + maskOffset;
// Fills the original region (jumpSize bytes) with NOPs
std::vector<u8> nopBytes(jumpSize, 0x90);
std::memcpy(reinterpret_cast<void*>(patchAddress), nopBytes.data(), nopBytes.size());
// Use "Target" to locate the start of the code cave
uintptr_t jump_target = PatternScan(targetStr);
if (jump_target == 0) {
LOG_ERROR(Loader, "PatternScan failed to Target with pattern: {}", targetStr);
return;
}
// Converts the Value attribute to a byte array (payload)
std::vector<u8> payload;
for (size_t i = 0; i < valueStr.length(); i += 2) {
std::string tempStr = 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));
return;
}
payload.push_back(static_cast<u8>(byteVal));
}
// Calculates the end of the code cave (where the return jump will be inserted)
uintptr_t code_cave_end = jump_target + payload.size();
// Write the payload to the code cave, from jump_target
std::memcpy(reinterpret_cast<void*>(jump_target), payload.data(), payload.size());
// Inserts the initial jump in the original region to divert to the code cave
u8 jumpInstruction[5];
jumpInstruction[0] = 0xE9;
s32 relJump = static_cast<s32>(jump_target - patchAddress - 5);
std::memcpy(&jumpInstruction[1], &relJump, sizeof(relJump));
std::memcpy(reinterpret_cast<void*>(patchAddress), jumpInstruction,
sizeof(jumpInstruction));
// Inserts jump back at the end of the code cave to resume execution after patching
u8 jumpBack[5];
jumpBack[0] = 0xE9;
// Calculates the relative offset to return to the instruction immediately following the
// overwritten region
s32 target_return = static_cast<s32>((patchAddress + jumpSize) - (code_cave_end + 5));
std::memcpy(&jumpBack[1], &target_return, sizeof(target_return));
std::memcpy(reinterpret_cast<void*>(code_cave_end), jumpBack, sizeof(jumpBack));
LOG_INFO(Loader,
"Applied Patch mask_jump32: {}, PatchAddress: {:#x}, JumpTarget: {:#x}, "
"CodeCaveEnd: {:#x}, JumpSize: {}",
modNameStr, patchAddress, jump_target, code_cave_end, jumpSize);
return;
}
if (cheatAddress == nullptr) {
LOG_ERROR(Loader, "Failed to get address for patch {}", modNameStr);
return;
}
std::vector<unsigned char> bytePatch;
for (size_t i = 0; i < valueStr.length(); i += 2) {
unsigned char byte =
static_cast<unsigned char>(std::strtol(valueStr.substr(i, 2).c_str(), nullptr, 16));
bytePatch.push_back(byte);
}
if (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);
}
static std::vector<int32_t> PatternToByte(const std::string& pattern) {
std::vector<int32_t> bytes;
const char* start = pattern.data();
const char* end = start + pattern.size();
for (const char* current = start; current < end; ++current) {
if (*current == '?') {
++current;
if (*current == '?')
++current;
bytes.push_back(-1);
} else {
bytes.push_back(strtoul(current, const_cast<char**>(&current), 16));
}
}
return bytes;
}
uintptr_t PatternScan(const std::string& signature) {
std::vector<int32_t> patternBytes = PatternToByte(signature);
const auto scanBytes = static_cast<uint8_t*>((void*)g_eboot_address);
const int32_t* sigPtr = patternBytes.data();
const size_t sigSize = patternBytes.size();
uint32_t foundResults = 0;
for (uint32_t i = 0; i < g_eboot_image_size - sigSize; ++i) {
bool found = true;
for (uint32_t j = 0; j < sigSize; ++j) {
if (scanBytes[i + j] != sigPtr[j] && sigPtr[j] != -1) {
found = false;
break;
}
}
if (found) {
foundResults++;
return reinterpret_cast<uintptr_t>(&scanBytes[i]);
}
}
return 0;
} }
} // namespace MemoryPatcher } // namespace MemoryPatcher

View File

@@ -2,52 +2,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <cstring>
#include <string> #include <string>
#include <vector> #include <vector>
#if defined(WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif
namespace MemoryPatcher { namespace MemoryPatcher {
extern EXPORT uintptr_t g_eboot_address;
extern uint64_t g_eboot_image_size;
extern std::string g_game_serial;
extern std::string patchFile;
enum PatchMask : uint8_t { enum PatchMask : uint8_t {
None, None,
Mask, Mask,
Mask_Jump32, Mask_Jump32,
}; };
struct patchInfo { struct PendingPatch {
std::string gameSerial; std::string modName;
std::string modNameStr; std::string address;
std::string offsetStr; std::string value;
std::string valueStr; std::string target;
std::string targetStr; std::string size;
std::string sizeStr; bool littleEndian = false;
bool isOffset; PatchMask mask = PatchMask::None;
bool littleEndian; int maskOffset = 0;
PatchMask patchMask;
int maskOffset;
}; };
std::string convertValueToHex(const std::string type, const std::string valueStr); std::string convertValueToHex(std::string type, std::string valueStr);
void OnGameLoaded(); std::vector<PendingPatch> readPatches(std::string gameSerial, std::string appVersion);
void AddPatchToQueue(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);
static std::vector<int32_t> PatternToByte(const std::string& pattern);
uintptr_t PatternScan(const std::string& signature);
} // namespace MemoryPatcher } // namespace MemoryPatcher

View File

@@ -1,28 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
namespace Common {
template <class T>
class Singleton {
public:
static T* Instance() {
if (!m_instance) {
m_instance = std::make_unique<T>();
}
return m_instance.get();
}
protected:
Singleton();
~Singleton();
private:
static inline std::unique_ptr<T> m_instance{};
};
} // namespace Common

View File

@@ -4,9 +4,7 @@
#include <fmt/core.h> #include <fmt/core.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/loader/elf.h" #include "core/file_format/elf.h"
namespace Core::Loader {
using namespace Common::FS; using namespace Common::FS;
@@ -530,5 +528,3 @@ void Elf::PHeaderDebugDump(const std::filesystem::path& file_name) {
} }
} }
} }
} // namespace Core::Loader

View File

@@ -458,8 +458,6 @@ struct eh_frame_hdr {
uint32_t fde_count; uint32_t fde_count;
}; };
namespace Core::Loader {
class Elf { class Elf {
public: public:
Elf() = default; Elf() = default;
@@ -517,5 +515,3 @@ private:
std::vector<elf_section_header> m_elf_shdr; std::vector<elf_section_header> m_elf_shdr;
elf_program_id_header m_self_id_header{}; elf_program_id_header m_self_id_header{};
}; };
} // namespace Core::Loader

View File

@@ -1,352 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/enum.h"
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Pad {
constexpr int ORBIS_PAD_MAX_TOUCH_NUM = 2;
constexpr int ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE = 12;
constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0;
constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2;
constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16;
enum class OrbisPadDeviceClass {
Invalid = -1,
Standard = 0,
Guitar = 1,
Drum = 2,
DjTurntable = 3,
Dancemat = 4,
Navigation = 5,
SteeringWheel = 6,
Stick = 7,
FightStick = 8,
Gun = 9,
};
struct OrbisPadDeviceClassExtendedInformation {
OrbisPadDeviceClass deviceClass;
u8 reserved[4];
union {
struct {
u8 capability;
u8 reserved1[1];
u16 maxPhysicalWheelAngle;
u8 reserved2[8];
} steeringWheel;
struct {
u8 capability;
u8 quantityOfSelectorSwitch;
u8 reserved[10];
} guitar;
struct {
u8 capability;
u8 reserved[11];
} drum;
struct {
u8 capability;
u8 reserved[11];
} flightStick;
u8 data[12];
} classData;
};
struct OrbisPadDeviceClassData {
OrbisPadDeviceClass deviceClass;
bool bDataValid;
union {
struct {
float steeringWheelAngle;
u16 steeringWheel;
u16 acceleratorPedal;
u16 brakePedal;
u16 clutchPedal;
u16 handBrake;
u8 gear;
u8 reserved[1];
} steeringWheel;
struct {
u8 toneNumber;
u8 whammyBar;
u8 tilt;
u8 fret;
u8 fretSolo;
u8 reserved[11];
} guitar;
struct {
u8 snare;
u8 tom1;
u8 tom2;
u8 floorTom;
u8 hihatCymbal;
u8 rideCymbal;
u8 crashCymbal;
u8 reserved[9];
} drum;
struct {
u16 stickAxisX;
u16 stickAxisY;
u8 stickTwist;
u8 throttle;
u8 trigger;
u8 rudderPedal;
u8 brakePedalLeft;
u8 brakePedalRight;
u8 antennaKnob;
u8 rangeKnob;
u8 reserved[4];
} flightStick;
struct {
u8 dataLen;
u8 reserved[3];
u8 data[ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE];
} others;
} classData;
};
struct OrbisPadAnalogButtons {
u8 l2;
u8 r2;
u8 padding[2];
};
struct OrbisPadAnalogStick {
u8 x;
u8 y;
};
enum class OrbisPadButtonDataOffset : u32 {
None = 0,
L3 = 0x2,
R3 = 0x4,
Options = 0x8,
Up = 0x10,
Right = 0x20,
Down = 0x40,
Left = 0x80,
L2 = 0x100,
R2 = 0x200,
L1 = 0x400,
R1 = 0x800,
Triangle = 0x1000,
Circle = 0x2000,
Cross = 0x4000,
Square = 0x8000,
TouchPad = 0x100000,
Intercepted = 0x80000000,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisPadButtonDataOffset)
struct OrbisFQuaternion {
float x, y, z, w;
};
struct OrbisFVector3 {
float x, y, z;
};
struct OrbisPadTouch {
u16 x;
u16 y;
u8 id;
u8 reserve[3];
};
struct OrbisPadTouchData {
u8 touchNum;
u8 reserve[3];
u32 reserve1;
OrbisPadTouch touch[ORBIS_PAD_MAX_TOUCH_NUM];
};
struct OrbisPadExtensionUnitData {
u32 extensionUnitId;
u8 reserve[1];
u8 dataLength;
u8 data[10];
};
struct OrbisPadData {
OrbisPadButtonDataOffset buttons;
OrbisPadAnalogStick leftStick;
OrbisPadAnalogStick rightStick;
OrbisPadAnalogButtons analogButtons;
OrbisFQuaternion orientation;
OrbisFVector3 acceleration;
OrbisFVector3 angularVelocity;
OrbisPadTouchData touchData;
bool connected;
u64 timestamp;
OrbisPadExtensionUnitData extensionUnitData;
u8 connectedCount;
u8 reserve[2];
u8 deviceUniqueDataLen;
u8 deviceUniqueData[ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE];
};
struct OrbisPadTouchPadInformation {
float pixelDensity;
struct {
u16 x;
u16 y;
} resolution;
};
struct OrbisPadStickInformation {
u8 deadZoneLeft;
u8 deadZoneRight;
};
struct OrbisPadControllerInformation {
OrbisPadTouchPadInformation touchPadInfo;
OrbisPadStickInformation stickInfo;
u8 connectionType;
u8 connectedCount;
bool connected;
OrbisPadDeviceClass deviceClass;
u8 reserve[8];
};
struct OrbisPadExtendedControllerInformation {
OrbisPadControllerInformation base;
u16 padType1;
u16 padType2;
u8 capability;
union {
u8 quantityOfSelectorSwitch;
int maxPhysicalWheelAngle;
u8 data[8];
};
};
struct OrbisPadOpenParam {
u8 reserve[8];
};
struct OrbisPadOpenExtParam {
u16 vendorId;
u16 productId;
u16 productId_2;
u8 reserve[10];
};
struct OrbisPadLightBarParam {
u8 r;
u8 g;
u8 b;
u8 reserve[1];
};
struct OrbisPadVibrationParam {
u8 largeMotor;
u8 smallMotor;
};
int PS4_SYSV_ABI scePadClose(s32 handle);
int PS4_SYSV_ABI scePadConnectPort();
int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation(
s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo);
int PS4_SYSV_ABI scePadDeviceClassParseData(s32 handle, const OrbisPadData* pData,
OrbisPadDeviceClassData* pDeviceClassData);
int PS4_SYSV_ABI scePadDeviceOpen();
int PS4_SYSV_ABI scePadDisableVibration();
int PS4_SYSV_ABI scePadDisconnectDevice();
int PS4_SYSV_ABI scePadDisconnectPort();
int PS4_SYSV_ABI scePadEnableAutoDetect();
int PS4_SYSV_ABI scePadEnableExtensionPort();
int PS4_SYSV_ABI scePadEnableSpecificDeviceClass();
int PS4_SYSV_ABI scePadEnableUsbConnection();
int PS4_SYSV_ABI scePadGetBluetoothAddress();
int PS4_SYSV_ABI scePadGetCapability();
int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo);
int PS4_SYSV_ABI scePadGetDataInternal();
int PS4_SYSV_ABI scePadGetDeviceId();
int PS4_SYSV_ABI scePadGetDeviceInfo();
int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle,
OrbisPadExtendedControllerInformation* pInfo);
int PS4_SYSV_ABI scePadGetExtensionUnitInfo();
int PS4_SYSV_ABI scePadGetFeatureReport();
int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index);
int PS4_SYSV_ABI scePadGetIdleCount();
int PS4_SYSV_ABI scePadGetInfo();
int PS4_SYSV_ABI scePadGetInfoByPortType();
int PS4_SYSV_ABI scePadGetLicenseControllerInformation();
int PS4_SYSV_ABI scePadGetMotionSensorPosition();
int PS4_SYSV_ABI scePadGetMotionTimerUnit();
int PS4_SYSV_ABI scePadGetSphereRadius();
int PS4_SYSV_ABI scePadGetVersionInfo();
int PS4_SYSV_ABI scePadInit();
int PS4_SYSV_ABI scePadIsBlasterConnected();
int PS4_SYSV_ABI scePadIsDS4Connected();
int PS4_SYSV_ABI scePadIsLightBarBaseBrightnessControllable();
int PS4_SYSV_ABI scePadIsMoveConnected();
int PS4_SYSV_ABI scePadIsMoveReproductionModel();
int PS4_SYSV_ABI scePadIsValidHandle();
int PS4_SYSV_ABI scePadMbusInit();
int PS4_SYSV_ABI scePadMbusTerm();
int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam);
int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, const OrbisPadOpenExtParam* pParam);
int PS4_SYSV_ABI scePadOpenExt2();
int PS4_SYSV_ABI scePadOutputReport();
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num);
int PS4_SYSV_ABI scePadReadBlasterForTracker();
int PS4_SYSV_ABI scePadReadExt();
int PS4_SYSV_ABI scePadReadForTracker();
int PS4_SYSV_ABI scePadReadHistory();
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData);
int PS4_SYSV_ABI scePadReadStateExt();
int PS4_SYSV_ABI scePadResetLightBar(s32 handle);
int PS4_SYSV_ABI scePadResetLightBarAll();
int PS4_SYSV_ABI scePadResetLightBarAllByPortType();
int PS4_SYSV_ABI scePadResetOrientation(s32 handle);
int PS4_SYSV_ABI scePadResetOrientationForTracker();
int PS4_SYSV_ABI scePadSetAngularVelocityDeadbandState(s32 handle, bool bEnable);
int PS4_SYSV_ABI scePadSetAutoPowerOffCount();
int PS4_SYSV_ABI scePadSetButtonRemappingInfo();
int PS4_SYSV_ABI scePadSetConnection();
int PS4_SYSV_ABI scePadSetExtensionReport();
int PS4_SYSV_ABI scePadSetFeatureReport();
int PS4_SYSV_ABI scePadSetForceIntercepted();
int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam);
int PS4_SYSV_ABI scePadSetLightBarBaseBrightness();
int PS4_SYSV_ABI scePadSetLightBarBlinking();
int PS4_SYSV_ABI scePadSetLightBarForTracker();
int PS4_SYSV_ABI scePadSetLoginUserNumber();
int PS4_SYSV_ABI scePadSetMotionSensorState(s32 handle, bool bEnable);
int PS4_SYSV_ABI scePadSetProcessFocus();
int PS4_SYSV_ABI scePadSetProcessPrivilege();
int PS4_SYSV_ABI scePadSetProcessPrivilegeOfButtonRemapping();
int PS4_SYSV_ABI scePadSetShareButtonMaskForRemotePlay();
int PS4_SYSV_ABI scePadSetTiltCorrectionState(s32 handle, bool bEnable);
int PS4_SYSV_ABI scePadSetUserColor();
int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pParam);
int PS4_SYSV_ABI scePadSetVibrationForce();
int PS4_SYSV_ABI scePadSetVrTrackingMode();
int PS4_SYSV_ABI scePadShareOutputData();
int PS4_SYSV_ABI scePadStartRecording();
int PS4_SYSV_ABI scePadStopRecording();
int PS4_SYSV_ABI scePadSwitchConnection();
int PS4_SYSV_ABI scePadVertualDeviceAddDevice();
int PS4_SYSV_ABI scePadVirtualDeviceAddDevice();
int PS4_SYSV_ABI scePadVirtualDeviceDeleteDevice();
int PS4_SYSV_ABI scePadVirtualDeviceDisableButtonRemapping();
int PS4_SYSV_ABI scePadVirtualDeviceGetRemoteSetting();
int PS4_SYSV_ABI scePadVirtualDeviceInsertData();
int PS4_SYSV_ABI Func_28B998C7D8A3DA1D();
int PS4_SYSV_ABI Func_298D21481F94C9FA();
int PS4_SYSV_ABI Func_51E514BCD3A05CA5();
int PS4_SYSV_ABI Func_89C9237E393DA243();
int PS4_SYSV_ABI Func_EF103E845B6F0420();
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Pad

View File

@@ -1,164 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QListView>
#include <QMessageBox>
#include <QString>
#include <QXmlStreamReader>
#include "common/logging/log.h"
#include "common/path_util.h"
#include "qt_gui/game_info.h"
#include "memory_patcher.h"
namespace MemoryPatcher {
QString patchDir;
std::vector<PendingPatch> readPatches(std::string gameSerial, std::string appVersion) {
std::vector<PendingPatch> pending;
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QDir dir(patchDir);
QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString& folder : folders) {
QString filesJsonPath = patchDir + "/" + folder + "/files.json";
QFile jsonFile(filesJsonPath);
if (!jsonFile.open(QIODevice::ReadOnly)) {
LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}",
folder.toStdString());
continue;
}
const QByteArray jsonData = jsonFile.readAll();
jsonFile.close();
const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
const QJsonObject jsonObject = jsonDoc.object();
QString selectedFileName;
const QString serial = QString::fromStdString(gameSerial);
for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
const QString filePath = it.key();
const QJsonArray idsArray = it.value().toArray();
if (idsArray.contains(QJsonValue(serial))) {
selectedFileName = filePath;
break;
}
}
if (selectedFileName.isEmpty()) {
LOG_ERROR(Loader, "No patch file found for the current serial in repository {}",
folder.toStdString());
continue;
}
const QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_ERROR(Loader, "Unable to open the file for reading.");
continue;
}
const QByteArray xmlData = file.readAll();
file.close();
QXmlStreamReader xmlReader(xmlData);
bool isEnabled = false;
std::string currentPatchName;
bool versionMatches = true;
while (!xmlReader.atEnd()) {
xmlReader.readNext();
if (!xmlReader.isStartElement()) {
continue;
}
if (xmlReader.name() == QStringLiteral("Metadata")) {
QString name = xmlReader.attributes().value("Name").toString();
currentPatchName = name.toStdString();
const QString appVer = xmlReader.attributes().value("AppVer").toString();
isEnabled = false;
for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
if (attr.name() == QStringLiteral("isEnabled")) {
isEnabled = (attr.value().toString() == "true");
}
}
versionMatches = (appVer.toStdString() == appVersion);
} else if (xmlReader.name() == QStringLiteral("PatchList")) {
while (!xmlReader.atEnd() &&
!(xmlReader.tokenType() == QXmlStreamReader::EndElement &&
xmlReader.name() == QStringLiteral("PatchList"))) {
xmlReader.readNext();
if (xmlReader.tokenType() != QXmlStreamReader::StartElement ||
xmlReader.name() != QStringLiteral("Line")) {
continue;
}
const QXmlStreamAttributes a = xmlReader.attributes();
const QString type = a.value("Type").toString();
const QString addr = a.value("Address").toString();
QString val = a.value("Value").toString();
const QString offStr = a.value("Offset").toString();
const QString tgt =
(type == "mask_jump32") ? a.value("Target").toString() : QString{};
const QString sz =
(type == "mask_jump32") ? a.value("Size").toString() : QString{};
if (!isEnabled) {
continue;
}
if ((type != "mask" && type != "mask_jump32") && !versionMatches) {
continue;
}
PendingPatch pp;
pp.modName = currentPatchName;
pp.address = addr.toStdString();
if (type == "mask" || type == "mask_jump32") {
if (!offStr.toStdString().empty()) {
pp.maskOffset = std::stoi(offStr.toStdString(), nullptr, 10);
}
pp.mask = (type == "mask") ? MemoryPatcher::PatchMask::Mask
: MemoryPatcher::PatchMask::Mask_Jump32;
pp.value = val.toStdString();
pp.target = tgt.toStdString();
pp.size = sz.toStdString();
} else {
pp.value = convertValueToHex(type.toStdString(), val.toStdString());
pp.target.clear();
pp.size.clear();
pp.mask = MemoryPatcher::PatchMask::None;
pp.maskOffset = 0;
}
pp.littleEndian = (type == "bytes16" || type == "bytes32" || type == "bytes64");
pending.emplace_back(std::move(pp));
}
}
}
if (xmlReader.hasError()) {
LOG_ERROR(Loader, "Failed to parse XML for {}", gameSerial);
} else {
LOG_INFO(Loader, "Patches parsed successfully, repository: {}", folder.toStdString());
}
}
return pending;
}
} // namespace MemoryPatcher

View File

@@ -1,25 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <string>
#include "common/memory_patcher.h"
namespace MemoryPatcher {
struct PendingPatch {
std::string modName;
std::string address;
std::string value;
std::string target;
std::string size;
bool littleEndian = false;
PatchMask mask = PatchMask::None;
int maskOffset = 0;
};
std::vector<PendingPatch> readPatches(std::string gameSerial, std::string appVersion);
} // namespace MemoryPatcher

View File

@@ -2,29 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/pad/pad.h"
#include "input/controller.h" #include "input/controller.h"
static std::string SelectedGamepad = ""; static std::string SelectedGamepad = "";
namespace GamepadSelect { namespace GamepadSelect {
int GetDefaultGamepad(SDL_JoystickID* gamepadIDs, int gamepadCount) {
char GUIDbuf[33];
if (Config::getDefaultControllerID() != "") {
for (int i = 0; i < gamepadCount; i++) {
SDL_GUIDToString(SDL_GetGamepadGUIDForID(gamepadIDs[i]), GUIDbuf, 33);
std::string currentGUID = std::string(GUIDbuf);
if (currentGUID == Config::getDefaultControllerID()) {
return i;
}
}
}
return -1;
}
int GetIndexfromGUID(SDL_JoystickID* gamepadIDs, int gamepadCount, std::string GUID) { int GetIndexfromGUID(SDL_JoystickID* gamepadIDs, int gamepadCount, std::string GUID) {
char GUIDbuf[33]; char GUIDbuf[33];
for (int i = 0; i < gamepadCount; i++) { for (int i = 0; i < gamepadCount; i++) {

View File

@@ -3,132 +3,8 @@
#pragma once #pragma once
#include <algorithm> #include <string>
#include <memory>
#include <mutex>
#include <SDL3/SDL_gamepad.h> #include <SDL3/SDL_gamepad.h>
#include "common/types.h"
#include "core/libraries/pad/pad.h"
namespace Input {
enum class Axis {
LeftX = 0,
LeftY = 1,
RightX = 2,
RightY = 3,
TriggerLeft = 4,
TriggerRight = 5,
AxisMax
};
struct TouchpadEntry {
u8 ID = 0;
bool state{};
u16 x{};
u16 y{};
};
class State {
public:
void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool);
void OnAxis(Axis, int);
void OnTouchpad(int touchIndex, bool isDown, float x, float y);
void OnGyro(const float[3]);
void OnAccel(const float[3]);
Libraries::Pad::OrbisPadButtonDataOffset buttonsState{};
u64 time = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}};
Libraries::Pad::OrbisFVector3 acceleration = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f};
};
class Engine {
public:
virtual ~Engine() = default;
virtual void Init() = 0;
virtual void SetLightBarRGB(u8 r, u8 g, u8 b) = 0;
virtual void SetVibration(u8 smallMotor, u8 largeMotor) = 0;
virtual State ReadState() = 0;
virtual float GetAccelPollRate() const = 0;
virtual float GetGyroPollRate() const = 0;
SDL_Gamepad* m_gamepad;
};
inline int GetAxis(int min, int max, int value) {
return std::clamp((255 * (value - min)) / (max - min), 0, 255);
}
constexpr u32 MAX_STATES = 32;
class GameController {
public:
GameController();
virtual ~GameController() = default;
void ReadState(State* state, bool* isConnected, int* connectedCount);
int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount);
State GetLastState() const;
void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed);
void AddState(const State& state);
void Axis(int id, Input::Axis axis, int value);
void Gyro(int id, const float gyro[3]);
void Acceleration(int id, const float acceleration[3]);
void SetLightBarRGB(u8 r, u8 g, u8 b);
void SetVibration(u8 smallMotor, u8 largeMotor);
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
void SetEngine(std::unique_ptr<Engine>);
Engine* GetEngine();
u32 Poll();
u8 GetTouchCount();
void SetTouchCount(u8 touchCount);
u8 GetSecondaryTouchCount();
void SetSecondaryTouchCount(u8 touchCount);
u8 GetPreviousTouchNum();
void SetPreviousTouchNum(u8 touchNum);
bool WasSecondaryTouchReset();
void UnsetSecondaryTouchResetBool();
void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
Libraries::Pad::OrbisFQuaternion GetLastOrientation();
std::chrono::steady_clock::time_point GetLastUpdate();
void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation);
private:
struct StateInternal {
bool obtained = false;
};
std::mutex m_mutex;
bool m_connected = true;
State m_last_state;
int m_connected_count = 0;
u32 m_states_num = 0;
u32 m_first_state = 0;
u8 m_touch_count = 0;
u8 m_secondary_touch_count = 0;
u8 m_previous_touch_count = 0;
u8 m_previous_touchnum = 0;
bool m_was_secondary_reset = false;
std::array<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> m_private;
std::chrono::steady_clock::time_point m_last_update = {};
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
std::unique_ptr<Engine> m_engine = nullptr;
};
} // namespace Input
namespace GamepadSelect { namespace GamepadSelect {

View File

@@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "SDL3/SDL_events.h"
#include "common/types.h"
#include "input/controller.h"
// +1 and +2 is taken
#define SDL_MOUSE_WHEEL_UP SDL_EVENT_MOUSE_WHEEL + 3
#define SDL_MOUSE_WHEEL_DOWN SDL_EVENT_MOUSE_WHEEL + 4
#define SDL_MOUSE_WHEEL_LEFT SDL_EVENT_MOUSE_WHEEL + 5
#define SDL_MOUSE_WHEEL_RIGHT SDL_EVENT_MOUSE_WHEEL + 7
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT SDL_GAMEPAD_BUTTON_COUNT + 1
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER SDL_GAMEPAD_BUTTON_COUNT + 2
#define SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT SDL_GAMEPAD_BUTTON_COUNT + 3
#define SDL_EVENT_TOGGLE_FULLSCREEN SDL_EVENT_USER + 1
#define SDL_EVENT_TOGGLE_PAUSE SDL_EVENT_USER + 2
#define SDL_EVENT_CHANGE_CONTROLLER SDL_EVENT_USER + 3
#define SDL_EVENT_TOGGLE_SIMPLE_FPS SDL_EVENT_USER + 4
#define SDL_EVENT_RELOAD_INPUTS SDL_EVENT_USER + 5
#define SDL_EVENT_MOUSE_TO_JOYSTICK SDL_EVENT_USER + 6
#define SDL_EVENT_MOUSE_TO_GYRO SDL_EVENT_USER + 7
#define SDL_EVENT_RDOC_CAPTURE SDL_EVENT_USER + 8
#define SDL_EVENT_QUIT_DIALOG SDL_EVENT_USER + 9
#define SDL_EVENT_MOUSE_WHEEL_OFF SDL_EVENT_USER + 10
#define LEFTJOYSTICK_HALFMODE 0x00010000
#define RIGHTJOYSTICK_HALFMODE 0x00020000
#define BACK_BUTTON 0x00040000
#define KEY_TOGGLE 0x00200000
#define MOUSE_GYRO_ROLL_MODE 0x00400000
#define HOTKEY_FULLSCREEN 0xf0000001
#define HOTKEY_PAUSE 0xf0000002
#define HOTKEY_SIMPLE_FPS 0xf0000003
#define HOTKEY_QUIT 0xf0000004
#define HOTKEY_RELOAD_INPUTS 0xf0000005
#define HOTKEY_TOGGLE_MOUSE_TO_JOYSTICK 0xf0000006
#define HOTKEY_TOGGLE_MOUSE_TO_GYRO 0xf0000007
#define HOTKEY_RENDERDOC 0xf0000008
#define SDL_UNMAPPED UINT32_MAX - 1

View File

@@ -1244,7 +1244,7 @@ void CheatsPatches::applyCheat(const QString& modName, bool enabled) {
if (!m_cheats.contains(modName)) if (!m_cheats.contains(modName))
return; return;
if (MemoryPatcher::g_eboot_address == 0 && enabled) { if (/* MemoryPatcher::g_eboot_address == 0 && */ enabled) {
QMessageBox::critical(this, tr("Error"), QMessageBox::critical(this, tr("Error"),
tr("Can't apply cheats before the game is started")); tr("Can't apply cheats before the game is started"));
uncheckAllCheatCheckBoxes(); uncheckAllCheatCheckBoxes();
@@ -1260,12 +1260,13 @@ void CheatsPatches::applyCheat(const QString& modName, bool enabled) {
std::string offsetStr = memoryMod.offset.toStdString(); std::string offsetStr = memoryMod.offset.toStdString();
std::string valueStr = value.toStdString(); std::string valueStr = value.toStdString();
if (MemoryPatcher::g_eboot_address == 0) /* if (MemoryPatcher::g_eboot_address == 0)
return; return;
// Determine if the hint field is present // Determine if the hint field is present
bool isHintPresent = m_cheats[modName].hasHint; bool isHintPresent = m_cheats[modName].hasHint;
MemoryPatcher::PatchMemory(modNameStr, offsetStr, valueStr, "", "", !isHintPresent, false); MemoryPatcher::PatchMemory(modNameStr, offsetStr, valueStr, "", "", !isHintPresent, false);
*/
} }
} }
@@ -1304,7 +1305,7 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
} }
if (MemoryPatcher::g_eboot_address == 0) { /* if (MemoryPatcher::g_eboot_address == 0) {
MemoryPatcher::patchInfo addingPatch; MemoryPatcher::patchInfo addingPatch;
addingPatch.gameSerial = patchInfo.serial.toStdString(); addingPatch.gameSerial = patchInfo.serial.toStdString();
addingPatch.modNameStr = patchName.toStdString(); addingPatch.modNameStr = patchName.toStdString();
@@ -1320,7 +1321,7 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
} }
MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(), MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(),
patchValue.toStdString(), "", "", false, littleEndian, patchValue.toStdString(), "", "", false, littleEndian,
patchMask); patchMask); */
} }
} }
} }

View File

@@ -8,7 +8,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "control_settings.h" #include "control_settings.h"
#include "input/input_handler.h" #include "input/controller.h"
#include "ui_control_settings.h" #include "ui_control_settings.h"
ControlSettings::ControlSettings(std::shared_ptr<GameInfoClass> game_info_get, bool isGameRunning, ControlSettings::ControlSettings(std::shared_ptr<GameInfoClass> game_info_get, bool isGameRunning,
@@ -1026,12 +1026,12 @@ void ControlSettings::Cleanup() {
SDL_QuitSubSystem(SDL_INIT_EVENTS); SDL_QuitSubSystem(SDL_INIT_EVENTS);
SDL_Quit(); SDL_Quit();
} else { } else {
if (!Config::getBackgroundControllerInput()) { /* if (!Config::getBackgroundControllerInput()) {
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
} }
SDL_Event checkGamepad{}; SDL_Event checkGamepad{};
checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER; checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER;
SDL_PushEvent(&checkGamepad); SDL_PushEvent(&checkGamepad); */
} }
} }

View File

@@ -5,7 +5,7 @@
#include <QFileDialog> #include <QFileDialog>
#include "core/loader/elf.h" #include "core/file_format/elf.h"
#include "game_list_frame.h" #include "game_list_frame.h"
class ElfViewer : public QTableWidget { class ElfViewer : public QTableWidget {
@@ -18,7 +18,7 @@ private:
void CheckElfFolders(); void CheckElfFolders();
void OpenElfFiles(); void OpenElfFiles();
Core::Loader::Elf m_elf_file; Elf m_elf_file;
QStringList dir_list; QStringList dir_list;
QStringList elf_headers_list; QStringList elf_headers_list;
QList<QString> list; QList<QString> list;

View File

@@ -12,7 +12,6 @@
#include "common/path_util.h" #include "common/path_util.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "input/controller.h" #include "input/controller.h"
#include "input/input_handler.h"
#include "sdl_event_wrapper.h" #include "sdl_event_wrapper.h"
#include "ui_hotkeys.h" #include "ui_hotkeys.h"

View File

@@ -10,7 +10,6 @@
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#include "common/path_util.h" #include "common/path_util.h"
#include "input/input_handler.h"
#include "kbm_config_dialog.h" #include "kbm_config_dialog.h"
#include "kbm_gui.h" #include "kbm_gui.h"
#include "kbm_help_dialog.h" #include "kbm_help_dialog.h"

View File

@@ -15,13 +15,12 @@
#include "check_update.h" #include "check_update.h"
#endif #endif
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/memory_patcher.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "control_settings.h" #include "control_settings.h"
#include "core/memory_patcher.h"
#include "game_install_dialog.h" #include "game_install_dialog.h"
#include "hotkeys.h" #include "hotkeys.h"
#include "input/input_handler.h"
#include "ipc/ipc_client.h" #include "ipc/ipc_client.h"
#include "kbm_gui.h" #include "kbm_gui.h"
#include "main_window.h" #include "main_window.h"