MemPatch.cs 3.2 KB

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