MemPatch.cs 3.2 KB

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