mirror of https://github.com/PCSX2/pcsx2.git
Patch: Add "bytes" type
Allows patching an arbitrary range of bytes.
This commit is contained in:
parent
940e211bb6
commit
9d3de8631c
|
@ -493,6 +493,69 @@ void iopMemWrite32(u32 mem, u32 value)
|
|||
}
|
||||
}
|
||||
|
||||
int iopMemSafeCmpBytes(u32 mem, const void* src, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
const u8* sptr = static_cast<const u8*>(src);
|
||||
const u8* const sptr_end = sptr + size;
|
||||
while (sptr != sptr_end)
|
||||
{
|
||||
u8* dst = iopVirtMemW<u8>(mem);
|
||||
if (!dst)
|
||||
return -1;
|
||||
|
||||
const u32 remaining_in_page = std::min(0x1000 - (mem & 0xfff), static_cast<u32>(sptr_end - sptr));
|
||||
const int res = std::memcmp(sptr, dst, remaining_in_page);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
sptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool iopMemSafeReadBytes(u32 mem, void* dst, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
u8* dptr = static_cast<u8*>(dst);
|
||||
u8* const dptr_end = dptr + size;
|
||||
while (dptr != dptr_end)
|
||||
{
|
||||
const u8* src = iopVirtMemR<u8>(mem);
|
||||
if (!src)
|
||||
return false;
|
||||
|
||||
const u32 remaining_in_page = std::min(0x1000 - (mem & 0xfff), static_cast<u32>(dptr_end - dptr));
|
||||
std::memcpy(dptr, src, remaining_in_page);
|
||||
dptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool iopMemSafeWriteBytes(u32 mem, const void* src, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
const u8* sptr = static_cast<const u8*>(src);
|
||||
const u8* const sptr_end = sptr + size;
|
||||
while (sptr != sptr_end)
|
||||
{
|
||||
u8* dst = iopVirtMemW<u8>(mem);
|
||||
if (!dst)
|
||||
return false;
|
||||
|
||||
const u32 remaining_in_page = std::min(0x1000 - (mem & 0xfff), static_cast<u32>(sptr_end - sptr));
|
||||
std::memcpy(dst, sptr, remaining_in_page);
|
||||
sptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string iopMemReadString(u32 mem, int maxlen)
|
||||
{
|
||||
std::string ret;
|
||||
|
|
|
@ -83,6 +83,11 @@ extern void iopMemWrite8 (u32 mem, u8 value);
|
|||
extern void iopMemWrite16(u32 mem, u16 value);
|
||||
extern void iopMemWrite32(u32 mem, u32 value);
|
||||
|
||||
// NOTE: Does not call MMIO handlers.
|
||||
extern int iopMemSafeCmpBytes(u32 mem, const void* src, u32 size);
|
||||
extern bool iopMemSafeReadBytes(u32 mem, void* dst, u32 size);
|
||||
extern bool iopMemSafeWriteBytes(u32 mem, const void* src, u32 size);
|
||||
|
||||
std::string iopMemReadString(u32 mem, int maxlen = 65536);
|
||||
|
||||
namespace IopMemory
|
||||
|
|
|
@ -58,13 +58,14 @@ namespace Patch
|
|||
EXTENDED_T,
|
||||
SHORT_BE_T,
|
||||
WORD_BE_T,
|
||||
DOUBLE_BE_T
|
||||
DOUBLE_BE_T,
|
||||
BYTES_T
|
||||
};
|
||||
|
||||
static constexpr std::array<const char*, 3> s_place_to_string = {{"0", "1", "2"}};
|
||||
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
|
||||
static constexpr std::array<const char*, 8> s_type_to_string = {
|
||||
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble"}};
|
||||
static constexpr std::array<const char*, 9> s_type_to_string = {
|
||||
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
|
||||
|
||||
template <typename EnumType, class ArrayType>
|
||||
static inline std::optional<EnumType> LookupEnumName(const std::string_view& val, const ArrayType& arr)
|
||||
|
@ -84,8 +85,29 @@ namespace Patch
|
|||
patch_data_type type;
|
||||
u32 addr;
|
||||
u64 data;
|
||||
u8* data_ptr;
|
||||
|
||||
// needed because of the pointer
|
||||
PatchCommand() { std::memset(this, 0, sizeof(*this)); }
|
||||
PatchCommand(const PatchCommand& p) = delete;
|
||||
PatchCommand(PatchCommand&& p)
|
||||
{
|
||||
std::memcpy(this, &p, sizeof(*this));
|
||||
p.data_ptr = nullptr;
|
||||
}
|
||||
~PatchCommand()
|
||||
{
|
||||
if (data_ptr)
|
||||
std::free(data_ptr);
|
||||
}
|
||||
|
||||
PatchCommand& operator=(const PatchCommand& p) = delete;
|
||||
PatchCommand& operator=(PatchCommand&& p)
|
||||
{
|
||||
std::memcpy(this, &p, sizeof(*this));
|
||||
p.data_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; }
|
||||
bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; }
|
||||
|
@ -96,7 +118,7 @@ namespace Patch
|
|||
s_cpu_to_string[static_cast<u8>(cpu)], s_type_to_string[static_cast<u8>(type)], addr, data);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PatchCommand) == 16, "IniPatch has no padding");
|
||||
static_assert(sizeof(PatchCommand) == 24, "IniPatch has no padding");
|
||||
|
||||
struct PatchGroup
|
||||
{
|
||||
|
@ -656,9 +678,9 @@ void Patch::UnloadPatches()
|
|||
s_active_dynamic_patches = {};
|
||||
s_enabled_patches = {};
|
||||
s_enabled_cheats = {};
|
||||
s_cheat_patches = {};
|
||||
s_game_patches = {};
|
||||
s_gamedb_patches = {};
|
||||
decltype(s_cheat_patches)().swap(s_cheat_patches);
|
||||
decltype(s_game_patches)().swap(s_game_patches);
|
||||
decltype(s_gamedb_patches)().swap(s_gamedb_patches);
|
||||
}
|
||||
|
||||
// PatchFunc Functions.
|
||||
|
@ -680,7 +702,8 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con
|
|||
const std::optional<patch_cpu_type> cpu = LookupEnumName<patch_cpu_type>(pieces[1], s_cpu_to_string);
|
||||
const std::optional<u32> addr = StringUtil::FromChars<u32>(pieces[2], 16, &addr_end);
|
||||
const std::optional<patch_data_type> type = LookupEnumName<patch_data_type>(pieces[3], s_type_to_string);
|
||||
const std::optional<u64> data = StringUtil::FromChars<u64>(pieces[4], 16, &data_end);
|
||||
std::optional<u64> data = StringUtil::FromChars<u64>(pieces[4], 16, &data_end);
|
||||
u8* data_ptr = nullptr;
|
||||
|
||||
if (!placetopatch.has_value())
|
||||
{
|
||||
|
@ -692,11 +715,6 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con
|
|||
PATCH_ERROR("Malformed address '{}', a hex number without prefix (e.g. 0123ABCD) is expected", pieces[2]);
|
||||
return;
|
||||
}
|
||||
else if (!data.has_value() || !data_end.empty())
|
||||
{
|
||||
PATCH_ERROR("Malformed data '{}', a hex number without prefix (e.g. 0123ABCD) is expected", pieces[4]);
|
||||
return;
|
||||
}
|
||||
if (!cpu.has_value())
|
||||
{
|
||||
PATCH_ERROR("Unrecognized CPU Target: '%.*s'", pieces[1]);
|
||||
|
@ -707,6 +725,28 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con
|
|||
PATCH_ERROR("Unrecognized Operand Size: '%.*s'", pieces[3]);
|
||||
return;
|
||||
}
|
||||
if (type.value() != BYTES_T)
|
||||
{
|
||||
if (!data.has_value() || !data_end.empty())
|
||||
{
|
||||
PATCH_ERROR("Malformed data '{}', a hex number without prefix (e.g. 0123ABCD) is expected", pieces[4]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// bit crappy to copy it, but eh, saves writing a new routine
|
||||
std::optional<std::vector<u8>> bytes = StringUtil::DecodeHex(pieces[4]);
|
||||
if (!bytes.has_value() || bytes->empty())
|
||||
{
|
||||
PATCH_ERROR("Malformed data '{}', a hex string without prefix (e.g. 0123ABCD) is expected", pieces[4]);
|
||||
return;
|
||||
}
|
||||
|
||||
data = bytes->size();
|
||||
data_ptr = static_cast<u8*>(std::malloc(bytes->size()));
|
||||
std::memcpy(data_ptr, bytes->data(), bytes->size());
|
||||
}
|
||||
|
||||
PatchCommand iPatch;
|
||||
iPatch.placetopatch = placetopatch.value();
|
||||
|
@ -714,7 +754,8 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con
|
|||
iPatch.addr = addr.value();
|
||||
iPatch.type = type.value();
|
||||
iPatch.data = data.value();
|
||||
group->patches.push_back(iPatch);
|
||||
iPatch.data_ptr = data_ptr;
|
||||
group->patches.push_back(std::move(iPatch));
|
||||
|
||||
#undef PATCH_ERROR
|
||||
}
|
||||
|
@ -769,7 +810,7 @@ void Patch::LoadDynamicPatches(const std::vector<DynamicPatch>& patches)
|
|||
}
|
||||
|
||||
static u32 SkipCount = 0, IterationCount = 0;
|
||||
static u32 IterationIncrement = 0, ValueIncrement = 0;
|
||||
static u32 IterationIncrement = 0;
|
||||
static u32 PrevCheatType = 0, PrevCheatAddr = 0, LastType = 0;
|
||||
|
||||
void Patch::writeCheat()
|
||||
|
@ -1307,6 +1348,14 @@ void Patch::ApplyPatch(const PatchCommand* p)
|
|||
memWrite64(p->addr, (u64)ledata);
|
||||
break;
|
||||
|
||||
case BYTES_T:
|
||||
{
|
||||
// We compare before writing so the rec doesn't get upset and invalidate when there's no change.
|
||||
if (vtlb_memSafeCmpBytes(p->addr, p->data_ptr, static_cast<u32>(p->data)) != 0)
|
||||
vtlb_memSafeWriteBytes(p->addr, p->data_ptr, static_cast<u32>(p->data));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1327,6 +1376,13 @@ void Patch::ApplyPatch(const PatchCommand* p)
|
|||
if (iopMemRead32(p->addr) != (u32)p->data)
|
||||
iopMemWrite32(p->addr, (u32)p->data);
|
||||
break;
|
||||
case BYTES_T:
|
||||
{
|
||||
if (iopMemSafeCmpBytes(p->addr, p->data_ptr, static_cast<u32>(p->data)) != 0)
|
||||
iopMemSafeWriteBytes(p->addr, p->data_ptr, static_cast<u32>(p->data));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -362,6 +362,72 @@ template bool vtlb_ramWrite<mem32_t>(u32 mem, const mem32_t& data);
|
|||
template bool vtlb_ramWrite<mem64_t>(u32 mem, const mem64_t& data);
|
||||
template bool vtlb_ramWrite<mem128_t>(u32 mem, const mem128_t& data);
|
||||
|
||||
int vtlb_memSafeCmpBytes(u32 mem, const void* src, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
const u8* sptr = static_cast<const u8*>(src);
|
||||
const u8* const sptr_end = sptr + size;
|
||||
while (sptr != sptr_end)
|
||||
{
|
||||
auto vmv = vtlbdata.vmap[mem >> VTLB_PAGE_BITS];
|
||||
if (vmv.isHandler(mem))
|
||||
return -1;
|
||||
|
||||
const size_t remaining_in_page =
|
||||
std::min(VTLB_PAGE_SIZE - (mem & VTLB_PAGE_MASK), static_cast<u32>(sptr_end - sptr));
|
||||
const int res = std::memcmp(sptr, reinterpret_cast<void*>(vmv.assumePtr(mem)), remaining_in_page);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
sptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vtlb_memSafeReadBytes(u32 mem, void* dst, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
u8* dptr = static_cast<u8*>(dst);
|
||||
u8* const dptr_end = dptr + size;
|
||||
while (dptr != dptr_end)
|
||||
{
|
||||
auto vmv = vtlbdata.vmap[mem >> VTLB_PAGE_BITS];
|
||||
if (vmv.isHandler(mem))
|
||||
return false;
|
||||
|
||||
const u32 remaining_in_page =
|
||||
std::min(VTLB_PAGE_SIZE - (mem & VTLB_PAGE_MASK), static_cast<u32>(dptr_end - dptr));
|
||||
std::memcpy(dptr, reinterpret_cast<void*>(vmv.assumePtr(mem)), remaining_in_page);
|
||||
dptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vtlb_memSafeWriteBytes(u32 mem, const void* src, u32 size)
|
||||
{
|
||||
// can memcpy so long as pages aren't crossed
|
||||
const u8* sptr = static_cast<const u8*>(src);
|
||||
const u8* const sptr_end = sptr + size;
|
||||
while (sptr != sptr_end)
|
||||
{
|
||||
auto vmv = vtlbdata.vmap[mem >> VTLB_PAGE_BITS];
|
||||
if (vmv.isHandler(mem))
|
||||
return false;
|
||||
|
||||
const size_t remaining_in_page =
|
||||
std::min(VTLB_PAGE_SIZE - (mem & VTLB_PAGE_MASK), static_cast<u32>(sptr_end - sptr));
|
||||
std::memcpy(reinterpret_cast<void*>(vmv.assumePtr(mem)), sptr, remaining_in_page);
|
||||
sptr += remaining_in_page;
|
||||
mem += remaining_in_page;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// TLB Miss / BusError Handlers
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -110,6 +110,11 @@ extern DataType vtlb_ramRead(u32 mem);
|
|||
template <typename DataType>
|
||||
extern bool vtlb_ramWrite(u32 mem, const DataType& value);
|
||||
|
||||
// NOTE: Does not call MMIO handlers.
|
||||
extern int vtlb_memSafeCmpBytes(u32 mem, const void* src, u32 size);
|
||||
extern bool vtlb_memSafeReadBytes(u32 mem, void* dst, u32 size);
|
||||
extern bool vtlb_memSafeWriteBytes(u32 mem, const void* src, u32 size);
|
||||
|
||||
using vtlb_ReadRegAllocCallback = int(*)();
|
||||
extern int vtlb_DynGenReadNonQuad(u32 bits, bool sign, bool xmm, int addr_reg, vtlb_ReadRegAllocCallback dest_reg_alloc = nullptr);
|
||||
extern int vtlb_DynGenReadNonQuad_Const(u32 bits, bool sign, bool xmm, u32 addr_const, vtlb_ReadRegAllocCallback dest_reg_alloc = nullptr);
|
||||
|
|
Loading…
Reference in New Issue