| 
									
										
										
										
											2001-09-09 00:35:19 +00:00
										 |  |  | # 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): | 
					
						
							| 
									
										
										
										
											2003-04-09 13:25:43 +00:00
										 |  |  |     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' | 
					
						
							| 
									
										
										
										
											2001-09-09 00:35:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def _test(): | 
					
						
							| 
									
										
										
										
											2003-04-09 13:25:43 +00:00
										 |  |  |     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) | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2001-09-09 00:35:19 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2003-04-09 13:25:43 +00:00
										 |  |  |     _test() | 
					
						
							|  |  |  |      |