# applesingle - a module to decode AppleSingle files import struct import MacOS import sys Error="applesingle.Error" verbose=0 # File header format: magic, version, unused, number of entries AS_HEADER_FORMAT="ll16sh" AS_HEADER_LENGTH=26 # The flag words for AppleSingle AS_MAGIC=0x00051600 AS_VERSION=0x00020000 # Entry header format: id, offset, length AS_ENTRY_FORMAT="lll" AS_ENTRY_LENGTH=12 # The id values AS_DATAFORK=1 AS_RESOURCEFORK=2 AS_IGNORE=(3,4,5,6,8,9,10,11,12,13,14,15) def decode(input, output, resonly=0): if type(input) == type(''): input = open(input, 'rb') # Should we also test for FSSpecs or FSRefs? header = input.read(AS_HEADER_LENGTH) try: magic, version, dummy, nentry = struct.unpack(AS_HEADER_FORMAT, header) except ValueError, arg: raise Error, "Unpack header error: %s"%arg if verbose: print 'Magic: 0x%8.8x'%magic print 'Version: 0x%8.8x'%version print 'Entries: %d'%nentry if magic != AS_MAGIC: raise Error, 'Unknown AppleSingle magic number 0x%8.8x'%magic if version != AS_VERSION: raise Error, 'Unknown AppleSingle version number 0x%8.8x'%version if nentry <= 0: raise Error, "AppleSingle file contains no forks" headers = [input.read(AS_ENTRY_LENGTH) for i in range(nentry)] didwork = 0 for hdr in headers: try: id, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr) except ValueError, arg: raise Error, "Unpack entry error: %s"%arg if verbose: print 'Fork %d, offset %d, length %d'%(id, offset, length) input.seek(offset) if length == 0: data = '' else: data = input.read(length) if len(data) != length: raise Error, 'Short read: expected %d bytes got %d'%(length, len(data)) if id == AS_DATAFORK: if verbose: print ' (data fork)' if not resonly: didwork = 1 fp = open(output, 'wb') fp.write(data) fp.close() elif id == AS_RESOURCEFORK: didwork = 1 if verbose: print ' (resource fork)' if resonly: fp = open(output, 'wb') else: fp = MacOS.openrf(output, 'wb') fp.write(data) fp.close() elif id in AS_IGNORE: if verbose: print ' (ignored)' else: raise Error, 'Unknown fork type %d'%id if not didwork: raise Error, 'No useful forks found' def _test(): if len(sys.argv) < 3 or sys.argv[1] == '-r' and len(sys.argv) != 4: print 'Usage: applesingle.py [-r] applesinglefile decodedfile' sys.exit(1) if sys.argv[1] == '-r': resonly = 1 del sys.argv[1] else: resonly = 0 decode(sys.argv[1], sys.argv[2], resonly=resonly) if __name__ == '__main__': _test()