blob: 1a50a677014a14a7520e0ca28fc53d4d526c6487 [file] [log] [blame]
Jack Jansenc70c3501997-05-06 16:14:34 +00001"""cfmfile - Interface to code fragments on file"""
2import struct
3import Res
4import macfs
5import string
6
7Error = 'cfmfile.Error'
8
9READ = 1
10WRITE = 2
11smAllScripts = -3
12BUFSIZE = 0x100000
13
14class FragmentInfo:
15 """Information on a single fragment"""
16 def __init__(self):
17 self.arch = 'pwpc'
18 self.current_version = 0
19 self.oldest_version = 0
20 self.stacksize = 0
21 self.libdir = 0
22 self.fragtype = 1
23 self.location = 1
24 self.offset = 0
25 self.length = 0
26 self.res_0 = 0
27 self.res_1 = 0
28 self.name = ''
29 self.ifp = None
30
31 def load(self, data):
32 if len(data) < 43:
33 raise Error, 'Not enough data in cfrg resource'
34 self.arch = data[:4]
35 self.update, self.current_version, self.oldest_version, \
36 self.stacksize, self.libdir, self.fragtype, self.location, \
37 self.offset, self.length, self.res_0, self.res_1, length = \
38 struct.unpack("llllhbbllllh", data[4:42])
39 namelen = ord(data[42])
40 self.name = data[43:43+namelen]
41 if len(self.name) != namelen:
42 raise Error, 'Not enough data in cfrg resource'
43 return length
44
45 def save(self):
46 length = (43+len(self.name)+3) & ~3
47 data = self.arch + struct.pack("llllhbbllllh", self.update, \
48 self.current_version, self.oldest_version, self.stacksize, \
49 self.libdir, self.fragtype, self.location, self.offset, \
50 self.length, self.res_0, self.res_1, length)
51 data = data + chr(len(self.name)) + self.name
52 data = data + ('\0'*(length-len(data)))
53 return data
54
55 def copydata(self, ofp):
56 """Copy fragment data to a new file, updating self.offset"""
57 if self.location != 1:
58 raise Error, 'Can only copy kOnDiskFlat (data fork) fragments'
59 if not self.ifp:
60 raise Error, 'No source file for fragment'
61 # Find out real length (if zero)
62 if self.length == 0:
63 self.ifp.seek(0, 2)
64 self.length = self.ifp.tell()
65 # Position input file and record new offset from output file
66 self.ifp.seek(self.offset)
67 self.offset = ofp.tell()
68 l = self.length
69 while l:
70 if l > BUFSIZE:
71 ofp.write(self.ifp.read(BUFSIZE))
72 l = l - BUFSIZE
73 else:
74 ofp.write(self.ifp.read(l))
75 l = 0
76 self.ifp = ofp
77
78 def setfile(self, ifp):
79 self.ifp = ifp
80
81class FragmentResource:
82
83 def __init__(self, data):
84 self.load(data)
85
86 def load(self, data):
87 r0, r1, version, r3, r4, r5, r6, nfrag = struct.unpack("llllllll", data[:32])
88 if version != 1:
89 raise Error, 'Unsupported cfrg version number %d'%version
90 data = data[32:]
91 self.fragments = []
92 for i in range(nfrag):
93 f = FragmentInfo()
94 len = f.load(data)
95 data = data[len:]
96 self.fragments.append(f)
97 if data:
98 raise Error, 'Spurious data after fragment descriptions'
99
100 def save(self):
101 data = struct.pack("llllllll", 0, 0, 1, 0, 0, 0, 0, len(self.fragments))
102 for f in self.fragments:
103 data = data+f.save()
104 return data
105
106 def setfile(self, ifp):
107 for f in self.fragments:
108 f.setfile(ifp)
109
110 def copydata(self, ofp):
111 for f in self.fragments:
112 f.copydata(ofp)
113
114 def getfragments(self):
115 return self.fragments
116
117 def addfragments(self, fragments):
118 self.fragments = self.fragments + fragments
119
120class ResourceCollection:
121 def __init__(self, fhandle):
122 self.reslist = []
123 self.fhandle = fhandle
124 oldresfile = Res.CurResFile()
125 Res.UseResFile(fhandle)
126 Res.SetResLoad(0)
127 ntypes = Res.Count1Types()
128 for itype in range(1, 1+ntypes):
129 type = Res.Get1IndType(itype)
130 nresources = Res.Count1Resources(type)
131 for ires in range(1, 1+nresources):
132 res = Res.Get1IndResource(type, ires)
133 id, type, name = res.GetResInfo()
134 self.reslist.append((type, id))
135 Res.SetResLoad(1)
136 Res.UseResFile(oldresfile)
137
138 def contains(self, type, id):
139 return (type, id) in self.reslist
140
141 def getresource(self, type, id):
142 oldresfile = Res.CurResFile()
143 Res.UseResFile(self.fhandle)
144 Res.SetResLoad(1)
145 resource = Res.Get1Resource(type, id)
146 Res.UseResFile(oldresfile)
147 return resource
148
149 def saveresto(self, type, id, fhandle):
150 oldresfile = Res.CurResFile()
151 resource = self.getresource(type, id)
152 id, type, name = resource.GetResInfo()
153 resource.DetachResource()
154 Res.UseResFile(fhandle)
155 resource.AddResource(type, id, name)
156 Res.UseResFile(oldresfile)
157
158 def getreslist(self):
159 return self.reslist
160
161class CfmFile(ResourceCollection, FragmentResource):
162
163 def __init__(self, fsspec):
164 rfork = Res.FSpOpenResFile(fsspec, READ)
165 dfork = open(fsspec.as_pathname(), 'rb')
166 ResourceCollection.__init__(self, rfork)
167 cfrg_resource = self.getresource('cfrg', 0)
168 FragmentResource.__init__(self, cfrg_resource.data)
169 self.setfile(dfork)
170
171def mergecfmfiles(inputs, output):
172 # Convert inputs/outputs to fsspecs
Jack Jansenc512be01997-05-07 15:50:56 +0000173 inputs = map(None, inputs)
Jack Jansenc70c3501997-05-06 16:14:34 +0000174 for i in range(len(inputs)):
175 if type(inputs[i]) == type(''):
176 inputs[i] = macfs.FSSpec(inputs[i])
177 if type(output) == type(''):
178 output = macfs.FSSpec(output)
179
180 input_list = []
181 for i in inputs:
182 input_list.append(CfmFile(i))
183
184 # Create output file, if needed
185 creator, tp = inputs[0].GetCreatorType()
186 try:
187 Res.FSpCreateResFile(output, creator, tp, smAllScripts)
188 except Res.Error:
189 pass
190
191 # Copy fragments
192 dfork = open(output.as_pathname(), 'wb')
193 for i in input_list:
194 i.copydata(dfork)
195 dfork.close()
196
197 # Merge cfrg's
198 for i in input_list[1:]:
199 input_list[0].addfragments(i.getfragments())
200
201 old_res_file = Res.CurResFile()
202 rfork = Res.FSpOpenResFile(output, WRITE)
203 Res.UseResFile(rfork)
204
205 # Write cfrg
206 data = input_list[0].save()
207 cfrg_resource = Res.Resource(data)
208 cfrg_resource.AddResource('cfrg', 0, '')
209 resources_done = [('cfrg', 0)]
210
211 # Write other resources
212 for i in input_list:
213 todo = i.getreslist()
214 for tp, id in todo:
215 if (tp, id) in resources_done:
216 continue
217 i.saveresto(tp, id, rfork)
218 resources_done.append(tp, id)
219
220def main():
221 list = []
222 while 1:
223 fss, ok = macfs.PromptGetFile("Next input file:", "shlb", "APPL")
224 if not ok: break
225 list.append(fss)
226 if not list:
227 sys.exit(0)
228 output, ok = macfs.StandardPutFile("Output file:")
229 if not ok:
230 sys.exit(0)
231 mergecfmfiles(list, output)
232
233if __name__ == '__main__':
234 main()
235
236