MemPatch.cs
1 using Ryujinx.Common.Logging; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 6 namespace Ryujinx.HLE.Loaders.Mods 7 { 8 public class MemPatch 9 { 10 readonly Dictionary<uint, byte[]> _patches = new(); 11 12 /// <summary> 13 /// Adds a patch to specified offset. Overwrites if already present. 14 /// </summary> 15 /// <param name="offset">Memory offset</param> 16 /// <param name="patch">The patch to add</param> 17 public void Add(uint offset, byte[] patch) 18 { 19 _patches[offset] = patch; 20 } 21 22 /// <summary> 23 /// Adds a patch in the form of an RLE (Fill mode). 24 /// </summary> 25 /// <param name="offset">Memory offset</param> 26 /// <param name="length"The fill length</param> 27 /// <param name="filler">The byte to fill</param> 28 public void AddFill(uint offset, int length, byte filler) 29 { 30 // TODO: Can be made space efficient by changing `_patches` 31 // Should suffice for now 32 byte[] patch = new byte[length]; 33 patch.AsSpan().Fill(filler); 34 35 _patches[offset] = patch; 36 } 37 38 /// <summary> 39 /// Adds all patches from an existing MemPatch 40 /// </summary> 41 /// <param name="patches">The patches to add</param> 42 public void AddFrom(MemPatch patches) 43 { 44 if (patches == null) 45 { 46 return; 47 } 48 49 foreach (var (patchOffset, patch) in patches._patches) 50 { 51 _patches[patchOffset] = patch; 52 } 53 } 54 55 /// <summary> 56 /// Applies all the patches added to this instance. 57 /// </summary> 58 /// <remarks> 59 /// Patches are applied in ascending order of offsets to guarantee 60 /// overlapping patches always apply the same way. 61 /// </remarks> 62 /// <param name="memory">The span of bytes to patch</param> 63 /// <param name="maxSize">The maximum size of the slice of patchable memory</param> 64 /// <param name="protectedOffset">A secondary offset used in special cases (NSO header)</param> 65 /// <returns>Successful patches count</returns> 66 public int Patch(Span<byte> memory, int protectedOffset = 0) 67 { 68 int count = 0; 69 foreach (var (offset, patch) in _patches.OrderBy(item => item.Key)) 70 { 71 int patchOffset = (int)offset; 72 int patchSize = patch.Length; 73 74 if (patchOffset < protectedOffset || patchOffset > memory.Length) 75 { 76 continue; // Add warning? 77 } 78 79 patchOffset -= protectedOffset; 80 81 if (patchOffset + patchSize > memory.Length) 82 { 83 patchSize = memory.Length - patchOffset; // Add warning? 84 } 85 86 Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); 87 88 patch.AsSpan(0, patchSize).CopyTo(memory.Slice(patchOffset, patchSize)); 89 90 count++; 91 } 92 93 return count; 94 } 95 } 96 }