using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.Linq; namespace Ryujinx.HLE.Loaders.Mods { public class MemPatch { readonly Dictionary _patches = new Dictionary(); /// /// Adds a patch to specified offset. Overwrites if already present. /// /// Memory offset /// The patch to add public void Add(uint offset, byte[] patch) { _patches[offset] = patch; } /// /// Adds a patch in the form of an RLE (Fill mode). /// /// Memory offset /// /// The byte to fill public void AddFill(uint offset, int length, byte filler) { // TODO: Can be made space efficient by changing `_patches` // Should suffice for now byte[] patch = new byte[length]; patch.AsSpan().Fill(filler); _patches[offset] = patch; } /// /// Adds all patches from an existing MemPatch /// /// The patches to add public void AddFrom(MemPatch patches) { if (patches == null) { return; } foreach (var (patchOffset, patch) in patches._patches) { _patches[patchOffset] = patch; } } /// /// Applies all the patches added to this instance. /// /// /// Patches are applied in ascending order of offsets to guarantee /// overlapping patches always apply the same way. /// /// The span of bytes to patch /// The maximum size of the slice of patchable memory /// A secondary offset used in special cases (NSO header) /// Successful patches count public int Patch(Span memory, int protectedOffset = 0) { int count = 0; foreach (var (offset, patch) in _patches.OrderBy(item => item.Key)) { int patchOffset = (int)offset; int patchSize = patch.Length; if (patchOffset < protectedOffset || patchOffset > memory.Length) { continue; // Add warning? } patchOffset -= protectedOffset; if (patchOffset + patchSize > memory.Length) { patchSize = memory.Length - patchOffset; // Add warning? } Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); patch.AsSpan(0, patchSize).CopyTo(memory.Slice(patchOffset, patchSize)); count++; } return count; } } }