{"id":193,"date":"2012-05-30T19:04:39","date_gmt":"2012-05-30T19:04:39","guid":{"rendered":"http:\/\/greg-kennedy.com\/?p=193"},"modified":"2019-05-03T21:45:32","modified_gmt":"2019-05-04T02:45:32","slug":"ines-header-fixer","status":"publish","type":"post","link":"https:\/\/greg-kennedy.com\/wordpress\/2012\/05\/30\/ines-header-fixer\/","title":{"rendered":"iNES Header Fixer"},"content":{"rendered":"<p>Auditing ROM collections has been made much simpler over the years thanks to concerted efforts of many cartridge purchasers, dumpers, cataloguers, and coders. \u00a0Using a combination of RomCenter (or ClrMamePro) and a No-Intro .Dat file, one can quickly fix an entire collection in one go: repairing names, verifying regions, checking for problem ROMs, producing have-lists, etc etc.<\/p>\n<p>NES roms throw a particular curveball because they carry a 16-byte header with a little info about the board within the cart. \u00a0Emulators need this info, but it isn&#8217;t verified by No-Intro since it&#8217;s not part of any real dump. \u00a0As a result iNES headers remain missing, incorrect, or filled with crap data. \u00a0A new repair tool is needed.<\/p>\n<p>ines-fix.py is a Python script which can repair defunct iNES headers using an external cart database. \u00a0It does this by extracting the data portion from the file, calculating a crc32, looking for the crc the xml file, and then rebuilding a header based on that info. \u00a0If the old and new headers differ, the file is overwritten with a fixed version.<\/p>\n<p>Be careful with this tool: it&#8217;s sort of a blunt object, and may trash your rom collection if not used carefully. \u00a0<strong>Make backups.<\/strong> \u00a0In particular it writes only iNES v1 headers, expecting emulators to cope with the shortcomings of this format.<\/p>\n<p>Output samples:<br \/>\n-&gt; No change to existing header<\/p>\n<pre>CRC check: 4318A2F8\nFound CRC match: Barker Bill's Trick Shooting\n*** Header unchanged: not writing replacement file.\n----------------------------------------------------<\/pre>\n<p>-&gt; Adding a battery<\/p>\n<pre>CRC check: 1F6EA423\nFound CRC match: Baseball Simulator 1.000\n*** HEADER UPDATED ***\noldHeader: 4e45531a081010000000000000000000\nnewHeader: 4e45531a081012000000000000000000\nAll done.  Wrote new file roms\/Baseball Simulator 1.000 (USA).nes<\/pre>\n<p>-&gt; Correcting a mapper, vertical mirroring, and removing &#8220;DiskDude!&#8221;<\/p>\n<pre>CRC check: 9BDE3267\nFound CRC match: Adventures of Dino Riki\n*** HEADER UPDATED ***\noldHeader: 4e45531a0204004469736b4475646521\nnewHeader: 4e45531a020431000000000000000000\nAll done.  Wrote new file roms\/Adventures of Dino Riki (USA).nes\n----------------------------------------------------<\/pre>\n<p>-&gt; Standardizing on &#8220;horizontal mirroring&#8221;<\/p>\n<pre>CRC check: 3ECA3DDA\nFound CRC match: Bases Loaded 3, Ryne Sandberg Plays\n*** HEADER UPDATED ***\noldHeader: 4e45531a101041000000000000000000\nnewHeader: 4e45531a101040000000000000000000\nAll done.  Wrote new file roms\/Bases Loaded 3 (USA).nes\n----------------------------------------------------<\/pre>\n<p>-&gt; Adding a missing header<\/p>\n<pre>CRC check: 50CCC8ED\nFound CRC match: Battleship\n*** HEADER UPDATED ***\noldHeader:\nnewHeader: 4e45531a020430000000000000000000\nAll done.  Wrote new file roms\/Battleship (USA).nes\n----------------------------------------------------<\/pre>\n<hr \/>\n<p>Here&#8217;s the script:<\/p>\n<p><!--more--><\/p>\n<pre>#!\/usr\/local\/bin\/python\n\"\"\"\nConverts NES ROMs to iNES format, applying correct iNES header.\nUsage: ines-fix &lt;infile&gt;\nSupported infile formats are .nes, .pas (headerless .nes)\n\nROM data is recognized by CRC32 using BootGod's master XML database\n  so make sure you have a local copy\n\"\"\"\n\nimport sys\nimport struct\nfrom binascii import crc32\nfrom xml.etree import ElementTree\n\n##### String holding location of cart db\ncart_xml = \"NesCarts (2011-09-10).xml\"\n# uncomment next line to use Nestopia's DB instead\n# cart_xml = \"NstDatabase.xml\"\n\n# Other required vars\ni_fmt = 'p'\nblob = None\nfound = 0\noldHeader = \"\"\n\n\n# Parse command-line\n\nif (len(sys.argv) != 2):\n    print \"Usage: \" + sys.argv[0] + \" &lt;infile&gt;\"\n    sys.exit(0)\n\n# Open rom database\n#print \"Attempting to open cart db \" + cart_xml\ntree = ElementTree.parse(cart_xml)\n#print \"DB opened!\"\n\n# Attempt to open supplied rom file\n\ntry:\n    with open(sys.argv[1], \"rb\") as f:\n        tag = f.read(4)\n        f.seek(0)\n        if (tag == \"NES\\x1A\"):\n            i_fmt = 'i'\n            oldHeader = f.read(16)\n\n#        print \"Detected \" + i_fmt + \" format for input file\"\n\n\n        blob = f.read()\n\nexcept IOError as (errno, strerror):\n    print \"Error opening \" + sys.argv[1] + \": \"\n    print \"I\/O error({0}): {1}\".format(errno, strerror)\n    sys.exit(2)\n\nif (len(blob) &gt; 0):\n    format_crc32 = format(crc32(blob) &amp; 0xFFFFFFFF, '08X')\n    print \"CRC check: \" + format_crc32\n\n    # go look up crc32 in db\n    game_list = tree.findall(\"game\")\n    for game in game_list:\n        cart_list = game.findall(\"cartridge\")\n        for cart in cart_list:\n            if (cart.attrib.get('crc') == format_crc32):\n                found=1\n                break\n        if (found):\n            break\n\n\n    if (found == 0):\n        print sys.argv[1]\n        print \"*** CART NOT FOUND IN DB\"\n        print \"----------------------------------------------------\"\n        sys.exit(2)\n\nprint \"Found CRC match: \" + game.attrib.get(\"name\").encode('ascii', 'ignore')\n#ElementTree.tostring(game)\n\n# retrieve data from game\nboard = cart.find(\"board\")\nmapper = int(board.attrib.get(\"mapper\"))\n\nprg_size = 0\nprg_list = board.findall(\"prg\")\nfor prg in prg_list:\n    prg_size = prg_size + int(prg.attrib.get(\"size\") [:-1])\n\nchr_size = 0\nchr_list = board.findall(\"chr\")\nfor chr in chr_list:\n    chr_size = chr_size + int(chr.attrib.get(\"size\") [:-1])\n\n\nbattery = 0\nwram_list = board.findall(\"wram\")\nfor wram in wram_list:\n    if (wram.attrib.get(\"battery\") is not None):\n        battery = int(wram.attrib.get(\"battery\"))\n\nchip_list = board.findall(\"chip\")\nfor chip in chip_list:\n    if (chip.attrib.get(\"battery\") is not None):\n        battery = int(chip.attrib.get(\"battery\"))\n\n\nmirror_4 = 0\nmirror_v = 0\npad = board.find(\"pad\")\nif (format_crc32 == \"CD50A092\" or \\\n    format_crc32 == \"EC968C51\" or \\\n    format_crc32 == \"404B2E8B\"):\n    mirror_4 = 1\nelif (pad is not None):\n# the \"h\" pad means \"v\" mirror\n    mirror_v = int(pad.attrib.get(\"h\"))\n\n\nmapper_lo = mapper &amp; 0x0F\nmapper_hi = mapper &amp; 0xF0\n\nnewHeader = \"NES\\x1a\" + \\\n              struct.pack(\"BBBB\", \\\n                ( prg_size \/ 16 ), \\\n                ( chr_size \/ 8 ), \\\n                (mapper_lo &lt;&lt; 4) + (mirror_4 &lt;&lt; 3) + (battery &lt;&lt; 1) + (mirror_v)\n, \\\n                (mapper_hi) ) + ( '\\0' * 8 )\n\nif (newHeader != oldHeader):\n    print \"*** HEADER UPDATED ***\\noldHeader: \" + oldHeader.encode('hex')\n    print \"newHeader: \" + newHeader.encode('hex')\n\n    # write new file\n    try:\n        with open(sys.argv[1], \"wb\") as f:\n            f.write( newHeader )\n            f.write( blob )\n\n\n    except IOError as (errno, strerror):\n        print \"Error opening \" + sys.argv[1]\n        print \"I\/O error({0}): {1}\".format(errno, strerror)\n        sys.exit(2)\n\n    print \"All done.  Wrote new file \" + sys.argv[1]\nelse:\n    print \"*** Header unchanged: not writing replacement file.\"\nprint \"----------------------------------------------------\"<\/pre>\n<p><b>For those having problems with copy-paste, you can download a .zip containing the script here:<br \/>\n<a href=\"http:\/\/greg-kennedy.com\/wordpress\/wp-content\/uploads\/2012\/05\/ines-fix.zip\">ines-fix<\/a><\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Auditing ROM collections has been made much simpler over the years thanks to concerted efforts of many cartridge purchasers, dumpers, cataloguers, and coders. \u00a0Using a combination of RomCenter (or ClrMamePro) and a No-Intro .Dat file, one can quickly fix an entire collection in one go: repairing names, verifying regions, checking for problem ROMs, producing have-lists, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,8],"tags":[],"class_list":["post-193","post","type-post","status-publish","format-standard","hentry","category-games","category-software"],"_links":{"self":[{"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/posts\/193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/comments?post=193"}],"version-history":[{"count":12,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/posts\/193\/revisions"}],"predecessor-version":[{"id":383,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/posts\/193\/revisions\/383"}],"wp:attachment":[{"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/media?parent=193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/categories?post=193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/greg-kennedy.com\/wordpress\/wp-json\/wp\/v2\/tags?post=193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}