diff --git a/atrcopy/__init__.py b/atrcopy/__init__.py index 8fe64b3..23c9cb7 100644 --- a/atrcopy/__init__.py +++ b/atrcopy/__init__.py @@ -20,7 +20,7 @@ from .kboot import KBootImage, add_xexboot_header from .segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, SegmentedFileSegment, user_bit_mask, match_bit_mask, comment_bit_mask, data_style, selected_bit_mask, diff_bit_mask, not_user_bit_mask, interleave_segments, SegmentList, get_style_mask, get_style_bits from .spartados import SpartaDosDiskImage from .cartridge import A8CartHeader, AtariCartImage -from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, iter_parsers, iter_known_segment_parsers, mime_parse_order, parsers_for_filename +from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, guess_container, iter_known_segment_parsers, mime_parse_order, parsers_for_filename from .magic import guess_detail_for_mime from .utils import to_numpy, text_to_int @@ -60,6 +60,11 @@ def find_diskimage(filename): print("Loading file %s" % filename) rawdata = SegmentData(fh.read()) parser = None + try: + unpacked = guess_container(rawdata.data) + except errors.UnsupportedContainer as e: + print(f"{filename}: {e}") + return None for mime in mime_parse_order: if options.verbose: print("Trying MIME type %s" % mime) diff --git a/atrcopy/container.py b/atrcopy/container.py new file mode 100644 index 0000000..338c4a5 --- /dev/null +++ b/atrcopy/container.py @@ -0,0 +1,12 @@ +import numpy as np + +from . import errors +from .segments import SegmentData + + +class DiskImageContainer: + def __init__(self, data): + self.unpacked = self.unpack_raw_data(data) + + def unpack_raw_data(self, data): + pass diff --git a/atrcopy/dcm.py b/atrcopy/dcm.py new file mode 100644 index 0000000..6cd326f --- /dev/null +++ b/atrcopy/dcm.py @@ -0,0 +1,41 @@ +import numpy as np + +from . import errors +from .container import DiskImageContainer +from .segments import SegmentData + + +class DCMContainer(DiskImageContainer): + valid_densities = { + 0: (720, 128), + 1: (720, 256), + 2: (1040, 128), + } + + def get_next(self): + try: + data = self.raw[self.index] + except IndexError: + raise errors.InvalidContainer + else: + self.index += 1 + return data + + def unpack_raw_data(self, data): + self.index = 0 + self.count = len(data) + self.raw = data + archive_type = self.get_next() + if archive_type == 0xf9 or archive_type == 0xfa: + archive_flags = self.get_next() + if archive_flags & 0x1f != 1: + if archive_type == 0xf9: + raise errors.InvalidContainer("DCM multi-file archive combined in the wrong order") + else: + raise errors.InvalidContainer("Expected pass one of DCM archive first") + density_flag = (archive_flags >> 5) & 3 + if density_flag not in self.valid_densities: + raise errors.InvalidContainer(f"Unsupported density flag {density_flag} in DCM") + else: + raise errors.InvalidContainer("Not a DCM file") + raise errors.UnsupportedContainer("DCM archives are not yet supported") diff --git a/atrcopy/errors.py b/atrcopy/errors.py index f4ccfa5..f27abee 100644 --- a/atrcopy/errors.py +++ b/atrcopy/errors.py @@ -65,3 +65,11 @@ class NotEnoughSpaceOnDisk(AtrError): class FileNotFound(AtrError): pass + + +class UnsupportedContainer(AtrError): + pass + + +class InvalidContainer(AtrError): + pass diff --git a/atrcopy/parsers.py b/atrcopy/parsers.py index 15a3ea4..00cac65 100644 --- a/atrcopy/parsers.py +++ b/atrcopy/parsers.py @@ -10,6 +10,7 @@ from .dos33 import Dos33DiskImage, ProdosDiskImage, Dos33BinFile from .standard_delivery import StandardDeliveryImage from . import errors from .magic import guess_detail_for_mime +from .dcm import DCMContainer import logging log = logging.getLogger(__name__) @@ -157,6 +158,22 @@ class ProdosSegmentParser(SegmentParser): image_type = ProdosDiskImage +known_containers = [ + DCMContainer, +] + + +def guess_container(r): + for c in known_containers: + try: + found = c(r) + except errors.InvalidContainer: + continue + else: + return found + return None + + def guess_parser_for_mime(mime, r, verbose=False): parsers = mime_parsers[mime] found = None @@ -182,6 +199,9 @@ def guess_parser_for_system(mime_base, r): def iter_parsers(r): + container = guess_container(r.data) + if container is not None: + r = container.unpacked for mime in mime_parse_order: p = guess_parser_for_mime(mime, r) if p is not None: