Jack Jansen | c70c350 | 1997-05-06 16:14:34 +0000 | [diff] [blame] | 1 | """cfmfile - Interface to code fragments on file""" |
| 2 | import struct |
| 3 | import Res |
| 4 | import macfs |
| 5 | import string |
| 6 | |
| 7 | Error = 'cfmfile.Error' |
| 8 | |
| 9 | READ = 1 |
| 10 | WRITE = 2 |
| 11 | smAllScripts = -3 |
| 12 | BUFSIZE = 0x100000 |
| 13 | |
| 14 | class 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 | |
| 81 | class 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 | |
| 120 | class 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 | |
| 161 | class 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 | |
| 171 | def mergecfmfiles(inputs, output): |
| 172 | # Convert inputs/outputs to fsspecs |
Jack Jansen | c512be0 | 1997-05-07 15:50:56 +0000 | [diff] [blame] | 173 | inputs = map(None, inputs) |
Jack Jansen | c70c350 | 1997-05-06 16:14:34 +0000 | [diff] [blame] | 174 | 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 | |
| 220 | def 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 | |
| 233 | if __name__ == '__main__': |
| 234 | main() |
| 235 | |
| 236 | |