def parseBuffer(data): for parser in RomInfoParser.getParsers(): if parser.isValidData(data): props = parser.parseBuffer(data) if props and any(props): return props return {}
def parse(filename): ext = None for parser in RomInfoParser.getParsers(): if not ext: ext = parser._getExtension(filename) if parser.isValidExtension(ext): props = parser.parse(filename) if props and any(props): return props return {}
# Only two values are defined: 00h - Japanese, 01h - Non-Japanese. props["destination"] = "Japan" if data[0x14a] == 0x00 else "" # 014C - Mask ROM version number of the game, usually 00h props["version"] = "%02X" % data[0x14c] # 014D - Header checksum, 8 bit checksum across the cartridge header bytes 0134-014C props["header_checksum"] = "%02X" % data[0x14d] # 014E-014F - Global checksum, 16 bit checksum across the whole cartridge ROM props["global_checksum"] = "%04X" % ((data[0x14e] << 8) | data[0x14f]) return props RomInfoParser.registerParser(GameboyParser()) gameboy_types = { 0x00: "ROM", 0x01: "ROM+MBC1", 0x02: "ROM+MBC1+RAM", 0x03: "ROM+MBC1+RAM+BATT", 0x05: "ROM+MBC2", 0x06: "ROM+MBC2+BATT", 0x0b: "ROM+MMM01", 0x0c: "ROM+MMM01+RAM", 0x0d: "ROM+MMM01+RAM+BATT", 0x0f: "ROM+MBC3+TIMER+BATT", 0x10: "ROM+MBC3+TIMER+RAM+BATT", 0x11: "ROM+MBC3", 0x12: "ROM+MBC3+RAM",
return any(data[case[0] : case[0] + len(case[1])] == case[1] for case in edge_cases) def getPublisher(self, copyright_str): """ Resolve a copyright string into a publisher name. It SHOULD be 4 characters after a (C) symbol, but there are variations. When the company uses a number as a company code, the copyright usually has this format: '(C)T-XX 1988.JUL', where XX is the company code. """ company = copyright_str[3:7] if "-" in company: company = company[company.rindex("-") + 1 : ] company = company.rstrip() return gensis_publishers.get(company, "") RomInfoParser.registerParser(GensisParser()) genesis_devices = { "J": "3B Joypad", "6": "6B Joypad", "K": "Keyboard", "P": "Printer", "B": "Control Ball", "F": "Floppy Drive", "L": "Activator", "4": "Team Player", "0": "MS Joypad", "R": "RS232C Serial", "T": "Tablet", "V": "Paddle",
def makeNativeFormat(self, data): """ Correct for word- and byte-swapping. """ if [b for b in data[:4]] == [0x37, 0x80, 0x40, 0x12]: # [BADC] data[::2], data[1::2] = data[1::2], data[::2] elif [b for b in data[:4]] == [0x40, 0x12, 0x37, 0x80]: # [DCBA] data[::4], data[1::4], data[2::4], data[3::4] = data[3::4], data[ 2::4], data[1::4], data[::4] elif [b for b in data[:4]] == [0x12, 0x40, 0x80, 0x37]: # [CDAB] data[::4], data[1::4], data[2::4], data[3::4] = data[2::4], data[ 3::4], data[::4], data[1::4] RomInfoParser.registerParser(Nintendo64Parser()) n64_regions = { 0x00: "", # Demo games 0x37: "", # Beta games 0x41: "Japan/USA", 0x44: "Germany", 0x45: "USA", 0x46: "France", 0x49: "Italy", 0x4A: "Japan", 0x50: "Europe", 0x53: "Spain", 0x55: "Australia", 0x59: "Australia", # Other PAL European codes
for p_code, p_desc in dc_peripherals.items(): if peripherals_code & p_code == p_code: peripherals.append(p_desc) props['compatible_peripherals'] = tuple(peripherals) props['media_info'] = tuple( int(x) for x in props['media_info_code'][6:].split('/')) props['release_date'] = datetime.date( *time.strptime(props['release_date_code'], "%Y%m%d")[:3]) props['regions'] = tuple( dc_regions.get(r) for r in props['region_code']) except (KeyError, IndexError, ValueError): return {} return props RomInfoParser.registerParser(DreamcastParser()) CDI_V2 = 0x80000004 CDI_V3 = 0x80000005 CDI_V35 = 0x80000006 cdi_track_start_mark = (0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF) cdi_track_sector_sizes = { 0: 2048, 1: 2336, 2: 2352 } cdi_track_modes = { 0: 'audio', 1: 'mode1', 2: 'mode2'
props["region_code"] = "%02X" % data[0x3e] return props def makeNativeFormat(self, data): """ Correct for word- and byte-swapping. """ if [b for b in data[:4]] == [0x37, 0x80, 0x40, 0x12]: # [BADC] data[::2], data[1::2] = data[1::2], data[::2] elif [b for b in data[:4]] == [0x40, 0x12, 0x37, 0x80]: # [DCBA] data[::4], data[1::4], data[2::4], data[3::4] = data[3::4], data[2::4], data[1::4], data[::4] elif [b for b in data[:4]] == [0x12, 0x40, 0x80, 0x37]: # [CDAB] data[::4], data[1::4], data[2::4], data[3::4] = data[2::4], data[3::4], data[::4], data[1::4] RomInfoParser.registerParser(Nintendo64Parser()) n64_regions = { 0x00: "", # Demo games 0x37: "", # Beta games 0x41: "Japan/USA", 0x44: "Germany", 0x45: "USA", 0x46: "France", 0x49: "Italy", 0x4A: "Japan", 0x50: "Europe", 0x53: "Spain", 0x55: "Australia", 0x59: "Australia",
# 00A0-00AB - Title, UPPER CASE ASCII, padded with 00h (if less than 12 chars) props["title"] = self._sanitize(data[0xa0 : 0xa0 + 12]) # 00AC-00AF - Code, UPPER CASE ASCII # This is the same code as the AGB-UTTD code which is printed on the package # and sticker on (commercial) cartridges (excluding the leading "AGB-" part). # See http://z9.invisionfree.com/Golden_Sun_Hacking/index.php?showtopic=241 # for the breakdown of what values U, TT and D can have. props["code"] = self._sanitize(data[0xac : 0xac + 4]) # 00B0-00B1 - Licensee, UPPER CASE ASCII pub = data[0xb0 : 0xb0 + 2].decode("ascii", "ignore") props["publisher"] = gameboy_publishers.get(pub, "") props["publisher_code"] = pub # 00B3 - Main unit code, identifies the required hardware (00h for current GBA models) props["unit_code"] = "%02X" % data[0xb3] # 00BC - Software version of the game, usually zero props["version"] = "%02X" % data[0xbc] # 00BD - Header checksum, 8 bit checksum across the cartridge header bytes 00A0-00BC props["header_checksum"] = "%02X" % data[0xbd] props["platform"] = "Game Boy Advance" return props RomInfoParser.registerParser(GBAParser())
return props def get_cstr(self, ptr, data): """ Parse a zero-terminated (c-style) string from a bytearray. 0xFFFF and 0x0000 are invalid ptr values and will return "". """ if ptr != 0xffff and ptr != 0 and ptr < len(data): term = ptr while term < len(data) and data[term]: term += 1 return self._sanitize(data[ptr : term]) return "" RomInfoParser.registerParser(MasterSystemParser()) mastersystem_romsize = { 0xa: "8 KB", 0xb: "16 KB", 0xc: "32 KB", 0xd: "48 KB", 0xe: "64 KB", 0xf: "128 KB", 0x0: "256 KB", 0x1: "512 KB", 0x2: "1024 KB", }
for p_code, p_desc in dc_peripherals.items(): if peripherals_code & p_code == p_code: peripherals.append(p_desc) props['compatible_peripherals'] = tuple(peripherals) props['media_info'] = tuple( int(x) for x in props['media_info_code'][6:].split('/')) props['release_date'] = datetime.date( *time.strptime(props['release_date_code'], "%Y%m%d")[:3]) props['regions'] = tuple( dc_regions.get(r) for r in props['region_code']) except (KeyError, IndexError, ValueError): return {} return props RomInfoParser.registerParser(DreamcastParser()) CDI_V2 = 0x80000004 CDI_V3 = 0x80000005 CDI_V35 = 0x80000006 cdi_track_start_mark = (0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF) cdi_track_sector_sizes = {0: 2048, 1: 2336, 2: 2352} cdi_track_modes = {0: 'audio', 1: 'mode1', 2: 'mode2'} dc_regions = { 'J': 'Asia', # Japan, Korea, Asian NTSC 'U': 'America', # North American NTSC, Brazilian PAL-M, Argentine PAL-N 'E': 'Europe' # European PAL }
kart = contents[(romType & 0xf) % 3] return kart def getCompanyCode(self, header): companyCode = -1 if header[0x2a] != 0x33: companyCode = ((header[0x2a] >> 4) & 0x0F) * 36 + (header[0x2a] & 0x0F) else: l = chr(header[0x00]).upper() r = chr(header[0x01]).upper() l2 = ord(l) - ord('7') if l > '9' else ord(l) - ord('0') r2 = ord(r) - ord('7') if r > '9' else ord(r) - ord('0') companyCode = l2 * 36 + r2 if l2 >= 0 and r2 >= 0 else -1 return companyCode RomInfoParser.registerParser(SNESParser()) # Souce: http://softpixel.com/~cwright/sianse/docs/Snesrom.txt # Snesrom.txt correction: South Korea should be NTSC snes_regions = { 0: "Japan", # NTSC 1: "USA/Canada", # NTSC 2: "Europe/Asia/Oceania", # PAL 3: "Sweden", # PAL 4: "Finland", # PAL 5: "Denmark", # PAL 6: "France", # PAL 7: "Holland", # PAL 8: "Spain", # PAL 9: "Germany/Austria/Switzerland", # PAL
# 014A - Destination code, if this version of the game is supposed to be sold in Japan. # Only two values are defined: 00h - Japanese, 01h - Non-Japanese. props["destination"] = "Japan" if data[0x14a] == 0x00 else "" # 014C - Mask ROM version number of the game, usually 00h props["version"] = "%02X" % data[0x14c] # 014D - Header checksum, 8 bit checksum across the cartridge header bytes 0134-014C props["header_checksum"] = "%02X" % data[0x14d] # 014E-014F - Global checksum, 16 bit checksum across the whole cartridge ROM props["global_checksum"] = "%04X" % ((data[0x14e] << 8) | data[0x14f]) return props RomInfoParser.registerParser(GameboyParser()) gameboy_types = { 0x00: "ROM", 0x01: "ROM+MBC1", 0x02: "ROM+MBC1+RAM", 0x03: "ROM+MBC1+RAM+BATT", 0x05: "ROM+MBC2", 0x06: "ROM+MBC2+BATT", 0x0b: "ROM+MMM01", 0x0c: "ROM+MMM01+RAM", 0x0d: "ROM+MMM01+RAM+BATT", 0x0f: "ROM+MBC3+TIMER+BATT", 0x10: "ROM+MBC3+TIMER+RAM+BATT", 0x11: "ROM+MBC3",
props["video_output"] = "" props["title"] = "" # Skip the UNIF header (0x20 / 32 bytes) and continue with chunked reads data = data[0x20 : ] while len(data) > 8: size = 0 sizestr = data[4:8] if len(sizestr) == 4: size = sizestr[0] | (sizestr[1] << 8) | (sizestr[2] << 16) | (sizestr[3] << 24) if size == 0: continue ID = self._sanitize(data[ : 4]) chunk = data[8 : 8 + size] data = data[8 + size : ] # Fast-forward past chunk's data if ID == "NAME": props["title"] = self._sanitize(chunk) elif ID == "TVCI": props["video_output"] = "NTSC" if chunk[0] == 0x00 else "PAL" if chunk[0] == 0x01 else "" elif ID == "BATR": props["battery"] = "yes" elif ID == "MIRR": if chunk[0] == 0x04: props["four_screen_vram"] = "yes" return props RomInfoParser.registerParser(NESParser())