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