/ src / Ryujinx.HLE / Loaders / Mods / IPSPatcher.cs
IPSPatcher.cs
  1  using Ryujinx.Common.Logging;
  2  using System;
  3  using System.IO;
  4  
  5  namespace Ryujinx.HLE.Loaders.Mods
  6  {
  7      class IpsPatcher
  8      {
  9          readonly MemPatch _patches;
 10  
 11          public IpsPatcher(BinaryReader reader)
 12          {
 13              _patches = ParseIps(reader);
 14              if (_patches != null)
 15              {
 16                  Logger.Info?.Print(LogClass.ModLoader, "IPS patch loaded successfully");
 17              }
 18          }
 19  
 20          private static MemPatch ParseIps(BinaryReader reader)
 21          {
 22              ReadOnlySpan<byte> ipsHeaderMagic = "PATCH"u8;
 23              ReadOnlySpan<byte> ipsTailMagic = "EOF"u8;
 24              ReadOnlySpan<byte> ips32HeaderMagic = "IPS32"u8;
 25              ReadOnlySpan<byte> ips32TailMagic = "EEOF"u8;
 26  
 27              MemPatch patches = new();
 28              var header = reader.ReadBytes(ipsHeaderMagic.Length).AsSpan();
 29  
 30              if (header.Length != ipsHeaderMagic.Length)
 31              {
 32                  return null;
 33              }
 34  
 35              bool is32;
 36              ReadOnlySpan<byte> tailSpan;
 37  
 38              if (header.SequenceEqual(ipsHeaderMagic))
 39              {
 40                  is32 = false;
 41                  tailSpan = ipsTailMagic;
 42              }
 43              else if (header.SequenceEqual(ips32HeaderMagic))
 44              {
 45                  is32 = true;
 46                  tailSpan = ips32TailMagic;
 47              }
 48              else
 49              {
 50                  return null;
 51              }
 52  
 53              byte[] buf = new byte[tailSpan.Length];
 54  
 55              bool ReadNext(int size) => reader.Read(buf, 0, size) != size;
 56  
 57              while (true)
 58              {
 59                  if (ReadNext(buf.Length))
 60                  {
 61                      return null;
 62                  }
 63  
 64                  if (buf.AsSpan().SequenceEqual(tailSpan))
 65                  {
 66                      break;
 67                  }
 68  
 69                  int patchOffset = is32 ? buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]
 70                                         : buf[0] << 16 | buf[1] << 8 | buf[2];
 71  
 72                  if (ReadNext(2))
 73                  {
 74                      return null;
 75                  }
 76  
 77                  int patchSize = buf[0] << 8 | buf[1];
 78  
 79                  if (patchSize == 0) // RLE/Fill mode
 80                  {
 81                      if (ReadNext(2))
 82                      {
 83                          return null;
 84                      }
 85  
 86                      int fillLength = buf[0] << 8 | buf[1];
 87  
 88                      if (ReadNext(1))
 89                      {
 90                          return null;
 91                      }
 92  
 93                      patches.AddFill((uint)patchOffset, fillLength, buf[0]);
 94                  }
 95                  else // Copy mode
 96                  {
 97                      var patch = reader.ReadBytes(patchSize);
 98  
 99                      if (patch.Length != patchSize)
100                      {
101                          return null;
102                      }
103  
104                      patches.Add((uint)patchOffset, patch);
105                  }
106              }
107  
108              return patches;
109          }
110  
111          public void AddPatches(MemPatch patches)
112          {
113              patches.AddFrom(_patches);
114          }
115      }
116  }