1+ # applesingle - a module to decode AppleSingle files
2+ import struct
3+ import MacOS
4+ import sys
5+
6+ Error = "applesingle.Error"
7+
8+ verbose = 0
9+
10+ # File header format: magic, version, unused, number of entries
11+ AS_HEADER_FORMAT = "ll16sh"
12+ AS_HEADER_LENGTH = 26
13+ # The flag words for AppleSingle
14+ AS_MAGIC = 0x00051600
15+ AS_VERSION = 0x00020000
16+
17+ # Entry header format: id, offset, length
18+ AS_ENTRY_FORMAT = "lll"
19+ AS_ENTRY_LENGTH = 12
20+
21+ # The id values
22+ AS_DATAFORK = 1
23+ AS_RESOURCEFORK = 2
24+ AS_IGNORE = (3 ,4 ,5 ,6 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 )
25+
26+ def decode (input , output , resonly = 0 ):
27+ if type (input ) == type ('' ):
28+ input = open (input , 'rb' )
29+ # Should we also test for FSSpecs or FSRefs?
30+ header = input .read (AS_HEADER_LENGTH )
31+ print `header`
32+ try :
33+ magic , version , dummy , nentry = struct .unpack (AS_HEADER_FORMAT , header )
34+ except ValueError , arg :
35+ raise Error , "Unpack header error: %s" % arg
36+ if verbose :
37+ print 'Magic: 0x%8.8x' % magic
38+ print 'Version: 0x%8.8x' % version
39+ print 'Entries: %d' % nentry
40+ if magic != AS_MAGIC :
41+ raise Error , 'Unknown AppleSingle magic number 0x%8.8x' % magic
42+ if version != AS_VERSION :
43+ raise Error , 'Unknown AppleSingle version number 0x%8.8x' % version
44+ if nentry <= 0 :
45+ raise Error , "AppleSingle file contains no forks"
46+ headers = [input .read (AS_ENTRY_LENGTH ) for i in range (nentry )]
47+ didwork = 0
48+ for hdr in headers :
49+ try :
50+ id , offset , length = struct .unpack (AS_ENTRY_FORMAT , hdr )
51+ except ValueError , arg :
52+ raise Error , "Unpack entry error: %s" % arg
53+ if verbose :
54+ print 'Fork %d, offset %d, length %d' % (id , offset , length )
55+ input .seek (offset )
56+ if length == 0 :
57+ data = ''
58+ else :
59+ data = input .read (length )
60+ if len (data ) != length :
61+ raise Error , 'Short read: expected %d bytes got %d' % (length , len (data ))
62+ if id == AS_DATAFORK :
63+ if verbose :
64+ print ' (data fork)'
65+ if not resonly :
66+ didwork = 1
67+ fp = open (output , 'wb' )
68+ fp .write (data )
69+ fp .close ()
70+ elif id == AS_RESOURCEFORK :
71+ didwork = 1
72+ if verbose :
73+ print ' (resource fork)'
74+ if resonly :
75+ fp = open (output , 'wb' )
76+ else :
77+ fp = MacOS .openrf (output , 'wb' )
78+ fp .write (data )
79+ fp .close ()
80+ elif id in AS_IGNORE :
81+ if verbose :
82+ print ' (ignored)'
83+ else :
84+ raise Error , 'Unknown fork type %d' % id
85+ if not didwork :
86+ raise Error , 'No useful forks found'
87+
88+ def _test ():
89+ if len (sys .argv ) < 3 or sys .argv [1 ] == '-r' and len (sys .argv ) != 4 :
90+ print 'Usage: applesingle.py [-r] applesinglefile decodedfile'
91+ sys .exit (1 )
92+ if sys .argv [1 ] == '-r' :
93+ resonly = 1
94+ del sys .argv [1 ]
95+ else :
96+ resonly = 0
97+ decode (sys .argv [1 ], sys .argv [2 ], resonly = resonly )
98+
99+ if __name__ == '__main__' :
100+ _test ()
101+
0 commit comments