mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-12 04:53:52 -06:00
feat: read the emuelec launchers
emuelec_launchers.py reads emuelecRunEmu.sh and the scripts and configs it starts at the tag batocera-systems is read at (v4.8): 36 records for RetroArch, retrorun, freej2me, MAME, PICO-8, Flycast, Amiberry, DuckStation, Yabasanshiro, jzIntv, Mupen64Plus, Hatari, AdvanceMAME, ScummVM, Dolphin and the MIDI synths.
This commit is contained in:
1 parent
7aacbbe214
commit
82a6b2f833
4 files changed
+1782
No files matched your search
@@ -4226,3 +4226,361 @@ systems:
|
||||
native_name: Phillips CDI
|
||||
native_id: cdi
|
||||
name: Phillips CDI
|
||||
launchers:
|
||||
- emulator: retroarch
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: "retroarch.cfg system_directory (--config)"
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/libretro_base/retroarch/package.mk:117, packages/sx05re/emuelec/bin/emuelecRunEmu.sh:380"
|
||||
- emulator: retrorun
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: retrorun -d
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:110, 466, 476"
|
||||
- emulator: freej2me
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/jdk
|
||||
passes_to: environment JAVA_HOME
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:129-133, packages/sx05re/libretro/freej2me/scripts/freej2me.sh:11"
|
||||
- emulator: freej2me
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- freej2me-lr.jar
|
||||
search_dir: /usr/lib/libretro
|
||||
passes_to: "${HOME}/roms/bios"
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/jdk/eeversion exists
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:129-130, packages/sx05re/libretro/freej2me/scripts/freej2me.sh:11-12"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- fm*
|
||||
search_dir: /usr/config/emuelec/configs/mame/hash
|
||||
passes_to: /storage/roms/bios/mame/hash
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/mame/hash/fmtowns_cd.xml absent
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:6-8"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- app*
|
||||
search_dir: /usr/config/emuelec/configs/mame/hash
|
||||
passes_to: /storage/roms/bios/mame/hash
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/mame/hash/apple2_flop_orig.xml absent
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:11-13"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- fm*
|
||||
search_dir: /usr/config/emuelec/configs/mame/ini
|
||||
passes_to: /storage/roms/bios/mame/ini
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/mame/ini/fmtownsux.ini absent
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:16-18"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- mame*
|
||||
search_dir: /usr/config/emuelec/configs/mame/ini
|
||||
passes_to: /storage/roms/bios/mame/ini
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/mame/ini/mame.ini absent
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame
|
||||
passes_to: mame.ini homepath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:10, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/bios
|
||||
passes_to: mame.ini rompath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:11, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/hash
|
||||
passes_to: mame.ini hashpath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:12, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/samples
|
||||
passes_to: mame.ini samplepath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:13, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/artwork
|
||||
passes_to: mame.ini artpath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:14, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/ini
|
||||
passes_to: mame.ini inipath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:16, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/cheat
|
||||
passes_to: mame.ini cheatpath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:18, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/crosshair
|
||||
passes_to: mame.ini crosshairpath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:19, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: mame
|
||||
build: libretro
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/mame/plugins
|
||||
passes_to: mame.ini pluginspath
|
||||
fails_when_missing: false
|
||||
depends_on_option: mame_read_config = enabled
|
||||
source_ref: "packages/sx05re/libretro/mame/config/ini/mame.ini:20, packages/sx05re/libretro/mame/config/MAME/MAME.opt:19,\
|
||||
\ packages/sx05re/emuelec/bin/emuelecRunEmu.sh:357-358, packages/sx05re/libretro/mame/scripts/mame.sh:21-23"
|
||||
- emulator: pico8
|
||||
build: standalone
|
||||
kind: moves
|
||||
names:
|
||||
- pico8_dyn
|
||||
case_insensitive: false
|
||||
search_dir: /storage/roms/bios/pico-8
|
||||
passes_to: /emuelec/bin/pico-8
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/roms/bios/pico-8 exists
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:305-307, packages/sx05re/emuelec/bin/pico8.sh:9-13,\
|
||||
\ 40-42"
|
||||
- emulator: flycastsa
|
||||
build: standalone
|
||||
kind: links
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/dc
|
||||
passes_to: symlink /storage/.local/share/flycast -> /storage/roms/bios/dc
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:198-200, packages/sx05re/emulators/flycastsa/scripts/flycast.sh:11,\
|
||||
\ 14"
|
||||
- emulator: flycast_dojo
|
||||
build: standalone
|
||||
kind: links
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/dc_dojo
|
||||
passes_to: symlink /storage/.local/share/flycast-dojo -> /storage/roms/bios/dc_dojo
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:201-203, packages/sx05re/emulators/flycast-dojo/scripts/flycastdojo.sh:11-12,\
|
||||
\ 28-30"
|
||||
- emulator: amiberry
|
||||
build: standalone
|
||||
kind: links
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: symlink /usr/config/amiberry/kickstarts -> /storage/roms/bios
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emulators/amiberry/package.mk:36"
|
||||
- emulator: amiberry-lite
|
||||
build: standalone
|
||||
kind: links
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: symlink /usr/config/amiberry-lite/kickstarts -> /storage/roms/bios
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emulators/amiberry-lite/package.mk:36"
|
||||
- emulator: timidity
|
||||
build: standalone
|
||||
kind: links
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/timidity/freepats
|
||||
passes_to: symlink /usr/config/timidity/freepats -> /storage/roms/bios/timidity/freepats
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/tools/sound/timidity/package.mk:38"
|
||||
- emulator: duckstation
|
||||
build: standalone
|
||||
kind: scans_dir
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: "settings.ini [BIOS] SearchDirectory (-settings), [BIOS] PathNTSCU, PathNTSCJ,\
|
||||
\ PathPAL unset"
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:207-209, packages/sx05re/emulators/duckstation/config/settings.ini:6-9,\
|
||||
\ packages/sx05re/emulators/duckstation/package.mk:56, packages/sx05re/emulators/duckstation/scripts/duckstation.sh:40"
|
||||
- emulator: yabasanshiroSA
|
||||
build: standalone
|
||||
kind: fixed_name
|
||||
names:
|
||||
- saturn_bios.bin
|
||||
case_insensitive: false
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: yabasanshiro -b
|
||||
fails_when_missing: false
|
||||
depends_on_option: hlebios != 1
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:343-345, packages/sx05re/emulators/yabasanshiroSA_1_11/scripts/yabasanshiro.sh:30-34,\
|
||||
\ 49"
|
||||
- emulator: yabasanshiroSA1_5
|
||||
build: standalone
|
||||
kind: fixed_name
|
||||
names:
|
||||
- saturn_bios.bin
|
||||
case_insensitive: false
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: yabasanshiro1_5 -b
|
||||
fails_when_missing: false
|
||||
depends_on_option: hlebios != 1
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:346-348, packages/sx05re/emulators/yabasanshiroSA_1_5/scripts/yabasanshiro1_5.sh:29-33,\
|
||||
\ 47"
|
||||
- emulator: jzintv
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: jzintv -p
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:337-339, packages/sx05re/emulators/jzintv/scripts/jzintv.sh:49"
|
||||
- emulator: rice, glide64mk2
|
||||
build: standalone
|
||||
kind: names_searched
|
||||
names:
|
||||
- "N64DD IPLROM (J).n64"
|
||||
- IPL.n64
|
||||
case_insensitive: false
|
||||
search_dir: /storage/roms/bios/Mupen64plus
|
||||
passes_to: mupen64plus --dd-ipl-rom
|
||||
fails_when_missing: false
|
||||
depends_on_option: "-f \"${GAMEDIR}/${GAMENAME}.ndd\""
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:232-237, packages/sx05re/emulators/mupen64plussa/mupen64plussa-core/m64p.sh:16,\
|
||||
\ 65-68, 96-98"
|
||||
- emulator: HATARISA
|
||||
build: standalone
|
||||
kind: fixed_name
|
||||
names:
|
||||
- tos.img
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: "Atari-ST-default.cfg [ROM] szTosImageFileName (--configfile)"
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:172-174, packages/sx05re/emulators/hatarisa/config/conf/Atari-ST-default.cfg:200,\
|
||||
\ packages/sx05re/emulators/hatarisa/package.mk:39, packages/sx05re/emulators/hatarisa/scripts/hatari.start:36,\
|
||||
\ 66, 73"
|
||||
- emulator: AdvanceMame
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/bios/advmame/samples
|
||||
passes_to: advmame.rc dir_sample
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:213-215, packages/sx05re/emulators/advancemame/config/advmame.rc_oga:9,\
|
||||
\ packages/sx05re/emulators/advancemame/config/advmame.rc_gf:9, packages/sx05re/emulators/advancemame/config/advmame.rc:12,\
|
||||
\ packages/sx05re/emulators/advancemame/package.mk:45-49, packages/sx05re/emulators/advancemame/bin/advmame.sh:80"
|
||||
- emulator: SCUMMVMSA
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/.config/scummvm/extra
|
||||
passes_to: scummvm --extrapath
|
||||
fails_when_missing: false
|
||||
depends_on_option: /storage/.config/scummvm/extra exists
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:251-255, packages/sx05re/emulators/scummvmsa/bin/scummvm.start:38-39,\
|
||||
\ 45"
|
||||
- emulator: scummvm
|
||||
build: libretro
|
||||
kind: copies
|
||||
names:
|
||||
- scummvm.ini
|
||||
search_dir: /storage/.config/scummvm
|
||||
passes_to: /storage/roms/bios/scummvm.ini
|
||||
fails_when_missing: false
|
||||
depends_on_option: scummvm.start add
|
||||
source_ref: "packages/sx05re/emulators/scummvmsa/bin/scummvm.start:9, 49-51, 54"
|
||||
- emulator: dolphin
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
names: []
|
||||
search_dir: /storage/roms/dolphin
|
||||
passes_to: environment XDG_DATA_HOME
|
||||
fails_when_missing: false
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:270-272, packages/sx05re/emulators/dolphinSA/scripts/dolphin.sh:28"
|
||||
- emulator: fluidsynth
|
||||
build: standalone
|
||||
kind: fixed_name
|
||||
slot: fluidsynth
|
||||
names:
|
||||
- sc/SC-55.sf2
|
||||
case_insensitive: false
|
||||
search_dir: /storage/roms/bios
|
||||
passes_to: fluidsynth -is
|
||||
fails_when_missing: false
|
||||
depends_on_option: ra_midi_output = fluidsynth
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:123-125, packages/sx05re/emuelec/bin/emuelec-utils:54,\
|
||||
\ 66-67"
|
||||
- emulator: mt32d
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
slot: cm32l
|
||||
names: []
|
||||
search_dir: /roms/bios
|
||||
passes_to: mt32d -f
|
||||
fails_when_missing: false
|
||||
depends_on_option: ra_midi_output = cm32l
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:123-125, packages/sx05re/emuelec/bin/emuelec-utils:54,\
|
||||
\ 75-76"
|
||||
- emulator: mt32d
|
||||
build: standalone
|
||||
kind: passes_path
|
||||
slot: mt32
|
||||
names: []
|
||||
search_dir: /roms/bios
|
||||
passes_to: mt32d -f
|
||||
fails_when_missing: false
|
||||
depends_on_option: ra_midi_output = mt32
|
||||
source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:123-125, packages/sx05re/emuelec/bin/emuelec-utils:54,\
|
||||
\ 79-80"
|
||||
@@ -0,0 +1,755 @@
|
||||
"""EmuELEC's launcher layer, read from its packages at the pinned tag.
|
||||
|
||||
emuelecRunEmu.sh starts every game. A libretro core runs in RetroArch (or
|
||||
retrorun) with /storage/roms/bios as its system directory. A standalone
|
||||
emulator runs from the script the `case ${PLATFORM}` arm names for the core
|
||||
ES passes (`"${EMU}" = "flycastsa"`), and that script links, moves, copies
|
||||
or passes the file. Some packages ship the config the emulator reads
|
||||
(duckstation, hatari, advancemame, mame), others link a BIOS folder at
|
||||
install time (amiberry, timidity). The emulator of each record is the core
|
||||
name the dispatch tests, or the package that installs the link.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import posixpath
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
try:
|
||||
from .configgen_launchers import Launcher
|
||||
from .rocknix_launchers import fixed_paths
|
||||
from .shell_launchers import (
|
||||
LauncherPatternMissing,
|
||||
Link,
|
||||
RepoTree,
|
||||
Script,
|
||||
describe_keys,
|
||||
settings,
|
||||
source_ref,
|
||||
words,
|
||||
)
|
||||
except ImportError:
|
||||
from configgen_launchers import Launcher
|
||||
from rocknix_launchers import fixed_paths
|
||||
from shell_launchers import (
|
||||
LauncherPatternMissing,
|
||||
Link,
|
||||
RepoTree,
|
||||
Script,
|
||||
describe_keys,
|
||||
settings,
|
||||
source_ref,
|
||||
words,
|
||||
)
|
||||
|
||||
SX05RE = "packages/sx05re"
|
||||
BIN = f"{SX05RE}/emuelec/bin"
|
||||
RUN_EMU = f"{BIN}/emuelecRunEmu.sh"
|
||||
UTILS = f"{BIN}/emuelec-utils"
|
||||
EMULATORS = f"{SX05RE}/emulators"
|
||||
LIBRETRO = f"{SX05RE}/libretro"
|
||||
RETROARCH_MK = f"{SX05RE}/libretro_base/retroarch/package.mk"
|
||||
BIOS_ROOT = "/storage/roms/bios"
|
||||
|
||||
_EMU_TEST = re.compile(r'\$\{?EMU\}?"?\s*==?\s*"([^"]+)"')
|
||||
_ARM = re.compile(r'^\s*((?:"[^"]+"\|?)+)\)\s*$')
|
||||
_RUNTHIS = re.compile(r"""^\s*RUNTHIS\+?=(['"])(.*)\1\s*$""")
|
||||
_SCRIPT = re.compile(r"[\w./-]+\.(?:sh|start)$")
|
||||
_ADVMAME_INSTALL = re.compile(r"cp -r \$\{PKG_DIR\}/config/(advmame\.rc\w*) \S+/advmame\.rc$")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Dispatch:
|
||||
"""The cores emuelecRunEmu.sh hands to one script, and the lines that do it."""
|
||||
|
||||
emulators: list[str] = field(default_factory=list)
|
||||
lines: set[int] = field(default_factory=set)
|
||||
|
||||
|
||||
class Runs:
|
||||
"""emuelecRunEmu.sh, read once: which core starts which script."""
|
||||
|
||||
def __init__(self, tree: RepoTree) -> None:
|
||||
self.script = Script(RUN_EMU, tree.read(RUN_EMU))
|
||||
self.by_script: dict[str, Dispatch] = {}
|
||||
emu: tuple[str, int] | None = None
|
||||
arm: tuple[list[str], int] | None = None
|
||||
for number, line in self.script.code():
|
||||
if found := _ARM.match(line):
|
||||
arm = ([p.strip('"') for p in found.group(1).split("|") if p], number)
|
||||
emu = None
|
||||
continue
|
||||
if re.match(r"\s*(;;|esac\b)", line):
|
||||
arm, emu = None, None
|
||||
continue
|
||||
if found := _EMU_TEST.search(line):
|
||||
emu = (found.group(1).removesuffix("_libretro"), number)
|
||||
continue
|
||||
if re.match(r"\s*fi\b", line):
|
||||
emu = None
|
||||
continue
|
||||
run = _RUNTHIS.match(line)
|
||||
command = words(run.group(2) if run else line)
|
||||
name = next(
|
||||
(posixpath.basename(w.text) for w in command if _SCRIPT.fullmatch(w.text)), None
|
||||
)
|
||||
if name is None or (emu is None and arm is None):
|
||||
continue
|
||||
names, at = ([emu[0]], emu[1]) if emu else arm # type: ignore[misc]
|
||||
entry = self.by_script.setdefault(name, Dispatch())
|
||||
entry.emulators.extend(n for n in names if n not in entry.emulators)
|
||||
entry.lines |= {at, number}
|
||||
if not self.by_script:
|
||||
raise self.script.missing("script started for a core")
|
||||
|
||||
def of(self, name: str) -> Dispatch:
|
||||
if name not in self.by_script:
|
||||
raise self.script.missing(f"start of {name}")
|
||||
return self.by_script[name]
|
||||
|
||||
def emulator(self, name: str) -> str:
|
||||
return ", ".join(self.of(name).emulators)
|
||||
|
||||
def ref(self, name: str) -> tuple[str, set[int]]:
|
||||
return (RUN_EMU, set(self.of(name).lines))
|
||||
|
||||
|
||||
def _read(tree: RepoTree, path: str) -> Script:
|
||||
return Script(path, tree.read(path))
|
||||
|
||||
|
||||
def _under(path: str, root: str = BIOS_ROOT) -> bool:
|
||||
return path.rstrip("/") == root or path.startswith(root + "/")
|
||||
|
||||
|
||||
def retroarch(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""system_directory, written by sed into the retroarch.cfg the package installs."""
|
||||
mk = _read(tree, RETROARCH_MK)
|
||||
sed_line, sed = mk.find(
|
||||
r"s/# system_directory =/system_directory =((?:\\/|[^/])+)/", "system_directory sed"
|
||||
)
|
||||
run_line, _ = runs.script.find(
|
||||
r"RUNTHIS='\$\{RABIN\}.* -L /tmp/cores/\$\{EMU\}\.so --config \$\{RACONF\}",
|
||||
"RetroArch start with --config",
|
||||
)
|
||||
return [
|
||||
Launcher(
|
||||
emulator="retroarch",
|
||||
build="libretro",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref([(mk.path, {sed_line}), (RUN_EMU, {run_line})]),
|
||||
search_dir=sed.group(1).replace("\\/", "/"),
|
||||
passes_to="retroarch.cfg system_directory (--config)",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def retrorun(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
script = runs.script
|
||||
base_line, base = script.find(r'^\s*RUNTHIS="(\w+)"\s*$', "retrorun command")
|
||||
test_line, _ = script.find(
|
||||
rf'if \[\[ "\$\{{EMULATOR\}}" = "{base.group(1)}" \]\]', f"{base.group(1)} test"
|
||||
)
|
||||
run_line, run = script.find(r"RUNTHIS\+=.*\s(-d)\s+(\S+)\s", "retrorun -d", base_line)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=base.group(1),
|
||||
build="libretro",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref([(RUN_EMU, {test_line, base_line, run_line})]),
|
||||
search_dir=run.group(2),
|
||||
passes_to=f"{base.group(1)} {run.group(1)}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def freej2me(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""JAVA_HOME set for the core, and the jar its script copies beside the JDK."""
|
||||
script = runs.script
|
||||
test_line, test = script.find(r'if \[ \$\{EMU\} == "(\w+)_libretro" \]', "core test")
|
||||
call_line, call = script.find(r"^\s*(\S+\.sh)\s*$", "script call", test_line)
|
||||
java_line, java = script.find(r"^\s*(JAVA_HOME)='([^']+)'", "JAVA_HOME", test_line)
|
||||
export_line, _ = script.find(rf"^\s*export {java.group(1)}\s*$", "export", java_line)
|
||||
core = test.group(1)
|
||||
path = f"{LIBRETRO}/{core}/scripts/{call.group(1)}"
|
||||
started = _read(tree, path)
|
||||
guard_line, guard = started.find(r'if \[\[ -f "([^"]+)" \]\]; then$', "JDK version test")
|
||||
copy_line, copy = started.find(r"^\s*cp -rf (\S+) (\S+)$", "jar copy", guard_line)
|
||||
source = copy.group(1)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=core,
|
||||
build="libretro",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref(
|
||||
[(RUN_EMU, {test_line, call_line, java_line, export_line}), (path, {guard_line})]
|
||||
),
|
||||
search_dir=java.group(2),
|
||||
passes_to=f"environment {java.group(1)}",
|
||||
),
|
||||
Launcher(
|
||||
emulator=core,
|
||||
build="libretro",
|
||||
kind="copies",
|
||||
names=[posixpath.basename(source)],
|
||||
source_ref=source_ref(
|
||||
[(RUN_EMU, {test_line, call_line}), (path, {guard_line, copy_line})]
|
||||
),
|
||||
search_dir=posixpath.dirname(source),
|
||||
passes_to=copy.group(2),
|
||||
depends_on_option=f"{guard.group(1)} exists",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def pico8(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The folder the user drops in bios/ is moved where the binary runs from."""
|
||||
path = f"{BIN}/pico8.sh"
|
||||
script = _read(tree, path)
|
||||
guard_line, guard = script.find(r'if \[\[ -e "([^"]+)" \]\]; then$', "new version test")
|
||||
folder = guard.group(1)
|
||||
copy_line, copy = script.find(
|
||||
rf"^\s*cp -rf {re.escape(folder)}/\* (\S+)$", "copy of the folder", guard_line
|
||||
)
|
||||
target = copy.group(1)
|
||||
remove_line, _ = script.find(rf"^\s*rm -rf {re.escape(folder)}$", "removal", copy_line)
|
||||
mode_line, mode = script.find(
|
||||
rf"^\s*chmod \+x {re.escape(target)}/([^/\s]+)$", "executable bit", remove_line
|
||||
)
|
||||
binary = mode.group(1)
|
||||
starts = {
|
||||
n for n, line in script.code() if re.match(rf"\s*{re.escape(target)}/{binary} -", line)
|
||||
}
|
||||
if not starts:
|
||||
raise script.missing(f"start of {target}/{binary}")
|
||||
name = posixpath.basename(path)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="moves",
|
||||
names=[binary],
|
||||
source_ref=source_ref(
|
||||
[runs.ref(name), (path, {guard_line, copy_line, remove_line, mode_line, *starts})]
|
||||
),
|
||||
case_insensitive=False,
|
||||
search_dir=folder,
|
||||
passes_to=target,
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f"{folder} exists",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def link_record(emulator: str, script: Script, link: Link, extra: set[int]) -> Launcher:
|
||||
return Launcher(
|
||||
emulator=emulator,
|
||||
build="standalone",
|
||||
kind="links",
|
||||
names=list(link.names),
|
||||
source_ref=source_ref([(script.path, link.lines | extra)]),
|
||||
expands=link.expands or None,
|
||||
search_dir=link.bios_side,
|
||||
passes_to=f"symlink {link.link} -> {link.target}",
|
||||
fails_when_missing=False,
|
||||
)
|
||||
|
||||
|
||||
def _link_guard(script: Script, link: Link) -> set[int]:
|
||||
"""The `[ ! -L <link> ]` test the link waits for, when it is the one just above."""
|
||||
first = max(link.lines)
|
||||
tests = [
|
||||
number
|
||||
for number, line in script.code()
|
||||
if number < first
|
||||
and re.search(r"\[ ! -L ", line)
|
||||
and link.link in script.expand(line, number, set())
|
||||
]
|
||||
return {tests[-1]} if tests and first - tests[-1] <= 4 else set()
|
||||
|
||||
|
||||
# Start scripts whose ln -s commands put a BIOS folder where the emulator reads.
|
||||
LINK_SCRIPTS = (
|
||||
f"{EMULATORS}/flycastsa/scripts/flycast.sh",
|
||||
f"{EMULATORS}/flycast-dojo/scripts/flycastdojo.sh",
|
||||
)
|
||||
|
||||
# Packages whose package.mk links a BIOS folder into the installed tree.
|
||||
LINK_PACKAGES = (
|
||||
f"{EMULATORS}/amiberry",
|
||||
f"{EMULATORS}/amiberry-lite",
|
||||
f"{SX05RE}/tools/sound/timidity",
|
||||
)
|
||||
|
||||
|
||||
def links(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
records = []
|
||||
for path in LINK_SCRIPTS:
|
||||
script = _read(tree, path)
|
||||
found = script.links(BIOS_ROOT)
|
||||
if not found:
|
||||
raise script.missing(f"ln -s of a path under {BIOS_ROOT}")
|
||||
name = posixpath.basename(path)
|
||||
for link in found:
|
||||
record = link_record(runs.emulator(name), script, link, _link_guard(script, link))
|
||||
record.source_ref = f"{source_ref([runs.ref(name)])}, {record.source_ref}"
|
||||
records.append(record)
|
||||
for package in LINK_PACKAGES:
|
||||
script = _read(tree, f"{package}/package.mk")
|
||||
found = script.links(BIOS_ROOT, fixed={"INSTALL": ""})
|
||||
if not found:
|
||||
raise script.missing(f"ln -s of a path under {BIOS_ROOT}")
|
||||
records.extend(
|
||||
link_record(posixpath.basename(package), script, link, set()) for link in found
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def duckstation(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The settings.ini the package ships hands a directory and leaves the paths empty."""
|
||||
package = f"{EMULATORS}/duckstation"
|
||||
path = f"{package}/config/settings.ini"
|
||||
found = {(s.section, s.key): s for s in settings(tree.read(path))}
|
||||
folder_key = ("BIOS", "SearchDirectory")
|
||||
file_keys = (("BIOS", "PathNTSCU"), ("BIOS", "PathNTSCJ"), ("BIOS", "PathPAL"))
|
||||
if folder_key not in found:
|
||||
raise LauncherPatternMissing(path, "[BIOS] SearchDirectory")
|
||||
set_files = [k for k in file_keys if k in found and found[k].value]
|
||||
if set_files:
|
||||
raise LauncherPatternMissing(path, f"empty [BIOS] {set_files[0][1]}")
|
||||
mk = _read(tree, f"{package}/package.mk")
|
||||
mk_line, _ = mk.find(r"cp -rf \$\{PKG_DIR\}/config/\* ", "config install")
|
||||
script = _read(tree, f"{package}/scripts/duckstation.sh")
|
||||
run_line, _ = script.find(r'-settings "\$\{CONFIG_DIR\}/settings\.ini"', "-settings")
|
||||
name = posixpath.basename(script.path)
|
||||
lines = {found[k].line for k in (folder_key, *file_keys) if k in found}
|
||||
unset = describe_keys("", file_keys).strip()
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="scans_dir",
|
||||
names=[],
|
||||
source_ref=source_ref(
|
||||
[runs.ref(name), (path, lines), (mk.path, {mk_line}), (script.path, {run_line})]
|
||||
),
|
||||
search_dir=found[folder_key].value,
|
||||
passes_to=f"settings.ini [BIOS] SearchDirectory (-settings), {unset} unset",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
YABASANSHIRO_SCRIPTS = (
|
||||
f"{EMULATORS}/yabasanshiroSA_1_11/scripts/yabasanshiro.sh",
|
||||
f"{EMULATORS}/yabasanshiroSA_1_5/scripts/yabasanshiro1_5.sh",
|
||||
)
|
||||
|
||||
|
||||
def yabasanshiro(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The BIOS flag, set when the HLE option is off and the file exists."""
|
||||
records = []
|
||||
for path in YABASANSHIRO_SCRIPTS:
|
||||
script = _read(tree, path)
|
||||
option_line, option = script.find(r"^\s*(\w+)=\$\(get_ee_setting (\w+) ", "HLE option")
|
||||
test_line, test = script.find(
|
||||
rf'if \[ "\$\{{{option.group(1)}\}}" != (\w+) \]', "HLE option test", option_line
|
||||
)
|
||||
exists_line, exists = script.find(r'if \[ -e "([^"]+)" \]', "BIOS test", test_line)
|
||||
bios = exists.group(1)
|
||||
flag_line, flag = script.find(
|
||||
rf'^\s*(\w+)="(-\w) {re.escape(bios)}"$', "BIOS flag", exists_line
|
||||
)
|
||||
run_line, run = script.find(
|
||||
rf"\{{\s*(\S+) .*\$\{{{flag.group(1)}\}}", "start with the flag", flag_line
|
||||
)
|
||||
name = posixpath.basename(path)
|
||||
records.append(
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="fixed_name",
|
||||
names=[posixpath.basename(bios)],
|
||||
source_ref=source_ref(
|
||||
[
|
||||
runs.ref(name),
|
||||
(path, {option_line, test_line, exists_line, flag_line, run_line}),
|
||||
]
|
||||
),
|
||||
case_insensitive=False,
|
||||
search_dir=posixpath.dirname(bios),
|
||||
passes_to=f"{run.group(1)} {flag.group(2)}",
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f"{option.group(2)} != {test.group(1)}",
|
||||
)
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def jzintv(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
path = f"{EMULATORS}/jzintv/scripts/jzintv.sh"
|
||||
script = _read(tree, path)
|
||||
run_line, run = script.find(r"^\s*exec (\S+) .*\s(-p)\s+(\S+)\s", "start with -p")
|
||||
name = posixpath.basename(path)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref([runs.ref(name), (path, {run_line})]),
|
||||
search_dir=run.group(3).rstrip("/"),
|
||||
passes_to=f"{run.group(1)} {run.group(2)}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def mupen64plus(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The 64DD IPL names, tried in order, passed only beside a .ndd disk."""
|
||||
path = f"{EMULATORS}/mupen64plussa/mupen64plussa-core/m64p.sh"
|
||||
script = _read(tree, path)
|
||||
fallbacks = [
|
||||
(number, found)
|
||||
for number, line in script.code()
|
||||
if (found := re.search(r'\[ ! -f "\$\{(\w+)\}" \] && \1="([^"]+)"', line))
|
||||
]
|
||||
if not fallbacks:
|
||||
raise script.missing("fallback name test")
|
||||
var = fallbacks[0][1].group(1)
|
||||
first = script.value(var, fallbacks[0][0])
|
||||
if first is None:
|
||||
raise script.missing(f"assignment of {var}")
|
||||
used = {first.line, *(n for n, _ in fallbacks)}
|
||||
paths = [
|
||||
script.expand(v, n, used)
|
||||
for n, v in [(first.line, first.value), *((n, f.group(2)) for n, f in fallbacks)]
|
||||
]
|
||||
folders = {posixpath.dirname(p) for p in paths}
|
||||
if len(folders) != 1:
|
||||
raise script.missing(f"one folder for the {var} names")
|
||||
test_line, test = script.find(
|
||||
rf'if \[ -f "([^"]+)" \] && \[ -f "\$\{{{var}\}}" \]', "disk and IPL test"
|
||||
)
|
||||
passes = [
|
||||
(number, found)
|
||||
for number, line in script.code()
|
||||
if (found := re.search(rf'^\s*(\S+) .*(--[\w-]+) "\$\{{{var}\}}"', line))
|
||||
]
|
||||
if not passes:
|
||||
raise script.missing(f"flag that hands {var}")
|
||||
flags = sorted({f"{f.group(1)} {f.group(2)}" for _, f in passes})
|
||||
name = posixpath.basename(path)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="names_searched",
|
||||
names=[posixpath.basename(p) for p in paths],
|
||||
source_ref=source_ref(
|
||||
[runs.ref(name), (path, used | {test_line} | {n for n, _ in passes})]
|
||||
),
|
||||
case_insensitive=False,
|
||||
search_dir=folders.pop(),
|
||||
passes_to=", ".join(flags),
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f'-f "{test.group(1)}"',
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def hatari(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
package = f"{EMULATORS}/hatarisa"
|
||||
script = _read(tree, f"{package}/scripts/hatari.start")
|
||||
copy_line, copy = script.find(r'^\s*cp \S+/([\w.-]+\.cfg) "\$(\w+)"', "default config copy")
|
||||
run_lines = {
|
||||
n
|
||||
for n, line in script.code()
|
||||
if n > copy_line and f'--configfile "${copy.group(2)}"' in line
|
||||
}
|
||||
if not run_lines:
|
||||
raise script.missing(f"--configfile ${copy.group(2)}")
|
||||
path = f"{package}/config/conf/{copy.group(1)}"
|
||||
found = settings(tree.read(path))
|
||||
keys = [
|
||||
(s.section, s.key)
|
||||
for s in found
|
||||
if _under(s.value) and "." in posixpath.basename(s.value)
|
||||
]
|
||||
if not keys:
|
||||
raise LauncherPatternMissing(path, f"key naming a file under {BIOS_ROOT}")
|
||||
value, lines = fixed_paths(found, keys, path)
|
||||
mk = _read(tree, f"{package}/package.mk")
|
||||
mk_line, _ = mk.find(r"cp -R \$\{PKG_DIR\}/config/\* ", "config install")
|
||||
name = posixpath.basename(script.path)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="fixed_name",
|
||||
names=list(value.names),
|
||||
source_ref=source_ref(
|
||||
[runs.ref(name), (path, lines), (mk.path, {mk_line}),
|
||||
(script.path, {copy_line, *run_lines})]
|
||||
),
|
||||
search_dir=value.search_dir,
|
||||
passes_to=f"{describe_keys(copy.group(1), value.keys)} (--configfile)",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def advancemame(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""Folder keys of the advmame.rc package.mk installs, one per device branch."""
|
||||
package = f"{EMULATORS}/advancemame"
|
||||
mk = _read(tree, f"{package}/package.mk")
|
||||
installs = [
|
||||
(number, found.group(1))
|
||||
for number, line in mk.code()
|
||||
if (found := _ADVMAME_INSTALL.search(line))
|
||||
]
|
||||
if not installs:
|
||||
raise mk.missing("advmame.rc install")
|
||||
by_value: dict[tuple[tuple[str, str], ...], list[tuple[str, set[int], int]]] = {}
|
||||
for mk_line, file in installs:
|
||||
path = f"{package}/config/{file}"
|
||||
found = [s for s in settings(tree.read(path), separator=" ") if _under(s.value)]
|
||||
if not found:
|
||||
raise LauncherPatternMissing(path, f"key naming a folder under {BIOS_ROOT}")
|
||||
value = tuple((s.key, s.value) for s in found)
|
||||
by_value.setdefault(value, []).append((path, {s.line for s in found}, mk_line))
|
||||
if len(by_value) != 1:
|
||||
raise mk.missing("one set of BIOS folders for every device")
|
||||
((value, configs),) = by_value.items()
|
||||
script = _read(tree, f"{package}/bin/advmame.sh")
|
||||
run_line, _ = script.find(r"^\s*(?:\w+=\S+ )*advmame ", "advmame start")
|
||||
name = posixpath.basename(script.path)
|
||||
refs = [
|
||||
runs.ref(name),
|
||||
*((path, lines) for path, lines, _ in configs),
|
||||
(mk.path, {mk_line for _, _, mk_line in configs}),
|
||||
(script.path, {run_line}),
|
||||
]
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref(refs),
|
||||
search_dir=folder,
|
||||
passes_to=f"advmame.rc {key}",
|
||||
)
|
||||
for key, folder in value
|
||||
]
|
||||
|
||||
|
||||
def mame(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The files mame.sh copies into bios/mame, and the folders mame.ini hands the core."""
|
||||
package = f"{LIBRETRO}/mame"
|
||||
script = _read(tree, f"{package}/scripts/mame.sh")
|
||||
name = posixpath.basename(script.path)
|
||||
emulator = runs.emulator(name)
|
||||
records = []
|
||||
guard: tuple[int, str] | None = None
|
||||
for number, line in script.code():
|
||||
if found := re.search(r'if \[ ! -f "([^"]+)" \]', line):
|
||||
guard = (number, found.group(1))
|
||||
continue
|
||||
copy = re.match(r'\s*cp -rf "([^"]+)/"(\S+) "([^"]+)"\s*$', line)
|
||||
if copy is None:
|
||||
continue
|
||||
if guard is None:
|
||||
raise script.missing(f"test before the copy at line {number}")
|
||||
if not _under(copy.group(3)):
|
||||
continue
|
||||
records.append(
|
||||
Launcher(
|
||||
emulator=emulator,
|
||||
build="libretro",
|
||||
kind="copies",
|
||||
names=[copy.group(2)],
|
||||
source_ref=source_ref([runs.ref(name), (script.path, {guard[0], number})]),
|
||||
search_dir=copy.group(1),
|
||||
passes_to=copy.group(3),
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f"{guard[1]} absent",
|
||||
)
|
||||
)
|
||||
if not records:
|
||||
raise script.missing(f"copy into {BIOS_ROOT}")
|
||||
ini = next((r for r in records if r.names == ["mame*"]), None)
|
||||
if ini is None:
|
||||
raise script.missing("copy of mame.ini")
|
||||
option_path = f"{package}/config/MAME/MAME.opt"
|
||||
option = next(
|
||||
(s for s in settings(tree.read(option_path)) if s.key == "mame_read_config"), None
|
||||
)
|
||||
if option is None:
|
||||
raise LauncherPatternMissing(option_path, "mame_read_config")
|
||||
ini_path = f"{package}/config/ini/mame.ini"
|
||||
# MAME reads from the folders its *path options name and writes to its
|
||||
# *_directory ones.
|
||||
folders = [
|
||||
(s, [p for p in s.value.split(";") if _under(p)])
|
||||
for s in settings(tree.read(ini_path), separator=" ")
|
||||
if s.key.endswith("path")
|
||||
]
|
||||
folders = [(s, parts) for s, parts in folders if parts]
|
||||
if not folders:
|
||||
raise LauncherPatternMissing(ini_path, f"path under {BIOS_ROOT}")
|
||||
for setting, parts in folders:
|
||||
for part in parts:
|
||||
records.append(
|
||||
Launcher(
|
||||
emulator=emulator,
|
||||
build="libretro",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref(
|
||||
[(ini_path, {setting.line}), (option_path, {option.line})]
|
||||
) + f", {ini.source_ref}",
|
||||
search_dir=part.rstrip("/"),
|
||||
passes_to=f"mame.ini {setting.key}",
|
||||
depends_on_option=f"mame_read_config = {option.value}",
|
||||
)
|
||||
)
|
||||
return records
|
||||
|
||||
|
||||
def scummvm(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""--extrapath for the standalone build, and the scummvm.ini copied for the core."""
|
||||
path = f"{EMULATORS}/scummvmsa/bin/scummvm.start"
|
||||
script = _read(tree, path)
|
||||
name = posixpath.basename(path)
|
||||
test_line, test = script.find(r'^\s*if \[ -d "([^"]+)" \]; then\s*$', "extra folder test")
|
||||
set_line, set_ = script.find(r'^\s*(\w+)="(--[\w-]+)=([^"]+)"', "extra path", test_line)
|
||||
run_line, run = script.find(rf"^\s*(\S+) .*\${set_.group(1)}\b", "start with it", set_line)
|
||||
core_line, core = script.find(r"-L /tmp/cores/(\w+)_libretro\.so", "libretro start")
|
||||
arm_line, arm = script.find(r'^\s*"(\w+)"\)', "setup arm", core_line)
|
||||
copy_line, copy = script.find(r"^\s*cp (\S+) (\S+)$", "copy into the BIOS folder", arm_line)
|
||||
used = {copy_line}
|
||||
source = script.expand(copy.group(1), copy_line, used)
|
||||
target = script.expand(copy.group(2), copy_line, used)
|
||||
if not _under(target):
|
||||
raise script.missing(f"copy into {BIOS_ROOT}")
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref([runs.ref(name), (path, {test_line, set_line, run_line})]),
|
||||
search_dir=set_.group(3),
|
||||
passes_to=f"{run.group(1)} {set_.group(2)}",
|
||||
depends_on_option=f"{test.group(1)} exists",
|
||||
),
|
||||
Launcher(
|
||||
emulator=core.group(1),
|
||||
build="libretro",
|
||||
kind="copies",
|
||||
names=[posixpath.basename(source)],
|
||||
source_ref=source_ref([(path, {core_line, arm_line} | used)]),
|
||||
search_dir=posixpath.dirname(source),
|
||||
passes_to=target,
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f"{name} {arm.group(1)}",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def dolphin(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
path = f"{EMULATORS}/dolphinSA/scripts/dolphin.sh"
|
||||
script = _read(tree, path)
|
||||
run_line, run = script.find(r"\b(XDG_DATA_HOME)=(\S+) .*\bdolphin-emu", "XDG_DATA_HOME start")
|
||||
name = posixpath.basename(path)
|
||||
return [
|
||||
Launcher(
|
||||
emulator=runs.emulator(name),
|
||||
build="standalone",
|
||||
kind="passes_path",
|
||||
names=[],
|
||||
source_ref=source_ref([runs.ref(name), (path, {run_line})]),
|
||||
search_dir=run.group(2),
|
||||
passes_to=f"environment {run.group(1)}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def midi(tree: RepoTree, runs: Runs) -> list[Launcher]:
|
||||
"""The MIDI synth set_midi_source starts for the ra_midi_output setting."""
|
||||
script = runs.script
|
||||
setting_line, setting = script.find(
|
||||
r'^\s*(\w+)=\$\(get_ee_setting "(\w*midi\w*)" ', "MIDI setting"
|
||||
)
|
||||
call_line, _ = script.find(
|
||||
rf'set_midi_source "\$\{{{setting.group(1)}\}}"', "set_midi_source call", setting_line
|
||||
)
|
||||
utils = _read(tree, UTILS)
|
||||
head_line, _ = utils.find(r"^\s*function set_midi_source\(\)", "set_midi_source")
|
||||
end_line, _ = utils.find(r"^\s*esac\b", "end of the source case", head_line)
|
||||
records = []
|
||||
arm: tuple[int, str] | None = None
|
||||
for number, line in utils.code():
|
||||
if number <= head_line or number >= end_line:
|
||||
continue
|
||||
if found := re.match(r"\s*([\w-]+)\)\s*$", line):
|
||||
arm = (number, found.group(1))
|
||||
continue
|
||||
command = [w.text for w in words(line)]
|
||||
paths = [
|
||||
(command[i - 1], value)
|
||||
for i, value in enumerate(command)
|
||||
if i and command[i - 1].startswith("-") and re.search(r"(^|/)bios(/|$)", value)
|
||||
]
|
||||
if not paths or arm is None:
|
||||
continue
|
||||
((flag, value),) = paths
|
||||
is_file = "." in posixpath.basename(value) and _under(value)
|
||||
records.append(
|
||||
Launcher(
|
||||
emulator=command[0],
|
||||
build="standalone",
|
||||
kind="fixed_name" if is_file else "passes_path",
|
||||
names=[posixpath.relpath(value, BIOS_ROOT)] if is_file else [],
|
||||
source_ref=source_ref(
|
||||
[(RUN_EMU, {setting_line, call_line}), (UTILS, {head_line, arm[0], number})]
|
||||
),
|
||||
slot=arm[1],
|
||||
case_insensitive=False if is_file else None,
|
||||
search_dir=BIOS_ROOT if is_file else value.rstrip("/"),
|
||||
passes_to=f"{command[0]} {flag}",
|
||||
fails_when_missing=False,
|
||||
depends_on_option=f"{setting.group(2)} = {arm[1]}",
|
||||
)
|
||||
)
|
||||
if not records:
|
||||
raise utils.missing("MIDI synth started with a BIOS path")
|
||||
return records
|
||||
|
||||
|
||||
EXTRACTORS: tuple[Callable[[RepoTree, Runs], list[Launcher]], ...] = (
|
||||
retroarch,
|
||||
retrorun,
|
||||
freej2me,
|
||||
mame,
|
||||
pico8,
|
||||
links,
|
||||
duckstation,
|
||||
yabasanshiro,
|
||||
jzintv,
|
||||
mupen64plus,
|
||||
hatari,
|
||||
advancemame,
|
||||
scummvm,
|
||||
dolphin,
|
||||
midi,
|
||||
)
|
||||
|
||||
|
||||
def extract_launchers(tree: RepoTree) -> list[dict]:
|
||||
"""Every launcher record the extractors find, in extractor order."""
|
||||
runs = Runs(tree)
|
||||
return [record.entry() for extractor in EXTRACTORS for record in extractor(tree, runs)]
|
||||
@@ -43,6 +43,7 @@ from .batocera_scraper import (
|
||||
platform_systems,
|
||||
requirements_from_systems,
|
||||
)
|
||||
from .emuelec_launchers import extract_launchers
|
||||
from .es_emulators import (
|
||||
SystemEmulators,
|
||||
attach_system_cores,
|
||||
@@ -50,6 +51,7 @@ from .es_emulators import (
|
||||
json_systems,
|
||||
system_cores,
|
||||
)
|
||||
from .shell_launchers import RepoTree
|
||||
|
||||
PLATFORM_NAME = "emuelec"
|
||||
|
||||
@@ -120,6 +122,14 @@ BUILDS_OF = {
|
||||
PORTS_SYSTEM = "ports"
|
||||
|
||||
|
||||
def launcher_revision(url: str) -> str:
|
||||
"""The tag or commit a batocera-systems URL is read at."""
|
||||
found = re.search(rf"/{REPO}/([^/]+)/packages/", url)
|
||||
if found is None:
|
||||
raise ValueError(f"{url} names no {REPO} revision")
|
||||
return found.group(1)
|
||||
|
||||
|
||||
def port_engines(text: str) -> list[tuple[str, str, str | None, bool]]:
|
||||
"""(port, engine, check_bios key, libretro?) for each entry of ports.yaml.
|
||||
|
||||
@@ -215,6 +225,10 @@ class Scraper(BaseScraper):
|
||||
"standalone_cores": standalone,
|
||||
"dual_mode_cores": dual,
|
||||
"systems": systems,
|
||||
# Read at the revision the BIOS list comes from.
|
||||
"launchers": extract_launchers(
|
||||
RepoTree.github(REPO, launcher_revision(self.url))
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,655 @@
|
||||
"""EmuELEC's launcher layer read from its packages.
|
||||
|
||||
Each fixture is a cut-down copy of the lines an extractor reads at EmuELEC
|
||||
v4.8 (02b45fe53b85c45c75dbad8e44a648092dec2e72). The records must name the
|
||||
core emuelecRunEmu.sh dispatches, carry the names in the order the code
|
||||
tries them, point at the fixture's own lines, and a construct that is gone
|
||||
must stop the scrape.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import tests # noqa: F401 offline, scratch under tmp/, the suite's own cache
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
from scraper import emuelec_launchers as el # noqa: E402
|
||||
from scraper.emuelec_scraper import launcher_revision # noqa: E402
|
||||
from scraper.shell_launchers import LauncherPatternMissing, RepoTree # noqa: E402
|
||||
|
||||
EMU = el.EMULATORS
|
||||
|
||||
|
||||
def text(block: str) -> str:
|
||||
return textwrap.dedent(block).lstrip("\n")
|
||||
|
||||
|
||||
RUN_EMU = text(
|
||||
"""
|
||||
if [[ "${EMULATOR}" = "libretro" ]]; then
|
||||
EMU="${CORE}_libretro"
|
||||
fi
|
||||
if [[ "${EMULATOR}" = "retrorun" ]]; then
|
||||
EMU="${CORE}_libretro"
|
||||
fi
|
||||
MIDI_OUTPUT=$(get_ee_setting "ra_midi_output" "${PLATFORM}" "${BASEROMNAME}")
|
||||
if [[ ! -z "${MIDI_OUTPUT}" ]]; then
|
||||
emuelec-utils set_midi_source "${MIDI_OUTPUT}" "${EMULATOR}"
|
||||
fi
|
||||
if [ ${EMU} == "freej2me_libretro" ]; then
|
||||
freej2me.sh
|
||||
|
||||
JAVA_HOME='/storage/roms/bios/jdk'
|
||||
export JAVA_HOME
|
||||
fi
|
||||
case ${PLATFORM} in
|
||||
"atarist")
|
||||
if [ "${EMU}" = "HATARISA" ]; then
|
||||
RUNTHIS='${TBASH} hatari.start "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"dreamcast"|"naomi")
|
||||
if [ "${EMU}" = "flycastsa" ]; then
|
||||
RUNTHIS='${TBASH} flycast.sh "${ROMNAME}"'
|
||||
elif [ "${EMU}" = "flycast_dojo" ]; then
|
||||
RUNTHIS='flycastdojo.sh "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"psx")
|
||||
if [ "${EMU}" = "duckstation" ]; then
|
||||
RUNTHIS='${TBASH} duckstation.sh "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"mame")
|
||||
if [ "${EMU}" = "AdvanceMame" ]; then
|
||||
RUNTHIS='${TBASH} advmame.sh "${PLATFORM}" "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"n64")
|
||||
if [ "${EMU}" = "rice" ]; then
|
||||
RUNTHIS='${TBASH} m64p.sh "${ROMNAME}"'
|
||||
elif [ "${EMU}" = "glide64mk2" ]; then
|
||||
RUNTHIS='${TBASH} m64p.sh "${ROMNAME}" m64p_gl64mk2'
|
||||
fi
|
||||
;;
|
||||
"scummvm")
|
||||
if [ "${EMU}" = "SCUMMVMSA" ]; then
|
||||
RUNTHIS='${TBASH} scummvm.start sa "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"wii"|"gamecube")
|
||||
if [ "${EMU}" = "dolphin" ]; then
|
||||
RUNTHIS='${TBASH} dolphin.sh "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"pico8")
|
||||
RUNTHIS='${TBASH} pico8.sh "${ROMNAME}"'
|
||||
;;
|
||||
"intellivision")
|
||||
if [ "${EMU}" = "jzintv" ]; then
|
||||
RUNTHIS='jzintv.sh "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
"saturn")
|
||||
if [ "${EMU}" = "yabasanshiroSA" ]; then
|
||||
RUNTHIS='yabasanshiro.sh "${ROMNAME}"'
|
||||
elif [ "${EMU}" = "yabasanshiroSA1_5" ]; then
|
||||
RUNTHIS='yabasanshiro1_5.sh "${ROMNAME}"'
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case ${PLATFORM} in
|
||||
"arcade"|"mame")
|
||||
if [ "$EMU" = "mame_libretro" ]; then
|
||||
mame.sh
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
RUNTHIS='${RABIN} ${VERBOSE} ${RA_ARGS} -L /tmp/cores/${EMU}.so --config ${RACONF} "${ROMNAME}"'
|
||||
RUNTHIS="retrorun"
|
||||
RUNTHIS+=' ${CMD_ROTATE} --triggers -g -d /storage/roms/bios /tmp/cores/${EMU}.so "${ROMNAME}"'
|
||||
"""
|
||||
)
|
||||
|
||||
UTILS = text(
|
||||
"""
|
||||
function set_midi_source()
|
||||
{
|
||||
case ${midi_source} in
|
||||
fluidsynth)
|
||||
fluidsynth -is /storage/roms/bios/sc/SC-55.sf2 &
|
||||
;;
|
||||
timidity)
|
||||
timidity -iA --module 1 &
|
||||
;;
|
||||
mt32)
|
||||
mt32d -o 2 -i 24 -f /roms/bios &
|
||||
;;
|
||||
esac
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
RETROARCH_MK = text(
|
||||
"""
|
||||
sed -i -e "s/# savestate_directory =/savestate_directory =\\/storage\\/roms\\/savestates/" ${INSTALL}/etc/retroarch.cfg
|
||||
sed -i -e "s/# system_directory =/system_directory =\\/storage\\/roms\\/bios/" ${INSTALL}/etc/retroarch.cfg
|
||||
"""
|
||||
)
|
||||
|
||||
FREEJ2ME_SH = text(
|
||||
"""
|
||||
install_java
|
||||
|
||||
if [[ -f "/storage/roms/bios/jdk/eeversion" ]]; then
|
||||
cp -rf /usr/lib/libretro/freej2me-lr.jar ${HOME}/roms/bios
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
PICO8_SH = text(
|
||||
"""
|
||||
if [[ -e "/storage/roms/bios/pico-8" ]]; then
|
||||
mkdir -p /emuelec/bin/pico-8
|
||||
cp -rf /storage/roms/bios/pico-8/* /emuelec/bin/pico-8
|
||||
rm -rf /storage/roms/bios/pico-8
|
||||
chmod +x /emuelec/bin/pico-8/pico8_dyn
|
||||
fi
|
||||
if [[ "${CART}" == *"/splore"* ]]; then
|
||||
/emuelec/bin/pico-8/pico8_dyn -splore -home /emuelec/configs/pico-8
|
||||
else
|
||||
/emuelec/bin/pico-8/pico8_dyn -run "${CART}" -home /emuelec/configs/pico-8
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
FLYCAST_SH = text(
|
||||
"""
|
||||
if [ ! -L "/storage/.local/share/flycast" ]; then
|
||||
mkdir -p "/storage/roms/bios/dc"
|
||||
rm -rf "/storage/.local/share/flycast"
|
||||
ln -sf "/storage/roms/bios/dc" "/storage/.local/share/flycast"
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
FLYCASTDOJO_SH = text(
|
||||
"""
|
||||
LOCAL_DATA="/storage/roms/bios/dc_dojo"
|
||||
LOCAL_HOME="/storage/.local/share/flycast-dojo"
|
||||
if [ ! -L "${LOCAL_HOME}" ]; then
|
||||
rm -rf "${LOCAL_HOME}"
|
||||
ln -sf "${LOCAL_DATA}" "${LOCAL_HOME}"
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
AMIBERRY_MK = text(
|
||||
"""
|
||||
cp -a ${PKG_DIR}/config/* ${INSTALL}/usr/config/amiberry/
|
||||
ln -s /storage/roms/bios ${INSTALL}/usr/config/amiberry/kickstarts
|
||||
"""
|
||||
)
|
||||
|
||||
TIMIDITY_MK = text(
|
||||
"""
|
||||
ln -sf /storage/roms/bios/timidity/freepats ${INSTALL}/usr/config/timidity/freepats
|
||||
ln -sf /storage/.config/timidity/freepats.cfg ${INSTALL}/etc/timidity/freepats.cfg
|
||||
"""
|
||||
)
|
||||
|
||||
DUCKSTATION_INI = text(
|
||||
"""
|
||||
[BIOS]
|
||||
SearchDirectory = /storage/roms/bios
|
||||
PathNTSCU =
|
||||
PathNTSCJ =
|
||||
PathPAL =
|
||||
"""
|
||||
)
|
||||
|
||||
DUCKSTATION_MK = text(
|
||||
"""
|
||||
cp -rf ${PKG_DIR}/config/* ${INSTALL}/usr/config/emuelec/configs/duckstation
|
||||
"""
|
||||
)
|
||||
|
||||
DUCKSTATION_SH = text(
|
||||
"""
|
||||
CONFIG_DIR="/emuelec/configs/duckstation"
|
||||
duckstation-nogui -fullscreen -settings "${CONFIG_DIR}/settings.ini" -- "${1}"
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def yabasanshiro_sh(program: str) -> str:
|
||||
return text(
|
||||
f"""
|
||||
BIOS=""
|
||||
HLEBIOS=$(get_ee_setting hlebios saturn "${{ROMNAME}}")
|
||||
if [ "${{HLEBIOS}}" != 1 ]; then
|
||||
if [ -e "/storage/roms/bios/saturn_bios.bin" ]; then
|
||||
BIOS="-b /storage/roms/bios/saturn_bios.bin"
|
||||
else
|
||||
BIOS=""
|
||||
fi
|
||||
fi
|
||||
{{ {program} -r 2 -i "${{1}}" ${{BIOS}}; }} > /emuelec/logs/emuelec.log 2>&1
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
JZINTV_SH = text(
|
||||
"""
|
||||
exec jzintv -f1 -z "$RES" -p /storage/roms/bios/ "$ROM" $KBD_OPT
|
||||
"""
|
||||
)
|
||||
|
||||
M64P_SH = text(
|
||||
"""
|
||||
BIOSDIR="/storage/roms/bios/Mupen64plus"
|
||||
IPLROM="${BIOSDIR}/N64DD IPLROM (J).n64"
|
||||
[ ! -f "${IPLROM}" ] && IPLROM="${BIOSDIR}/IPL.n64"
|
||||
if [ -f "${GAMEDIR}/${GAMENAME}.ndd" ] && [ -f "${IPLROM}" ]; then
|
||||
RENMODE="1"
|
||||
else
|
||||
IPLROM=""
|
||||
fi
|
||||
mupen64plus --fullscreen --dd-ipl-rom "${IPLROM}" --dd-disk "${DDDISK}" "${FILE}" ;;
|
||||
"""
|
||||
)
|
||||
|
||||
HATARI_START = text(
|
||||
"""
|
||||
HATARI_TMP_CONFIG="$HATARI_TMP_DIR"/.hatari-temp.cfg
|
||||
cp $HATARI_CONFIG_DIR/Atari-ST-default.cfg "$HATARI_TMP_CONFIG"
|
||||
hatari --configfile "$HATARI_TMP_CONFIG" --disk-a "$1"
|
||||
"""
|
||||
)
|
||||
|
||||
HATARI_CFG = text(
|
||||
"""
|
||||
[HardDisk]
|
||||
szHardDiskDirectory = /storage/.hatari
|
||||
|
||||
[ROM]
|
||||
szTosImageFileName = /storage/roms/bios/tos.img
|
||||
szCartridgeImageFileName =
|
||||
"""
|
||||
)
|
||||
|
||||
HATARI_MK = text(
|
||||
"""
|
||||
cp -R ${PKG_DIR}/config/* ${INSTALL}/usr/config/hatari
|
||||
"""
|
||||
)
|
||||
|
||||
ADVMAME_MK = text(
|
||||
"""
|
||||
if [ "${DEVICE}" == "OdroidGoAdvance" ]; then
|
||||
cp -r ${PKG_DIR}/config/advmame.rc_oga ${INSTALL}/usr/config/emuelec/configs/advmame/advmame.rc
|
||||
else
|
||||
cp -r ${PKG_DIR}/config/advmame.rc ${INSTALL}/usr/config/emuelec/configs/advmame/advmame.rc
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
ADVMAME_RC = text(
|
||||
"""
|
||||
dir_rom /storage/roms/mame
|
||||
dir_sample /storage/roms/bios/advmame/samples
|
||||
"""
|
||||
)
|
||||
|
||||
ADVMAME_SH = text(
|
||||
"""
|
||||
SDL_AUDIODRIVER=alsa advmame ${ARG} -quiet
|
||||
"""
|
||||
)
|
||||
|
||||
MAME_SH = text(
|
||||
"""
|
||||
if [ ! -f "/storage/roms/bios/mame/hash/fmtowns_cd.xml" ]; then
|
||||
mkdir -p /storage/roms/bios/mame/hash
|
||||
cp -rf "/usr/config/emuelec/configs/mame/hash/"fm* "/storage/roms/bios/mame/hash"
|
||||
fi
|
||||
if [ ! -f "/storage/roms/bios/mame/ini/mame.ini" ]; then
|
||||
mkdir -p /storage/roms/bios/mame/ini
|
||||
cp -rf "/usr/config/emuelec/configs/mame/ini/"mame* "/storage/roms/bios/mame/ini"
|
||||
fi
|
||||
if [ ! -f "/storage/.config/retroarch/config/MAME/MAME.opt" ]; then
|
||||
cp -rf "/usr/config/emuelec/configs/mame/MAME/"MAME* "/storage/.config/retroarch/config/MAME"
|
||||
fi
|
||||
"""
|
||||
)
|
||||
|
||||
MAME_INI = text(
|
||||
"""
|
||||
rompath /storage/roms/bios/mame/bios;/storage/roms/mame;/storage/roms/arcade
|
||||
samplepath /storage/roms/bios/mame/samples
|
||||
cfg_directory /storage/roms/bios/mame/mame/cfg
|
||||
"""
|
||||
)
|
||||
|
||||
MAME_OPT = text(
|
||||
"""
|
||||
mame_boot_to_bios = "disabled"
|
||||
mame_read_config = "enabled"
|
||||
"""
|
||||
)
|
||||
|
||||
SCUMMVM_START = text(
|
||||
"""
|
||||
CONFIG_DIR="/storage/.config/scummvm"
|
||||
if [ -d "/storage/.config/scummvm/extra" ]; then
|
||||
EXTRA="--extrapath=/storage/.config/scummvm/extra"
|
||||
fi
|
||||
case $1 in
|
||||
"sa")
|
||||
scummvm --fullscreen --joystick=0 $EXTRA "${GAME}"
|
||||
;;
|
||||
"libretro")
|
||||
retroarch -L /tmp/cores/scummvm_libretro.so --config ${RATMPCONF} "${GAME}"
|
||||
;;
|
||||
"add")
|
||||
scummvm --add --path="/storage/roms/scummvm" --recursive
|
||||
cp ${CONFIG_DIR}/scummvm.ini /storage/roms/bios/scummvm.ini
|
||||
;;
|
||||
esac
|
||||
"""
|
||||
)
|
||||
|
||||
DOLPHIN_SH = text(
|
||||
"""
|
||||
XDG_CONFIG_HOME=/emuelec/configs XDG_DATA_HOME=/storage/roms/dolphin /usr/bin/dolphin-emu-nogui -p fbdev "${1}"
|
||||
"""
|
||||
)
|
||||
|
||||
FILES = {
|
||||
el.RUN_EMU: RUN_EMU,
|
||||
el.UTILS: UTILS,
|
||||
el.RETROARCH_MK: RETROARCH_MK,
|
||||
f"{el.LIBRETRO}/freej2me/scripts/freej2me.sh": FREEJ2ME_SH,
|
||||
f"{el.BIN}/pico8.sh": PICO8_SH,
|
||||
f"{EMU}/flycastsa/scripts/flycast.sh": FLYCAST_SH,
|
||||
f"{EMU}/flycast-dojo/scripts/flycastdojo.sh": FLYCASTDOJO_SH,
|
||||
f"{EMU}/amiberry/package.mk": AMIBERRY_MK,
|
||||
f"{EMU}/amiberry-lite/package.mk": AMIBERRY_MK.replace("amiberry/", "amiberry-lite/"),
|
||||
f"{el.SX05RE}/tools/sound/timidity/package.mk": TIMIDITY_MK,
|
||||
f"{EMU}/duckstation/config/settings.ini": DUCKSTATION_INI,
|
||||
f"{EMU}/duckstation/package.mk": DUCKSTATION_MK,
|
||||
f"{EMU}/duckstation/scripts/duckstation.sh": DUCKSTATION_SH,
|
||||
el.YABASANSHIRO_SCRIPTS[0]: yabasanshiro_sh("yabasanshiro"),
|
||||
el.YABASANSHIRO_SCRIPTS[1]: yabasanshiro_sh("yabasanshiro1_5"),
|
||||
f"{EMU}/jzintv/scripts/jzintv.sh": JZINTV_SH,
|
||||
f"{EMU}/mupen64plussa/mupen64plussa-core/m64p.sh": M64P_SH,
|
||||
f"{EMU}/hatarisa/scripts/hatari.start": HATARI_START,
|
||||
f"{EMU}/hatarisa/config/conf/Atari-ST-default.cfg": HATARI_CFG,
|
||||
f"{EMU}/hatarisa/package.mk": HATARI_MK,
|
||||
f"{EMU}/advancemame/package.mk": ADVMAME_MK,
|
||||
f"{EMU}/advancemame/config/advmame.rc": ADVMAME_RC,
|
||||
f"{EMU}/advancemame/config/advmame.rc_oga": ADVMAME_RC,
|
||||
f"{EMU}/advancemame/bin/advmame.sh": ADVMAME_SH,
|
||||
f"{el.LIBRETRO}/mame/scripts/mame.sh": MAME_SH,
|
||||
f"{el.LIBRETRO}/mame/config/ini/mame.ini": MAME_INI,
|
||||
f"{el.LIBRETRO}/mame/config/MAME/MAME.opt": MAME_OPT,
|
||||
f"{EMU}/scummvmsa/bin/scummvm.start": SCUMMVM_START,
|
||||
f"{EMU}/dolphinSA/scripts/dolphin.sh": DOLPHIN_SH,
|
||||
}
|
||||
|
||||
|
||||
def tree(**changes: str | None) -> RepoTree:
|
||||
files = dict(FILES)
|
||||
for path, content in changes.items():
|
||||
if content is None:
|
||||
files.pop(path, None)
|
||||
else:
|
||||
files[path] = content
|
||||
return RepoTree.from_files(files)
|
||||
|
||||
|
||||
def records(extractor, **changes: str | None) -> list[dict]:
|
||||
built = tree(**changes)
|
||||
return [r.entry() for r in extractor(built, el.Runs(built))]
|
||||
|
||||
|
||||
def ref(path: str, lines: str) -> str:
|
||||
return f"{path}:{lines}"
|
||||
|
||||
|
||||
class Dispatch(unittest.TestCase):
|
||||
def test_each_script_is_named_after_the_core_that_starts_it(self):
|
||||
runs = el.Runs(tree())
|
||||
self.assertEqual(runs.emulator("flycast.sh"), "flycastsa")
|
||||
self.assertEqual(runs.emulator("flycastdojo.sh"), "flycast_dojo")
|
||||
self.assertEqual(runs.emulator("m64p.sh"), "rice, glide64mk2")
|
||||
self.assertEqual(runs.emulator("mame.sh"), "mame")
|
||||
self.assertEqual(runs.emulator("freej2me.sh"), "freej2me")
|
||||
|
||||
def test_an_arm_without_a_core_test_names_its_platform(self):
|
||||
self.assertEqual(el.Runs(tree()).emulator("pico8.sh"), "pico8")
|
||||
|
||||
def test_a_script_no_longer_started_raises(self):
|
||||
runs = el.Runs(tree())
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
runs.of("gone.sh")
|
||||
|
||||
def test_a_dispatch_without_any_script_raises(self):
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
el.Runs(tree(**{el.RUN_EMU: "echo nothing\n"}))
|
||||
|
||||
|
||||
class LibretroRecords(unittest.TestCase):
|
||||
def test_retroarch_system_directory_comes_from_the_sed(self):
|
||||
(record,) = records(el.retroarch)
|
||||
self.assertEqual(record["search_dir"], "/storage/roms/bios")
|
||||
self.assertEqual(record["kind"], "passes_path")
|
||||
self.assertEqual(
|
||||
record["source_ref"], f"{ref(el.RETROARCH_MK, '2')}, {ref(el.RUN_EMU, '80')}"
|
||||
)
|
||||
|
||||
def test_retrorun_is_handed_the_folder_with_d(self):
|
||||
(record,) = records(el.retrorun)
|
||||
self.assertEqual(
|
||||
(record["emulator"], record["search_dir"], record["passes_to"]),
|
||||
("retrorun", "/storage/roms/bios", "retrorun -d"),
|
||||
)
|
||||
self.assertEqual(record["source_ref"], ref(el.RUN_EMU, "4, 81-82"))
|
||||
|
||||
def test_freej2me_gets_java_home_and_its_jar_copied(self):
|
||||
java, jar = records(el.freej2me)
|
||||
self.assertEqual(java["passes_to"], "environment JAVA_HOME")
|
||||
self.assertEqual(java["search_dir"], "/storage/roms/bios/jdk")
|
||||
self.assertEqual(jar["kind"], "copies")
|
||||
self.assertEqual(jar["names"], ["freej2me-lr.jar"])
|
||||
self.assertEqual(jar["passes_to"], "${HOME}/roms/bios")
|
||||
self.assertEqual(jar["depends_on_option"], "/storage/roms/bios/jdk/eeversion exists")
|
||||
|
||||
def test_mame_copies_its_own_files_and_reads_only_path_options(self):
|
||||
found = records(el.mame)
|
||||
copies = [r for r in found if r["kind"] == "copies"]
|
||||
self.assertEqual([r["names"] for r in copies], [["fm*"], ["mame*"]])
|
||||
folders = [(r["passes_to"], r["search_dir"]) for r in found if r["kind"] == "passes_path"]
|
||||
self.assertEqual(
|
||||
folders,
|
||||
[
|
||||
("mame.ini rompath", "/storage/roms/bios/mame/bios"),
|
||||
("mame.ini samplepath", "/storage/roms/bios/mame/samples"),
|
||||
],
|
||||
)
|
||||
self.assertTrue(all(r["depends_on_option"] == "mame_read_config = enabled"
|
||||
for r in found if r["kind"] == "passes_path"))
|
||||
|
||||
def test_mame_without_the_read_config_option_raises(self):
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.mame, **{f"{el.LIBRETRO}/mame/config/MAME/MAME.opt": "x = 1\n"})
|
||||
|
||||
|
||||
class StandaloneRecords(unittest.TestCase):
|
||||
def test_pico8_moves_the_folder_and_runs_the_binary(self):
|
||||
(record,) = records(el.pico8)
|
||||
self.assertEqual(record["kind"], "moves")
|
||||
self.assertEqual(record["names"], ["pico8_dyn"])
|
||||
self.assertEqual(record["search_dir"], "/storage/roms/bios/pico-8")
|
||||
self.assertEqual(record["passes_to"], "/emuelec/bin/pico-8")
|
||||
self.assertEqual(
|
||||
record["source_ref"],
|
||||
f"{ref(el.RUN_EMU, '57-58')}, {ref(f'{el.BIN}/pico8.sh', '1-5, 8-10')}",
|
||||
)
|
||||
|
||||
def test_pico8_without_the_removal_is_not_a_move(self):
|
||||
changed = PICO8_SH.replace(" rm -rf /storage/roms/bios/pico-8\n", "")
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.pico8, **{f"{el.BIN}/pico8.sh": changed})
|
||||
|
||||
def test_links_from_scripts_and_packages(self):
|
||||
found = records(el.links)
|
||||
self.assertEqual(
|
||||
[(r["emulator"], r["search_dir"]) for r in found],
|
||||
[
|
||||
("flycastsa", "/storage/roms/bios/dc"),
|
||||
("flycast_dojo", "/storage/roms/bios/dc_dojo"),
|
||||
("amiberry", "/storage/roms/bios"),
|
||||
("amiberry-lite", "/storage/roms/bios"),
|
||||
("timidity", "/storage/roms/bios/timidity/freepats"),
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
found[1]["source_ref"],
|
||||
f"{ref(el.RUN_EMU, '26-27')}, "
|
||||
f"{ref(f'{EMU}/flycast-dojo/scripts/flycastdojo.sh', '1-5')}",
|
||||
)
|
||||
self.assertEqual(
|
||||
found[2]["passes_to"], "symlink /usr/config/amiberry/kickstarts -> /storage/roms/bios"
|
||||
)
|
||||
|
||||
def test_a_script_without_a_bios_link_raises(self):
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.links, **{f"{EMU}/flycastsa/scripts/flycast.sh": "flycast \"${1}\"\n"})
|
||||
|
||||
def test_duckstation_scans_the_folder_its_config_names(self):
|
||||
(record,) = records(el.duckstation)
|
||||
self.assertEqual((record["kind"], record["search_dir"]), ("scans_dir", "/storage/roms/bios"))
|
||||
|
||||
def test_duckstation_with_a_fixed_bios_path_raises(self):
|
||||
changed = DUCKSTATION_INI.replace("PathPAL =", "PathPAL = /storage/roms/bios/scph5502.bin")
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.duckstation, **{f"{EMU}/duckstation/config/settings.ini": changed})
|
||||
|
||||
def test_yabasanshiro_passes_the_bios_unless_hle_is_chosen(self):
|
||||
new, old = records(el.yabasanshiro)
|
||||
self.assertEqual((new["emulator"], old["emulator"]), ("yabasanshiroSA", "yabasanshiroSA1_5"))
|
||||
self.assertEqual(new["names"], ["saturn_bios.bin"])
|
||||
self.assertEqual(new["passes_to"], "yabasanshiro -b")
|
||||
self.assertEqual(old["passes_to"], "yabasanshiro1_5 -b")
|
||||
self.assertEqual(new["depends_on_option"], "hlebios != 1")
|
||||
self.assertIs(new["fails_when_missing"], False)
|
||||
|
||||
def test_jzintv_is_handed_the_bios_folder(self):
|
||||
(record,) = records(el.jzintv)
|
||||
self.assertEqual((record["search_dir"], record["passes_to"]), ("/storage/roms/bios", "jzintv -p"))
|
||||
|
||||
def test_mupen64plus_tries_its_ipl_names_in_order(self):
|
||||
(record,) = records(el.mupen64plus)
|
||||
self.assertEqual(record["kind"], "names_searched")
|
||||
self.assertEqual(record["names"], ["N64DD IPLROM (J).n64", "IPL.n64"])
|
||||
self.assertEqual(record["search_dir"], "/storage/roms/bios/Mupen64plus")
|
||||
self.assertEqual(record["passes_to"], "mupen64plus --dd-ipl-rom")
|
||||
self.assertEqual(record["depends_on_option"], '-f "${GAMEDIR}/${GAMENAME}.ndd"')
|
||||
|
||||
def test_mupen64plus_without_a_fallback_raises(self):
|
||||
changed = M64P_SH.replace('[ ! -f "${IPLROM}" ] && IPLROM="${BIOSDIR}/IPL.n64"\n', "")
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.mupen64plus, **{f"{EMU}/mupen64plussa/mupen64plussa-core/m64p.sh": changed})
|
||||
|
||||
def test_hatari_reads_the_tos_named_by_its_default_config(self):
|
||||
(record,) = records(el.hatari)
|
||||
self.assertEqual(record["names"], ["tos.img"])
|
||||
self.assertEqual(
|
||||
record["passes_to"], "Atari-ST-default.cfg [ROM] szTosImageFileName (--configfile)"
|
||||
)
|
||||
|
||||
def test_advancemame_one_record_when_every_device_agrees(self):
|
||||
(record,) = records(el.advancemame)
|
||||
self.assertEqual(record["search_dir"], "/storage/roms/bios/advmame/samples")
|
||||
self.assertNotIn("device", record)
|
||||
|
||||
def test_advancemame_devices_that_disagree_raise(self):
|
||||
changed = ADVMAME_RC.replace("bios/advmame", "bios/other")
|
||||
with self.assertRaises(LauncherPatternMissing):
|
||||
records(el.advancemame, **{f"{EMU}/advancemame/config/advmame.rc_oga": changed})
|
||||
|
||||
def test_scummvm_extrapath_and_the_ini_copied_for_the_core(self):
|
||||
extra, ini = records(el.scummvm)
|
||||
self.assertEqual(extra["passes_to"], "scummvm --extrapath")
|
||||
self.assertEqual(extra["depends_on_option"], "/storage/.config/scummvm/extra exists")
|
||||
self.assertEqual((ini["emulator"], ini["build"]), ("scummvm", "libretro"))
|
||||
self.assertEqual(ini["search_dir"], "/storage/.config/scummvm")
|
||||
self.assertEqual(ini["passes_to"], "/storage/roms/bios/scummvm.ini")
|
||||
|
||||
def test_dolphin_data_home(self):
|
||||
(record,) = records(el.dolphin)
|
||||
self.assertEqual(record["passes_to"], "environment XDG_DATA_HOME")
|
||||
self.assertEqual(record["search_dir"], "/storage/roms/dolphin")
|
||||
|
||||
def test_midi_sources_that_read_the_bios_folder(self):
|
||||
found = records(el.midi)
|
||||
self.assertEqual(
|
||||
[(r["emulator"], r["kind"], r["names"], r["search_dir"]) for r in found],
|
||||
[
|
||||
("fluidsynth", "fixed_name", ["sc/SC-55.sf2"], "/storage/roms/bios"),
|
||||
("mt32d", "passes_path", [], "/roms/bios"),
|
||||
],
|
||||
)
|
||||
self.assertEqual(found[0]["depends_on_option"], "ra_midi_output = fluidsynth")
|
||||
self.assertEqual(
|
||||
found[1]["source_ref"], f"{ref(el.RUN_EMU, '7-9')}, {ref(el.UTILS, '1, 10-11')}"
|
||||
)
|
||||
|
||||
def test_every_extractor_runs_on_the_fixtures(self):
|
||||
found = el.extract_launchers(tree())
|
||||
self.assertEqual(len(found), 26)
|
||||
|
||||
|
||||
class Revision(unittest.TestCase):
|
||||
def test_the_revision_is_the_one_the_bios_list_is_read_at(self):
|
||||
url = (
|
||||
"https://raw.githubusercontent.com/EmuELEC/EmuELEC/v4.8/"
|
||||
"packages/sx05re/emuelec/bin/batocera/batocera-systems"
|
||||
)
|
||||
self.assertEqual(launcher_revision(url), "v4.8")
|
||||
|
||||
def test_a_url_without_a_revision_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
launcher_revision("https://example.org/batocera-systems")
|
||||
|
||||
|
||||
class PlatformRecords(unittest.TestCase):
|
||||
"""The launchers platforms/emuelec.yml carries, as the scraper wrote them."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
cls.config = yaml.safe_load((REPO_ROOT / "platforms" / "emuelec.yml").read_text())
|
||||
|
||||
def test_the_platform_carries_launchers(self):
|
||||
self.assertTrue(self.config.get("launchers"))
|
||||
|
||||
def test_every_reference_is_in_the_emuelec_packages(self):
|
||||
for record in self.config["launchers"]:
|
||||
for part in record["source_ref"].split(", "):
|
||||
if ":" in part:
|
||||
self.assertTrue(part.startswith(f"{el.SX05RE}/"), record)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user