From d22f2cdf3f08204439adb43a140c140b29a4d53d Mon Sep 17 00:00:00 2001 From: Rob McMullen Date: Thu, 8 Nov 2018 10:12:53 -0800 Subject: [PATCH] Initial recognition of 5200 carts by sha1 signature --- atrcopy/__init__.py | 28 +++++----- atrcopy/cartridge.py | 5 +- atrcopy/parsers.py | 64 +++++++++++++++++----- atrcopy/signatures.py | 122 ++++++++++++++++++++++++++++++++++++++++++ gen-sha.py | 47 ++++++++++++++++ 5 files changed, 238 insertions(+), 28 deletions(-) create mode 100644 atrcopy/signatures.py create mode 100644 gen-sha.py diff --git a/atrcopy/__init__.py b/atrcopy/__init__.py index 123606c..d926ca2 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, RomImage -from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_for_mime, guess_parser_for_system, guess_container, iter_parsers, iter_known_segment_parsers, mime_parse_order, parsers_for_filename +from .parsers import SegmentParser, DefaultSegmentParser, guess_parser_by_size, guess_parser_for_mime, guess_parser_for_system, guess_container, 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 from .dummy import LocalFilesystem @@ -67,18 +67,20 @@ def find_diskimage(filename, verbose=False): if container is not None: data = container.unpacked rawdata = SegmentData(data) - for mime in mime_parse_order: - if verbose: - print("Trying MIME type %s" % mime) - parser = guess_parser_for_mime(mime, rawdata, verbose) - if parser is None: - continue - if verbose: - print("Found parser %s" % parser.menu_name) - mime2 = guess_detail_for_mime(mime, rawdata, parser) - if mime != mime2 and verbose: - print("Signature match: %s" % mime2) - break + mime, parser = guess_parser_by_size(rawdata) + if parser is None: + for mime in mime_parse_order: + if verbose: + print("Trying MIME type %s" % mime) + parser = guess_parser_for_mime(mime, rawdata, verbose) + if parser is None: + continue + if verbose: + print("Found parser %s" % parser.menu_name) + mime2 = guess_detail_for_mime(mime, rawdata, parser) + if mime != mime2 and verbose: + print("Signature match: %s" % mime2) + break if parser is None: raise errors.UnsupportedDiskImage("Unknown disk image type") else: diff --git a/atrcopy/cartridge.py b/atrcopy/cartridge.py index 902eba6..9564339 100644 --- a/atrcopy/cartridge.py +++ b/atrcopy/cartridge.py @@ -223,6 +223,7 @@ class BaseAtariCartImage(DiskImageBase): # force the header to be the specified cart type self.header = A8CartHeader() self.header.set_type(self.cart_type) + self.check_size() def check_size(self): if not self.header.valid: @@ -289,14 +290,14 @@ class AtariCartImage(BaseAtariCartImage): raise errors.InvalidDiskImage("Cart type doesn't match type defined in header") -class Atari8bitCartImage(BaseAtariCartImage): +class Atari8bitCartImage(AtariCartImage): def strict_check(self): if "5200" in self.header.cart_name: raise errors.InvalidDiskImage("5200 Carts don't work in the home computers.") AtariCartImage.strict_check(self) -class Atari5200CartImage(BaseAtariCartImage): +class Atari5200CartImage(AtariCartImage): def strict_check(self): if "5200" not in self.header.cart_name: raise errors.InvalidDiskImage("Home computer carts don't work in the 5200.") diff --git a/atrcopy/parsers.py b/atrcopy/parsers.py index 08257d2..ff5cff4 100644 --- a/atrcopy/parsers.py +++ b/atrcopy/parsers.py @@ -1,3 +1,5 @@ +import hashlib + import numpy as np from .segments import SegmentData, DefaultSegment @@ -12,6 +14,7 @@ from . import errors from .magic import guess_detail_for_mime from . import container from .dcm import DCMContainer +from .signatures import sha1_signatures import logging log = logging.getLogger(__name__) @@ -69,6 +72,7 @@ class SegmentParser: self.segments.append(self.container_segment(r, 0, name=self.menu_name)) try: log.debug("Trying %s" % self.image_type) + log.debug(self.image_type.__mro__) self.image = self.get_image(r) self.check_image() self.image.parse_segments() @@ -85,6 +89,7 @@ class SegmentParser: s.reconstruct_raw(new_rawdata) def get_image(self, r): + log.info(f"checking image type {self.image_type}") return self.image_type(r) def check_image(self): @@ -142,15 +147,16 @@ class AtariCartSegmentParser(SegmentParser): cart_info = None def get_image(self, r): + log.info(f"checking cart type {self.cart_type}: {self.image_type}") return self.image_type(r, self.cart_type) -class Atari8bitCartParser(SegmentParser): +class Atari8bitCartParser(AtariCartSegmentParser): menu_name = "Atari Home Computer Cartridge" image_type = Atari8bitCartImage -class Atari5200CartParser(SegmentParser): +class Atari5200CartParser(AtariCartSegmentParser): menu_name = "Atari 5200 Cartridge" image_type = Atari5200CartImage @@ -203,6 +209,34 @@ def guess_container(r, verbose=False): return None +def guess_parser_by_size(r, verbose=False): + found = None + mime = None + size = len(r) + if size in sha1_signatures: + print(r) + sha_hash = hashlib.sha1(r.data).hexdigest() + log.info(f"{size} in signature database, attempting to match {sha_hash}") + try: + match = sha1_signatures[size][sha_hash] + except KeyError: + pass + else: + mime = match[0] + log.info(f"found match: {match}") + parsers = mime_parsers[mime] + for parser in parsers: + try: + found = parser(r, False) + break + except errors.InvalidSegmentParser as e: + if verbose: + log.info("parser isn't %s: %s" % (parser.__name__, str(e))) + pass + if found is None: + log.info(f"no matching signature") + return mime, found + def guess_parser_for_mime(mime, r, verbose=False): parsers = mime_parsers[mime] found = None @@ -231,12 +265,15 @@ def iter_parsers(r): container = guess_container(r.data) if container is not None: r = SegmentData(container.unpacked) - 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 + mime, parser = guess_parser_by_size(r) + if parser is None: + for mime in mime_parse_order: + p = guess_parser_for_mime(mime, r) + if p is not None: + parser = p + mime = guess_detail_for_mime(mime, r, p) + break + return mime, parser def parsers_for_filename(name): @@ -274,7 +311,7 @@ mime_parsers = { "application/vnd.atari8bit.cart": [ Atari8bitCartParser, ], - "application/vnd.atari8bit.5200_cart": [ + "application/vnd.atari5200.cart": [ Atari5200CartParser, ], "application/vnd.mame_rom": [ @@ -296,7 +333,7 @@ mime_parse_order = [ "application/vnd.atari8bit.atr", "application/vnd.atari8bit.xex", "application/vnd.atari8bit.cart", - "application/vnd.atari8bit.5200_cart", + "application/vnd.atari5200.cart", "application/vnd.mame_rom", "application/vnd.apple2.dsk", "application/vnd.apple2.bin", @@ -307,7 +344,7 @@ pretty_mime = { "application/vnd.atari8bit.atr": "Atari 8-bit Disk Image", "application/vnd.atari8bit.xex": "Atari 8-bit Executable", "application/vnd.atari8bit.cart": "Atari 8-bit Cartridge", - "application/vnd.atari8bit.5200_cart":"Atari 5200 Cartridge", + "application/vnd.atari5200.cart": "Atari 5200 Cartridge", "application/vnd.mame_rom": "MAME", "application/vnd.rom": "ROM Image", "application/vnd.apple2.dsk": "Apple ][ Disk Image", @@ -320,11 +357,12 @@ for k in sizes: for c in grouped_carts[k]: t = c[0] name = c[1] - kclass = type("AtariCartSegmentParser%d" % t, (AtariCartSegmentParser,), {'cart_type': t, 'cart_info': c, 'menu_name': "%s Cartridge" % name}) if "5200" in name: - key = "application/vnd.atari8bit.5200_cart" + key = "application/vnd.atari5200.cart" + kclass = type("Atari5200CartSegmentParser%d" % t, (Atari5200CartParser, AtariCartSegmentParser), {'cart_type': t, 'cart_info': c, 'menu_name': "%s Cartridge" % name}) else: key = "application/vnd.atari8bit.cart" + kclass = type("Atari8bitCartSegmentParser%d" % t, (Atari8bitCartParser, AtariCartSegmentParser), {'cart_type': t, 'cart_info': c, 'menu_name': "%s Cartridge" % name}) mime_parsers[key].append(kclass) diff --git a/atrcopy/signatures.py b/atrcopy/signatures.py new file mode 100644 index 0000000..d5fb5fb --- /dev/null +++ b/atrcopy/signatures.py @@ -0,0 +1,122 @@ +sha1_signatures = { +2048: { + '6ad7a1e8c9fad486fbec9498cb48bf5bc3adc530': ('application/vnd.atari5200.cart', '[BIOS] Atari 5200 (USA)'), +}, +4096: { + '5236bc13e4b06d2e16d70b0a0c98588e1726da0c': ('application/vnd.atari5200.cart', 'Boogie (USA) (Demo)'), + 'a80f876a89a8ceacc71da5d9c02b546970b17730': ('application/vnd.atari5200.cart', 'Kaboom! (USA)'), + 'f5f9c7f97bf09e5647e808d892b656f3ee1e19e0': ('application/vnd.atari5200.cart', 'Super Breakout (USA)'), + 'e58e1f257923694dbeb586c860f385e74e460f90': ('application/vnd.atari5200.cart', 'Yellow Submarine (USA) (Demo)'), +}, +8192: { + 'e64fb1a353c72b87c5483bb88cb3523ea5d37b59': ('application/vnd.atari5200.cart', '5200 Menu (USA) (Proto)'), + '18697dd8e640fa1578b3b4ae09af484c14fb8410': ('application/vnd.atari5200.cart', 'Asteroids (USA) (Proto)'), + 'd3ff99e080985c9c8e25389dfa74835e8c9be508': ('application/vnd.atari5200.cart', "Atari PAM - Pete's Test (USA)"), + 'af81c400f1af0ee13a24da5e8b642442a0fc9479': ('application/vnd.atari5200.cart', 'Atari PAM System Test (USA) (v1.02)'), + 'ce9bd3b0847c5e187c3e8f667c8f5ea771c49965': ('application/vnd.atari5200.cart', "Carol Shaw's River Raid (USA)"), + 'ea7b40ce1106378e1d409cab6634fb2e483ff238': ('application/vnd.atari5200.cart', 'Dreadnaught Factor, The (USA)'), + 'd9a695757f0494aee92a50ab8a49558745e0cedf': ('application/vnd.atari5200.cart', 'Frogger (USA)'), + '0df0d7f11d89e2c5b59cc9f98d67d98e203831aa': ('application/vnd.atari5200.cart', 'Galaxian (USA)'), + '80926bb270ea66cb5cacc379de1fc16d8a3d814b': ('application/vnd.atari5200.cart', 'Gorf (USA)'), + 'c7eb2b4a46a197adfedf33c2ccc17d8424d681c4': ('application/vnd.atari5200.cart', 'K-Razy Shoot-Out (USA)'), + '9f31c85a13e919135438590601e33bef11a8df8a': ('application/vnd.atari5200.cart', 'Keystone Kapers (USA)'), + 'f5512460c6b6d3a1baa0b23adf6a3a37fe308eb3': ('application/vnd.atari5200.cart', 'MegaMania (USA)'), + '5c8ec22e131810ccac08066ca96e9944ccfa4ecb': ('application/vnd.atari5200.cart', 'Missile Command (USA)'), + '2e7ae57260624d1f5710d445a0936f0c280a6655': ('application/vnd.atari5200.cart', 'Mountain King (USA)'), + '002e6f8c7533b03f8041e7f1896589c0ab0e17a6': ('application/vnd.atari5200.cart', "Mr. Do!'s Castle (USA)"), + 'd1c7dc3de097a4bfab82235acec69a97e6e05f18': ('application/vnd.atari5200.cart', 'Pitfall! (USA)'), + 'e6966bde5cd4167020c9d21bab613af1648b4d75': ('application/vnd.atari5200.cart', 'Q-bert (USA)'), + 'cf411db770217b5af5fcfa32725cc587a8b9e710': ('application/vnd.atari5200.cart', 'Space Invaders (USA)'), + 'bf77048abc36ba2cba5eeeac812802d6a0eba891': ('application/vnd.atari5200.cart', 'Star Wars - Return of the Jedi - Death Star Battle (USA)'), + '8205093f5fb1fdf0ccb71d667ea0a3849fb0a0f2': ('application/vnd.atari5200.cart', 'Super Cobra (USA)'), + '55da563b4394508c23d866fb1e543262fb45536d': ('application/vnd.atari5200.cart', 'Zenji (USA)'), +}, +16384: { + 'e18a603b18b85e24f9c938b33b6077a767c1fa9c': ('application/vnd.atari5200.cart', 'A.E. (USA) (Proto)'), + 'db203973df13b071da46d52fdb078a76f4db428d': ('application/vnd.atari5200.cart', 'Activision Decathlon, The (USA)'), + '67e405c37e55dec4071018e8166ab4faf9f21df5': ('application/vnd.atari5200.cart', 'Astro Chase (USA)'), + '5cc3b4245faf01c30f77ac20b0b4940f4f8f83d8': ('application/vnd.atari5200.cart', 'Atari PAM Diagnostics (USA) (v2.0)'), + 'e086e9868aa913e87f94676fd600f003892e73a1': ('application/vnd.atari5200.cart', 'Atari PAM Diagnostics (USA) (v2.3)'), + 'a339cddfc28477c21c58783d2d3b0cf0f4626ddf': ('application/vnd.atari5200.cart', 'Battlezone (USA) (Proto)'), + 'b75d555d059ad61992f9f5a234129ecbf9504c35': ('application/vnd.atari5200.cart', 'Beamrider (USA)'), + 'e56ccc6bf427a832ce00540b02d065a2688260dc': ('application/vnd.atari5200.cart', 'BerZerk (USA)'), + 'ab3130256d16af09cac8b11749dfaff6aaaa3ea3': ('application/vnd.atari5200.cart', 'Blaster (USA) (Proto)'), + '1e1a20c104bf4d55eca0ca1e72dc5dc64fa6b1b5': ('application/vnd.atari5200.cart', 'Blue Print (USA)'), + 'bc7c81f758dd3e224e42ac8a16c1a12d69aed722': ('application/vnd.atari5200.cart', 'Buck Rogers - Planet of Zoom (USA)'), + 'eb343b90d3de696e69b1f039d4fb2c6afb0d80c0': ('application/vnd.atari5200.cart', 'Centipede (USA)'), + '9f1ee191319ef0261cc7074a81c0e6d6b7464cd2': ('application/vnd.atari5200.cart', 'Choplifter! (USA)'), + 'e0203c2124127eba2d679be548fa257ecc5b2607': ('application/vnd.atari5200.cart', 'Congo Bongo (USA)'), + 'fa6d77e6e9afd0ea42fc9f9807932bf9e8fb170e': ('application/vnd.atari5200.cart', 'Countermeasure (USA)'), + 'b051f0fb2e816787ab6eed0cb008b9f8cc9963dc': ('application/vnd.atari5200.cart', "David Crane's Pitfall II - Lost Caverns (USA)"), + '44f6dbf5013a70e646d5c5232981d517069fc449': ('application/vnd.atari5200.cart', 'Defender (USA)'), + '387a76ba5912251e2285b7722004ad75e9ede523': ('application/vnd.atari5200.cart', 'Dig Dug (USA)'), + '48625af4f3e9fe538dab2a0b463887fc8c6b138d': ('application/vnd.atari5200.cart', 'Final Legacy (USA) (Proto)'), + '69dcc8f59ca449f13dc896b4b29b534762d97064': ('application/vnd.atari5200.cart', 'Frisky Tom (USA) (Proto)'), + '3d815594cf674694b04bb0ff90bf635d7d402fc2': ('application/vnd.atari5200.cart', 'Frogger II - Threeedeep! (USA)'), + 'd0d8650eb07d1679004d491360c2c041e37c61cd': ('application/vnd.atari5200.cart', 'Gyruss (USA)'), + 'd286ecb6793a0dda9c9c68f6355dbe3ade6922f9': ('application/vnd.atari5200.cart', 'H.E.R.O. (USA)'), + 'ae765943ae8d909b6ca6ff1560ba027adeeaa515': ('application/vnd.atari5200.cart', 'James Bond 007 (USA)'), + '0f74125edac90ffd1274a128df2970c239348fb5': ('application/vnd.atari5200.cart', 'Joust (USA)'), + 'a3e760d09e4dc6053286967576b5b7d42ee2fd38': ('application/vnd.atari5200.cart', 'Jr. Pac-Man (USA) (Proto)'), + 'fa02ee04cfad90c6ccf9d9b984e3b7b3327fec12': ('application/vnd.atari5200.cart', 'Jungle Hunt (USA)'), + 'c3dbeed7adc0e282bf76332ab5a3209550e26d02': ('application/vnd.atari5200.cart', 'Kangaroo (USA)'), + '4046f60602254a7d86d2a6efd1a330455eaec119': ('application/vnd.atari5200.cart', 'Last Starfighter, The (USA) (Proto)'), + 'bc4a536616ac45689b3ad790f6b1394ebbd8cc7e': ('application/vnd.atari5200.cart', 'Looney Tunes Hotel (USA) (Proto)'), + '32d86a381feff43149b66336dcc4ba2523273228': ('application/vnd.atari5200.cart', 'Meteorites (USA)'), + '2f152a857a8a58d602b3c717612d9744423ee495': ('application/vnd.atari5200.cart', 'Microgammon SB (USA) (Proto)'), + '1243a8ecfc02a74ae4e1f4fcda38502776f45cb0': ('application/vnd.atari5200.cart', 'Millipede (USA) (Proto)'), + '0564b1867a0b570d66dfcbc11adc3e51a2c6f28c': ('application/vnd.atari5200.cart', 'Miner 2049er Starring Bounty Bob (USA)'), + 'ef4d3ee7b4da2ef79c6f4d0d1833f10c1eb7725e': ('application/vnd.atari5200.cart', 'Miniature Golf (USA) (Proto)'), + '7e2a05625598c7e6615f969aa695e64315cd9846': ('application/vnd.atari5200.cart', "Montezuma's Revenge featuring Panama Joe (USA)"), + '02dd5b9278c06ae8cbb6dacc094e238fbacf08d2': ('application/vnd.atari5200.cart', 'Moon Patrol (USA)'), + 'f93d72444d76075273e508555423936530875148': ('application/vnd.atari5200.cart', 'Ms. Pac-Man (USA)'), + 'ee4b3c6f08aa9fdfab532eec98136306b6923f75': ('application/vnd.atari5200.cart', 'Pac-Man (USA)'), + 'd504764c5894ee99c0b5e2024cbe833ebba3ab17': ('application/vnd.atari5200.cart', 'Pole Position (USA)'), + 'e615aaa0c1d56ac83b91f6087970fa708d6433bf': ('application/vnd.atari5200.cart', 'Popeye (USA)'), + '786cdf54d1625e0d3ea8814506fdeeba5e4ece48': ('application/vnd.atari5200.cart', 'QIX (USA)'), + '17a41c5f9a32f8a71939cfaa0e96807b951aedad': ('application/vnd.atari5200.cart', 'Quest for Quintana Roo (USA)'), + '2b43f74db4833272d2048de8fa32b857af8e090f': ('application/vnd.atari5200.cart', 'RealSports Basketball (USA) (82-11-05) (Proto)'), + 'a0dbd654ed3ba2583ca175a1356924d9529884df': ('application/vnd.atari5200.cart', 'RealSports Football (USA)'), + 'c911cc914f3923d5aeb1a897e6dc4a551a9a53da': ('application/vnd.atari5200.cart', 'RealSports Soccer (USA)'), + '2e6c7472e37926cdfd3d855a4bd37a18fa83d8cf': ('application/vnd.atari5200.cart', 'RealSports Tennis (USA)'), + '0a7aaef5b0ede927cea13e724906207e1db48068': ('application/vnd.atari5200.cart', 'Road Runner (USA) (Proto)'), + '9175e6daca58102ac8b9729d7b0336cf09977d12': ('application/vnd.atari5200.cart', 'Robotron 2084 (USA)'), + '127ed1c31c3e85d10212eee7b9e6d71aef7dd891': ('application/vnd.atari5200.cart', 'Space Dungeon (USA)'), + '5ae20e767d9819fe601f3b1ea6d4506ae2f2ceef': ('application/vnd.atari5200.cart', 'Space Shuttle - A Journey Into Space (USA)'), + '5f0354d8792d652313e5a0fa5d395e091c801d97': ('application/vnd.atari5200.cart', 'Sport Goofy (USA) (Proto)'), + '57dfc22c9805555f957ed51a2dc7bf35ad00c9d9': ('application/vnd.atari5200.cart', 'Star Raiders (USA)'), + '19801454ab86f2a5cf691fcd577a71ad2b8dc7d5': ('application/vnd.atari5200.cart', 'Star Trek - Strategic Operations Simulator (USA)'), + 'ab676ffe054b23bdd43cee2498afefaabbf9355b': ('application/vnd.atari5200.cart', 'Star Wars - The Arcade Game (USA)'), + '09d192a3598cdadfb61eceb430b3d5892e40962f': ('application/vnd.atari5200.cart', 'Stargate (USA) (Proto)'), + '238cb448555d8bb5c96aa0354718411e8777c613': ('application/vnd.atari5200.cart', 'Super Pac-Man (USA) (Proto)'), + '85fcea0a4d4d1f22437801c96229ba96e0f6b711': ('application/vnd.atari5200.cart', 'Tempest (USA) (Proto)'), + '9dfbb5d8b77504c66ce3ac4382daf79820393dd8': ('application/vnd.atari5200.cart', 'Track and Field (USA) (Proto)'), + '49debdf3c133fde9905d6d9137da5d8dff8d6283': ('application/vnd.atari5200.cart', 'Wizard of Wor (USA)'), + '8b5512d3d2a952c99b9377919f0e06481161d81e': ('application/vnd.atari5200.cart', 'Xari Arena (USA) (Proto)'), + '18315bec1e62102290feeb77fb290e1266cf5bfc': ('application/vnd.atari5200.cart', 'Zone Ranger (USA)'), +}, +32768: { + '1dfd9cb5eb608590c84e14376291f8c02fb1ab93': ('application/vnd.atari5200.cart', 'Ballblazer (USA)'), + '510ccb20df2ecdbe7f8373de6a9fc11493e8c3f2': ('application/vnd.atari5200.cart', 'Barroom Baseball (USA) (Proto)'), + '9b7ecd444b6239442ef1f54cd8e14c294c064da2': ('application/vnd.atari5200.cart', 'Black Belt (USA) (Proto)'), + 'c1a56a1fc51e09b1d8e93af40ddd7596847e6164': ('application/vnd.atari5200.cart', 'Castle Blast (USA) (Unl)'), + '4e022e101d7346a0a8618018372740e7333b4a36': ('application/vnd.atari5200.cart', 'Castle Crisis (USA) (Unl)'), + 'f3c32b39580d589799dcead98bb206fbf3df0504': ('application/vnd.atari5200.cart', 'Gremlins (USA)'), + 'ca7d5d0871be19ca22d01019fd512b5836e3130a': ('application/vnd.atari5200.cart', 'Joust (USA) 32k'), + '9547c56e2951e1b3a713b0b99d3edb43c9d3cdd8': ('application/vnd.atari5200.cart', 'Koffi - Yellow Kopter (USA) (Unl)'), + 'a293b6b06050e11029d97137f2b1b36059567273': ('application/vnd.atari5200.cart', 'Mario Bros. (USA)'), + 'ff0f1d9ed0ae45e3c81b252f3d0a335e03b0aa4b': ('application/vnd.atari5200.cart', 'Meebzork (USA) (Proto)'), + '19ff7622dbbc2915e3e98d8d966340e7e893ff34': ('application/vnd.atari5200.cart', 'Pengo (USA)'), + 'a94d06739fa19453fa586a836eaacae11fe93989': ('application/vnd.atari5200.cart', 'RealSports Baseball (USA)'), + '9b21ac7fec8d556585c6970231318ef1f501b2b5': ('application/vnd.atari5200.cart', 'RealSports Basketball (USA)'), + '9edb066a37ca7657979952c946ab89be7926f4f8': ('application/vnd.atari5200.cart', 'RealSports Basketball (USA) (83-10-31) (Proto)'), + '9253d7286cf87999474b37df242ba23b4358ef4a': ('application/vnd.atari5200.cart', 'Rescue on Fractalus! (USA)'), + '688133761c462d6865bf673ed298b4d828120940': ('application/vnd.atari5200.cart', 'Spitfire (USA) (Proto)'), + '6c633de50e6cc0c0c40061cd24e6f78f552a438f': ('application/vnd.atari5200.cart', 'Vanguard (USA)'), + 'c3cf33216dfa484309b923f21829d040486bc481': ('application/vnd.atari5200.cart', 'Xevious (USA) (Proto)'), + '4a4c4a25c8ed1b0ae79ca22684d7d311a564a7b5': ('application/vnd.atari5200.cart', 'Zaxxon (USA)'), +}, +40960: { + 'cfdd2f8dabb03d8784f336bb0c804270dc3a8a6c': ('application/vnd.atari5200.cart', 'Bounty Bob Strikes Back! (USA)'), +}, +} # end sha1_signatures \ No newline at end of file diff --git a/gen-sha.py b/gen-sha.py new file mode 100644 index 0000000..3e6a3b0 --- /dev/null +++ b/gen-sha.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +import os +import sys +import hashlib +from collections import defaultdict +import pprint + +def parse(filename, mime): + data = open(filename, 'rb').read() + h = hashlib.sha1(data).hexdigest() + name = os.path.basename(os.path.splitext(filename)[0]) + return len(data), h, name + + + +if __name__ == '__main__': + source = "atrcopy/signatures.py" + try: + with open(source, 'r') as fh: + source_text = fh.read() + except OSError: + source_text = "sha1_signatures = {}" + try: + exec(source_text) + except: + raise + print(sha1_signatures) + mime = sys.argv[1] + new_signatures = defaultdict(dict) + new_signatures.update(sha1_signatures) + for filename in sys.argv[2:]: + print(f"parsing {filename}") + size, hash_string, name = parse(filename, mime) + print(f"{size} {hash_string} {mime} {name}") + new_signatures[size][hash_string] = (mime, name) + lines = [] + lines.append("sha1_signatures = {") + for k,v in sorted(new_signatures.items()): + lines.append(f"{k}: {{") + for h,n in sorted(v.items(), key=lambda a:(a[1], a[0])): + lines.append(f" '{h}': {n},") + lines.append("},") + lines.append("} # end sha1_signatures") + + print("\n".join(lines)) + with open(source, 'w') as fh: + fh.write("\n".join(lines)) \ No newline at end of file