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