diff --git a/sosar.py b/sosar.py index 1c0bd9e..7a57cdc 100755 --- a/sosar.py +++ b/sosar.py @@ -14,8 +14,39 @@ def cmd_mkfs(args, disk): print('XXX mkfs') +def cmd_extract(args, disk): + for sf in disk.files(path = '', recursive = True): + name = sf.get_name() + #print(name) + #print(len(name)) + eof = sf.get_eof() + data = sf.read(0, eof) + #print(len(data)) + with open(name, 'wb') as f: + f.write(data) + break # XXX for debug, only extract first file + + parser = argparse.ArgumentParser() +fmt_group = parser.add_mutually_exclusive_group() + +fmt_group.add_argument('--do', + dest = 'format', + action = 'store_const', + const = 'do', + help = "image in DOS sector order") + +fmt_group.add_argument('--po', + dest = 'format', + action = 'store_const', + const = 'po', + help = "image in SOS/ProDOS sector order") + +parser.add_argument('image', + type = str, + help = "SOS/ProDOS disk image") + subparsers = parser.add_subparsers(title = 'commands', dest = 'cmd') @@ -40,23 +71,19 @@ mkfs_parser.add_argument('--size', default = 280, help = 'filesystem size in blocks') -parser.add_argument('image', - type = str, - help = "SOS/ProDOS disk image") +extract_parser = subparsers.add_parser('x', + help = 'extract file(s)') +extract_parser.set_defaults(cmd_fn = cmd_extract) -fmt_group = parser.add_mutually_exclusive_group() +extract_parser.add_argument('-r', '--recursive', + action = 'store_true', + help = 'recursively extract subdirectory content') -fmt_group.add_argument('--do', - dest = 'format', - action = 'store_const', - const = 'do', - help = "image in DOS sector order") - -fmt_group.add_argument('--po', - dest = 'format', - action = 'store_const', - const = 'po', - help = "image in SOS/ProDOS sector order") +extract_parser.add_argument('filename', + type = str, + nargs = '+', + help = 'filename(s) to extract', +) args = parser.parse_args() #print(args) @@ -72,7 +99,8 @@ else: sys.exit(2) file_mode = { 'mkfs': 'w', - 'ls': 'r' } [args.cmd] + 'b' + 'ls': 'r', + 'x': 'r' } [args.cmd] + 'b' image = open(args.image, file_mode) diff --git a/sosdisk.py b/sosdisk.py index f09e7e6..eeac0b5 100644 --- a/sosdisk.py +++ b/sosdisk.py @@ -124,7 +124,7 @@ sos_valid_fn_chars = set(string.ascii_uppercase + string.digits + '.') def bytes_to_sos_filename(l, b): assert len(b) == 15 assert 1 <= l <= 15 - s = str(b, 'ascii') + s = str(b[:l], 'ascii') assert all(c in sos_valid_fn_chars for c in s[:l]) assert all(c == 0 for c in b[l:]) return s.lower() @@ -213,6 +213,21 @@ class SOSStorage: def is_sparse(self): return self.data_blocks != (self.last_block_index + 1) + def read(self, + offset = 0, + length = 0): + data = bytearray(length) + while length > 0: + block_index = offset // self.disk.block_size + block_offset = offset % self.disk.block_size + chunk_length = min(length, self.disk.block_size - block_offset) + if block_index in self.index: + block_number = self.index[block_index] + data[offset:offset+chunk_length] = self.disk.get_blocks(block_number)[block_offset:block_offset+chunk_length] + offset += chunk_length + length -= chunk_length + return data + class SOSSeedling(SOSStorage): def __init__(self, disk, key_pointer): @@ -271,7 +286,12 @@ class SOSDirectoryEntry: pass -class SOSVolumeDirectoryHeader(SOSDirectoryEntry): +class SOSDirectoryHeader(SOSDirectoryEntry): + def __init__(self, disk): + super().__init__(disk) + + +class SOSVolumeDirectoryHeader(SOSDirectoryHeader): def __init__(self, disk, entry_data): super().__init__(disk) (storage_nl, name_b, self.reserved, creation_b, self.version, self.min_version, self.access, self.entry_length, self.entries_per_block, self.file_count, self.bitmap_pointer, self.total_blocks) = struct.unpack('