aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/Loaders/Mods/IPSPatcher.cs
blob: 510fec0590d48d10e20d95f4e5457791eb1f9069 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using Ryujinx.Common.Logging;
using System;
using System.IO;
using System.Text;

namespace Ryujinx.HLE.Loaders.Mods
{
    class IpsPatcher
    {
        MemPatch _patches;

        public IpsPatcher(BinaryReader reader)
        {
            _patches = ParseIps(reader);
            if (_patches != null)
            {
                Logger.Info?.Print(LogClass.ModLoader, "IPS patch loaded successfully");
            }
        }

        private static MemPatch ParseIps(BinaryReader reader)
        {
            ReadOnlySpan<byte> IpsHeaderMagic   = "PATCH"u8;
            ReadOnlySpan<byte> IpsTailMagic     = "EOF"u8;
            ReadOnlySpan<byte> Ips32HeaderMagic = "IPS32"u8;
            ReadOnlySpan<byte> Ips32TailMagic   = "EEOF"u8;

            MemPatch patches = new MemPatch();
            var header = reader.ReadBytes(IpsHeaderMagic.Length).AsSpan();

            if (header.Length != IpsHeaderMagic.Length)
            {
                return null;
            }

            bool is32;
            ReadOnlySpan<byte> tailSpan;

            if (header.SequenceEqual(IpsHeaderMagic))
            {
                is32 = false;
                tailSpan = IpsTailMagic;
            }
            else if (header.SequenceEqual(Ips32HeaderMagic))
            {
                is32 = true;
                tailSpan = Ips32TailMagic;
            }
            else
            {
                return null;
            }

            byte[] buf = new byte[tailSpan.Length];

            bool ReadNext(int size) => reader.Read(buf, 0, size) != size;

            while (true)
            {
                if (ReadNext(buf.Length))
                {
                    return null;
                }

                if (buf.AsSpan().SequenceEqual(tailSpan))
                {
                    break;
                }

                int patchOffset = is32 ? buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]
                                       : buf[0] << 16 | buf[1] << 8 | buf[2];

                if (ReadNext(2))
                {
                    return null;
                }

                int patchSize = buf[0] << 8 | buf[1];

                if (patchSize == 0) // RLE/Fill mode
                {
                    if (ReadNext(2))
                    {
                        return null;
                    }

                    int fillLength = buf[0] << 8 | buf[1];

                    if (ReadNext(1))
                    {
                        return null;
                    }

                    patches.AddFill((uint)patchOffset, fillLength, buf[0]);
                }
                else // Copy mode
                {
                    var patch = reader.ReadBytes(patchSize);

                    if (patch.Length != patchSize)
                    {
                        return null;
                    }

                    patches.Add((uint)patchOffset, patch);
                }
            }

            return patches;
        }

        public void AddPatches(MemPatch patches)
        {
            patches.AddFrom(_patches);
        }
    }
}