Riivolution: add search_count attribute to memory patches

This allows you to write a single memory search patch that gets applied
multiple times at different offsets.

Note that for implementation simplicity this count currently resets for
each DOL section. This is also the existing behavior for single-match
patches.
This commit is contained in:
Tillmann Karras 2023-12-04 14:10:04 +00:00
parent e87a22fbf4
commit c9ad4ec68d
3 changed files with 12 additions and 4 deletions

View File

@ -211,6 +211,7 @@ std::optional<Disc> ParseString(std::string_view xml, std::string xml_path)
memory.m_original = ReadHexString(patch_subnode.attribute("original").as_string()); memory.m_original = ReadHexString(patch_subnode.attribute("original").as_string());
memory.m_ocarina = patch_subnode.attribute("ocarina").as_bool(false); memory.m_ocarina = patch_subnode.attribute("ocarina").as_bool(false);
memory.m_search = patch_subnode.attribute("search").as_bool(false); memory.m_search = patch_subnode.attribute("search").as_bool(false);
memory.m_search_count = patch_subnode.attribute("search_count").as_uint(1);
memory.m_align = patch_subnode.attribute("align").as_uint(1); memory.m_align = patch_subnode.attribute("align").as_uint(1);
} }
} }

View File

@ -146,9 +146,12 @@ struct Memory
bool m_ocarina = false; bool m_ocarina = false;
// If true, the offset is not known, and instead we should search for the m_original bytes in // If true, the offset is not known, and instead we should search for the m_original bytes in
// memory and replace them where found. Only searches in MEM1, and only replaces the first match. // memory and replace them where found. Only searches in MEM1.
bool m_search = false; bool m_search = false;
// For m_search. Replaces up to m_search_count occurrences.
u32 m_search_count = 1;
// For m_search. The byte stride between search points. // For m_search. The byte stride between search points.
u32 m_align = 1; u32 m_align = 1;
}; };

View File

@ -565,17 +565,21 @@ static void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& pat
static void ApplySearchMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch, static void ApplySearchMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch, u32 ram_start, u32 length) const Memory& memory_patch, u32 ram_start, u32 length)
{ {
if (memory_patch.m_original.empty() || memory_patch.m_align == 0) if (memory_patch.m_original.empty() || memory_patch.m_align == 0 || memory_patch.m_search_count == 0)
return; return;
const u32 stride = memory_patch.m_align; const u32 stride = memory_patch.m_align;
u32 times = memory_patch.m_search_count;
for (u32 i = 0; i < length - (stride - 1); i += stride) for (u32 i = 0; i < length - (stride - 1); i += stride)
{ {
const u32 address = ram_start + i; const u32 address = ram_start + i;
if (MemoryMatchesAt(guard, address, memory_patch.m_original)) if (MemoryMatchesAt(guard, address, memory_patch.m_original))
{ {
ApplyMemoryPatch(guard, address, GetMemoryPatchValue(patch, memory_patch), {}); const std::vector<u8> value = GetMemoryPatchValue(patch, memory_patch);
break; INFO_LOG_FMT(BOOT, "Applying memory patch at [0x{:08X}, 0x{:08X})", address, address + value.size());
ApplyMemoryPatch(guard, address, value, {});
if (--times == 0)
break;
} }
} }
} }