diff --git a/atrcopy/__init__.py b/atrcopy/__init__.py index 29d52b7..29344ad 100644 --- a/atrcopy/__init__.py +++ b/atrcopy/__init__.py @@ -14,7 +14,7 @@ from kboot import KBootImage, add_xexboot_header from segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, user_bit_mask, match_bit_mask, comment_bit_mask, data_bit_mask, selected_bit_mask, diff_bit_mask, not_user_bit_mask, interleave_segments from spartados import SpartaDosDiskImage from cartridge import A8CartHeader -from parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, iter_known_segment_parsers, mime_parse_order +from parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, iter_parsers, iter_known_segment_parsers, mime_parse_order from utils import to_numpy diff --git a/atrcopy/mame.py b/atrcopy/mame.py new file mode 100644 index 0000000..9af8aa4 --- /dev/null +++ b/atrcopy/mame.py @@ -0,0 +1,65 @@ +import zipfile + +import numpy as np + +from errors import * +from segments import SegmentData, EmptySegment, ObjSegment +from diskimages import DiskImageBase +from utils import to_numpy + +import logging +log = logging.getLogger(__name__) + + +class MameZipImage(DiskImageBase): + def __init__(self, rawdata, filename=""): + self.zipdata = rawdata + fh = self.zipdata.stringio + if zipfile.is_zipfile(fh): + with zipfile.ZipFile(fh) as zf: + self.check_zip_size(zf) + self.create_rawdata(zf) + else: + raise InvalidDiskImage("Not a MAME zip file") + DiskImageBase.__init__(self, self.rawdata, filename) + + def __str__(self): + return "MAME Zip file, %d ROMs, orig_size=%d, uncompressed=%d" % (len(self.zip_segment_info), len(self.zipdata), len(self.rawdata)) + + def setup(self): + self.check_size() + + def strict_check(self): + pass + + def relaxed_check(self): + pass + + def check_zip_size(self, zf): + for item in zf.infolist(): + _, r = divmod(item.file_size, 256) + if r > 0: + raise InvalidDiskImage("zip entry not 256 byte multiple") + + def create_rawdata(self, zf): + roms = [] + segment_info = [] + offset = 0 + for item in zf.infolist(): + rom = np.fromstring(zf.open(item).read(), dtype=np.uint8) + roms.append(rom) + segment_info.append((offset, item.file_size, item.filename, item.CRC)) + offset += item.file_size + data = np.concatenate(roms) + self.zip_segment_info = segment_info + self.rawdata = SegmentData(data) + + def check_size(self): + pass + + def parse_segments(self): + r = self.rawdata + self.segments = [] + for offset, size, name, crc in self.zip_segment_info: + end = offset + size + self.segments.append(ObjSegment(r[offset:end], 0, offset, offset, end, name=name)) diff --git a/atrcopy/parsers.py b/atrcopy/parsers.py index 05ff069..b280784 100644 --- a/atrcopy/parsers.py +++ b/atrcopy/parsers.py @@ -6,6 +6,7 @@ from kboot import KBootImage from ataridos import AtariDosDiskImage, AtariDosFile from spartados import SpartaDosDiskImage from cartridge import AtariCartImage, get_known_carts +from mame import MameZipImage from errors import * @@ -84,6 +85,11 @@ class AtariCartSegmentParser(SegmentParser): return self.image_type(r, self.cart_type) +class MameZipParser(SegmentParser): + menu_name = "MAME ROM Zipfile" + image_type = MameZipImage + + def guess_parser_for_mime(mime, r): parsers = mime_parsers[mime] found = None @@ -103,6 +109,13 @@ def guess_parser_for_system(mime_base, r): return mime, p return None, None +def iter_parsers(r): + for mime in mime_parse_order: + p = guess_parser_for_mime(mime, r) + if p is not None: + return mime, p + return None, None + mime_parsers = { "application/vnd.atari8bit.atr": [ @@ -114,20 +127,27 @@ mime_parsers = { "application/vnd.atari8bit.xex": [ XexSegmentParser, ], + "application/vnd.mame_rom": [ + MameZipParser, + ], } mime_parse_order = [ "application/vnd.atari8bit.atr", "application/vnd.atari8bit.xex", + "CARTS", # Will get filled in below + "application/vnd.mame_rom", ] pretty_mime = { "application/vnd.atari8bit.atr": "Atari 8-bit Disk Image", "application/vnd.atari8bit.xex": "Atari 8-bit Executable", + "application/vnd.mame_rom": "MAME" } grouped_carts = get_known_carts() sizes = sorted(grouped_carts.keys()) +cart_order = [] for k in sizes: if k > 128: key = "application/vnd.atari8bit.large_cart" @@ -136,13 +156,15 @@ for k in sizes: key = "application/vnd.atari8bit.%dkb_cart" % k pretty = "Atari 8-bit %dKB Cartridge" % k if key not in mime_parsers: - mime_parse_order.append(key) + cart_order.append(key) pretty_mime[key] = pretty mime_parsers[key] = [] for c in grouped_carts[k]: t = c[0] kclass = type("AtariCartSegmentParser%d" % t, (AtariCartSegmentParser,), {'cart_type': t, 'cart_info': c, 'menu_name': "%s Cartridge" % c[1]}) mime_parsers[key].append(kclass) +i = mime_parse_order.index("CARTS") +mime_parse_order[i:i+1] = cart_order known_segment_parsers = [DefaultSegmentParser] diff --git a/atrcopy/segments.py b/atrcopy/segments.py index 5243a17..7436ae9 100644 --- a/atrcopy/segments.py +++ b/atrcopy/segments.py @@ -1,4 +1,5 @@ import bisect +import cStringIO import numpy as np @@ -95,6 +96,11 @@ class SegmentData(object): def __len__(self): return len(self.data) + + @property + def stringio(self): + buf = cStringIO.StringIO(self.data[:]) + return buf def get_data(self): return self.data