Skip to content

Instantly share code, notes, and snippets.

@Miss-Inputs
Last active August 29, 2021 09:40
Show Gist options
  • Save Miss-Inputs/e8f44d7e5b8315e88fe0ed09b48a2c81 to your computer and use it in GitHub Desktop.
Save Miss-Inputs/e8f44d7e5b8315e88fe0ed09b48a2c81 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import os
import glob
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from xml.etree import ElementTree
source_path = '/home/megan/351ELEC'
es_config_path = os.path.join(source_path, 'packages', 'ui', '351elec-emulationstation', 'config')
es_systems_path = os.path.join(es_config_path, 'es_systems.cfg')
es_systems = ElementTree.parse(es_systems_path)
#This feels like the wrong way to get the locally "built" core info directory, but the version can change…
try:
core_info_directory = glob.glob(os.path.join(source_path, 'build.351ELEC-RG351P.aarch64', 'core-info-*'))[0]
except IndexError:
core_info_directory = None
variant_systems = {
#We probably expect all the emulators for these to be the same
#'arcade': ['cps1', 'cps2', 'cps3', 'neogeo'], #maybe not though
'gb': ['gbh'],
'gbc': ['gbch'],
'gba': ['gbah'],
'gamegear': ['ggh'],
'megacd': ['segacd'],
'megadrive': ['megadrive-japan', 'genesis', 'genh'],
'nes': ['famicom', 'nesh'],
'pcengine': ['tg16'],
'pcenginecd': ['tg16cd'],
'psp': ['pspminis'],
'snes': ['snesh', 'sfc'],
}
subset_systems = {
#Where x: y; we expect all emulators for y to also emulate x, but there could reasonably be an emulator for x which doesn't do y
'nes': ['fds'],
'snes': ['snesmsu1', 'satellaview'],
'pcengine': ['supergrafx', 'pcenginecd'],
'tg16': ['tg16cd'],
'megadrive': ['megacd', 'sega32x'],
'odyssey2': ['videopac'], #maybe this is an okay assumption to make?
#Maybe amiga > amigacd32?
#amstradcpc > gx4000 but that ended up not being a thing
'gb': ['gbc'],
'ngp': ['ngpc'],
'wonderswan': ['wonderswancolor'],
}
systems = {system.findtext('name'): system for system in es_systems.findall('system')}
def get_emulators(system_name):
emulators_tag = systems[system_name].find('emulators')
if emulators_tag is None:
#This isn't a concern, tools/screenshots etc has no emulators defined
return {}
return {emulator.attrib['name']: [core.text for core in emulator.find('cores').findall('core')] for emulator in emulators_tag.findall('emulator')}
def get_emulator_set(system_name):
return {emulator_name + '/' + core for emulator_name, emulators in get_emulators(system_name).items() for core in emulators}
def parse_core_info(core_info):
d = {}
for line in core_info.splitlines():
line = line.strip()
if not line:
continue
if line.startswith('#'):
continue
if ' = ' not in line:
print('wat', line)
continue
k, v = line.split(' = ', 1)
d[k] = v
return d
def get_core_info(core_name):
info_name = core_name + '_libretro.info'
core_info_path = os.path.join(core_info_directory, info_name)
if core_info_directory:
try:
with open(core_info_path, 'rt') as core_info:
return parse_core_info(core_info.read())
except FileNotFoundError:
print(core_name, 'does not have an info file in the local build, maybe it is new')
url = 'http://raw.githubusercontent.com/libretro/libretro-core-info/master/' + info_name.replace('beetle_', 'mednafen_') #Going to be renamed upstream later I guess
try:
with urlopen(url) as response:
data = response.read().decode('utf-8')
with open(core_info_path, 'wt') as core_info:
core_info.write(data)
return parse_core_info(data)
except (HTTPError, URLError):
print('Warning!', core_name, 'does not have an info file remotely')
return None
def get_core_extensions(core_name):
core_info = get_core_info(core_name)
if not core_info:
return set()
supported_extensions = core_info.get('supported_extensions')
if not supported_extensions:
return set()
return set(supported_extensions.strip('"').split('|'))
def get_system_extensions(system):
return set(ext.lstrip('.').lower() for ext in system.findtext('extension').strip().split(' '))
def check_emus_are_same():
for base_variant_system, variants in variant_systems.items():
base_emulators = get_emulator_set(base_variant_system)
base_extensions = get_system_extensions(systems[base_variant_system])
for variant in variants:
variant_emulators = get_emulator_set(variant)
for base_only_emulator in base_emulators - variant_emulators:
print(base_only_emulator, 'exists in', base_variant_system, 'but not', variant)
for variant_only_emulator in variant_emulators - base_emulators:
print(variant_only_emulator, 'exists in', variant, 'but not', base_variant_system)
variant_extensions = get_system_extensions(systems[variant])
base_only_extensions = base_extensions - variant_extensions
if base_only_extensions:
print(base_variant_system, 'has extensions that', variant, 'does not have (maybe intended):', base_only_extensions)
variant_only_extensions = variant_extensions - base_extensions
if variant_only_extensions:
print(variant, 'has extensions that', base_variant_system, 'does not have (maybe intended):', variant_only_extensions)
for base_subset_system, subsets in subset_systems.items():
base_emulators = get_emulator_set(base_subset_system)
for subset in subsets:
subset_emulators = get_emulator_set(subset)
for subset_only_emulator in subset_emulators - base_emulators:
print(subset_only_emulator, 'exists in', subset, 'but not', base_subset_system)
def check_libretro_cores():
core_extensions = {}
defined_exts_for_systems_using_core = {}
for system_name, system in systems.items():
emulators = get_emulators(system_name)
if not emulators:
continue
cores = emulators.get('libretro', emulators.get('retroarch'))
if not cores:
continue
for core in cores:
if core not in core_extensions:
core_extensions[core] = get_core_extensions(core)
if core not in defined_exts_for_systems_using_core:
defined_exts_for_systems_using_core[core] = []
defined_exts_for_systems_using_core[core] += list(get_system_extensions(system))
print(core_extensions['fceumm'])
for system_name, system in systems.items():
emulators = get_emulators(system_name)
retroarch_cores = emulators.get('libretro') #This is going to be changed to 'retroarch' in the not too distant future I think
retrorun_cores = emulators.get('retrorun')
if retroarch_cores is not None:
if retrorun_cores is not None:
for retrorun_only in set(retrorun_cores) - set(retroarch_cores):
print(system_name, 'has', retrorun_only, 'core for retrorun but not RetroArch')
for retroarch_only in set(retroarch_cores) - set(retrorun_cores):
print(system_name, 'has', retroarch_only, 'core for RetroArch but not retrorun, but retrorun is used for this system') #Maybe that is allowed?
if system_name not in ('doom', 'scummvm', 'ecwolf'):
defined_extensions = get_system_extensions(system)
system_core_extensions = {core: core_extensions[core] for core in retroarch_cores}
all_core_extensions = set(ext for core_exts in system_core_extensions.values() for ext in core_exts)
if not (set(emulators) - {'libretro', 'retroarch', 'retrorun'}):
#Don't try and figure out any weird extensions if there are any standalone emulators, since we don't know the extensions of standalone emulators
defined_junk_extensions = (defined_extensions - {'7z', 'zip'}) - all_core_extensions
if defined_junk_extensions:
print(system_name, 'has extensions defined which are not listed as supported for any of the cores:', defined_junk_extensions)
if system_name not in [v for vv in variant_systems.values() for v in vv]:
for core_name in retroarch_cores:
if not core_name.startswith(('fbneo', 'fbalpha', 'mame')):
#Hmm I'm tired and looking at this code like a week later, does it make sense or should it be using defined_extensions
extra_extensions = core_extensions[core_name] - set(defined_exts_for_systems_using_core[core_name])
if extra_extensions:
print('May not be an issue but', core_name, 'seems to support', extra_extensions, 'not defined for', system_name)
check_emus_are_same()
check_libretro_cores()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment