forked from xndcn/pebble-firmware-utils
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathpatcher.py
executable file
·88 lines (79 loc) · 3.39 KB
/
patcher.py
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
#!/usr/bin/env python3
from libpatcher import Patch, Ranges, parseFile
def parse_args():
import argparse
parser = argparse.ArgumentParser(
description="Pebble firmware patcher")
parser.add_argument("patch", nargs='+', type=argparse.FileType("r"),
help="File with a patch to apply")
parser.add_argument("-o", "--output", required=True,
type=argparse.FileType("wb"),
help="Output file name")
parser.add_argument("-t", "--tintin", nargs='?', default="tintin_fw.bin",
type=argparse.FileType("rb"),
help="Input tintin_fw file, defaults to tintin_fw.bin")
parser.add_argument("-d", "--debug", action="store_true",
help="Print debug information while patching")
parser.add_argument("-D", "--define", action="append", default=[],
help="Add some #define'd constant. "
"Usage: either -D constant or -D name=val")
parser.add_argument("-i", "--ignore-length", action="store_true",
help="Don't check for mask length "
"when overwriting block (dangerous!")
parser.add_argument("-a", "--append", action="store_true",
help="Use space in the end of firmware "
"to store floating blocks")
parser.add_argument("-A", "--always-append", action="store_true",
help="Same as --append, but doesn't check "
"for maximum file size. "
"Useful for PebbleTime firmware "
"which seems to have other size limits")
parser.add_argument("-c", "--codebase", type=lambda x: int(x, base=0),
default=0x8004000,
help="Codebase of the binary. "
"Defaults to 0x8004000 (which is for 3.x fw); "
"for 1.x-2.x set it to 0x8010000")
return parser.parse_args()
def patch_fw(args):
data = args.tintin.read()
# this is a library patch,
# which will hold all #included blocks
library = Patch("#library", binary=data)
# this holds list of ranges
ranges = Ranges()
if args.append or args.always_append:
ranges.add_eof(data, 0x70000 if args.append else 0x1000000,
0x48)
# this is for #defined and pre#defined values
definitions = {}
for d in args.define:
if '=' in d:
name, val = d.split('=', 1)
definitions[name] = val
else:
definitions[d] = True
# Read all requested patch files
patches = [library]
print("Loading files:")
for f in args.patch:
print(f.name)
patches.append(parseFile(f, definitions, libpatch=library))
# Bind them all to real binary (i.e. scan masks)...
print("Binding patches:")
for p in patches: # including library
print(p)
p.bindall(data, ranges, args.codebase)
# ...and apply
print("Applying patches:")
for p in patches:
print(p)
data = p.apply(data, args.codebase, ignore=args.ignore_length)
print("Saving...")
# restore eof bytes, if file-end range was used
data = ranges.restore_tail(data)
args.output.write(data)
args.output.close()
print("Done.")
if __name__ == "__main__":
args = parse_args()
patch_fw(args)