blob: 1a50a677014a14a7520e0ca28fc53d4d526c6487 [file] [log] [blame]
"""cfmfile - Interface to code fragments on file"""
import struct
import Res
import macfs
import string
Error = 'cfmfile.Error'
READ = 1
WRITE = 2
smAllScripts = -3
BUFSIZE = 0x100000
class FragmentInfo:
"""Information on a single fragment"""
def __init__(self):
self.arch = 'pwpc'
self.current_version = 0
self.oldest_version = 0
self.stacksize = 0
self.libdir = 0
self.fragtype = 1
self.location = 1
self.offset = 0
self.length = 0
self.res_0 = 0
self.res_1 = 0
self.name = ''
self.ifp = None
def load(self, data):
if len(data) < 43:
raise Error, 'Not enough data in cfrg resource'
self.arch = data[:4]
self.update, self.current_version, self.oldest_version, \
self.stacksize, self.libdir, self.fragtype, self.location, \
self.offset, self.length, self.res_0, self.res_1, length = \
struct.unpack("llllhbbllllh", data[4:42])
namelen = ord(data[42])
self.name = data[43:43+namelen]
if len(self.name) != namelen:
raise Error, 'Not enough data in cfrg resource'
return length
def save(self):
length = (43+len(self.name)+3) & ~3
data = self.arch + struct.pack("llllhbbllllh", self.update, \
self.current_version, self.oldest_version, self.stacksize, \
self.libdir, self.fragtype, self.location, self.offset, \
self.length, self.res_0, self.res_1, length)
data = data + chr(len(self.name)) + self.name
data = data + ('\0'*(length-len(data)))
return data
def copydata(self, ofp):
"""Copy fragment data to a new file, updating self.offset"""
if self.location != 1:
raise Error, 'Can only copy kOnDiskFlat (data fork) fragments'
if not self.ifp:
raise Error, 'No source file for fragment'
# Find out real length (if zero)
if self.length == 0:
self.ifp.seek(0, 2)
self.length = self.ifp.tell()
# Position input file and record new offset from output file
self.ifp.seek(self.offset)
self.offset = ofp.tell()
l = self.length
while l:
if l > BUFSIZE:
ofp.write(self.ifp.read(BUFSIZE))
l = l - BUFSIZE
else:
ofp.write(self.ifp.read(l))
l = 0
self.ifp = ofp
def setfile(self, ifp):
self.ifp = ifp
class FragmentResource:
def __init__(self, data):
self.load(data)
def load(self, data):
r0, r1, version, r3, r4, r5, r6, nfrag = struct.unpack("llllllll", data[:32])
if version != 1:
raise Error, 'Unsupported cfrg version number %d'%version
data = data[32:]
self.fragments = []
for i in range(nfrag):
f = FragmentInfo()
len = f.load(data)
data = data[len:]
self.fragments.append(f)
if data:
raise Error, 'Spurious data after fragment descriptions'
def save(self):
data = struct.pack("llllllll", 0, 0, 1, 0, 0, 0, 0, len(self.fragments))
for f in self.fragments:
data = data+f.save()
return data
def setfile(self, ifp):
for f in self.fragments:
f.setfile(ifp)
def copydata(self, ofp):
for f in self.fragments:
f.copydata(ofp)
def getfragments(self):
return self.fragments
def addfragments(self, fragments):
self.fragments = self.fragments + fragments
class ResourceCollection:
def __init__(self, fhandle):
self.reslist = []
self.fhandle = fhandle
oldresfile = Res.CurResFile()
Res.UseResFile(fhandle)
Res.SetResLoad(0)
ntypes = Res.Count1Types()
for itype in range(1, 1+ntypes):
type = Res.Get1IndType(itype)
nresources = Res.Count1Resources(type)
for ires in range(1, 1+nresources):
res = Res.Get1IndResource(type, ires)
id, type, name = res.GetResInfo()
self.reslist.append((type, id))
Res.SetResLoad(1)
Res.UseResFile(oldresfile)
def contains(self, type, id):
return (type, id) in self.reslist
def getresource(self, type, id):
oldresfile = Res.CurResFile()
Res.UseResFile(self.fhandle)
Res.SetResLoad(1)
resource = Res.Get1Resource(type, id)
Res.UseResFile(oldresfile)
return resource
def saveresto(self, type, id, fhandle):
oldresfile = Res.CurResFile()
resource = self.getresource(type, id)
id, type, name = resource.GetResInfo()
resource.DetachResource()
Res.UseResFile(fhandle)
resource.AddResource(type, id, name)
Res.UseResFile(oldresfile)
def getreslist(self):
return self.reslist
class CfmFile(ResourceCollection, FragmentResource):
def __init__(self, fsspec):
rfork = Res.FSpOpenResFile(fsspec, READ)
dfork = open(fsspec.as_pathname(), 'rb')
ResourceCollection.__init__(self, rfork)
cfrg_resource = self.getresource('cfrg', 0)
FragmentResource.__init__(self, cfrg_resource.data)
self.setfile(dfork)
def mergecfmfiles(inputs, output):
# Convert inputs/outputs to fsspecs
inputs = map(None, inputs)
for i in range(len(inputs)):
if type(inputs[i]) == type(''):
inputs[i] = macfs.FSSpec(inputs[i])
if type(output) == type(''):
output = macfs.FSSpec(output)
input_list = []
for i in inputs:
input_list.append(CfmFile(i))
# Create output file, if needed
creator, tp = inputs[0].GetCreatorType()
try:
Res.FSpCreateResFile(output, creator, tp, smAllScripts)
except Res.Error:
pass
# Copy fragments
dfork = open(output.as_pathname(), 'wb')
for i in input_list:
i.copydata(dfork)
dfork.close()
# Merge cfrg's
for i in input_list[1:]:
input_list[0].addfragments(i.getfragments())
old_res_file = Res.CurResFile()
rfork = Res.FSpOpenResFile(output, WRITE)
Res.UseResFile(rfork)
# Write cfrg
data = input_list[0].save()
cfrg_resource = Res.Resource(data)
cfrg_resource.AddResource('cfrg', 0, '')
resources_done = [('cfrg', 0)]
# Write other resources
for i in input_list:
todo = i.getreslist()
for tp, id in todo:
if (tp, id) in resources_done:
continue
i.saveresto(tp, id, rfork)
resources_done.append(tp, id)
def main():
list = []
while 1:
fss, ok = macfs.PromptGetFile("Next input file:", "shlb", "APPL")
if not ok: break
list.append(fss)
if not list:
sys.exit(0)
output, ok = macfs.StandardPutFile("Output file:")
if not ok:
sys.exit(0)
mergecfmfiles(list, output)
if __name__ == '__main__':
main()