src/tools/py2sis/ensymble/symbianutil.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     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")