blob: cef28fe18c29a944202e33b100e21c0e7c07a11d [file] [log] [blame]
Jack Jansen87440e41998-07-31 09:41:59 +00001"""codefragments.py -- wrapper to modify code fragments."""
2
3# © 1998, Just van Rossum, Letterror
4
5__version__ = "0.8b3"
6__author__ = "jvr"
7
8import macfs
9import struct
10import Res
11import os
12import sys
13
14DEBUG = 0
15
16error = "cfm.error"
17
18BUFSIZE = 0x80000
19
20def mergecfmfiles(srclist, dst, architecture = 'fat'):
21 """Merge all files in srclist into a new file dst.
22
23 If architecture is given, only code fragments of that type will be used:
24 "pwpc" for PPC, "m68k" for cfm68k. This does not work for "classic"
25 68k code, since it does not use code fragments to begin with.
26 If architecture is None, all fragments will be used, enabling FAT binaries.
27 """
28
29 srclist = list(srclist)
30 for i in range(len(srclist)):
31 if type(srclist[i]) == macfs.FSSpecType:
32 srclist[i] = srclist[i].as_pathname()
33 if type(dst) == macfs.FSSpecType:
34 dst = dst.as_pathname()
35
36 dstfile = open(dst, "wb")
Jack Jansend13c3852000-06-20 21:59:25 +000037 rf = Res.FSpOpenResFile(dst, 3)
Jack Jansen87440e41998-07-31 09:41:59 +000038 try:
39 dstcfrg = CfrgResource()
40 for src in srclist:
41 srccfrg = CfrgResource(src)
42 for frag in srccfrg.fragments:
43 if frag.architecture == 'pwpc' and architecture == 'm68k':
44 continue
45 if frag.architecture == 'm68k' and architecture == 'pwpc':
46 continue
47 dstcfrg.append(frag)
48
49 frag.copydata(dstfile)
50
51 cfrgres = Res.Resource(dstcfrg.build())
52 Res.UseResFile(rf)
53 cfrgres.AddResource('cfrg', 0, "")
54 finally:
55 dstfile.close()
56 rf = Res.CloseResFile(rf)
57
58
59class CfrgResource:
60
61 def __init__(self, path = None):
62 self.version = 1
63 self.fragments = []
64 self.path = path
65 if path is not None and os.path.exists(path):
66 currentresref = Res.CurResFile()
Jack Jansend13c3852000-06-20 21:59:25 +000067 resref = Res.FSpOpenResFile(path, 1)
Jack Jansen87440e41998-07-31 09:41:59 +000068 Res.UseResFile(resref)
69 try:
70 try:
71 data = Res.Get1Resource('cfrg', 0).data
72 except Res.Error:
73 raise Res.Error, "no Œcfrg¹ resource found", sys.exc_traceback
74 finally:
75 Res.CloseResFile(resref)
76 Res.UseResFile(currentresref)
77 self.parse(data)
78 if self.version <> 1:
79 raise error, "unknown 'cfrg' resource format"
80
81 def parse(self, data):
82 (res1, res2, self.version,
83 res3, res4, res5, res6,
84 self.memberCount) = struct.unpack("8l", data[:32])
85 data = data[32:]
86 while data:
87 frag = FragmentDescriptor(self.path, data)
88 data = data[frag.memberSize:]
89 self.fragments.append(frag)
90
91 def build(self):
92 self.memberCount = len(self.fragments)
93 data = struct.pack("8l", 0, 0, self.version, 0, 0, 0, 0, self.memberCount)
94 for frag in self.fragments:
95 data = data + frag.build()
96 return data
97
98 def append(self, frag):
99 self.fragments.append(frag)
100
101
102class FragmentDescriptor:
103
104 def __init__(self, path, data = None):
105 self.path = path
106 if data is not None:
107 self.parse(data)
108
109 def parse(self, data):
110 self.architecture = data[:4]
111 ( self.updatelevel,
112 self.currentVersion,
113 self.oldDefVersion,
114 self.stacksize,
115 self.applibdir,
116 self.fragtype,
117 self.where,
118 self.offset,
119 self.length,
120 self.res1, self.res2,
121 self.memberSize,) = struct.unpack("4lhBB4lh", data[4:42])
122 pname = data[42:self.memberSize]
123 self.name = pname[1:1+ord(pname[0])]
124
125 def build(self):
126 data = self.architecture
127 data = data + struct.pack("4lhBB4l",
128 self.updatelevel,
129 self.currentVersion,
130 self.oldDefVersion,
131 self.stacksize,
132 self.applibdir,
133 self.fragtype,
134 self.where,
135 self.offset,
136 self.length,
137 self.res1, self.res2)
138 self.memberSize = len(data) + 2 + 1 + len(self.name)
139 # pad to 4 byte boundaries
140 if self.memberSize % 4:
141 self.memberSize = self.memberSize + 4 - (self.memberSize % 4)
142 data = data + struct.pack("hb", self.memberSize, len(self.name))
143 data = data + self.name
144 data = data + '\000' * (self.memberSize - len(data))
145 return data
146
147 def getfragment(self):
148 if self.where <> 1:
149 raise error, "can¹t read fragment, unsupported location"
150 f = open(self.path, "rb")
151 f.seek(self.offset)
152 if self.length:
153 frag = f.read(self.length)
154 else:
155 frag = f.read()
156 f.close()
157 return frag
158
159 def copydata(self, outfile):
160 if self.where <> 1:
161 raise error, "can¹t read fragment, unsupported location"
162 infile = open(self.path, "rb")
163 if self.length == 0:
164 infile.seek(0, 2)
165 self.length = infile.tell()
166
167 # Position input file and record new offset from output file
168 infile.seek(self.offset)
169
170 # pad to 16 byte boundaries
171 offset = outfile.tell()
172 if offset % 16:
173 offset = offset + 16 - (offset % 16)
174 outfile.seek(offset)
175 self.offset = offset
176
177 l = self.length
178 while l:
179 if l > BUFSIZE:
180 outfile.write(infile.read(BUFSIZE))
181 l = l - BUFSIZE
182 else:
183 outfile.write(infile.read(l))
184 l = 0
185 infile.close()
186