0
|
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")
|