src/tools/py2sis/ensymble/rscfile.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 ##############################################################################
       
     5 # rscfile.py - Symbian OS compiled resource file (RSC) utilities
       
     6 # Copyright 2006, 2007 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 
       
    27 import symbianutil
       
    28 
       
    29 
       
    30 ##############################################################################
       
    31 # Module-level functions which are normally only used by this module
       
    32 ##############################################################################
       
    33 
       
    34 def makeuidfromoffset(offset):
       
    35     '''Convert a Symbian OS resource file offset to a UID.
       
    36 
       
    37     ---- From rcomp v7.01 source ----
       
    38     space: 0, A: 1, B: 2, ..., Z: 26
       
    39 
       
    40     ABCD corresponds to the number 4321 which becomes
       
    41     ((4*27 + 3) * 27 + 2) * 27 + 1.
       
    42     ----                         ----
       
    43 
       
    44     The description above contains an error. The actual situation
       
    45     is reversed: ABCD corresponds to the number 1234 which results in
       
    46     ((1*27 + 2) * 27 + 3) * 27 + 4.
       
    47     '''
       
    48 
       
    49     if len(offset) not in range(1, 5):
       
    50         raise ValueError("offset must be four characters or less")
       
    51     uid = 0L
       
    52     offset = offset.upper()
       
    53     for c in offset:
       
    54         try:
       
    55             ordc = " ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(c)
       
    56         except ValueError:
       
    57             raise ValueError("invalid character '%s' in offset" % c)
       
    58         uid *= 27
       
    59         uid += ordc
       
    60     return uid
       
    61 
       
    62 
       
    63 ##############################################################################
       
    64 # Resource class for containing binary fields
       
    65 ##############################################################################
       
    66 
       
    67 class Resource(object):
       
    68     '''A Symbian OS resource type
       
    69 
       
    70     Limitations:
       
    71 
       
    72     - Write only
       
    73     - Available types are limited to BYTE, WORD, LONG, LLINK and LTEXT.
       
    74     - Unicode compression is not supported.'''
       
    75 
       
    76     def __init__(self, fieldtypes, *args):
       
    77         self.fieldtypes = fieldtypes
       
    78         self.fieldvalues = []
       
    79 
       
    80         if len(self.fieldtypes) != len(args):
       
    81             raise ValueError("invalid number of field values")
       
    82 
       
    83         offset = 0
       
    84         for n in xrange(len(args)):
       
    85             ftype = self.fieldtypes[n]
       
    86             fval = args[n]
       
    87             if ftype == "BYTE":
       
    88                 if fval < 0:
       
    89                     fval += 0x100
       
    90                 if fval < 0 or fval > 255:
       
    91                     raise ValueError("byte integer too large")
       
    92                 self.fieldvalues.append(struct.pack("<B", fval))
       
    93                 offset += 1
       
    94             elif ftype == "WORD":
       
    95                 if fval < 0:
       
    96                     fval += 0x10000
       
    97                 if fval < 0 or fval > 65535:
       
    98                     raise ValueError("word integer too large")
       
    99                 self.fieldvalues.append(struct.pack("<H", fval))
       
   100                 offset += 2
       
   101             elif ftype == "LONG" or ftype == "LLINK":
       
   102                 if fval < 0:
       
   103                     fval += 0x100000000L
       
   104                 if fval < 0 or fval > 4294967295:
       
   105                     raise ValueError("long integer too large")
       
   106                 self.fieldvalues.append(struct.pack("<L", fval))
       
   107                 offset += 4
       
   108             elif ftype == "LTEXT":
       
   109                 if len(fval) > 255:
       
   110                     raise ValueError("Unicode string too long")
       
   111                 self.fieldvalues.append(struct.pack("<B", len(fval)))
       
   112                 offset += 1
       
   113                 if len(fval) > 0:
       
   114                     if (offset & 1) != 0:
       
   115                         # Odd offset. Add padding byte (only if length > 0).
       
   116                         self.fieldvalues.append(struct.pack("B", 0xab))
       
   117                         offset += 1
       
   118                     fval_enc = fval.encode("UTF-16LE")
       
   119                     self.fieldvalues.append(fval_enc)
       
   120                     offset += len(fval_enc)
       
   121 
       
   122             # TODO: Arrays, recursive structs
       
   123             # TODO: TEXT, DOUBLE, BUF, BUF8, BUF<n>, LINK, SRLINK
       
   124 
       
   125     def tostring(self):
       
   126         return "".join(self.fieldvalues)
       
   127 
       
   128     # TODO: fromstring()
       
   129 
       
   130 
       
   131 ##############################################################################
       
   132 # RSCWriter class for creating Symbian OS compiled resource files (RSC)
       
   133 ##############################################################################
       
   134 
       
   135 class RSCWriter(object):
       
   136     '''A Symbian OS compiled resource file (RSC) file generator
       
   137 
       
   138     Limitations:
       
   139 
       
   140     - Output format is always "Compressed Unicode resource format".
       
   141     - Despite the format name, nothing is compressed.'''
       
   142 
       
   143     def __init__(self, uid2, uid3 = None, offset = None):
       
   144         self.resources = []
       
   145 
       
   146         if ((uid3 == None and offset == None) or
       
   147             (uid3 != None and offset != None)):
       
   148             raise AttributeError("one of uid3 or offset required, not both")
       
   149 
       
   150         self.flags = 0x00
       
   151         if offset != None:
       
   152             try:
       
   153                 uid3 = makeuidfromoffset(offset)
       
   154             except:
       
   155                 raise ValueError("invalid offset '%s'" % offset)
       
   156             self.flags = 0x01
       
   157 
       
   158         self.uid2 = uid2
       
   159         self.uid3 = uid3
       
   160 
       
   161     def addresource(self, resource):
       
   162         self.addrawresource(resource.tostring())
       
   163 
       
   164     def addrawresource(self, string):
       
   165         self.resources.append(string)
       
   166 
       
   167     def tostring(self):
       
   168         # UIDs (UID1 always 0x101f4a6b)
       
   169         fields = [symbianutil.uidstostring(0x101f4a6bL, self.uid2, self.uid3)]
       
   170 
       
   171         # Flags
       
   172         fields.append(struct.pack("<B", self.flags))
       
   173 
       
   174         # Longest resource (to be updated)
       
   175         fields.append("\0\0")
       
   176 
       
   177         # Unicode compression bitmap (no compression)
       
   178         fields.append("\0" * ((len(self.resources) + 7) / 8))
       
   179 
       
   180         # Resource contents
       
   181         offsets = []
       
   182         foffset = len("".join(fields))
       
   183         maxrlen = 0
       
   184         for res in self.resources:
       
   185             # Find longest resource.
       
   186             rlen = len(res)
       
   187             if rlen > maxrlen:
       
   188                 maxrlen = rlen
       
   189             offsets.append(foffset)
       
   190             fields.append(res)
       
   191             foffset += rlen
       
   192         offsets.append(foffset)
       
   193 
       
   194         # Update longest resource.
       
   195         fields[2] = struct.pack("<H", maxrlen)
       
   196 
       
   197         # Resource index
       
   198         for off in offsets:
       
   199             fields.append(struct.pack("<H", off))
       
   200 
       
   201         # TODO: Ineffiecient. Improve.
       
   202         return "".join(fields)