1
|
|
|
#!/usr/bin/env python3 |
2
|
4 |
|
"""This module is used for creation of autoloaders. |
3
|
|
|
A higher level interface for :mod:`bbarchivist.pseudocap`.""" |
4
|
|
|
|
5
|
4 |
|
import os # path work |
6
|
4 |
|
import glob # filename matching |
7
|
4 |
|
from bbarchivist import exceptions # exception handling |
8
|
4 |
|
from bbarchivist import pseudocap # implement cap |
9
|
4 |
|
from bbarchivist import jsonutils # json |
10
|
|
|
|
11
|
4 |
|
__author__ = "Thurask" |
12
|
4 |
|
__license__ = "WTFPL v2" |
13
|
4 |
|
__copyright__ = "Copyright 2015-2016 Thurask" |
14
|
|
|
|
15
|
|
|
|
16
|
4 |
|
def read_files(localdir, core=False): |
17
|
|
|
""" |
18
|
|
|
Read list of signed files, return name assignments. |
19
|
|
|
|
20
|
|
|
:param localdir: Directory to use. |
21
|
|
|
:type localdir: str |
22
|
|
|
|
23
|
|
|
:param core: If we're using a core OS image. Default is false. |
24
|
|
|
:type core: bool |
25
|
|
|
""" |
26
|
4 |
|
oslist = read_os_files(localdir, core) |
27
|
|
|
# [8960, 8x30, 8974, ti] |
28
|
4 |
|
radlist = read_radio_files(localdir) |
29
|
|
|
# [ti, z10, z10_vzw, q10, z30, z3, 8974] |
30
|
4 |
|
pairdict = {} |
31
|
4 |
|
mapping = {0:3, 1:0, 2:0, 3:0, 4:0, 5:1, 6:2} |
32
|
4 |
|
for idx, rad in enumerate(radlist): |
33
|
4 |
|
pairdict[rad] = oslist[mapping[idx]] |
34
|
4 |
|
filtdict = {k: v for k, v in pairdict.items() if k} # pop None |
35
|
4 |
|
return filtdict |
36
|
|
|
|
37
|
|
|
|
38
|
4 |
|
def find_signed_file(match, localdir, title, silent=False): |
39
|
|
|
""" |
40
|
|
|
Use pattern matching to find a signed file in a directory. |
41
|
|
|
|
42
|
|
|
:param match: Match pattern to use. |
43
|
|
|
:type match: str |
44
|
|
|
|
45
|
|
|
:param localdir: Directory to use. |
46
|
|
|
:type localdir: str |
47
|
|
|
|
48
|
|
|
:param title: File type, in case it isn't found. |
49
|
|
|
:type title: str |
50
|
|
|
|
51
|
|
|
:param silent: Don't print that a file wasn't found. Default is False. |
52
|
|
|
:type silent: bool |
53
|
|
|
""" |
54
|
4 |
|
try: |
55
|
4 |
|
signedfile = glob.glob(os.path.join(localdir, match))[0] |
56
|
4 |
|
except IndexError: |
57
|
4 |
|
signedfile = None |
58
|
4 |
|
if not silent: |
59
|
4 |
|
print("No {0} found".format(title)) |
60
|
4 |
|
return signedfile |
61
|
|
|
|
62
|
|
|
|
63
|
4 |
|
def read_os_files(localdir, core=False): |
64
|
|
|
""" |
65
|
|
|
Read list of OS signed files, return name assignments. |
66
|
|
|
|
67
|
|
|
:param localdir: Directory to use. |
68
|
|
|
:type localdir: str |
69
|
|
|
|
70
|
|
|
:param core: If we're using a core OS image. Default is false. |
71
|
|
|
:type core: bool |
72
|
|
|
""" |
73
|
4 |
|
if core: |
74
|
4 |
|
fix8960 = "*qc8960.*_sfi.BB*.signed" |
75
|
4 |
|
fixomap_new = "*winchester.*_sfi.BB*.signed" |
76
|
4 |
|
fixomap_old = "*os.factory_sfi.BB*.signed" |
77
|
4 |
|
fix8930 = "*qc8x30.BB*.signed" |
78
|
4 |
|
fix8974_new = "*qc8974.BB*.signed" |
79
|
4 |
|
fix8974_old = "*qc8974.*_sfi.BB*.signed" |
80
|
|
|
else: |
81
|
4 |
|
fix8960 = "*qc8960.*_sfi.desktop.BB*.signed" |
82
|
4 |
|
fixomap_new = "*winchester.*_sfi.desktop.BB*.signed" |
83
|
4 |
|
fixomap_old = "*os.factory_sfi.desktop.BB*.signed" |
84
|
4 |
|
fix8930 = "*qc8x30.desktop.BB*.signed" |
85
|
4 |
|
fix8974_new = "*qc8974.desktop.BB*.signed" |
86
|
4 |
|
fix8974_old = "*qc8974.*_sfi.desktop.BB*.signed" |
87
|
|
|
# 8960 |
88
|
4 |
|
os_8960 = find_signed_file(fix8960, localdir, "8960 OS") |
89
|
|
|
# 8x30 (10.3.1 MR+) |
90
|
4 |
|
os_8x30 = find_signed_file(fix8930, localdir, "8x30 OS", True) |
91
|
4 |
|
if os_8x30 is None: |
92
|
4 |
|
os_8x30 = find_signed_file(fix8960, localdir, "8x30 OS") |
93
|
|
|
# 8974 |
94
|
4 |
|
os_8974 = find_signed_file(fix8974_new, localdir, "8974 OS", True) |
95
|
4 |
|
if os_8974 is None: |
96
|
4 |
|
os_8974 = find_signed_file(fix8974_old, localdir, "8974 OS") |
97
|
|
|
# OMAP |
98
|
4 |
|
os_ti = find_signed_file(fixomap_new, localdir, "OMAP OS", True) |
99
|
4 |
|
if os_ti is None: |
100
|
4 |
|
os_ti = find_signed_file(fixomap_old, localdir, "OMAP OS") |
101
|
4 |
|
return [os_8960, os_8x30, os_8974, os_ti] |
102
|
|
|
|
103
|
|
|
|
104
|
4 |
|
def read_radio_files(localdir): |
105
|
|
|
""" |
106
|
|
|
Read list of radio signed files, return name assignments. |
107
|
|
|
|
108
|
|
|
:param localdir: Directory to use. |
109
|
|
|
:type localdir: str |
110
|
|
|
""" |
111
|
|
|
# STL100-1 |
112
|
4 |
|
radio_ti = find_signed_file("*radio.m5730*.signed", localdir, "OMAP radio") |
113
|
|
|
# STL100-X |
114
|
4 |
|
radio_z10 = find_signed_file("*radio.qc8960.BB*.signed", localdir, "8960 radio") |
115
|
|
|
# STL100-4 |
116
|
4 |
|
radio_z10_vzw = find_signed_file("*radio.qc8960*omadm*.signed", localdir, "VZW 8960 radio") |
117
|
|
|
# Q10/Q5 |
118
|
4 |
|
radio_q10 = find_signed_file("*radio.qc8960*wtr.*signed", localdir, "Q10/Q5 radio") |
119
|
|
|
# Z30/Classic |
120
|
4 |
|
radio_z30 = find_signed_file("*radio.qc8960*wtr5*.signed", localdir, "Z30/Classic radio") |
121
|
|
|
# Z3 |
122
|
4 |
|
radio_z3 = find_signed_file("*radio.qc8930*wtr5*.signed", localdir, "Z3 radio") |
123
|
|
|
# Passport |
124
|
4 |
|
radio_8974 = find_signed_file("*radio.qc8974*wtr2*.signed", localdir, "Passport radio") |
125
|
4 |
|
return [radio_ti, radio_z10, radio_z10_vzw, radio_q10, radio_z30, radio_z3, radio_8974] |
126
|
|
|
|
127
|
|
|
|
128
|
4 |
|
def zeropad(splitver, idx, padlen): |
129
|
|
|
""" |
130
|
|
|
Zero-pad an element of an OS/radio version to a certain length. |
131
|
|
|
|
132
|
|
|
:param splitver: OS/radio version, but split into quarters. |
133
|
|
|
:type splitver: list(str) |
134
|
|
|
|
135
|
|
|
:param idx: Index of splitver which must be checked. |
136
|
|
|
:type idx: int |
137
|
|
|
|
138
|
|
|
:param padlen: Length to pad to. |
139
|
|
|
:type padlen: int |
140
|
|
|
""" |
141
|
4 |
|
if len(splitver[idx]) < padlen: |
142
|
4 |
|
splitver[idx] = splitver[idx].rjust(padlen, "0") |
143
|
4 |
|
return splitver |
144
|
|
|
|
145
|
|
|
|
146
|
4 |
|
def versionpad(splitver): |
147
|
|
|
""" |
148
|
|
|
Properly pad an OS/radio version. |
149
|
|
|
|
150
|
|
|
:param splitver: OS/radio version, but split into quarters. |
151
|
|
|
:type splitver: list(str) |
152
|
|
|
""" |
153
|
4 |
|
splitver = zeropad(splitver, 2, 2) |
154
|
4 |
|
splitver = zeropad(splitver, 3, 4) |
155
|
4 |
|
return splitver |
156
|
|
|
|
157
|
|
|
|
158
|
4 |
|
def pretty_formatter(osversion, radioversion): |
159
|
|
|
""" |
160
|
|
|
Format OS/radio versions to cope with systems with poor sorting. |
161
|
|
|
|
162
|
|
|
:param osversion: OS version, 10.x.y.zzzz. |
163
|
|
|
:type osversion: str |
164
|
|
|
|
165
|
|
|
:param radioversion: Radio version, 10.x.y.zzzz. |
166
|
|
|
:type radioversion: str |
167
|
|
|
""" |
168
|
|
|
# 10.x.y.zzz becomes 10.x.0y.0zzz |
169
|
4 |
|
splitos = osversion.split(".") |
170
|
4 |
|
splitos = versionpad(splitos) |
171
|
4 |
|
the_os = ".".join(splitos) |
172
|
4 |
|
splitrad = radioversion.split(".") |
173
|
4 |
|
splitrad = versionpad(splitrad) |
174
|
4 |
|
the_radio = ".".join(splitrad) |
175
|
4 |
|
return the_os, the_radio |
176
|
|
|
|
177
|
|
|
|
178
|
4 |
|
def format_suffix(altradio=None, radioversion=None, core=False): |
179
|
|
|
""" |
180
|
|
|
Formulate suffix for hybrid autoloaders. |
181
|
|
|
|
182
|
|
|
:param altradio: If a hybrid autoloader is being made. |
183
|
|
|
:type altradio: bool |
184
|
|
|
|
185
|
|
|
:param radioversion: The hybrid radio version, if applicable. |
186
|
|
|
:type radioversion: str |
187
|
|
|
|
188
|
|
|
:param core: If we're using a core OS image. Default is false. |
189
|
|
|
:type core: bool |
190
|
|
|
""" |
191
|
4 |
|
suffix = "_R{0}".format(radioversion) if altradio and radioversion else "" |
192
|
4 |
|
if core: |
193
|
4 |
|
suffix += "_CORE" |
194
|
4 |
|
return suffix |
195
|
|
|
|
196
|
|
|
|
197
|
4 |
|
def generate_loaders(osversion, radioversion, radios=True, localdir=None, altradio=False, core=False): |
198
|
|
|
""" |
199
|
|
|
Create and label autoloaders for :mod:`bbarchivist.scripts.archivist`. |
200
|
|
|
|
201
|
|
|
:param osversion: OS version, 10.x.y.zzzz. |
202
|
|
|
:type osversion: str |
203
|
|
|
|
204
|
|
|
:param radioversion: Radio version, 10.x.y.zzzz. |
205
|
|
|
:type radioversion: str |
206
|
|
|
|
207
|
|
|
:param radios: Whether to make radios or not. True by default. |
208
|
|
|
:type radios: bool |
209
|
|
|
|
210
|
|
|
:param localdir: Working path. Default is local dir. |
211
|
|
|
:type localdir: str |
212
|
|
|
|
213
|
|
|
:param altradio: If we're using an alternate radio. Default is false. |
214
|
|
|
:type altradio: bool |
215
|
|
|
|
216
|
|
|
:param core: If we're using a core OS image. Default is false. |
217
|
|
|
:type core: bool |
218
|
|
|
""" |
219
|
|
|
# default parsing |
220
|
4 |
|
localdir = os.getcwd() if localdir is None else localdir |
221
|
4 |
|
print("GETTING FILENAMES...") |
222
|
4 |
|
filedict = read_files(localdir, core) |
223
|
4 |
|
osversion, radioversion = pretty_formatter(osversion, radioversion) |
224
|
4 |
|
suffix = format_suffix(altradio, radioversion, core) |
225
|
|
|
# Generate loaders |
226
|
4 |
|
print("CREATING LOADERS...") |
227
|
4 |
|
filtrad = [rad for rad in filedict.keys() if rad] # pop None |
228
|
4 |
|
generate_individual_loaders(filtrad, osversion, radioversion, suffix, filedict, radios, localdir) |
229
|
|
|
|
230
|
|
|
|
231
|
4 |
|
def generate_individual_loaders(filtrad, osversion, radioversion, suffix, filedict, radios, localdir): |
232
|
|
|
""" |
233
|
|
|
Generate individual loaders when generating several at once. |
234
|
|
|
|
235
|
|
|
:param filtrad: List of radio files, if they exist. |
236
|
|
|
:type filtrad: list(str) |
237
|
|
|
|
238
|
|
|
:param osversion: OS version, 10.x.y.zzzz. |
239
|
|
|
:type osversion: str |
240
|
|
|
|
241
|
|
|
:param radioversion: Radio version, 10.x.y.zzzz. |
242
|
|
|
:type radioversion: str |
243
|
|
|
|
244
|
|
|
:param suffix: Alternate radio, or blank. |
245
|
|
|
:type suffix: str |
246
|
|
|
|
247
|
|
|
:param filedict: Dictionary of radio:OS pairs. |
248
|
|
|
:type filedict: dict(str: str) |
249
|
|
|
|
250
|
|
|
:param radios: Whether to make radios or not. True by default. |
251
|
|
|
:type radios: bool |
252
|
|
|
|
253
|
|
|
:param localdir: Working path. Default is local dir. |
254
|
|
|
:type localdir: str |
255
|
|
|
""" |
256
|
4 |
|
for radval in filtrad: |
257
|
4 |
|
device = generate_device(radval) |
258
|
4 |
|
osname = generate_filename(device, osversion, suffix) |
259
|
4 |
|
osfile = filedict[radval] |
260
|
4 |
|
if osfile is not None: |
261
|
4 |
|
wrap_pseudocap(osname, localdir, osfile, radval) |
262
|
4 |
|
if radios: |
263
|
4 |
|
radname = generate_filename(device, radioversion, "") |
264
|
4 |
|
wrap_pseudocap(radname, localdir, radval) |
265
|
|
|
|
266
|
|
|
|
267
|
|
|
|
268
|
4 |
|
def wrap_pseudocap(filename, folder, first, second=None): |
269
|
|
|
""" |
270
|
|
|
A filtered, excepting wrapper for pseudocap. |
271
|
|
|
|
272
|
|
|
:param filename: The title of the new loader. |
273
|
|
|
:type filename: str |
274
|
|
|
|
275
|
|
|
:param folder: The folder to create the loader in. |
276
|
|
|
:type folder: str |
277
|
|
|
|
278
|
|
|
:param first: The first signed file, required. |
279
|
|
|
:type first: str |
280
|
|
|
|
281
|
|
|
:param second: The second signed file, optional. |
282
|
|
|
:type second: str |
283
|
|
|
""" |
284
|
4 |
|
if first is None: |
285
|
4 |
|
print("No OS!") |
286
|
4 |
|
raise SystemError |
287
|
4 |
|
try: |
288
|
4 |
|
pseudocap.make_autoloader(filename, [first, second], folder=folder) |
289
|
4 |
|
except (OSError, IndexError, SystemError) as exc: |
290
|
4 |
|
msg = "Could not create {0}".format(filename) |
291
|
4 |
|
exceptions.handle_exception(exc, msg, None) |
292
|
|
|
|
293
|
|
|
|
294
|
4 |
|
def generate_skeletons(): |
295
|
|
|
""" |
296
|
|
|
Read JSON to get a dict of all filename components. |
297
|
|
|
""" |
298
|
4 |
|
namelist = {0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None} |
299
|
4 |
|
data = jsonutils.load_json('integermap') |
300
|
4 |
|
for key in data: |
301
|
4 |
|
if key['id'] in namelist: |
302
|
4 |
|
namelist[key['id']] = (key['parts']) |
303
|
4 |
|
namelist[key['id']].append(".exe") |
304
|
4 |
|
return namelist |
305
|
|
|
|
306
|
|
|
|
307
|
4 |
|
def generate_device(radio): |
308
|
|
|
""" |
309
|
|
|
Read JSON to get the device integer ID from device radio. |
310
|
|
|
|
311
|
|
|
:param radio: The radio filename to look up. |
312
|
|
|
:type radio: str |
313
|
|
|
""" |
314
|
4 |
|
data = jsonutils.load_json('integermap') |
315
|
4 |
|
for key in data: |
316
|
4 |
|
if key['radtype'] in radio: |
317
|
4 |
|
idx = int(key['id']) |
318
|
4 |
|
break |
319
|
4 |
|
return idx |
320
|
|
|
|
321
|
|
|
|
322
|
4 |
|
def generate_filename(device, version, suffix=None): |
323
|
|
|
""" |
324
|
|
|
Use skeleton dict to create loader filenames. |
325
|
|
|
|
326
|
|
|
:param device: Device to use. |
327
|
|
|
:type device: int |
328
|
|
|
|
329
|
|
|
:param version: OS or radio version. |
330
|
|
|
:type version: str |
331
|
|
|
|
332
|
|
|
:param suffix: Alternate radio, or blank. |
333
|
|
|
:type suffix: str |
334
|
|
|
""" |
335
|
4 |
|
thed = generate_skeletons() |
336
|
4 |
|
if device < 0: |
337
|
4 |
|
return None |
338
|
4 |
|
dev = thed[device] |
339
|
4 |
|
if suffix is None: |
340
|
4 |
|
suffix = "" |
341
|
4 |
|
return "{0}{1}{2}{3}{4}".format(dev[0], version, suffix, dev[1], dev[2]) |
342
|
|
|
|
343
|
|
|
|
344
|
4 |
|
def generate_lazy_loader( |
345
|
|
|
osversion, device, |
346
|
|
|
localdir=None, altradio=None, core=False): |
347
|
|
|
""" |
348
|
|
|
Create and label autoloaders for :mod:`bbarchivist.scripts.lazyloader`. |
349
|
|
|
:func:`generate_loaders`, but for making one OS/radio loader. |
350
|
|
|
|
351
|
|
|
:param osversion: OS version, 10.x.y.zzzz. |
352
|
|
|
:type osversion: str |
353
|
|
|
|
354
|
|
|
:param device: Selected device, from |
355
|
|
|
:type device: int |
356
|
|
|
|
357
|
|
|
:param localdir: Working path. Default is local dir. |
358
|
|
|
:type localdir: str |
359
|
|
|
|
360
|
|
|
:param altradio: The alternate radio in use, if there is one. |
361
|
|
|
:type altradio: str |
362
|
|
|
|
363
|
|
|
:param core: If we're using a core OS image. Default is false. |
364
|
|
|
:type core: bool |
365
|
|
|
""" |
366
|
|
|
# default parsing |
367
|
4 |
|
localdir = os.getcwd() if localdir is None else localdir |
368
|
4 |
|
print("CREATING LOADER...") |
369
|
4 |
|
suffix = format_suffix(bool(altradio), altradio, core) |
370
|
4 |
|
osfile = None |
371
|
4 |
|
absoglob = "{0}{1}".format(localdir, os.sep) |
372
|
4 |
|
try: |
373
|
4 |
|
osfile = str(glob.glob("{0}*_sfi*.signed".format(absoglob))[0]) |
374
|
4 |
|
except IndexError: |
375
|
4 |
|
print("No OS found") |
376
|
|
|
else: |
377
|
4 |
|
generate_lazy_set(osversion, device, osfile, suffix, absoglob, localdir) |
378
|
|
|
|
379
|
|
|
|
380
|
4 |
|
def generate_lazy_set(osversion, device, osfile, suffix, absoglob, localdir=None): |
381
|
|
|
""" |
382
|
|
|
Get radio file and then generate autoloader. |
383
|
|
|
|
384
|
|
|
:param osversion: OS version, 10.x.y.zzzz. |
385
|
|
|
:type osversion: str |
386
|
|
|
|
387
|
|
|
:param device: Selected device, from |
388
|
|
|
:type device: int |
389
|
|
|
|
390
|
|
|
:param osfile: OS signed filename. |
391
|
|
|
:type osfile: str |
392
|
|
|
|
393
|
|
|
:param suffix: Loader name suffix. |
394
|
|
|
:type suffix: str |
395
|
|
|
|
396
|
|
|
:param absoglob: Local path + path separator. |
397
|
|
|
:type absoglob: str |
398
|
|
|
|
399
|
|
|
:param localdir: Working path. Default is local dir. |
400
|
|
|
:type localdir: str |
401
|
|
|
""" |
402
|
4 |
|
radiofile = None |
403
|
4 |
|
try: |
404
|
4 |
|
sset = set(glob.glob("{0}*.signed".format(absoglob))) |
405
|
4 |
|
rset = sset - set(glob.glob("{0}*_sfi*.signed".format(absoglob))) |
406
|
4 |
|
radiofile = str(list(rset)[0]) |
407
|
4 |
|
except IndexError: |
408
|
4 |
|
print("No radio found") |
409
|
|
|
else: |
410
|
4 |
|
loadername = generate_lazy_filename(osversion, suffix, device) |
411
|
4 |
|
wrap_pseudocap(loadername, localdir, osfile, radiofile) |
412
|
|
|
|
413
|
|
|
|
414
|
4 |
|
def generate_lazy_filename(osversion, suffix, device): |
415
|
|
|
""" |
416
|
|
|
Read JSON to formulate a single filename. |
417
|
|
|
|
418
|
|
|
:param osversion: OS version. |
419
|
|
|
:type osversion: str |
420
|
|
|
|
421
|
|
|
:param suffix: Alternate radio, or just blank. |
422
|
|
|
:type suffix: str |
423
|
|
|
|
424
|
|
|
:param device: Device to use. |
425
|
|
|
:type device: int |
426
|
|
|
""" |
427
|
4 |
|
data = jsonutils.load_json('integermap') |
428
|
4 |
|
for key in data: |
429
|
4 |
|
if key['id'] == device: |
430
|
4 |
|
fname = key['parts'] |
431
|
4 |
|
break |
432
|
|
|
return "{0}{1}{2}{3}.exe".format(fname[0], osversion, suffix, fname[1]) |
433
|
|
|
|