src/tools/py2sis/ensymble/cmd_altere32.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 ##############################################################################
       
     5 # cmd_signsis.py - Ensymble command line tool, altere32 command
       
     6 # Copyright 2006, 2007, 2008 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 sys
       
    26 import os
       
    27 import getopt
       
    28 import locale
       
    29 import struct
       
    30 
       
    31 import symbianutil
       
    32 
       
    33 
       
    34 ##############################################################################
       
    35 # Help texts
       
    36 ##############################################################################
       
    37 
       
    38 shorthelp = 'Alter the IDs and capabilities of e32image files (EXEs, DLLs)'
       
    39 longhelp  = '''altere32
       
    40     [--uid=0x01234567] [--secureid=0x01234567] [--vendorid=0x01234567]
       
    41     [--caps=Cap1+Cap2+...] [--heapsize=min,max] [--inplace]
       
    42     [--encoding=terminal,filesystem] [--verbose]
       
    43     <infile> [outfile]
       
    44 
       
    45 Alter the IDs, capabilities and heap sizes of e32image files (Symbian OS
       
    46 EXEs and DLLs).
       
    47 
       
    48 Options:
       
    49     infile      - Path of the original e32image file (or many, if --inplace set)
       
    50     outfile     - Path of the modified e32image file (not used with --inplace)
       
    51     uid         - Symbian OS UID for the e32image
       
    52     secureid    - Secure ID for the e32image (should normally be same as UID)
       
    53     vendorid    - Vendor ID for the e32image
       
    54     caps        - Capability names, separated by "+"
       
    55     heapsize    - Heap size, minimum and/or maximum (not altered by default)
       
    56     inplace     - Allow more than one input file, modify input files in-place
       
    57     encoding    - Local character encodings for terminal and filesystem
       
    58     verbose     - Print extra statistics
       
    59 
       
    60 When modifying the UID, the secure ID should be modified accordingly.
       
    61 Modifying UIDs of application EXEs is generally not possible, because
       
    62 applications usually include the UID in program code as well.
       
    63 '''
       
    64 
       
    65 
       
    66 ##############################################################################
       
    67 # Parameters
       
    68 ##############################################################################
       
    69 
       
    70 MAXE32FILESIZE  = 1024 * 1024 * 8   # Eight megabytes
       
    71 
       
    72 
       
    73 ##############################################################################
       
    74 # Global variables
       
    75 ##############################################################################
       
    76 
       
    77 debug = False
       
    78 
       
    79 
       
    80 ##############################################################################
       
    81 # Public module-level functions
       
    82 ##############################################################################
       
    83 
       
    84 def run(pgmname, argv):
       
    85     global debug
       
    86 
       
    87     # Determine system character encodings.
       
    88     try:
       
    89         # getdefaultlocale() may sometimes return None.
       
    90         # Fall back to ASCII encoding in that case.
       
    91         terminalenc = locale.getdefaultlocale()[1] + ""
       
    92     except TypeError:
       
    93         # Invalid locale, fall back to ASCII terminal encoding.
       
    94         terminalenc = "ascii"
       
    95 
       
    96     try:
       
    97         # sys.getfilesystemencoding() was introduced in Python v2.3 and
       
    98         # it can sometimes return None. Fall back to ASCII if something
       
    99         # goes wrong.
       
   100         filesystemenc = sys.getfilesystemencoding() + ""
       
   101     except (AttributeError, TypeError):
       
   102         filesystemenc = "ascii"
       
   103 
       
   104     try:
       
   105         gopt = getopt.gnu_getopt
       
   106     except:
       
   107         # Python <v2.3, GNU-style parameter ordering not supported.
       
   108         gopt = getopt.getopt
       
   109 
       
   110     # Parse command line arguments.
       
   111     short_opts = "u:s:r:b:H:ie:vh"
       
   112     long_opts = [
       
   113         "uid=", "secureid=", "vendorid=", "caps=", "heapsize=",
       
   114         "inplace", "encoding=", "verbose", "debug", "help"
       
   115     ]
       
   116     args = gopt(argv, short_opts, long_opts)
       
   117 
       
   118     opts = dict(args[0])
       
   119     pargs = args[1]
       
   120 
       
   121     if len(pargs) == 0:
       
   122         raise ValueError("no input e32image file name given")
       
   123 
       
   124     # Override character encoding of command line and filesystem.
       
   125     encs = opts.get("--encoding", opts.get("-e", "%s,%s" % (terminalenc,
       
   126                                                             filesystemenc)))
       
   127     try:
       
   128         terminalenc, filesystemenc = encs.split(",")
       
   129     except (ValueError, TypeError):
       
   130         raise ValueError("invalid encoding string '%s'" % encs)
       
   131 
       
   132     # Get UID3.
       
   133     uid3 = opts.get("--uid", opts.get("-u", None))
       
   134     if uid3 != None:
       
   135         uid3 = parseuid(pgmname, uid3)
       
   136 
       
   137     # Get secure ID.
       
   138     secureid = opts.get("--secureid", opts.get("-s", None))
       
   139     if secureid != None:
       
   140         secureid = parseuid(pgmname, secureid)
       
   141 
       
   142     # Get vendor ID.
       
   143     vendorid = opts.get("--vendorid", opts.get("-r", None))
       
   144     if vendorid != None:
       
   145         vendorid = parseuid(pgmname, vendorid)
       
   146 
       
   147     # Get capabilities and normalize the names.
       
   148     caps = opts.get("--caps", opts.get("-b", None))
       
   149     if caps != None:
       
   150         capmask = symbianutil.capstringtomask(caps)
       
   151         caps = symbianutil.capmasktostring(capmask, True)
       
   152     else:
       
   153         capmask = None
       
   154 
       
   155     # Get heap sizes.
       
   156     heapsize = opts.get("--heapsize", opts.get("-H", None))
       
   157     if heapsize != None:
       
   158         try:
       
   159             heapsize = heapsize.split(",", 1)
       
   160             heapsizemin = symbianutil.parseintmagnitude(heapsize[0])
       
   161             if len(heapsize) == 1:
       
   162                 # Only one size given, use it as both.
       
   163                 heapsizemax = heapsizemin
       
   164             else:
       
   165                 heapsizemax = symbianutil.parseintmagnitude(heapsize[1])
       
   166         except (ValueError, TypeError, IndexError):
       
   167             raise ValueError("%s: invalid heap size, one or two values expected"
       
   168                              % ",".join(heapsize))
       
   169 
       
   170         # Warn if the minimum heap size is larger than the maximum heap size.
       
   171         # Resulting e32image file will probably prevent any SIS from installing.
       
   172         if heapsizemin > heapsizemax:
       
   173             print ("%s: warning: minimum heap size larger than "
       
   174                    "maximum heap size" % pgmname)
       
   175     else:
       
   176         heapsizemin = None
       
   177         heapsizemax = None
       
   178 
       
   179     # Determine parameter format. Modifying files in-place or not.
       
   180     inplace = False
       
   181     if "--inplace" in opts.keys() or "-i" in opts.keys():
       
   182         inplace = True
       
   183 
       
   184     # Determine e32image input / output file names.
       
   185     files = [name.decode(terminalenc).encode(filesystemenc) for name in pargs]
       
   186     if not inplace:
       
   187         if len(files) == 2:
       
   188             if os.path.isdir(files[1]):
       
   189                 # Output to directory, use input file name.
       
   190                 files[1] = os.path.join(files[1], os.path.basename(files[0]))
       
   191         else:
       
   192             raise ValueError("wrong number of arguments")
       
   193 
       
   194     # Determine verbosity.
       
   195     verbose = False
       
   196     if "--verbose" in opts.keys() or "-v" in opts.keys():
       
   197         verbose = True
       
   198 
       
   199     # Determine if debug output is requested.
       
   200     if "--debug" in opts.keys():
       
   201         debug = True
       
   202 
       
   203     # Ingredients for successful e32image file alteration:
       
   204     #
       
   205     # terminalenc      Terminal character encoding (autodetected)
       
   206     # filesystemenc    File system name encoding (autodetected)
       
   207     # files            File names of e32image files, filesystemenc encoded
       
   208     # uid3             Application UID3, long integer or None
       
   209     # secureid         Secure ID, long integer or None
       
   210     # vendorid         Vendor ID, long integer or None
       
   211     # caps, capmask    Capability names and bitmask or None
       
   212     # heapsizemin      Heap that must be available for the app. to start or None
       
   213     # heapsizemax      Maximum amount of heap the app. can allocate or None
       
   214     # inplace          Multiple input files or single input / output pair
       
   215     # verbose          Boolean indicating verbose terminal output
       
   216 
       
   217     if verbose:
       
   218         print
       
   219         if not inplace:
       
   220             print "Input e32image file      %s"        % (
       
   221                 files[0].decode(filesystemenc).encode(terminalenc))
       
   222             print "Output e32image file     %s"        % (
       
   223                 files[1].decode(filesystemenc).encode(terminalenc))
       
   224         else:
       
   225             print "Input e32image file(s)   %s"        % " ".join(
       
   226                 [f.decode(filesystemenc).encode(terminalenc) for f in files])
       
   227         if uid3 != None:
       
   228             print "UID                      0x%08x" % uid3
       
   229         else:
       
   230             print "UID                      <not set>"
       
   231         if secureid != None:
       
   232             print "Secure ID                0x%08x" % secureid
       
   233         else:
       
   234             print "Secure ID                <not set>"
       
   235         if vendorid != None:
       
   236             print "Vendor ID                0x%08x" % vendorid
       
   237         else:
       
   238             print "Vendor ID                <not set>"
       
   239         if caps != None:
       
   240             print "Capabilities             0x%x (%s)" % (capmask, caps)
       
   241         else:
       
   242             print "Capabilities             <not set>"
       
   243         if heapsizemin != None:
       
   244             print "Heap size in bytes       %d, %d" % (heapsizemin, heapsizemax)
       
   245         else:
       
   246             print "Heap size in bytes       <not set>"
       
   247         print
       
   248 
       
   249     if ((uid3, secureid, vendorid, caps, heapsizemin) ==
       
   250         (None, None, None, None, None)):
       
   251         print "%s: no options set, doing nothing" % pgmname
       
   252         return
       
   253 
       
   254     for infile in files:
       
   255         # Read input e32image file.
       
   256         f = file(infile, "rb")
       
   257         instring = f.read(MAXE32FILESIZE + 1)
       
   258         f.close()
       
   259 
       
   260         if len(instring) > MAXE32FILESIZE:
       
   261             raise ValueError("input e32image file too large")
       
   262 
       
   263         # Modify the e32image header.
       
   264         try:
       
   265             outstring = symbianutil.e32imagecrc(instring, uid3,
       
   266                                                 secureid, vendorid, heapsizemin,
       
   267                                                 heapsizemax, capmask)
       
   268         except ValueError:
       
   269             raise ValueError("%s: not a valid e32image file" % infile)
       
   270 
       
   271         if not inplace:
       
   272             outfile = files[1]
       
   273         else:
       
   274             outfile = infile
       
   275 
       
   276         # Write output e32image file.
       
   277         f = file(outfile, "wb")
       
   278         f.write(outstring)
       
   279         f.close()
       
   280 
       
   281         if not inplace:
       
   282             # While --inplace is not in effect, files[1] is the output
       
   283             # file name, so must stop after one iteration.
       
   284             break
       
   285 
       
   286 
       
   287 ##############################################################################
       
   288 # Module-level functions which are normally only used by this module
       
   289 ##############################################################################
       
   290 
       
   291 def parseuid(pgmname, uid):
       
   292     if uid.lower().startswith("0x"):
       
   293         # Prefer hex UIDs with leading "0x".
       
   294         uid = long(uid, 16)
       
   295     else:
       
   296         try:
       
   297             if len(uid) == 8:
       
   298                 # Assuming hex UID even without leading "0x".
       
   299                 print ('%s: warning: assuming hex UID even '
       
   300                        'without leading "0x"' % pgmname)
       
   301                 uid = long(uid, 16)
       
   302             else:
       
   303                 # Decimal UID.
       
   304                 uid = long(uid)
       
   305                 print ('%s: warning: decimal UID converted to 0x%08x' %
       
   306                        (pgmname, uid))
       
   307         except ValueError:
       
   308             raise ValueError("invalid UID string '%s'" % uid)
       
   309     return uid