diff options
Diffstat (limited to 'Ryujinx.HLE/Loaders/Mods/MemPatch.cs')
-rw-r--r-- | Ryujinx.HLE/Loaders/Mods/MemPatch.cs | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/Ryujinx.HLE/Loaders/Mods/MemPatch.cs b/Ryujinx.HLE/Loaders/Mods/MemPatch.cs new file mode 100644 index 00000000..0fc19159 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Mods/MemPatch.cs @@ -0,0 +1,98 @@ +using Ryujinx.Cpu; +using System; +using System.Collections.Generic; +using System.Linq; + +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.Loaders.Mods +{ + public class MemPatch + { + readonly Dictionary<uint, byte[]> _patches = new Dictionary<uint, byte[]>(); + + /// <summary> + /// Adds a patch to specified offset. Overwrites if already present. + /// </summary> + /// <param name="offset">Memory offset</param> + /// <param name="patch">The patch to add</param> + public void Add(uint offset, byte[] patch) + { + _patches[offset] = patch; + } + + /// <summary> + /// Adds a patch in the form of an RLE (Fill mode). + /// </summary> + /// <param name="offset">Memory offset</param> + /// <param name="length"The fill length</param> + /// <param name="filler">The byte to fill</param> + 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; + } + + /// <summary> + /// Adds all patches from an existing MemPatch + /// </summary> + /// <param name="patches">The patches to add</param> + public void AddFrom(MemPatch patches) + { + if (patches == null) + { + return; + } + + foreach (var (patchOffset, patch) in patches._patches) + { + _patches[patchOffset] = patch; + } + } + + /// <summary> + /// Applies all the patches added to this instance. + /// </summary> + /// <remarks> + /// Patches are applied in ascending order of offsets to guarantee + /// overlapping patches always apply the same way. + /// </remarks> + /// <param name="memory">The span of bytes to patch</param> + /// <param name="maxSize">The maximum size of the slice of patchable memory</param> + /// <param name="protectedOffset">A secondary offset used in special cases (NSO header)</param> + /// <returns>Successful patches count</returns> + public int Patch(Span<byte> 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 - (int)patchOffset; // Add warning? + } + + Logger.PrintInfo(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); + + patch.AsSpan().Slice(0, patchSize).CopyTo(memory.Slice(patchOffset, patchSize)); + + count++; + } + + return count; + } + } +}
\ No newline at end of file |