pimpmyplus/appledouble.py

125 lines
3.2 KiB
Python

"""
Reference:
http://formats.kaitai.io/apple_single_double/index.html
http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf
above linke is dead, archive:
https://web.archive.org/web/20180311140826/http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf
"""
import enum
from ctypes import BigEndianStructure, c_uint32, c_uint16, c_char
from io import RawIOBase
from typing import List, Optional
class AppleDoubleHeader(BigEndianStructure):
_pack_ = 1
_fields_ = [
('signature', c_uint32),
('version', c_uint32),
('reserved', c_uint32 * 4), # Must all be zero
('num_entries', c_uint16)
]
class AppleDoubleEntry(BigEndianStructure):
_pack_ = 1
_fields_ = [
('type', c_uint32),
('body_offset', c_uint32),
('body_length', c_uint32)
]
class EntryType(enum.Enum):
data_fork = 1
resource_fork = 2
real_name = 3 # File name on a file system that supports all the attributes.
comment = 4
icon_bw = 5
icon_color = 6
file_dates_info = 8 # File creation, modification, access date/timestamps.
finder_info = 9
macintosh_file_info = 10
prodos_file_info = 11
msdos_file_info = 12
afp_short_name = 13
afp_file_info = 14
afp_directory_id = 15
class Point(BigEndianStructure):
"""Specifies 2D coordinate in QuickDraw grid."""
_pack_ = 1
_fields_ = [
('x', c_uint16),
('y', c_uint16),
]
class FinderInfo(BigEndianStructure):
"""From the older Inside Macintosh publication, Volume II page 84 or Volume IV page 104."""
_pack_ = 1
_fields_ = [
('fdType', c_char * 4),
('fdCreator', c_char * 4),
('fdFlags', c_uint16),
('fdLocation', Point), # File icon's coordinates when displaying this folder.
('fdFolder', c_uint16) # File folder ID (=window).
]
class Entry(object):
def __init__(self):
self.info = None
self.data = None
class AppleDouble(object):
def __init__(self):
self.header = AppleDoubleHeader()
self.entries: List[Entry] = []
def get_entry(self, type: EntryType) -> Optional[Entry]:
for entry in self.entries:
if entry.info.type == type.value:
return entry
return None
SIGNATURE_APPLE_SINGLE = 0x00051600
SIGNATURE_APPLE_DOUBLE = 0x00051607
def parse(f: RawIOBase) -> AppleDouble:
header = AppleDoubleHeader()
f.readinto(header)
# Validate signature
if header.signature not in (SIGNATURE_APPLE_SINGLE, SIGNATURE_APPLE_DOUBLE):
raise ValueError('Invalid signature')
entries = []
for i in range(header.num_entries):
entry = Entry()
info = AppleDoubleEntry()
f.readinto(info)
entry.info = info
entries.append(entry)
for entry in entries:
info = entry.info
f.seek(info.body_offset)
data = f.read(info.body_length)
if info.type == EntryType.finder_info.value:
entry.data = FinderInfo.from_buffer_copy(data)
else:
entry.data = data
result = AppleDouble()
result.header = header
result.entries = entries
return result