1 #include "core/patchrom.hpp"
2 #include "core/misc.hpp"
4 #include <nall/ips.hpp>
5 #include <nall/bps/patch.hpp>
10 void throw_bps_error(nall::bpspatch::result r
)
14 case nall::bpspatch::unknown
:
15 throw std::runtime_error("Unknown error status");
16 case nall::bpspatch::success
:
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");
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
)
41 throw std::runtime_error("Offsets are not supported for .bps patches");
42 std::vector
<char> _original
= original
;
43 std::vector
<char> _patch
= patch
;
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.
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
;
57 memcpy(&ret
[0], &tmp
, p
.size());
59 } else if(r
!= nall::bpspatch::target_too_small
) {
60 //This is actual error in patch.
63 size_t tsize
= p
.size();
65 //Okay, do it for real.
66 std::vector
<char> ret
;
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
);
72 if(r
!= nall::bpspatch::success
)
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");
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) {
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;
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
;
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");
112 //This operation needs to clip the start as it is out of bounds.
113 while(rec_size
> 0 && rec_noff
+ rec_size
<= 0) {
117 while(rec_rlesize
> 0 && rec_noff
+ rec_rlesize
<= 0) {
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
;
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);
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
)
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);
152 std::pair
<size_t, size_t> r
= rewrite_ips_record(_patch
, wsize
, patch
, roffset
, offset
);
158 _patch
.resize(wsize
);
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
);
168 p
.source(reinterpret_cast<uint8_t*>(&_original
[0]), _original
.size());
169 p
.modify(reinterpret_cast<uint8_t*>(&_patch
[0]), _patch
.size());
171 throw std::runtime_error("Error applying IPS patch");
172 std::vector
<char> ret
;
174 memcpy(&ret
[0], p
.data
, p
.size
);
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' &&
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
);
188 throw std::runtime_error("Unknown patch file format");