236 lines
5.9 KiB
Python
236 lines
5.9 KiB
Python
"""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
|
|
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()
|
|
|
|
|