Use bsnes core internal IPS and BPS patchers
[lsnes.git] / src / core / patchrom.cpp
blob68b5c8ff985d46fa22be7f746228b985aa2215b2
1 #include "core/patchrom.hpp"
2 #include "core/misc.hpp"
4 #include <nall/ips.hpp>
5 #include <nall/bps/patch.hpp>
6 #include <cstring>
8 namespace
10 void throw_bps_error(nall::bpspatch::result r)
12 switch(r)
14 case nall::bpspatch::unknown:
15 throw std::runtime_error("Unknown error status");
16 case nall::bpspatch::success:
17 break;
18 case nall::bpspatch::patch_too_small:
19 throw std::runtime_error("Patch too small to be valid");
20 case nall::bpspatch::patch_invalid_header:
21 throw std::runtime_error("Patch has invalid header");
22 case nall::bpspatch::source_too_small:
23 throw std::runtime_error("Source file is too small");
24 case nall::bpspatch::target_too_small:
25 throw std::runtime_error("INTERNAL ERROR: Target file is too small");
26 case nall::bpspatch::source_checksum_invalid:
27 throw std::runtime_error("Source file fails CRC check");
28 case nall::bpspatch::target_checksum_invalid:
29 throw std::runtime_error("Result fails CRC check");
30 case nall::bpspatch::patch_checksum_invalid:
31 throw std::runtime_error("Corrupt patch file");
32 default:
33 throw std::runtime_error("Unknown error applying patch");
37 std::vector<char> do_patch_bps(const std::vector<char>& original, const std::vector<char>& patch,
38 int32_t offset) throw(std::bad_alloc, std::runtime_error)
40 if(offset)
41 throw std::runtime_error("Offsets are not supported for .bps patches");
42 std::vector<char> _original = original;
43 std::vector<char> _patch = patch;
44 nall::bpspatch p;
46 p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
47 p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
49 //Do trial apply to get the size.
50 uint8_t tmp;
51 p.target(&tmp, 1);
52 nall::bpspatch::result r = p.apply();
53 if(r == nall::bpspatch::success) {
54 //Fun, the output is 0 or 1 bytes.
55 std::vector<char> ret;
56 ret.resize(p.size());
57 memcpy(&ret[0], &tmp, p.size());
58 return ret;
59 } else if(r != nall::bpspatch::target_too_small) {
60 //This is actual error in patch.
61 throw_bps_error(r);
63 size_t tsize = p.size();
65 //Okay, do it for real.
66 std::vector<char> ret;
67 ret.resize(tsize);
68 p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
69 p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
70 p.target(reinterpret_cast<uint8_t*>(&ret[0]), tsize);
71 r = p.apply();
72 if(r != nall::bpspatch::success)
73 throw_bps_error(r);
74 return ret;
77 std::pair<size_t, size_t> rewrite_ips_record(std::vector<char>& _patch, size_t woffset,
78 const std::vector<char>& patch, size_t roffset, int32_t offset)
80 if(patch.size() < roffset + 3)
81 throw std::runtime_error("Patch incomplete");
82 uint32_t a, b, c;
83 a = patch[roffset + 0];
84 b = patch[roffset + 1];
85 c = patch[roffset + 2];
86 uint32_t rec_off = a * 65536 + b * 256 + c;
87 if(rec_off == 0x454F46) {
88 //EOF.
89 memcpy(&_patch[woffset], "EOF", 3);
90 return std::make_pair(3, 3);
92 if(patch.size() < roffset + 5)
93 throw std::runtime_error("Patch incomplete");
94 a = patch[roffset + 3];
95 b = patch[roffset + 4];
96 uint32_t rec_size = a * 256 + b;
97 uint32_t rec_rlesize = 0;
98 uint32_t rec_rawsize = 0;
99 if(!rec_size) {
100 if(patch.size() < roffset + 8)
101 throw std::runtime_error("Patch incomplete");
102 a = patch[roffset + 5];
103 b = patch[roffset + 6];
104 rec_rlesize = a * 256 + b;
105 rec_rawsize = 8;
106 } else
107 rec_rawsize = 5 + rec_size;
108 int32_t rec_noff = rec_off + offset;
109 if(rec_noff > 0xFFFFFF)
110 throw std::runtime_error("Offset exceeds IPS 16MiB limit");
111 if(rec_noff < 0) {
112 //This operation needs to clip the start as it is out of bounds.
113 while(rec_size > 0 && rec_noff + rec_size <= 0) {
114 rec_noff++;
115 rec_size--;
117 while(rec_rlesize > 0 && rec_noff + rec_rlesize <= 0) {
118 rec_noff++;
119 rec_rlesize--;
121 if(rec_noff + rec_size + rec_rlesize <= 0)
122 return std::make_pair(0, rec_rawsize); //Completely out of bounds.
124 //Write the modified record.
125 _patch[woffset + 0] = (rec_noff >> 16);
126 _patch[woffset + 1] = (rec_noff >> 8);
127 _patch[woffset + 2] = rec_noff;
128 _patch[woffset + 3] = (rec_size >> 8);
129 _patch[woffset + 4] = rec_size;
130 if(rec_size == 0) {
131 //RLE.
132 _patch[woffset + 5] = (rec_rlesize >> 8);
133 _patch[woffset + 6] = rec_rlesize;
134 _patch[woffset + 7] = patch[roffset + 7];
135 return std::make_pair(8, 8);
136 } else
137 memcpy(&_patch[woffset + 5], &patch[roffset + rec_rawsize - rec_size], rec_size);
138 return std::make_pair(5 + rec_size, rec_rawsize);
141 std::vector<char> rewrite_ips_offset(const std::vector<char>& patch, int32_t offset)
143 size_t wsize = 5;
144 size_t roffset = 5;
145 if(patch.size() < 5)
146 throw std::runtime_error("IPS file doesn't even have magic");
147 std::vector<char> _patch;
148 //The result is at most the size of the original.
149 _patch.resize(patch.size());
150 memcpy(&_patch[0], "PATCH", 5);
151 while(true) {
152 std::pair<size_t, size_t> r = rewrite_ips_record(_patch, wsize, patch, roffset, offset);
153 wsize += r.first;
154 roffset += r.second;
155 if(r.first == 3)
156 break; //EOF.
158 _patch.resize(wsize);
159 return _patch;
162 std::vector<char> do_patch_ips(const std::vector<char>& original, const std::vector<char>& patch,
163 int32_t offset) throw(std::bad_alloc, std::runtime_error)
165 std::vector<char> _original = original;
166 std::vector<char> _patch = rewrite_ips_offset(patch, offset);
167 nall::ips p;
168 p.source(reinterpret_cast<uint8_t*>(&_original[0]), _original.size());
169 p.modify(reinterpret_cast<uint8_t*>(&_patch[0]), _patch.size());
170 if(!p.apply())
171 throw std::runtime_error("Error applying IPS patch");
172 std::vector<char> ret;
173 ret.resize(p.size);
174 memcpy(&ret[0], p.data, p.size);
175 return ret;
179 std::vector<char> do_patch_file(const std::vector<char>& original, const std::vector<char>& patch,
180 int32_t offset) throw(std::bad_alloc, std::runtime_error)
182 if(patch.size() > 5 && patch[0] == 'P' && patch[1] == 'A' && patch[2] == 'T' && patch[3] == 'C' &&
183 patch[4] == 'H')
184 return do_patch_ips(original, patch, offset);
185 else if(patch.size() > 4 && patch[0] == 'B' && patch[1] == 'P' && patch[2] == 'S' && patch[3] == '1')
186 return do_patch_bps(original, patch, offset);
187 else
188 throw std::runtime_error("Unknown patch file format");