From 2b9527602988e621096200b7c0be6485450efd8a Mon Sep 17 00:00:00 2001 From: Rob McMullen Date: Mon, 30 Apr 2018 22:28:47 -0700 Subject: [PATCH] Added magic signature detection * appends detail to MIME type if match; e.g. application/vnd.atari8bit.atr.jumpman --- atrcopy/__init__.py | 4 +++ atrcopy/magic.py | 73 +++++++++++++++++++++++++++++++++++++++++++++ atrcopy/parsers.py | 3 ++ 3 files changed, 80 insertions(+) create mode 100644 atrcopy/magic.py diff --git a/atrcopy/__init__.py b/atrcopy/__init__.py index 28e416a..66c3a5c 100644 --- a/atrcopy/__init__.py +++ b/atrcopy/__init__.py @@ -24,6 +24,7 @@ from .segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, O 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 .magic import guess_detail_for_mime from .utils import to_numpy, text_to_int @@ -70,6 +71,9 @@ def find_diskimage(filename): continue if options.verbose: print("Found parser %s" % parser.menu_name) + mime2 = guess_detail_for_mime(mime, rawdata, parser) + if mime != mime2 and options.verbose: + print("Signature match: %s" % mime2) break if parser is None: print("%s: Unknown disk image type" % filename) diff --git a/atrcopy/magic.py b/atrcopy/magic.py new file mode 100644 index 0000000..c72fc37 --- /dev/null +++ b/atrcopy/magic.py @@ -0,0 +1,73 @@ +import numpy as np + +import logging +log = logging.getLogger(__name__) + + +magic = [ + {'mime': "application/vnd.atari8bit.atr.getaway_pd", + 'name': "Getaway Public Domain ATR", + 'signature': [ + (slice(8, 10), [0x82, 0x39]), + (slice(12, 16), [0x67, 0x21, 0x70, 0x64]), + ], + }, + + {'mime': "application/vnd.atari8bit.xex.getaway", + 'name': "Getaway XEX", + 'signature': [ + (slice(0, 6), [0xff, 0xff, 0x80, 0x2a, 0xff, 0x8a]), + ], + }, + + {'mime': "application/vnd.atari8bit.atr.getaway", + 'name': "Getaway ATR", + 'signature': [ + (slice(0x10, 0x19), [0x00, 0xc1, 0x80, 0x0f, 0xcc, 0x22, 0x18, 0x60, 0x0e]), + ], + }, + + {'mime': "application/vnd.atari8bit.atr.jumpman_level_tester", + 'name': "Jumpman Level Tester from Omnivore", + 'signature': [ + (slice(0, 5), [0x96, 0x02 , 0xd0 , 0x05 , 0x80]), + (0x0196 + 0x3f, 0x4c), + (0x0196 + 0x48, 0x20), + (0x0196 + 0x4b, 0x60), + (0x0196 + 0x4c, 0xff), + ], + }, + + {'mime': "application/vnd.atari8bit.atr.jumpman", + 'name': "Jumpman", + 'signature': [ + (slice(0, 5), [0x96, 0x02 , 0x80 , 0x16 , 0x80]), + (0x0810 + 0x3f, 0x4c), + (0x0810 + 0x48, 0x20), + (0x0810 + 0x4b, 0x60), + (0x0810 + 0x4c, 0xff), + ], + }, +] + + +def check_signature(raw, sig): + for index, expected in sig: + actual = raw.data[index].tolist() + if actual == expected: + log.debug(" match at %s: %s" % (str(index), str(expected))) + if actual != expected: + log.debug(" failed at %s: %s != %s" % (str(index), str(expected), str(raw.data[index]))) + return False + return True + + +def guess_detail_for_mime(mime, raw, parser): + for entry in magic: + if entry['mime'].startswith(mime): + log.debug("checking signature for %s" % entry['mime']) + if check_signature(raw, entry['signature']): + log.debug("found signature: %s" % entry['name']) + return entry['mime'] + return mime + diff --git a/atrcopy/parsers.py b/atrcopy/parsers.py index cc4823c..36a7cb4 100644 --- a/atrcopy/parsers.py +++ b/atrcopy/parsers.py @@ -12,6 +12,7 @@ from .mame import MameZipImage from .dos33 import Dos33DiskImage, ProdosDiskImage, Dos33BinFile from .standard_delivery import StandardDeliveryImage from .errors import * +from .magic import guess_detail_for_mime import logging log = logging.getLogger(__name__) @@ -178,6 +179,7 @@ def guess_parser_for_system(mime_base, r): if mime.startswith(mime_base): p = guess_parser_for_mime(mime, r) if p is not None: + mime = guess_detail_for_mime(mime, r, p) return mime, p return None, None @@ -186,6 +188,7 @@ def iter_parsers(r): for mime in mime_parse_order: p = guess_parser_for_mime(mime, r) if p is not None: + mime = guess_detail_for_mime(mime, r, p) return mime, p return None, None