Skip to content

Instantly share code, notes, and snippets.

@goncalomb
Last active May 18, 2022 21:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goncalomb/5c8724771d75140e6a6dc9ffeef800c8 to your computer and use it in GitHub Desktop.
Save goncalomb/5c8724771d75140e6a6dc9ffeef800c8 to your computer and use it in GitHub Desktop.
Script to rename PSX and PS2 rom files.
#!/bin/env python3
# Copyright (c) 2022 Gonçalo Baltazar <me@goncalomb.com>
# MIT License
import os, argparse, shutil, tempfile, requests, zipfile
import xml.etree.ElementTree as ET
tmp_dir = os.path.join(tempfile.gettempdir(), 'ps-names')
datfile_list = [
('psx', 'http://redump.org/datfile/psx/serial,version'),
('ps2', 'http://redump.org/datfile/ps2/serial,version'),
]
def download_redump_datfile(url: str, local_path: str):
with tempfile.TemporaryFile() as tf:
with requests.get(url, stream=True) as r:
r.raise_for_status()
for chunk in r.iter_content(None):
tf.write(chunk)
with zipfile.ZipFile(tf) as zf:
fname = ''
for zi in zf.infolist():
if os.path.splitext(zi.filename)[1] == '.dat':
fname = zi.filename
break
if fname:
with open(local_path, 'wb') as fp:
fp.write(zf.read(fname))
return True
return False
def read_datfile(url: str, tmp_local_name: str):
local_file = os.path.join(tmp_dir, tmp_local_name)
if os.path.isfile(local_file):
print("using local file '%s'" % (local_file))
else:
try: os.mkdir(tmp_dir)
except FileExistsError: pass
print("dowloading %s" % (url))
if download_redump_datfile(url, local_file):
print("saved to '%s'" % (local_file))
else:
print("datfile not found (outdated downloader?)")
data = []
root = ET.parse(local_file).getroot()
def el_find_text(el: ET.Element, path: str):
el_found = el.find(path)
return None if el_found is None else el_found.text
for el_game in root.findall('./game'):
data.append({
'name': el_game.get('name'),
'category': el_find_text(el_game, './category'),
'serial': el_find_text(el_game, './serial'),
'version': el_find_text(el_game, './version'),
'description': el_find_text(el_game, './description'),
})
return data
def cleanup_serial(s: str):
return s.strip().replace(' ', '-').upper()
class Database:
def __init__(self):
self.data = {}
for (slug, url) in datfile_list:
self.data[slug] = read_datfile(url, slug + '.dat')
self.data_by_serial = {}
for d in self.data.values():
for game in d:
if game['serial']:
for serial in map(cleanup_serial, game['serial'].split(',')):
if serial in self.data_by_serial:
# print("duplicate serial '%s', '%s' and '%s'" % (serial, self.data_by_serial[serial]['name'], game['name']))
# duplicate serial, use shortest name (TODO: could use a better heuristic)
if len(game['name']) < len(self.data_by_serial[serial]['name']):
self.data_by_serial[serial] = game
else:
self.data_by_serial[serial] = game
def get_game_by_serial(self, serial):
s = serial
if s in self.data_by_serial:
return self.data_by_serial[s]
s = cleanup_serial(s)
if s in self.data_by_serial:
return self.data_by_serial[s]
s = s.replace('_', '-')
if s in self.data_by_serial:
return self.data_by_serial[s]
s = s.replace('.', '')
if s in self.data_by_serial:
return self.data_by_serial[s]
return None
def get_name_by_serial(self, serial):
game = self.get_game_by_serial(serial)
if game:
return game['name']
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='ps-names')
subparsers = parser.add_subparsers(title='commands', dest='command', required=True)
parser_rename = subparsers.add_parser('rename', description='rename files')
parser_rename.add_argument('--confirm', action='store_true', help='confirm operation')
parser_rename.add_argument('files', nargs='+', help='list of files')
parser_delete_tmp = subparsers.add_parser('delete-tmp', description='delete temporary files')
args = parser.parse_args()
if args.command == 'rename':
db = Database()
for f in args.files:
if os.path.isfile(f):
(head, tail) = os.path.split(f)
(name, ext) = os.path.splitext(tail)
new_name = db.get_name_by_serial(name)
if new_name:
if args.confirm:
print("renaming '%s' to '%s'" % (f, new_name + ext))
f_new = os.path.join(head, new_name + ext)
os.rename(f, f_new)
else:
print("would rename '%s' to '%s'" % (f, new_name + ext))
else:
print("name not found for '%s'" % (tail))
else:
print("'%s' is not a file" % (f))
if args.confirm:
print("dry run, use --confirm to rename")
if args.command == 'delete-tmp':
if os.path.isdir(tmp_dir):
print("removing '%s'" % (tmp_dir))
shutil.rmtree(tmp_dir)
else:
print("nothing to remove")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment