fix: drop names DraStic never opens

DraStic opens nds_bios_arm9.bin, nds_bios_arm7.bin and nds_firmware.bin and checks their size. Its nine aliases are in none of its binaries, and neither platform launcher passes them: ROCKNIX links the two BIOS names from its bios folder, and RetroPie runs DraStic from $md_conf_root/nds/drastic (drastic.sh:48), now a passes_path record. The collection answers DraStic's names with its DS dumps in generate_db, where repository names belong.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 12:44:33 +02:00
1 parent 20829d09ce
commit 8036d36fe8
7 files changed
+143 -11

No files matched your search

-3
View File
@@ -131,7 +131,6 @@ notes: |
files:
- name: nds_bios_arm9.bin
aliases: [NDS_Bios9.bin, bios9.bin, biosnds9.rom]
path: system/nds_bios_arm9.bin
system: nintendo-ds
required: false
@@ -154,7 +153,6 @@ files:
source_ref: "drastic r2.5.2.2 initialize_memory 0x1c4b0-0x1c4d4, libdrastic_arm64.so 0x2b6f0-0x2b708, DraSticActivity.smali:4084-4090, drastic_bios_readme.txt:1-31"
- name: nds_bios_arm7.bin
aliases: [NDS_Bios7.bin, bios7.bin, biosnds7.rom]
path: system/nds_bios_arm7.bin
system: nintendo-ds
required: false
@@ -177,7 +175,6 @@ files:
source_ref: "drastic r2.5.2.2 initialize_memory 0x1c508-0x1c52c, libdrastic_arm64.so 0x2b740-0x2b758, DraSticActivity.smali:4078-4083"
- name: nds_firmware.bin
aliases: [NDS_Lite_Firmware.bin, dsfirmware.bin, firmware.bin]
path: system/nds_firmware.bin
system: nintendo-ds
required: false
+9
View File
@@ -506,6 +506,15 @@ launchers:
kind: package
names: []
source_ref: "scriptmodules/emulators/drastic.sh:12, 40"
- emulator: drastic
package: drastic
build: standalone
kind: passes_path
names: []
search_dir: $md_conf_root/nds/drastic
passes_to: working directory of $md_inst/drastic.sh
fails_when_missing: false
source_ref: "scriptmodules/emulators/drastic.sh:48"
- emulator: fbzx
package: fbzx
repo: "git https://github.com/rastersoft/fbzx :_get_branch_fbzx"
+6 -3
View File
@@ -614,9 +614,12 @@ def _collect_all_aliases(files: dict) -> dict:
["sgb2_bios.bin", "sgb2_boot.bin", "sgb2.boot.rom"],
["sgb1.program.rom", "SGB1.sfc/program.rom"],
["sgb2.program.rom", "SGB2.sfc/program.rom"],
# Nintendo DS
["bios7.bin", "nds7.bin"],
["bios9.bin", "nds9.bin"],
# Nintendo DS. DraStic opens nds_bios_arm9.bin, nds_bios_arm7.bin
# and nds_firmware.bin and checks their size only: the collection
# answers those names with its DS dumps.
["NDS_Bios7.bin", "bios7.bin", "nds7.bin", "nds_bios_arm7.bin"],
["NDS_Bios9.bin", "bios9.bin", "nds9.bin", "nds_bios_arm9.bin"],
["NDS_Lite_Firmware.bin", "nds_firmware.bin"],
["dsi_sd_card.bin", "nds_sd_card.bin"],
# MSX
["MSX.ROM", "MSX.rom", "Machines/Shared Roms/MSX.rom"],
+32
View File
@@ -48,6 +48,11 @@ OUTSIDE_BIOS: dict[str, tuple[str, ...]] = {
"gngeopi": ("-B",), # gngeo -B/--biospath <directory of neogeo.zip>
}
# Packages whose emulator reads its system files below the working directory
# the generated launch script enters with pushd, a folder outside $biosdir:
# DraStic opens system/<name> under it. Each must still pushd.
WORKING_DIRECTORY: tuple[str, ...] = ("drastic",)
_FUNCTION = re.compile(r"^function\s+([\w.+-]+)\s*\(\)\s*\{\s*$")
_ASSIGN = re.compile(r"^(?:local\s+)?([A-Za-z_]\w*)=(.*)$")
_DECLARE = re.compile(r"^local\s+([A-Za-z_]\w*(?:\s+[A-Za-z_]\w*)*)\s*$")
@@ -518,6 +523,7 @@ class PackageReader:
if links:
self._links(links)
self._outside_bios(events)
self._working_directory(events)
return self.records
@staticmethod
@@ -821,6 +827,32 @@ class PackageReader:
)
def _working_directory(self, events: list[Event]) -> None:
"""The folder a generated launch script enters before it runs the emulator."""
if self.package.id not in WORKING_DIRECTORY:
return
found = False
for event in events:
if not event.heredoc or not event.words or event.words[0] != ["pushd"]:
continue
script = _redirect_target(event.opener)
if script is None or len(event.words) != 2 or len(event.words[1]) != 1:
raise LauncherPatternMissing(
f"{event.path}:{event.line}", f"pushd outside a launch script: {event.raw}"
)
found = True
self.record(
"passes_path", [], [(event.path, event.line)],
search_dir=event.words[1][0].strip("\"'"),
passes_to=f"working directory of {script}",
fails_when_missing=False, depends_on_option=self._depends(event),
)
if not found:
raise LauncherPatternMissing(
self.package.path, "the launch script no longer enters a working directory"
)
def _redirect_target(words: list[list[str]]) -> str | None:
"""The file a `cat > FILE << EOF` or `cat << EOF >> FILE` line writes."""
flat = [w[0] for w in words if w]
+38
View File
@@ -153,5 +153,43 @@ class AnUnreadableSourceStopsTheRun(unittest.TestCase):
self.assertIn("broken.yml", str(caught.exception))
class DrasticNamesTheDSDumps(unittest.TestCase):
"""DraStic opens nds_bios_arm9.bin, nds_bios_arm7.bin and nds_firmware.bin
and checks their size only. Its profile listed nine other names none of
its binaries contains, which were the only way the collection answered
its own three names."""
def _aliases(self, files: dict) -> dict:
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp:
previous = os.getcwd()
os.chdir(tmp)
try:
Path("emulators").mkdir()
return generate_db._collect_all_aliases(files)
finally:
os.chdir(previous)
def test_the_collection_answers_drastic_names(self):
files = {
"a9": {"name": "NDS_Bios9.bin", "path": "bios/Nintendo/DS/NDS_Bios9.bin", "md5": "m9"},
"a7": {"name": "NDS_Bios7.bin", "path": "bios/Nintendo/DS/NDS_Bios7.bin", "md5": "m7"},
"fw": {"name": "NDS_Lite_Firmware.bin",
"path": "bios/Nintendo/DS/NDS_Lite_Firmware.bin", "md5": "mf"},
}
aliases = self._aliases(files)
named = {sha: {a["name"] for a in entries} for sha, entries in aliases.items()}
self.assertIn("nds_bios_arm9.bin", named["a9"])
self.assertIn("nds_bios_arm7.bin", named["a7"])
self.assertIn("nds_firmware.bin", named["fw"])
def test_the_profile_carries_no_name_drastic_never_opens(self):
import yaml
profile = yaml.safe_load((REPO_ROOT / "emulators" / "drastic.yml").read_text())
self.assertEqual(
[e["name"] for e in profile["files"] if e.get("aliases")], [],
)
if __name__ == "__main__":
unittest.main()
+41
View File
@@ -269,6 +269,47 @@ class CommandLines(unittest.TestCase):
rl.extract_launchers(files)
DRASTIC = module("drastic", """
function configure_drastic() {
mkRomDir "nds"
addEmulator 1 "$md_id" "nds" "$md_inst/drastic.sh %ROM%"
addSystem "nds"
[[ "$md_mode" == "remove" ]] && return
isPlatform "kms" && ! isPlatform "x11" && setBackend "$md_id" "x11"
cat > "$md_inst/drastic.sh" << _EOF_
#!/bin/bash
pushd "$md_conf_root/nds/drastic"
$md_inst/drastic "\\$1"
popd
_EOF_
chmod +x "$md_inst/drastic.sh"
}
""", 36)
class WorkingDirectory(unittest.TestCase):
def test_drastic_reads_below_the_folder_its_script_enters(self):
found = records({f"{EMULATORS}/drastic.sh": DRASTIC}, "drastic")
entered = [r for r in found if r["kind"] == "passes_path"]
self.assertEqual(
[(r["search_dir"], r["passes_to"], r["source_ref"]) for r in entered],
[(
"$md_conf_root/nds/drastic",
"working directory of $md_inst/drastic.sh",
f"{EMULATORS}/drastic.sh:48",
)],
)
def test_a_launch_script_that_no_longer_enters_a_folder_raises(self):
files = {f"{EMULATORS}/drastic.sh": DRASTIC.replace('pushd "', 'echo "')}
with self.assertRaises(rl.LauncherPatternMissing):
rl.extract_launchers(files)
class LaunchScripts(unittest.TestCase):
def test_fs_uae_refuses_to_start_without_kick13(self):
files = {f"{EMULATORS}/fs-uae.sh": FS_UAE, f"{EMULATORS}/fs-uae/fs-uae.sh": FS_UAE_LAUNCHER}
+17 -5
View File
@@ -326,6 +326,10 @@ Each package also gets one `kind: package` record, and that record is what
maps a package to the profile of the build it compiles: `repo` names the
project (`minivmac` builds github.com/minivmac/minivmac, Mini vMac 37, not the
libretro port), so the mapping never rests on stripping `lr-` from a name.
DraStic reads its BIOS below the folder its generated launch script enters
(`pushd "$md_conf_root/nds/drastic"`, `drastic.sh:48`), outside `$biosdir`:
that folder is a `passes_path` record, and the scrape stops if the script no
longer enters one.
RetroDECK's records come from each component's `component_prepare.sh`,
`component_launcher.sh` and `component_functions.sh`, and from the presets
they copy, with `$bios_path` as the framework names it. In both, every line
@@ -337,17 +341,25 @@ stops with `LauncherPatternMissing`.
cross-reference both of them consume. A `names_searched` list is one place:
the names are tried in order and one held file fills it, so a core file the
launcher would never open once another name of the list is there is not
reported missing. A `fixed_name` or `core_option` name is a place of its own.
reported missing. A `fixed_name` or `core_option` name is a place of its own,
and so is a `links` name: ROCKNIX links `/storage/roms/bios/nds_bios_arm9.bin`
into DraStic's system folder, so that path is the one DraStic reads.
Only a record that applies without condition settles the core's own name: a
core option the user may never set, or a record tied to one system, leaves
the core's default in use elsewhere.
the core's default in use elsewhere. Settled, the core's own name is neither
reported nor packed, held or not: EmuDeck passes `Complex_4627v1.03.bin` to
xemu, which then never opens `Complex_4627.bin`.
A place that no declared file and no packed core file fills receives, under
the launcher's name, a file the core accepts: the file the collection knows
under that name when the core takes its content, otherwise the copy held for
the core entry read under that name. Records that only hand over a directory
(`passes_path`, `scans_dir`) or copy a file the platform already names
(`links`, `copies`) place nothing, and a `search_dir` the code describes
rather than names ("directory of the Chihiro BIOS found") is skipped.
(`passes_path`, `scans_dir`, a `links` record with no names) or copy a file
the platform already names (`copies`) place nothing, and a `search_dir` the
code describes rather than names ("directory of the Chihiro BIOS found") is
skipped. `$biosdir`, `${biosPath}` and `$bios_path` are the BIOS directory as
RetroPie, EmuDeck and RetroDECK spell it, and a path from a root
(`/userdata/bios`, `$HOME/RetroPie/BIOS/dc`) is placed by the platform's
`base_destination`.
### Inheritance