1 r
"""Routines to decode AppleSingle files
10 def openrf(path
, mode
):
11 return open(path
+ '.rsrc', mode
)
12 openrf
= classmethod(openrf
)
22 # all of the errors in this module are really errors in the input
23 # so I think it should test positive against ValueError.
24 class Error(ValueError):
27 # File header format: magic, version, unused, number of entries
28 AS_HEADER_FORMAT
=">LL16sh"
30 # The flag words for AppleSingle
34 # Entry header format: id, offset, length
35 AS_ENTRY_FORMAT
=">lll"
41 AS_IGNORE
=(3,4,5,6,8,9,10,11,12,13,14,15)
43 class AppleSingle(object):
47 def __init__(self
, fileobj
, verbose
=False):
48 header
= fileobj
.read(AS_HEADER_LENGTH
)
50 magic
, version
, ig
, nentry
= struct
.unpack(AS_HEADER_FORMAT
, header
)
51 except ValueError, arg
:
52 raise Error
, "Unpack header error: %s" % (arg
,)
54 print 'Magic: 0x%8.8x' % (magic
,)
55 print 'Version: 0x%8.8x' % (version
,)
56 print 'Entries: %d' % (nentry
,)
58 raise Error
, "Unknown AppleSingle magic number 0x%8.8x" % (magic
,)
59 if version
!= AS_VERSION
:
60 raise Error
, "Unknown AppleSingle version number 0x%8.8x" % (version
,)
62 raise Error
, "AppleSingle file contains no forks"
63 headers
= [fileobj
.read(AS_ENTRY_LENGTH
) for i
in xrange(nentry
)]
67 restype
, offset
, length
= struct
.unpack(AS_ENTRY_FORMAT
, hdr
)
68 except ValueError, arg
:
69 raise Error
, "Unpack entry error: %s" % (arg
,)
71 print "Fork %d, offset %d, length %d" % (restype
, offset
, length
)
73 data
= fileobj
.read(length
)
74 if len(data
) != length
:
75 raise Error
, "Short read: expected %d bytes got %d" % (length
, len(data
))
76 self
.forks
.append((restype
, data
))
77 if restype
== AS_DATAFORK
:
79 elif restype
== AS_RESOURCEFORK
:
80 self
.resourcefork
= data
82 def tofile(self
, path
, resonly
=False):
83 outfile
= open(path
, 'wb')
86 if self
.resourcefork
is None:
87 raise Error
, "No resource fork found"
89 fp
.write(self
.resourcefork
)
91 elif (self
.resourcefork
is None and self
.datafork
is None):
92 raise Error
, "No useful forks found"
94 if self
.datafork
is not None:
96 fp
.write(self
.datafork
)
98 if self
.resourcefork
is not None:
99 fp
= MacOS
.openrf(path
, '*wb')
100 fp
.write(self
.resourcefork
)
103 def decode(infile
, outpath
, resonly
=False, verbose
=False):
104 """decode(infile, outpath [, resonly=False, verbose=False])
106 Creates a decoded file from an AppleSingle encoded file.
107 If resonly is True, then it will create a regular file at
108 outpath containing only the resource fork from infile.
109 Otherwise it will create an AppleDouble file at outpath
110 with the data and resource forks from infile. On platforms
111 without the MacOS module, it will create inpath and inpath+'.rsrc'
112 with the data and resource forks respectively.
115 if not hasattr(infile
, 'read'):
116 if isinstance(infile
, Carbon
.File
.Alias
):
117 infile
= infile
.ResolveAlias()[0]
118 if isinstance(infile
, (Carbon
.File
.FSSpec
, Carbon
.File
.FSRef
)):
119 infile
= infile
.as_pathname()
120 infile
= open(infile
, 'rb')
122 asfile
= AppleSingle(infile
, verbose
=verbose
)
123 asfile
.tofile(outpath
, resonly
=resonly
)
126 if len(sys
.argv
) < 3 or sys
.argv
[1] == '-r' and len(sys
.argv
) != 4:
127 print 'Usage: applesingle.py [-r] applesinglefile decodedfile'
129 if sys
.argv
[1] == '-r':
134 decode(sys
.argv
[1], sys
.argv
[2], resonly
=resonly
)
136 if __name__
== '__main__':