src/tools/py2sis/ensymble/decodemif.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 ##############################################################################
       
     5 # decodemif.py - Decodes a Symbian OS v9.x multi-image file (MIF)
       
     6 # Copyright 2006, 2007 Jussi Ylänen
       
     7 #
       
     8 # This program 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 # Version history
       
    26 # ---------------
       
    27 #
       
    28 # v0.03 2006-09-22
       
    29 # Replaced every possible range(...) with xrange(...) for efficiency
       
    30 #
       
    31 # v0.02 2006-08-22
       
    32 # Added file type recognition (SVG, binary SVG or other)
       
    33 #
       
    34 # v0.01 2006-08-14
       
    35 # Added support for strange index entries (length 0)
       
    36 #
       
    37 # v0.00 2006-08-13
       
    38 # Work started
       
    39 ##############################################################################
       
    40 
       
    41 VERSION = "v0.02 2006-09-22"
       
    42 
       
    43 import sys
       
    44 import os
       
    45 import struct
       
    46 import getopt
       
    47 import random
       
    48 import tempfile
       
    49 
       
    50 # Parameters
       
    51 MAXMIFFILESIZE      = 1024 * 1024       # Arbitrary maximum size of MIF file
       
    52 
       
    53 tempdir = None
       
    54 dumpcounter = 0
       
    55 
       
    56 def mkdtemp(template):
       
    57     '''
       
    58     Create a unique temporary directory.
       
    59 
       
    60     tempfile.mkdtemp() was introduced in Python v2.3. This is for
       
    61     backward compatibility.
       
    62     '''
       
    63 
       
    64     # Cross-platform way to determine a suitable location for temporary files.
       
    65     systemp = tempfile.gettempdir()
       
    66 
       
    67     if not template.endswith("XXXXXX"):
       
    68         raise ValueError("invalid template for mkdtemp(): %s" % template)
       
    69 
       
    70     for n in xrange(10000):
       
    71         randchars = []
       
    72         for m in xrange(6):
       
    73             randchars.append(random.choice("abcdefghijklmnopqrstuvwxyz"))
       
    74 
       
    75         tempdir = os.path.join(systemp, template[: -6]) + "".join(randchars)
       
    76 
       
    77         try:
       
    78             os.mkdir(tempdir, 0700)
       
    79             return tempdir
       
    80         except OSError:
       
    81             pass
       
    82 
       
    83 def dumpdata(data):
       
    84     '''Dumps data to a file in a temporary directory.'''
       
    85 
       
    86     global tempdir, dumpcounter
       
    87 
       
    88     if tempdir == None:
       
    89         # Create temporary directory for dumped files.
       
    90         tempdir = mkdtemp("decodemif-XXXXXX")
       
    91         dumpcounter = 0
       
    92 
       
    93     # Determine file type.
       
    94     if data[0:5] == "<?xml":
       
    95         ext = "svg"
       
    96     elif data[0:4] == '\xcc\x56\xfa\x03':
       
    97         ext = "svgb"
       
    98     else:
       
    99         ext = "dat"
       
   100 
       
   101     filename = os.path.join(tempdir, "dump%04d.%s" % (dumpcounter, ext))
       
   102     dumpcounter += 1
       
   103     f = file(filename, "wb")
       
   104     f.write(data)
       
   105     f.close()
       
   106     print "%s written" % filename
       
   107 
       
   108 def main():
       
   109     global tempdir, dumpcounter
       
   110 
       
   111     pgmname     = os.path.basename(sys.argv[0])
       
   112     pgmversion  = VERSION
       
   113 
       
   114     try:
       
   115         try:
       
   116             gopt = getopt.gnu_getopt
       
   117         except:
       
   118             # Python <v2.3, GNU-style parameter ordering not supported.
       
   119             gopt = getopt.getopt
       
   120 
       
   121         # Parse command line using getopt.
       
   122         short_opts = "t:h"
       
   123         long_opts = [
       
   124             "dumpdir", "help"
       
   125         ]
       
   126         args = gopt(sys.argv[1:], short_opts, long_opts)
       
   127 
       
   128         opts = dict(args[0])
       
   129         pargs = args[1]
       
   130 
       
   131         if "--help" in opts.keys() or "-h" in opts.keys():
       
   132             # Help requested.
       
   133             print (
       
   134 '''
       
   135 DecodeMIF - Symbian OS v9.x MIF file decoder %(pgmversion)s
       
   136 
       
   137 usage: %(pgmname)s [--dumpdir=DIR] [miffiles...]
       
   138 
       
   139         -t, --dumpdir       - Directory to use for dumped files (or automatic)
       
   140         miffiles            - MIF files to decode (stdin if not given or -)
       
   141 
       
   142 ''' % locals())
       
   143             return 0
       
   144 
       
   145         # A temporary directory is generated by default.
       
   146         tempdir = opts.get("--dumpdir", opts.get("-t", None))
       
   147 
       
   148         if len(pargs) == 0:
       
   149             miffilenames = ["-"]
       
   150         else:
       
   151             miffilenames = pargs
       
   152 
       
   153         for miffilename in miffilenames:
       
   154             if miffilename == '-':
       
   155                 miffile = sys.stdin
       
   156             else:
       
   157                 miffile = file(miffilename, "rb")
       
   158 
       
   159             try:
       
   160                 # Load the whole MIF file as a string.
       
   161                 mifdata = miffile.read(MAXMIFFILESIZE)
       
   162                 if len(mifdata) == MAXMIFFILESIZE:
       
   163                     raise IOError("%s: file too large" % miffilename)
       
   164             finally:
       
   165                 if miffile != sys.stdin:
       
   166                     miffile.close()
       
   167 
       
   168             # Verify MIF signature.
       
   169             if mifdata[:4] != "B##4":
       
   170                 raise ValueError("%s: not a MIF file" % miffilename)
       
   171 
       
   172             if len(mifdata) < 16:
       
   173                 raise ValueError("%s: file too short" % miffilename)
       
   174 
       
   175             entries = struct.unpack("<L", mifdata[12:16])[0] / 2
       
   176 
       
   177             # Verify header length:
       
   178             # 16-byte header, 16 bytes per index entry
       
   179             if len(mifdata) < (16 + 16 * entries):
       
   180                 raise ValueError("%s: file too short" % miffilename)
       
   181 
       
   182             # Read index.
       
   183             index = []
       
   184             for n in xrange(entries):
       
   185                 hdroff = 16 + n * 16
       
   186                 a = struct.unpack("<L", mifdata[hdroff +  0:hdroff + 4])[0]
       
   187                 b = struct.unpack("<L", mifdata[hdroff +  4:hdroff + 8])[0]
       
   188                 c = struct.unpack("<L", mifdata[hdroff +  8:hdroff + 12])[0]
       
   189                 d = struct.unpack("<L", mifdata[hdroff + 12:hdroff + 16])[0]
       
   190 
       
   191                 if b == 0 and d == 0:
       
   192                     # Unknown index entry type, skip it.
       
   193                     continue
       
   194 
       
   195                 if a != c or b != d:
       
   196                     raise ValueError("%s: invalid index entry %d" %
       
   197                                      (miffilename, n))
       
   198 
       
   199                 # Check total length of file.
       
   200                 if a + b > len(mifdata):
       
   201                     raise ValueError("%s: index %d out of range" %
       
   202                                      (miffilename, n))
       
   203 
       
   204                 index.append((a, b))
       
   205 
       
   206             n = len(index)
       
   207             print "%s: %s %s inside" % (miffilename, n or "no",
       
   208                                         ((n == 1) and "file") or "files")
       
   209 
       
   210             # Extract contents.
       
   211             for i in index:
       
   212                 offset = i[0]
       
   213                 length = i[1]
       
   214                 if mifdata[offset:offset + 4] != "C##4":
       
   215                     raise ValueError("%s: invalid file header %d" %
       
   216                                      (miffilename, n))
       
   217 
       
   218                 print "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" % (
       
   219                     tuple(struct.unpack("<LLLLLLL", mifdata[(offset + 4):
       
   220                                                             (offset + 32)])))
       
   221                 dumpdata(mifdata[offset + 32:offset + length + 32])
       
   222     except (TypeError, ValueError, IOError, OSError), e:
       
   223         return "%s: %s" % (pgmname, str(e))
       
   224     except KeyboardInterrupt:
       
   225         return ""
       
   226 
       
   227 # Call main if run as stand-alone executable.
       
   228 if __name__ == '__main__':
       
   229     sys.exit(main())