|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 ############################################################################## |
|
5 # symbianutil.py - Utilities for working with Symbian OS-related data |
|
6 # Copyright 2006, 2007, 2008, 2009 Jussi Ylänen |
|
7 # |
|
8 # This file is part of Ensymble developer utilities for Symbian OS(TM). |
|
9 # |
|
10 # Ensymble is free software; you can redistribute it and/or modify |
|
11 # it under the terms of the GNU General Public License as published by |
|
12 # the Free Software Foundation; either version 2 of the License, or |
|
13 # (at your option) any later version. |
|
14 # |
|
15 # Ensymble is distributed in the hope that it will be useful, |
|
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
18 # GNU General Public License for more details. |
|
19 # |
|
20 # You should have received a copy of the GNU General Public License |
|
21 # along with Ensymble; if not, write to the Free Software |
|
22 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
23 ############################################################################## |
|
24 |
|
25 import struct |
|
26 import zlib |
|
27 |
|
28 |
|
29 ############################################################################## |
|
30 # Miscellaneous functions |
|
31 ############################################################################## |
|
32 |
|
33 def uidstostring(uid1, uid2, uid3): |
|
34 '''Return a string of UIDs and a checksum.''' |
|
35 |
|
36 crc = uidcrc(uid1, uid2, uid3) |
|
37 return struct.pack("<LLLL", uid1, uid2, uid3, crc) |
|
38 |
|
39 def ise32image(string): |
|
40 '''Check if a given string contains a valid E32Image header. |
|
41 Return "EXE", "DLL" or None.''' |
|
42 |
|
43 if len(string) < 156: |
|
44 # Minimum header size is 156 bytes. |
|
45 return None |
|
46 |
|
47 if string[16:20] != "EPOC": |
|
48 # Wrong cookie, not an E32Image header. |
|
49 return None |
|
50 |
|
51 # Get UIDs as integers. |
|
52 uid1, uid2, uid3 = struct.unpack("<LLL", string[:12]) |
|
53 |
|
54 # Verify UID checksum. |
|
55 uidstr = uidstostring(uid1, uid2, uid3) |
|
56 if uidstr != string[:16]: |
|
57 # Invalid UID checksum. |
|
58 return None |
|
59 |
|
60 # Check type of E32Image header. |
|
61 if uid1 == 0x10000079L: |
|
62 return "DLL" |
|
63 elif uid1 == 0x1000007AL: |
|
64 return "EXE" |
|
65 |
|
66 # Not an EXE or DLL. |
|
67 return None |
|
68 |
|
69 def e32imageinfo(image): |
|
70 '''Return a tuple with the UID1, UID2, UID3, secure ID, vendor ID and |
|
71 capabilities (as a string) of the given e32image.''' |
|
72 |
|
73 if ise32image(image) == None: |
|
74 raise ValueError("not a valid e32image header") |
|
75 |
|
76 uid1, uid2, uid3 = struct.unpack("<LLL", image[:12]) |
|
77 secureid = struct.unpack("<L", image[128:132])[0] |
|
78 vendorid = struct.unpack("<L", image[132:136])[0] |
|
79 capabilities = struct.unpack("<Q", image[136:144])[0] |
|
80 |
|
81 return (uid1, uid2, uid3, secureid, vendorid, capabilities) |
|
82 |
|
83 def parseintmagnitude(string): |
|
84 '''Parse an integer and a magnitude. Magnitude can be "k" or "M" (case |
|
85 is not important). There may be no white-space between the integer and |
|
86 magnitude. Magnitudes are interpreted as 1024 and 1048576.''' |
|
87 |
|
88 string = string.lower() |
|
89 |
|
90 if string[-1] == "k": |
|
91 magnitude = 1024 |
|
92 string = string[:-1] |
|
93 elif string[-1] == "m": |
|
94 magnitude = 1024 * 1024 |
|
95 string = string[:-1] |
|
96 else: |
|
97 magnitude = 1 |
|
98 |
|
99 return int(string, 10) * magnitude |
|
100 |
|
101 def uidfromname(basename): |
|
102 '''Generate a test-range UID (0xe0000000 to 0xefffffff) from a |
|
103 Unicode name.''' |
|
104 |
|
105 # Normalise case. |
|
106 basename = basename.lower() |
|
107 |
|
108 # Convert Unicode name to an unambiguous byte string. |
|
109 basename = basename.encode("utf8") |
|
110 |
|
111 # Calculate a 32-bit CCITT CRC and set top four bits to "e". |
|
112 return (crc32ccitt(basename) & 0x0fffffffL) | 0xe0000000L |
|
113 |
|
114 def e32imagecaps(string): |
|
115 '''Check if a given string is an E32Image file and return its |
|
116 capabilities or None if not an E32Image. |
|
117 |
|
118 Returned value can be directly used as the "capabilities" |
|
119 attribute of sisfile.SimpleSISWriter().addfile() call.''' |
|
120 |
|
121 if ise32image(string) == None: |
|
122 return None |
|
123 |
|
124 return struct.unpack("<Q", string[136:144])[0] |
|
125 |
|
126 |
|
127 ############################################################################## |
|
128 # Checksum functions for various types of checksums in Symbian OS |
|
129 ############################################################################## |
|
130 |
|
131 def crc16ccitt(string, initialvalue = 0x0000, finalxor = 0x0000): |
|
132 '''Calculate a CCITT CRC-16 checksum using a |
|
133 slow and straightforward algorithm.''' |
|
134 |
|
135 value = initialvalue |
|
136 for c in string: |
|
137 value ^= (ord(c) << 8) |
|
138 for b in xrange(8): |
|
139 value <<= 1 |
|
140 if value & 0x10000: |
|
141 value ^= 0x1021 |
|
142 value &= 0xffff |
|
143 |
|
144 return value ^ finalxor |
|
145 |
|
146 def crc32ccitt(data, initialvalue = 0x00000000L, finalxor = 0x00000000L): |
|
147 '''Use zlib to calculate a CCITT CRC-32 checksum. Work around zlib |
|
148 signedness problems.''' |
|
149 |
|
150 if initialvalue >= 0x80000000L: |
|
151 initialvalue -= 0x100000000L |
|
152 initialvalue = int(initialvalue) |
|
153 |
|
154 value = long(zlib.crc32(data, initialvalue)) |
|
155 |
|
156 if value < 0: |
|
157 value += 0x100000000L |
|
158 |
|
159 return value ^ finalxor |
|
160 |
|
161 def uidcrc(uid1, uid2, uid3): |
|
162 '''Calculate a Symbian OS UID checksum.''' |
|
163 |
|
164 # Convert UIDs to a string and group even and odd characters |
|
165 # into separate strings (in a Python v2.2 compatible way). |
|
166 uidstr = struct.pack("<LLL", uid1, uid2, uid3) |
|
167 evenchars = "".join([uidstr[n] for n in range(0, 12, 2)]) |
|
168 oddchars = "".join([uidstr[n] for n in range(1, 12, 2)]) |
|
169 |
|
170 # Calculate 16-bit CCITT CRCs for even and odd characters. |
|
171 evencrc = crc16ccitt(evenchars) |
|
172 oddcrc = crc16ccitt(oddchars) |
|
173 |
|
174 # Resulting 32-bit UID CRC is a combination of the two 16-bit CCITT CRCs. |
|
175 return (long(oddcrc) << 16) | evencrc |
|
176 |
|
177 def e32imagecrc(image, uid3 = None, secureid = None, vendorid = None, |
|
178 heapsizemin = None, heapsizemax = None, capabilities = None): |
|
179 '''Return a modified e32image (or just the header) with UID checksum |
|
180 and header checksum (CCITT CRC-32) recalculated. Optionally modify |
|
181 the UID3, secure ID, vendor ID, heap size and capability bit mask.''' |
|
182 |
|
183 if ise32image(image) == None: |
|
184 raise ValueError("not a valid e32image header") |
|
185 |
|
186 # Get original UIDs as integers. |
|
187 uid1, uid2, uid3_orig = struct.unpack("<LLL", image[:12]) |
|
188 |
|
189 # Get modified or original IDs depending on parameters. Convert to strings. |
|
190 if uid3 == None: |
|
191 uid3 = uid3_orig |
|
192 uid3str = struct.pack("<L", uid3) |
|
193 |
|
194 if secureid == None: |
|
195 secureidstr = image[128:132] |
|
196 else: |
|
197 secureidstr = struct.pack("<L", secureid) |
|
198 |
|
199 if vendorid == None: |
|
200 vendoridstr = image[132:136] |
|
201 else: |
|
202 vendoridstr = struct.pack("<L", vendorid) |
|
203 |
|
204 if heapsizemin == None: |
|
205 heapsizeminstr = image[56:60] |
|
206 else: |
|
207 heapsizeminstr = struct.pack("<l", heapsizemin) |
|
208 |
|
209 if heapsizemax == None: |
|
210 heapsizemaxstr = image[60:64] |
|
211 else: |
|
212 heapsizemaxstr = struct.pack("<l", heapsizemax) |
|
213 |
|
214 if capabilities == None: |
|
215 capabilitiesstr = image[136:144] |
|
216 else: |
|
217 capabilitiesstr = struct.pack("<Q", capabilities) |
|
218 |
|
219 # Re-calculate UID checksum. |
|
220 uidstr = uidstostring(uid1, uid2, uid3) |
|
221 |
|
222 # Use initial CRC of 0xc90fdaa2L (KImageCrcInitialiser in f32image.h). |
|
223 initialcrcstr = struct.pack("<L", 0xc90fdaa2L) |
|
224 |
|
225 # Construct a new header for CRC-32 calculation. |
|
226 newheader = "%s%s%s%s%s%s%s%s%s%s%s" % (uidstr, image[16:20], initialcrcstr, |
|
227 image[24:56], heapsizeminstr, |
|
228 heapsizemaxstr, image[64:128], |
|
229 secureidstr, vendoridstr, |
|
230 capabilitiesstr, image[144:156]) |
|
231 |
|
232 crc32 = crc32ccitt(newheader, 0xffffffffL, 0xffffffffL) |
|
233 crc32str = struct.pack("<L", crc32) |
|
234 |
|
235 # Construct and return a new image (or header) with the correct checksum. |
|
236 return "%s%s%s%s" % (newheader[0:20], crc32str, |
|
237 newheader[24:156], image[156:]) |
|
238 |
|
239 |
|
240 ############################################################################## |
|
241 # Symbian OS language mappings |
|
242 ############################################################################## |
|
243 |
|
244 langinfo = [ |
|
245 ("AF", "Afrikaans", 34), |
|
246 ("SQ", "Albanian", 35), |
|
247 ("AM", "AmericanEnglish", 10), |
|
248 ("AH", "Amharic", 36), |
|
249 ("AR", "Arabic", 37), |
|
250 ("HY", "Armenian", 38), |
|
251 ("AU", "Australian", 20), |
|
252 ("AS", "Austrian", 22), |
|
253 ("BE", "Belarussian", 40), |
|
254 ("BL", "BelgianFlemish", 19), |
|
255 ("BF", "BelgianFrench", 21), |
|
256 ("BN", "Bengali", 41), |
|
257 ("BP", "BrazilianPortuguese", 76), |
|
258 ("BG", "Bulgarian", 42), |
|
259 ("MY", "Burmese", 43), |
|
260 ("CE", "CanadianEnglish", 46), |
|
261 ("CF", "CanadianFrench", 51), |
|
262 ("CA", "Catalan", 44), |
|
263 ("HR", "Croatian", 45), |
|
264 ("CG", "CyprusGreek", 55), |
|
265 ("CT", "CyprusTurkish", 91), |
|
266 ("CS", "Czech", 25), |
|
267 ("DA", "Danish", 7), |
|
268 ("DU", "Dutch", 18), |
|
269 ("EN", "English", 1), |
|
270 ("ET", "Estonian", 49), |
|
271 ("FA", "Farsi", 50), |
|
272 ("FS", "FinlandSwedish", 85), |
|
273 ("FI", "Finnish", 9), |
|
274 ("FR", "French", 2), |
|
275 ("KA", "Georgian", 53), |
|
276 ("GE", "German", 3), |
|
277 ("EL", "Greek", 54), |
|
278 ("GU", "Gujarati", 56), |
|
279 ("HE", "Hebrew", 57), |
|
280 ("HI", "Hindi", 58), |
|
281 ("HK", "HongKongChinese", 30), |
|
282 ("HU", "Hungarian", 17), |
|
283 ("IC", "Icelandic", 15), |
|
284 ("IN", "Indonesian", 59), |
|
285 ("IE", "InternationalEnglish", 47), |
|
286 ("IF", "InternationalFrench", 24), |
|
287 ("OS", "InternationalSpanish", 82), |
|
288 ("GA", "Irish", 60), |
|
289 ("IT", "Italian", 5), |
|
290 ("JA", "Japanese", 32), |
|
291 ("KN", "Kannada", 62), |
|
292 ("KK", "Kazakh", 63), |
|
293 ("KM", "Khmer", 64), |
|
294 ("KO", "Korean", 65), |
|
295 ("LO", "Laothian", 66), |
|
296 ("LS", "LatinAmericanSpanish", 83), |
|
297 ("LV", "Latvian", 67), |
|
298 ("LT", "Lithuanian", 68), |
|
299 ("MK", "Macedonian", 69), |
|
300 ("MS", "Malay", 70), |
|
301 ("ML", "Malayalam", 71), |
|
302 ("MR", "Marathi", 72), |
|
303 ("MO", "Moldavian", 73), |
|
304 ("MN", "Mongolian", 74), |
|
305 ("NZ", "NewZealand", 23), |
|
306 ("NO", "Norwegian", 8), |
|
307 ("NN", "NorwegianNynorsk", 75), |
|
308 ("PL", "Polish", 27), |
|
309 ("PO", "Portuguese", 13), |
|
310 ("ZH", "PRCChinese", 31), |
|
311 ("PA", "Punjabi", 77), |
|
312 ("RO", "Romanian", 78), |
|
313 ("RU", "Russian", 16), |
|
314 ("GD", "ScotsGaelic", 52), |
|
315 ("SR", "Serbian", 79), |
|
316 ("SI", "Sinhalese", 80), |
|
317 ("SK", "Slovak", 26), |
|
318 ("SL", "Slovenian", 28), |
|
319 ("SO", "Somali", 81), |
|
320 ("SF", "SouthAfricanEnglish", 48), # "SF" is also "SwissFrench" |
|
321 ("SP", "Spanish", 4), |
|
322 ("SH", "Swahili", 84), |
|
323 ("SW", "Swedish", 6), |
|
324 ("SF", "SwissFrench", 11), # "SF" is also "SouthAfricanEnglish" |
|
325 ("SG", "SwissGerman", 12), |
|
326 ("SZ", "SwissItalian", 61), |
|
327 ("TL", "Tagalog", 39), |
|
328 ("TC", "TaiwanChinese", 29), |
|
329 ("TA", "Tamil", 87), |
|
330 ("TE", "Telugu", 88), |
|
331 ("TH", "Thai", 33), |
|
332 ("BO", "Tibetan", 89), |
|
333 ("TI", "Tigrinya", 90), |
|
334 ("TU", "Turkish", 14), |
|
335 ("TK", "Turkmen", 92), |
|
336 ("UK", "Ukrainian", 93), |
|
337 ("UR", "Urdu", 94), |
|
338 ("VI", "Vietnamese", 96), |
|
339 ("CY", "Welsh", 97), |
|
340 ("ZU", "Zulu", 98) |
|
341 ] |
|
342 |
|
343 langidtonum = dict([(lid, lnum) for lid, lname, lnum in langinfo]) |
|
344 langnametonum = dict([(lname, lnum) for lid, lname, lnum in langinfo]) |
|
345 langnumtoname = dict([(lnum, lname) for lid, lname, lnum in langinfo]) |
|
346 |
|
347 |
|
348 ############################################################################## |
|
349 # Symbian OS capabilities |
|
350 ############################################################################## |
|
351 |
|
352 capinfo = [ |
|
353 ("TCB", 0), |
|
354 ("CommDD", 1), |
|
355 ("PowerMgmt", 2), |
|
356 ("MultimediaDD", 3), |
|
357 ("ReadDeviceData", 4), |
|
358 ("WriteDeviceData", 5), |
|
359 ("DRM", 6), |
|
360 ("TrustedUI", 7), |
|
361 ("ProtServ", 8), |
|
362 ("DiskAdmin", 9), |
|
363 ("NetworkControl", 10), |
|
364 ("AllFiles", 11), |
|
365 ("SwEvent", 12), |
|
366 ("NetworkServices", 13), |
|
367 ("LocalServices", 14), |
|
368 ("ReadUserData", 15), |
|
369 ("WriteUserData", 16), |
|
370 ("Location", 17), |
|
371 ("SurroundingsDD", 18), |
|
372 ("UserEnvironment", 19) |
|
373 ] |
|
374 |
|
375 numcaps = 20 |
|
376 allcapsmask = (1L << numcaps) - 1 |
|
377 |
|
378 capnametonum = dict([(cname.lower(), cnum) for cname, cnum in capinfo]) |
|
379 |
|
380 def capstringtomask(string): |
|
381 '''Parse a capability string in which capability |
|
382 names are separated with + (include capability) |
|
383 and - (exclude capability).''' |
|
384 |
|
385 if string == "": |
|
386 # Empty string denotes no capabilities. |
|
387 return 0L |
|
388 |
|
389 try: |
|
390 # Allow numerical representation for capabilities. |
|
391 capmask = int(string, 0) |
|
392 if capmask < 0: |
|
393 raise ValueError |
|
394 return capmask |
|
395 except ValueError: |
|
396 # Capabilities not in numerical form, continue with parsing. |
|
397 pass |
|
398 |
|
399 # Erase an optional initial "+" character. |
|
400 if string[0] == '+': |
|
401 string = string[1:] |
|
402 |
|
403 # Split string before each "+" and "-" character. |
|
404 startpos = 0 |
|
405 capnames = [] |
|
406 for stoppos in xrange(len(string)): |
|
407 if string[stoppos] in ("+", "-"): |
|
408 capnames.append(string[startpos:stoppos]) |
|
409 startpos = stoppos |
|
410 capnames.append(string[startpos:]) # The last one |
|
411 |
|
412 # Add initial "+" for the first name. |
|
413 capnames[0] = "+%s" % capnames[0] |
|
414 |
|
415 # Find a bit mask for each capability name. |
|
416 capmask = 0x00000000L |
|
417 for cname in capnames: |
|
418 # Convert capability name to lowercase for capnametonum[]. |
|
419 cnamelower = cname.lower() |
|
420 |
|
421 if cnamelower[1:] == "all": |
|
422 mask = allcapsmask |
|
423 elif cnamelower[1:] == "none": |
|
424 mask = 0x00000000L |
|
425 else: |
|
426 try: |
|
427 mask = 1L << (capnametonum[cnamelower[1:]]) |
|
428 except KeyError: |
|
429 raise ValueError("invalid capability name '%s'" % cname[1:]) |
|
430 |
|
431 if cname[0] == '-': |
|
432 # Remove capability. |
|
433 capmask &= ~mask |
|
434 else: |
|
435 # Add capability. |
|
436 capmask |= mask |
|
437 |
|
438 return capmask |
|
439 |
|
440 def capmasktostring(capmask, shortest = False): |
|
441 '''Generate (optionally) the shortest possible capability |
|
442 string using either capability names separated with + (include |
|
443 capability) or - (exclude capability).''' |
|
444 |
|
445 if capmask == 0L: |
|
446 # Special string for no capabilities. |
|
447 return "NONE" |
|
448 |
|
449 # Construct a list of set and unset capabilities. |
|
450 poscnames = [] |
|
451 negcnames = ["ALL"] |
|
452 for cap in capinfo: |
|
453 mask = (1L << cap[1]) |
|
454 if capmask & mask: |
|
455 poscnames.append(cap[0]) |
|
456 capmask &= ~mask |
|
457 else: |
|
458 negcnames.append(cap[0]) |
|
459 |
|
460 # Check that all capability bits are handled. |
|
461 if capmask != 0L: |
|
462 raise ValueError("invalid capability bits in mask: 0x%08x" % capmask) |
|
463 |
|
464 posstring = "+".join(poscnames) |
|
465 negstring = "-".join(negcnames) |
|
466 |
|
467 # Return the shortest string if requested, otherwise the "positive" string. |
|
468 if shortest and len(posstring) > len(negstring): |
|
469 return negstring |
|
470 return posstring |
|
471 |
|
472 def capmasktorawdata(capmask): |
|
473 '''Convert capability bit mask to raw four- or eight-character string.''' |
|
474 |
|
475 if capmask < (1L << 32): |
|
476 return struct.pack("<L", int(capmask)) |
|
477 elif capmask < (1L << 64): |
|
478 return struct.pack("<Q", capmask) |
|
479 else: |
|
480 raise ValueError("capability bit mask too long") |
|
481 |
|
482 def rawdatatocapmask(rawdata): |
|
483 '''Convert raw four- or eight-character string to capability bit mask.''' |
|
484 |
|
485 if len(rawdata) == 4: |
|
486 return struct.unpack("<L", rawdata)[0] |
|
487 elif len(rawdata) == 8: |
|
488 return struct.unpack("<Q", rawdata)[0] |
|
489 else: |
|
490 raise ValueError("string length not a multiple of 32 bits") |