Skip to content

Commit 3982e41

Browse files
committed
added bps format
1 parent b467289 commit 3982e41

File tree

7 files changed

+343
-2
lines changed

7 files changed

+343
-2
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ add_library(patch patch.c)
88

99
add_subdirectory(ips)
1010
add_subdirectory(ups)
11+
add_subdirectory(bps)
1112

12-
target_link_libraries(patch PRIVATE ips ups)
13+
target_link_libraries(patch PRIVATE ips ups bps)
1314
target_include_directories(patch PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
1415

1516
target_compile_options(patch PRIVATE

bps/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cmake_minimum_required(VERSION 3.0.2)
2+
3+
project(bps LANGUAGES C)
4+
add_library(bps bps.c)
5+
target_include_directories(bps PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
6+
7+
target_compile_options(bps PRIVATE
8+
$<$<OR:$<C_COMPILER_ID:Clang>,$<C_COMPILER_ID:AppleClang>,$<C_COMPILER_ID:GNU>>:
9+
-Wall
10+
-Wextra
11+
>
12+
$<$<C_COMPILER_ID:MSVC>:
13+
/W4
14+
>
15+
)

bps/bps.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#include "bps.h"
2+
#include <string.h>
3+
4+
5+
/* header is 4 bytes plus at least 2 bytes for input and output size */
6+
#define PATCH_HEADER_SIZE 0x4
7+
#define PATCH_MIN_SIZE (PATCH_HEADER_SIZE + 1 + 1 + 1 + 2 + 12)
8+
9+
10+
static const uint32_t CRC32_TABLE[0x100] =
11+
{
12+
0xD202EF8D, 0xA505DF1B, 0x3C0C8EA1, 0x4B0BBE37, 0xD56F2B94, 0xA2681B02, 0x3B614AB8, 0x4C667A2E,
13+
0xDCD967BF, 0xABDE5729, 0x32D70693, 0x45D03605, 0xDBB4A3A6, 0xACB39330, 0x35BAC28A, 0x42BDF21C,
14+
0xCFB5FFE9, 0xB8B2CF7F, 0x21BB9EC5, 0x56BCAE53, 0xC8D83BF0, 0xBFDF0B66, 0x26D65ADC, 0x51D16A4A,
15+
0xC16E77DB, 0xB669474D, 0x2F6016F7, 0x58672661, 0xC603B3C2, 0xB1048354, 0x280DD2EE, 0x5F0AE278,
16+
0xE96CCF45, 0x9E6BFFD3, 0x0762AE69, 0x70659EFF, 0xEE010B5C, 0x99063BCA, 0x000F6A70, 0x77085AE6,
17+
0xE7B74777, 0x90B077E1, 0x09B9265B, 0x7EBE16CD, 0xE0DA836E, 0x97DDB3F8, 0x0ED4E242, 0x79D3D2D4,
18+
0xF4DBDF21, 0x83DCEFB7, 0x1AD5BE0D, 0x6DD28E9B, 0xF3B61B38, 0x84B12BAE, 0x1DB87A14, 0x6ABF4A82,
19+
0xFA005713, 0x8D076785, 0x140E363F, 0x630906A9, 0xFD6D930A, 0x8A6AA39C, 0x1363F226, 0x6464C2B0,
20+
0xA4DEAE1D, 0xD3D99E8B, 0x4AD0CF31, 0x3DD7FFA7, 0xA3B36A04, 0xD4B45A92, 0x4DBD0B28, 0x3ABA3BBE,
21+
0xAA05262F, 0xDD0216B9, 0x440B4703, 0x330C7795, 0xAD68E236, 0xDA6FD2A0, 0x4366831A, 0x3461B38C,
22+
0xB969BE79, 0xCE6E8EEF, 0x5767DF55, 0x2060EFC3, 0xBE047A60, 0xC9034AF6, 0x500A1B4C, 0x270D2BDA,
23+
0xB7B2364B, 0xC0B506DD, 0x59BC5767, 0x2EBB67F1, 0xB0DFF252, 0xC7D8C2C4, 0x5ED1937E, 0x29D6A3E8,
24+
0x9FB08ED5, 0xE8B7BE43, 0x71BEEFF9, 0x06B9DF6F, 0x98DD4ACC, 0xEFDA7A5A, 0x76D32BE0, 0x01D41B76,
25+
0x916B06E7, 0xE66C3671, 0x7F6567CB, 0x0862575D, 0x9606C2FE, 0xE101F268, 0x7808A3D2, 0x0F0F9344,
26+
0x82079EB1, 0xF500AE27, 0x6C09FF9D, 0x1B0ECF0B, 0x856A5AA8, 0xF26D6A3E, 0x6B643B84, 0x1C630B12,
27+
0x8CDC1683, 0xFBDB2615, 0x62D277AF, 0x15D54739, 0x8BB1D29A, 0xFCB6E20C, 0x65BFB3B6, 0x12B88320,
28+
0x3FBA6CAD, 0x48BD5C3B, 0xD1B40D81, 0xA6B33D17, 0x38D7A8B4, 0x4FD09822, 0xD6D9C998, 0xA1DEF90E,
29+
0x3161E49F, 0x4666D409, 0xDF6F85B3, 0xA868B525, 0x360C2086, 0x410B1010, 0xD80241AA, 0xAF05713C,
30+
0x220D7CC9, 0x550A4C5F, 0xCC031DE5, 0xBB042D73, 0x2560B8D0, 0x52678846, 0xCB6ED9FC, 0xBC69E96A,
31+
0x2CD6F4FB, 0x5BD1C46D, 0xC2D895D7, 0xB5DFA541, 0x2BBB30E2, 0x5CBC0074, 0xC5B551CE, 0xB2B26158,
32+
0x04D44C65, 0x73D37CF3, 0xEADA2D49, 0x9DDD1DDF, 0x03B9887C, 0x74BEB8EA, 0xEDB7E950, 0x9AB0D9C6,
33+
0x0A0FC457, 0x7D08F4C1, 0xE401A57B, 0x930695ED, 0x0D62004E, 0x7A6530D8, 0xE36C6162, 0x946B51F4,
34+
0x19635C01, 0x6E646C97, 0xF76D3D2D, 0x806A0DBB, 0x1E0E9818, 0x6909A88E, 0xF000F934, 0x8707C9A2,
35+
0x17B8D433, 0x60BFE4A5, 0xF9B6B51F, 0x8EB18589, 0x10D5102A, 0x67D220BC, 0xFEDB7106, 0x89DC4190,
36+
0x49662D3D, 0x3E611DAB, 0xA7684C11, 0xD06F7C87, 0x4E0BE924, 0x390CD9B2, 0xA0058808, 0xD702B89E,
37+
0x47BDA50F, 0x30BA9599, 0xA9B3C423, 0xDEB4F4B5, 0x40D06116, 0x37D75180, 0xAEDE003A, 0xD9D930AC,
38+
0x54D13D59, 0x23D60DCF, 0xBADF5C75, 0xCDD86CE3, 0x53BCF940, 0x24BBC9D6, 0xBDB2986C, 0xCAB5A8FA,
39+
0x5A0AB56B, 0x2D0D85FD, 0xB404D447, 0xC303E4D1, 0x5D677172, 0x2A6041E4, 0xB369105E, 0xC46E20C8,
40+
0x72080DF5, 0x050F3D63, 0x9C066CD9, 0xEB015C4F, 0x7565C9EC, 0x0262F97A, 0x9B6BA8C0, 0xEC6C9856,
41+
0x7CD385C7, 0x0BD4B551, 0x92DDE4EB, 0xE5DAD47D, 0x7BBE41DE, 0x0CB97148, 0x95B020F2, 0xE2B71064,
42+
0x6FBF1D91, 0x18B82D07, 0x81B17CBD, 0xF6B64C2B, 0x68D2D988, 0x1FD5E91E, 0x86DCB8A4, 0xF1DB8832,
43+
0x616495A3, 0x1663A535, 0x8F6AF48F, 0xF86DC419, 0x660951BA, 0x110E612C, 0x88073096, 0xFF000000
44+
};
45+
46+
/* SOURCE: http://home.thep.lu.se/~bjorn/crc/ */
47+
static uint32_t crc32(const uint8_t* data, const size_t size)
48+
{
49+
if (!data || !size)
50+
{
51+
return 0;
52+
}
53+
54+
uint32_t crc = 0;
55+
56+
for (size_t i = 0; i < size; ++i)
57+
{
58+
crc = CRC32_TABLE[(uint8_t)crc ^ data[i]] ^ crc >> 8;
59+
}
60+
61+
return crc;
62+
}
63+
64+
/* this can fail if the int is bigger than 8 bytes */
65+
static size_t vln_read(const uint8_t* data, size_t* offset)
66+
{
67+
size_t result = 0;
68+
size_t shift = 0;
69+
70+
/* just in case its a bad patch, only run until max size */
71+
for (uint8_t i = 0; i < sizeof(size_t); ++i)
72+
{
73+
const uint8_t value = data[*offset];
74+
++*offset;
75+
76+
if (value & 0x80)
77+
{
78+
result += (value & 0x7F) << shift;
79+
break;
80+
}
81+
82+
result += (value | 0x80) << shift;
83+
shift += 7;
84+
}
85+
86+
return result;
87+
}
88+
89+
bool bps_verify_header(const uint8_t* patch, size_t patch_size)
90+
{
91+
if (!patch || patch_size < PATCH_HEADER_SIZE)
92+
{
93+
return false;
94+
}
95+
96+
if (patch[0] != 'B' || patch[1] != 'P' || patch[2] != 'S' || patch[3] != '1')
97+
{
98+
return false;
99+
}
100+
101+
return true;
102+
}
103+
104+
/* dst_size: [optional] */
105+
/* src_size: [optional] */
106+
/* meta_size: [optional] */
107+
/* offset: [optional] */
108+
bool bps_get_sizes(
109+
const uint8_t* patch, size_t patch_size,
110+
size_t* dst_size, size_t* src_size, size_t* meta_size, size_t* offset
111+
) {
112+
size_t offset_local = PATCH_HEADER_SIZE;
113+
114+
const size_t source_size = vln_read(patch, &offset_local);
115+
const size_t target_size = vln_read(patch, &offset_local);
116+
const size_t metadata_size = vln_read(patch, &offset_local);
117+
118+
if (dst_size)
119+
{
120+
*dst_size = target_size;
121+
}
122+
if (src_size)
123+
{
124+
*src_size = source_size;
125+
}
126+
if (meta_size)
127+
{
128+
*meta_size = metadata_size;
129+
}
130+
if (offset)
131+
{
132+
*offset = offset_local;
133+
}
134+
135+
return true;
136+
}
137+
138+
/* dst_size: large enough to fit entire output */
139+
bool bps_patch(
140+
uint8_t* dst, size_t dst_size,
141+
const uint8_t* src, size_t src_size,
142+
const uint8_t* patch, size_t patch_size
143+
) {
144+
if (!bps_verify_header(patch, patch_size))
145+
{
146+
return false;
147+
}
148+
149+
size_t patch_offset = PATCH_HEADER_SIZE;
150+
size_t src_relative_offset = 0;
151+
size_t dst_offset = 0;
152+
size_t dst_relative_offset = 0;
153+
154+
size_t source_size = 0;
155+
size_t target_size = 0;
156+
size_t metadata_size = 0;
157+
158+
if (!bps_get_sizes(patch, patch_size, &target_size, &source_size, &metadata_size, &patch_offset))
159+
{
160+
return false;
161+
}
162+
163+
if (src_size != source_size)
164+
{
165+
return false;
166+
}
167+
168+
if (dst_size != target_size)
169+
{
170+
return false;
171+
}
172+
173+
/* skip over metadata */
174+
patch_offset += metadata_size;
175+
176+
/* crc's are at the last 12 bytes, each 4 bytes each. */
177+
uint32_t src_crc = 0;
178+
uint32_t dst_crc = 0;
179+
uint32_t patch_crc = 0;
180+
181+
memcpy(&src_crc, patch + (patch_size - 12), sizeof(src_crc));
182+
memcpy(&dst_crc, patch + (patch_size - 8), sizeof(dst_crc));
183+
memcpy(&patch_crc, patch + (patch_size - 4), sizeof(patch_crc));
184+
185+
if (src_crc != crc32(src, src_size))
186+
{
187+
return false;
188+
}
189+
190+
/* we don't check it's own crc32 (obviously) */
191+
if (patch_crc != crc32(patch, patch_size - 4))
192+
{
193+
return false;
194+
}
195+
196+
/* we've read the crc's now, reduce the size. */
197+
patch_size -= 12;
198+
199+
enum Action
200+
{
201+
SourceRead = 0,
202+
TargetRead = 1,
203+
SourceCopy = 2,
204+
TargetCopy = 3,
205+
};
206+
207+
while (patch_offset < patch_size)
208+
{
209+
const size_t data = vln_read(patch, &patch_offset);
210+
const enum Action action = data & 3;
211+
size_t len = (data >> 2) + 1;
212+
213+
switch (action)
214+
{
215+
case SourceRead: {
216+
while (len--)
217+
{
218+
dst[dst_offset] = src[dst_offset];
219+
dst_offset++;
220+
}
221+
} break;
222+
223+
case TargetRead: {
224+
while (len--)
225+
{
226+
dst[dst_offset++] = patch[patch_offset++];
227+
}
228+
} break;
229+
230+
case SourceCopy: {
231+
const size_t data = vln_read(patch, &patch_offset);
232+
src_relative_offset += (data & 1 ? -1 : +1) * (data >> 1);
233+
while (len--)
234+
{
235+
dst[dst_offset++] = src[src_relative_offset++];
236+
}
237+
} break;
238+
239+
case TargetCopy: {
240+
const size_t data = vln_read(patch, &patch_offset);
241+
dst_relative_offset += (data & 1 ? -1 : +1) * (data >> 1);
242+
while (len--)
243+
{
244+
dst[dst_offset++] = dst[dst_relative_offset++];
245+
}
246+
} break;
247+
248+
}
249+
}
250+
251+
if (dst_crc != crc32(dst, dst_size))
252+
{
253+
return false;
254+
}
255+
256+
return true;
257+
}

bps/bps.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#include <stdbool.h>
8+
#include <stdint.h>
9+
#include <stddef.h>
10+
11+
12+
bool bps_verify_header(const uint8_t* patch, size_t patch_size);
13+
14+
/* dst_size: [optional] */
15+
/* src_size: [optional] */
16+
/* meta_size: [optional] */
17+
/* offset: [optional] */
18+
bool bps_get_sizes(
19+
const uint8_t* patch, size_t patch_size,
20+
size_t* dst_size, size_t* src_size, size_t* meta_size, size_t* offset
21+
);
22+
23+
/* dst_size: large enough to fit entire output */
24+
bool bps_patch(
25+
uint8_t* dst, size_t dst_size,
26+
const uint8_t* src, size_t src_size,
27+
const uint8_t* patch, size_t patch_size
28+
);
29+
30+
#ifdef __cplusplus
31+
}
32+
#endif

examples/patcher/main.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ static int get_patch_type(const char* file_name)
2424
{
2525
[PatchType_IPS] = ".ips",
2626
[PatchType_UPS] = ".ups",
27+
[PatchType_BPS] = ".bps",
2728
};
2829

2930
const char* ext = strrchr(file_name, '.');
@@ -42,6 +43,11 @@ static int get_patch_type(const char* file_name)
4243
return PatchType_UPS;
4344
}
4445

46+
if (!strcmp(ext, extentions[PatchType_BPS]))
47+
{
48+
return PatchType_BPS;
49+
}
50+
4551
return -1;
4652
}
4753

@@ -163,7 +169,7 @@ int main(int argc, char** argv)
163169
const int patch_type = get_patch_type(patch_filename);
164170
if (patch_type == -1)
165171
{
166-
return cleanup("unkown patch type");
172+
return cleanup("unknown patch type");
167173
}
168174

169175
if (PatchError_OK != patch(patch_type, &OUT_DATA, &OUT_SIZE, ROM_DATA, ROM_SIZE, PATCH_DATA, PATCH_SIZE))

patch.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "patch.h"
22
#include "ips/ips.h"
33
#include "ups/ups.h"
4+
#include "bps/bps.h"
45
#include <stdlib.h>
56

67

@@ -68,6 +69,34 @@ enum PatchError patch(
6869
goto fail;
6970
}
7071
} break;
72+
73+
case PatchType_BPS: {
74+
if (!bps_verify_header(patch_data, patch_size))
75+
{
76+
error = PatchError_HEADER;
77+
goto fail;
78+
}
79+
80+
// bps patches can increase / decrease the size of src data.
81+
if (!bps_get_sizes(patch_data, patch_size, dst_size, NULL, NULL, NULL))
82+
{
83+
error = PatchError_BAD_SIZE;
84+
goto fail;
85+
}
86+
87+
*dst_data = malloc(*dst_size);
88+
if (!*dst_data)
89+
{
90+
error = PatchError_MALLOC;
91+
goto fail;
92+
}
93+
94+
if (!bps_patch(*dst_data, *dst_size, src_data, src_size, patch_data, patch_size))
95+
{
96+
error = PatchError_PATCH;
97+
goto fail;
98+
}
99+
} break;
71100
}
72101

73102
return PatchError_OK;

patch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ enum PatchType
1313
{
1414
PatchType_IPS,
1515
PatchType_UPS,
16+
PatchType_BPS,
1617
};
1718

1819
enum PatchError

0 commit comments

Comments
 (0)