Files
firmware/database/parse_gc.py
2025-05-10 09:15:46 +02:00

164 lines
4.5 KiB
Python

import os
replacer = r'\((.*)\)'
class GameId:
name = ""
id = ""
region = ""
def __init__(self, name, id):
self.name = name
self.id = id
parts = id.strip().split("-")
if parts and len(parts) > 2:
self.id = parts[2]
if len(parts[-1]) < 3:
self.region = parts[-2]
else:
self.region = parts[-1]
self.region = re.sub(replacer, "", self.region).strip()
def __str__(self):
return "Id " + self.id + " Name " + self.name + " Region " + self.region
def __lt__(self, o):
return self.name < o.name
def getFileName(rootdir):
regex = re.compile('(.*dat$)')
filename = None
for root, dirs, files in os.walk(rootdir):
for file in files:
if regex.match(file):
filename = "{}/{}".format(root, file)
return filename
def parseGameEntry(element):
name = element.attrib["name"]
serials = element.findall("serial")
game_serials = []
if serials and serials[0].text:
for s in serials[0].text.split(","):
if len(s.strip()) > 0:
game_serials.append(s.strip())
return (name, game_serials)
def createGameList(name_to_serials):
gamenames_full = list(name_to_serials.keys())
gamenames_full.sort()
gameList = []
# Try to figure out multi disc games by game name
parent_serials = {}
for game in gamenames_full:
for serial in name_to_serials[game]:
gameName = re.sub(replacer, "", game).strip()
game_item = GameId(gameName, serial)
gameList.append(game_item)
return gameList
import xml.etree.ElementTree as ET
import re
def createDbFile(rootdir, outputdir):
dirname = rootdir.split("/")[-1]
if len(dirname) < 1:
dirname = rootdir.split("/")[-2]
tree = ET.parse(getFileName(rootdir))
root = tree.getroot()
name_to_serials = {}
regions = []
# Create Mapping from serial to full game name
for element in root:
if element.tag == 'game':
name, serials = parseGameEntry(element)
if len(serials) < 1:
continue
name_to_serials[name] = serials
redump_games = createGameList(name_to_serials)
gamenames = []
games_sorted = {}
# Create Prefix list and game name list
# Create dict that contains all games sorted by prefix
for game in redump_games:
if game.name not in gamenames:
gamenames.append(game.name)
games_sorted[game.id] = game
print("Redump {} Game Names".format(len(gamenames)))
print("Redump {} Games".format(len(redump_games)))
redump_games.sort()
term = 0
game_ids_offset = 0
game_names_base_offset = 0 + (len(games_sorted) * 12) + 12
offset = game_names_base_offset
game_name_to_offset = {}
print(f"Offset Base {hex(offset)}")
# Calculate offset for each game name
for gamename in gamenames:
if gamename not in game_name_to_offset:
game_name_to_offset[gamename] = offset
offset = offset + len(gamename) + 1
with open("{}/gamedbgc.dat".format(outputdir), "wb") as out:
# Next: write game entries for each index in the format:
# 4 Byte: Game ID without prefix, Big Endian
# 4 Byte: Offset to game name, Big Endian
# 4 Byte: Parent Game ID - if multi disc this is equal to Game ID
for id in games_sorted:
game = games_sorted[id]
out.write(game.id.encode('ascii'))
out.write(game_name_to_offset[game.name].to_bytes(4, 'big'))
out.write(game.region.encode('ascii'))
out.write(int(0).to_bytes(1, 'big'))
# print("Game Name: {} Offset: {}".format(game.name, game_name_to_offset[game.name]))
out.write(term.to_bytes(12, 'big'))
# Last: write null terminated game names
for game in game_name_to_offset:
out.write(game.encode('ascii'))
out.write(term.to_bytes(1, 'big'))
from urllib.request import urlopen
from io import BytesIO
from zipfile import ZipFile
def downloadDat(path):
url = "http://redump.org/datfile/gc/serial"
http_response = urlopen(url)
zipfile = ZipFile(BytesIO(http_response.read()))
zipfile.extractall(path=path)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("dirname")
parser.add_argument("outputdir")
args = parser.parse_args()
downloadDat(args.dirname)
createDbFile(args.dirname, args.outputdir)