fix: record what RetroBat writes for xemu and Cxbx

The xemu generator writes eeprom.bin and xbox_hdd.qcow2 into
saves/<system> from its own resources and passes both in xemu.toml,
copies chihiro_eeprom.bin between saves/chihiro and the emulator folder,
and points the Chihiro snapshot store at saves/chihiro. The Cxbx
generator runs each build from its emulator folder (chihiro, chihiro-ds,
cxbx-reloaded, else cxbx-r), where it writes settings.ini, writes
EEPROM.bin into cxbx-reloaded for an Xbox, and mounts an .iso with its
own xbox-iso-vfs.exe after checking the Dokan 2 runtime. Nine records,
read at emulatorLauncher ca486005.

A program the launcher runs from its own folder is no launcher read
left to collect. xemu-chihiro lists the two files it generates,
chihiro_eeprom.bin and chihiro_snapshots.qcow2, as unsourceable.
This commit is contained in:
Abdessamad Derraz committed 2026-11-10 02:35:51 +01:00
1 parent 9c46f7471d
commit 7f38954041
6 files changed
+563 -1

No files matched your search

+19
View File
@@ -117,6 +117,25 @@ files:
description: "Base board configuration EEPROM"
source_ref: "source hw/xbox/chihiro/chihiro.c:2336-2401, source system/vl.c:3221-3223"
- name: chihiro_eeprom.bin
system: sega-chihiro
required: false
size: 256
validation: [size]
unsourceable: "generated with the debug key in the data directory when absent and written back by the machine"
description: "Chihiro EEPROM"
note: "A file carrying the retail key is renamed aside and a debug one generated in its place. Any other size than 256 bytes stops the machine with a message."
source_ref: "source system/vl.c:2978-3067"
- name: chihiro_snapshots.qcow2
system: sega-chihiro
required: false
config_key: chihiro.roms.snapshot_store_path
unsourceable: "created empty when missing, then holds the VM snapshots"
description: "Chihiro VM snapshot store"
note: "Defaults to the data directory. A store that exists and cannot be opened leaves snapshots unavailable."
source_ref: "source system/vl.c:2051-2110, source config_spec.yml:916-921"
- name: ver1305.bin
system: sega-chihiro
required: false
+100
View File
@@ -4069,6 +4069,69 @@ launchers:
depends_on_option: core in capsimgCore, rom extension ".ipf", the target does not
exist
source_ref: "emulatorLauncher/Generators/LibRetro.Generator.cs:1018-1027, 2750"
- emulator: chihiro
build: standalone
kind: passes_path
names: []
search_dir: "<AppConfig.GetFullPath(\"chihiro\")>"
passes_to: "Settings::GetDataLocation, the folder holding settings.ini, which the\
\ launcher writes beside the binary"
fails_when_missing: false
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:99-101, 158"
- emulator: chihiro-gun
build: standalone
kind: passes_path
names: []
search_dir: "<AppConfig.GetFullPath(\"chihiro-ds\")>"
passes_to: "Settings::GetDataLocation, the folder holding settings.ini, which the\
\ launcher writes beside the binary"
fails_when_missing: false
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:104-106, 158"
- emulator: cxbx-reloaded
build: standalone
kind: passes_path
names: []
search_dir: "<AppConfig.GetFullPath(\"cxbx-reloaded\")>"
passes_to: "Settings::GetDataLocation, the folder holding settings.ini, which the\
\ launcher writes beside the binary"
fails_when_missing: false
depends_on_option: "neither core nor emulator is \"chihiro\" or \"chihiro-gun\"\
, <AppConfig.GetFullPath(\"cxbx-r\")> when \"cxbx-reloaded\" is not set"
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:111-115, 158"
- emulator: chihiro, chihiro-gun, cxbx-reloaded
build: standalone
kind: fixed_name
names:
- xbox-iso-vfs.exe
case_insensitive: true
search_dir: <emulatorLauncher folder>
passes_to: exec
fails_when_missing: xbox-iso-vfs is required and is not installed
depends_on_option: rom extension ".iso"
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:25-30"
- emulator: chihiro, chihiro-gun, cxbx-reloaded
build: standalone
kind: fixed_name
names:
- dokan2.dll
case_insensitive: true
search_dir: '%DokanLibrary2%'
passes_to: "nothing passed: checked before xbox-iso-vfs.exe mounts the image"
fails_when_missing: Dokan 2 is required and is not installed
depends_on_option: rom extension ".iso"
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:25-26, 33-45"
- emulator: cxbx-reloaded
build: standalone
kind: fixed_name
names:
- EEPROM.bin
case_insensitive: true
search_dir: "<AppConfig.GetFullPath(\"cxbx-reloaded\")>"
passes_to: "written from the launcher's eeprom resource when absent, then its language\
\ set: the emulator reads it from its data folder"
fails_when_missing: "never missing: the launcher writes it"
depends_on_option: system == "xbox"
source_ref: "emulatorLauncher/Generators/Cxbx.Generator.cs:216-223"
- emulator: demul
build: standalone
kind: passes_path
@@ -5590,6 +5653,43 @@ launchers:
fails_when_missing: "warning '[Generator] Chihiro media board flash missing next\
\ to the BIOS.'"
source_ref: "emulatorLauncher/Generators/Xemu.Generator.cs:438-441"
- emulator: xemu
build: standalone
kind: fixed_name
names:
- eeprom.bin
- xbox_hdd.qcow2
case_insensitive: true
search_dir: saves/<system>
passes_to: "xemu.toml [sys.files] eeprom_path, hdd_path; written from the launcher's\
\ eeprom and xbox_hdd_qcow2 resources when absent, the disk image unzipped"
fails_when_missing: "never missing: the launcher writes both"
depends_on_option: "the saves folder is set and exists, not (emulator == \"xemu-chihiro\"\
\ or system == \"chihiro\")"
source_ref: "emulatorLauncher/Generators/Xemu.Generator.cs:48, 82-106, 278, 281"
- emulator: xemu, xemu-chihiro
build: standalone
kind: copies
names:
- chihiro_eeprom.bin
case_insensitive: true
search_dir: saves/chihiro
passes_to: <emulator>/chihiro_eeprom.bin, restored when the saved copy is newer
or the emulator folder holds none, copied back after the run
fails_when_missing: false
depends_on_option: emulator == "xemu-chihiro" or system == "chihiro"
source_ref: "emulatorLauncher/Generators/Xemu.Generator.cs:48, 480-520"
- emulator: xemu, xemu-chihiro
build: standalone
kind: fixed_name
names:
- chihiro_snapshots.qcow2
case_insensitive: true
search_dir: saves/chihiro
passes_to: "xemu.toml [chihiro.roms] snapshot_store_path, created by the emulator"
fails_when_missing: false
depends_on_option: emulator == "xemu-chihiro" or system == "chihiro"
source_ref: "emulatorLauncher/Generators/Xemu.Generator.cs:48, 446-449"
- emulator: xm6pro
build: standalone
kind: copies
+9 -1
View File
@@ -1097,6 +1097,11 @@ LAUNCHER_READS_KEY = "_launcher_reads_not_held"
# A package record names no file: it records what a package builds.
_NAMELESS_KINDS = frozenset({"package"})
# The folder of the platform's own launcher, as its scraper spells it: a
# program the launcher runs from there (RetroBat's xbox-iso-vfs.exe) is part
# of the platform, never a file to collect.
PLATFORM_OWN_FOLDERS = frozenset({"<emulatorLauncher folder>"})
def launcher_file_names(record: dict) -> list[str]:
"""The concrete file names a launcher record reads, in code order.
@@ -1221,7 +1226,8 @@ def launcher_reads_not_held(
amiga-os-130.rom that exists (Amiberry.Generator.cs:674-699). A copy
whose source lies outside the BIOS directory brings a file the platform
installs itself, from its own package or written by its own script
(RetroPie's lr-vecx copies $md_inst/system.bin). A file handed to the
(RetroPie's lr-vecx copies $md_inst/system.bin), and so does a program
the launcher runs from its own folder. A file handed to the
setting a reader's profile entry is read from is that entry's file,
which the pack places under the launcher's name.
"""
@@ -1250,6 +1256,8 @@ def launcher_reads_not_held(
base = str(config.get("base_destination") or "bios")
if record.get("kind") == "copies" and not launcher_directories(record, base):
continue
if str(record.get("search_dir") or "") in PLATFORM_OWN_FOLDERS:
continue
settings = name_settings(record, names)
def by_setting(name: str) -> bool:
+284
View File
@@ -26,6 +26,7 @@ SOURCE_FILES = (
f"{GENERATORS}/Bizhawk.Generator.cs",
f"{GENERATORS}/Blastem.Generator.cs",
f"{GENERATORS}/Citron.Generator.cs",
f"{GENERATORS}/Cxbx.Generator.cs",
f"{GENERATORS}/Demul.Generator.cs",
f"{GENERATORS}/Desmume.Generator.cs",
f"{GENERATORS}/Dolphin.Generator.cs",
@@ -1358,6 +1359,287 @@ def xemu(sources: dict[str, Source]) -> list[Record]:
return records
def xemu_saves(sources: dict[str, Source]) -> list[Record]:
"""The files the xemu generator keeps below the saves folder.
An Xbox gets eeprom.bin and xbox_hdd.qcow2 in saves/<system>, written
from the launcher's own resources when absent, and their paths in
xemu.toml. A Chihiro keeps chihiro_eeprom.bin there, copied into the
emulator folder before the run and back after it, and its snapshot
store.
"""
src = sources[f"{GENERATORS}/Xemu.Generator.cs"]
chihiro = src.find(r'(_chihiro) = \(emulator == "([\w-]+)" \|\| system == "(\w+)"\);', "Chihiro switch")
chihiro_when = f'emulator == "{chihiro.group(2)}" or system == "{chihiro.group(3)}"'
saves = src.find(
r'if \(!string\.IsNullOrEmpty\(AppConfig\["saves"\]\) && Directory\.Exists\(AppConfig\.GetFullPath\("saves"\)\)\)'
r'\s*\{\s*string savePath = Path\.Combine\(AppConfig\.GetFullPath\("saves"\), system\);',
"Xbox save folder",
)
eeprom = src.find(
r'if \(!File\.Exists\(Path\.Combine\(savePath, "([^"]+)"\)\)\)\s*\{[^{}]*?'
r'File\.WriteAllBytes\(Path\.Combine\(savePath, "\1"\), Properties\.Resources\.(\w+)\);\s*\}',
"EEPROM written from resources",
saves.end(),
)
hdd = src.find(
r'if \(!File\.Exists\(Path\.Combine\(savePath, "([^"]+)"\)\)\)\s*\{[^{}]*?'
r'File\.WriteAllBytes\(zipFile, Properties\.Resources\.(\w+)\);[^{}]*?Zip\.Extract\(zipFile, savePath\);[^{}]*?\}',
"hard disk image written from resources",
eeprom.end(),
)
eeprom_key = src.find(
r'ini\.WriteValue\("([\w.]+)", "(eeprom_path)", "\'" \+ eepromPath', "eeprom_path write"
)
hdd_key = src.find(r'ini\.WriteValue\("([\w.]+)", "(hdd_path)", "\'" \+ hddPath', "hdd_path write")
records = [
_record(
"xemu",
"standalone",
"fixed_name",
[eeprom.group(1), hdd.group(1)],
source_ref(
(
src.path,
[
src.lines(chihiro.start(), chihiro.end()),
src.lines(saves.start(), hdd.end()),
src.lines(eeprom_key.start(), eeprom_key.end()),
src.lines(hdd_key.start(), hdd_key.end()),
],
)
),
case_insensitive=True,
search_dir="saves/<system>",
passes_to=(
f"xemu.toml [{eeprom_key.group(1)}] {eeprom_key.group(2)}, {hdd_key.group(2)};"
f" written from the launcher's {eeprom.group(2)} and {hdd.group(2)} resources"
" when absent, the disk image unzipped"
),
fails_when_missing="never missing: the launcher writes both",
depends_on_option=_joined("the saves folder is set and exists", f"not ({chihiro_when})"),
)
]
setup = src.find(r"private void SetupChihiroEeprom\(string path\)", "SetupChihiroEeprom")
body = src.block_at(setup.end())
folder = src.find(
r'string savePath = Path\.Combine\(AppConfig\.GetFullPath\("saves"\), "(\w+)"\);',
"Chihiro save folder",
body[0],
body[1],
)
pair = src.find(
r'_chihiroEeprom = Path\.Combine\(path, "([^"]+)"\);\s*'
r'_chihiroEepromSave = Path\.Combine\(savePath, "\1"\);',
"Chihiro EEPROM pair",
folder.end(),
body[1],
)
save = src.find(r"private void SaveChihiroEeprom\(\)", "SaveChihiroEeprom")
save_body = src.block_at(save.end())
records.append(
_record(
"xemu, xemu-chihiro",
"standalone",
"copies",
[pair.group(1)],
source_ref(
(
src.path,
[
src.lines(chihiro.start(), chihiro.end()),
src.statement_span(body),
src.statement_span(save_body),
],
)
),
case_insensitive=True,
search_dir=f"saves/{folder.group(1)}",
passes_to=(
f"<emulator>/{pair.group(1)}, restored when the saved copy is newer or the"
" emulator folder holds none, copied back after the run"
),
fails_when_missing=False,
depends_on_option=chihiro_when,
)
)
store = src.find(
r'string savePath = Path\.Combine\(AppConfig\.GetFullPath\("saves"\), "(\w+)"\);\s*'
r'if \(!Directory\.Exists\(savePath\)\) try \{ Directory\.CreateDirectory\(savePath\); \} catch \{ \}\s*'
r'if \(Directory\.Exists\(savePath\)\)\s*'
r'ini\.WriteValue\("([\w.]+)", "(\w+)", "\'" \+ Path\.Combine\(savePath, "([^"]+)"\)',
"Chihiro snapshot store",
)
records.append(
_record(
"xemu, xemu-chihiro",
"standalone",
"fixed_name",
[store.group(4)],
source_ref((src.path, [src.lines(chihiro.start(), chihiro.end()),
src.lines(store.start(), store.end())])),
case_insensitive=True,
search_dir=f"saves/{store.group(1)}",
passes_to=f"xemu.toml [{store.group(2)}] {store.group(3)}, created by the emulator",
fails_when_missing=False,
depends_on_option=chihiro_when,
)
)
return records
# What the Cxbx generator hands each build: the data folder is the one its
# settings.ini sits in (Cxbx-Reloaded Settings::FindSettingsLocation), which
# the launcher writes beside the binary.
CXBX_DATA_FOLDER = "Settings::GetDataLocation"
def cxbx(sources: dict[str, Source]) -> list[Record]:
"""The folder each Cxbx build runs from, and the EEPROM written there.
The generator picks the emulator folder by core or emulator name, opens
settings.ini in it, and for an Xbox writes EEPROM.bin into the
cxbx-reloaded folder from its own resource when absent.
"""
src = sources[f"{GENERATORS}/Cxbx.Generator.cs"]
named = src.find_all(
r'(?:else )?if \(\(core != null && core == "([\w-]+)"\) \|\| '
r'\(emulator != null && emulator == "\1"\)\)\s*\{\s*path = AppConfig\.GetFullPath\("([\w-]+)"\);',
"emulator folder by name",
)
fallbacks = src.find_all(
r'if \(string\.IsNullOrEmpty\(path\)\)\s*path = AppConfig\.GetFullPath\("([\w-]+)"\);',
"default emulator folder",
)
settings = src.find(
r'IniFile\.FromFile\(Path\.Combine\(path, "(settings\.ini)"\)', "settings.ini beside the binary"
)
settings_span = src.lines(settings.start(), settings.end())
passes_to = (
f"{CXBX_DATA_FOLDER}, the folder holding {settings.group(1)},"
" which the launcher writes beside the binary"
)
records = []
for match in named:
records.append(
_record(
match.group(1),
"standalone",
"passes_path",
[],
source_ref((src.path, [src.lines(match.start(), match.end()), settings_span])),
search_dir=f'<AppConfig.GetFullPath("{match.group(2)}")>',
passes_to=passes_to,
fails_when_missing=False,
)
)
first, *rest = fallbacks
others = "neither core nor emulator is " + " or ".join(f'"{m.group(1)}"' for m in named)
records.append(
_record(
first.group(1),
"standalone",
"passes_path",
[],
source_ref(
(
src.path,
[src.lines(first.start(), first.end()),
*(src.lines(m.start(), m.end()) for m in rest), settings_span],
)
),
search_dir=f'<AppConfig.GetFullPath("{first.group(1)}")>',
passes_to=passes_to,
fails_when_missing=False,
depends_on_option=_joined(
others,
*(f'<AppConfig.GetFullPath("{m.group(1)}")> when "{first.group(1)}" is not set'
for m in rest),
),
)
)
records.extend(_cxbx_mount(src, [*(m.group(1) for m in named), first.group(1)]))
eeprom = src.find(
r'if \(system == "(\w+)"\)\s*\{\s*string (\w+) = Path\.Combine\(AppConfig\.GetFullPath\("([\w-]+)"\), "([^"]+)"\);'
r'\s*if \(!File\.Exists\(\2\)\)\s*File\.WriteAllBytes\(\2, Properties\.Resources\.(\w+)\);'
r'\s*WriteXboxEEPROM\(\2\);\s*\}',
"EEPROM written from resources",
)
records.append(
_record(
eeprom.group(3),
"standalone",
"fixed_name",
[eeprom.group(4)],
source_ref((src.path, [src.lines(eeprom.start(), eeprom.end())])),
case_insensitive=True,
search_dir=f'<AppConfig.GetFullPath("{eeprom.group(3)}")>',
passes_to=(
f"written from the launcher's {eeprom.group(5)} resource when absent,"
" then its language set: the emulator reads it from its data folder"
),
fails_when_missing="never missing: the launcher writes it",
depends_on_option=f'system == "{eeprom.group(1)}"',
)
)
return records
def _cxbx_mount(src: Source, emulators: list[str]) -> list[Record]:
"""What the generator needs to mount an .iso before Cxbx opens it: its
own xbox-iso-vfs.exe, run from the launcher's folder, and the Dokan 2
runtime installed on the system."""
iso = src.find(
r'if \(Path\.GetExtension\(rom\)\.ToLowerInvariant\(\) != "(\.\w+)"\)\s*return rom;',
"mount for disc images only",
)
vfs = src.find(
r'string (\w+) = Path\.Combine\(Path\.GetDirectoryName\(typeof\(\w+\)\.Assembly\.Location\), "([^"]+)"\);'
r'\s*if \(!File\.Exists\(\1\)\)\s*throw new ApplicationException\("([^"]+)"\);',
"mount tool beside the launcher",
iso.end(),
)
dokan = src.find(
r'string (\w+) = Environment\.GetEnvironmentVariable\("(\w+)"\);.*?'
r'\1 = Path\.Combine\(\1, "([^"]+)"\);\s*if \(!File\.Exists\(\1\)\)\s*\{[^{}]*?'
r'throw new ApplicationException\("([^"]+)"\);\s*\}',
"Dokan runtime check",
vfs.end(),
flags=re.S,
)
iso_span = src.lines(iso.start(), iso.end())
when = f'rom extension "{iso.group(1)}"'
emulator = ", ".join(emulators)
return [
_record(
emulator,
"standalone",
"fixed_name",
[vfs.group(2)],
source_ref((src.path, [iso_span, src.lines(vfs.start(), vfs.end())])),
case_insensitive=True,
search_dir="<emulatorLauncher folder>",
passes_to="exec",
fails_when_missing=vfs.group(3),
depends_on_option=when,
),
_record(
emulator,
"standalone",
"fixed_name",
[dokan.group(3)],
source_ref((src.path, [iso_span, src.lines(dokan.start(), dokan.end())])),
case_insensitive=True,
search_dir=f"%{dokan.group(2)}%",
passes_to=f"nothing passed: checked before {vfs.group(2)} mounts the image",
fails_when_missing=dokan.group(4),
depends_on_option=when,
),
]
def openmsx(sources: dict[str, Source]) -> list[Record]:
src = sources[f"{GENERATORS}/OpenMSX.Generator.cs"]
table = src.find(
@@ -3521,6 +3803,7 @@ EXTRACTORS: tuple[Callable[[dict[str, Source]], list[Record]], ...] = (
blastem,
bsyndrome,
capsimg,
cxbx,
demul,
desmume,
dolphin,
@@ -3551,6 +3834,7 @@ EXTRACTORS: tuple[Callable[[dict[str, Source]], list[Record]], ...] = (
value_folders,
winuae,
xemu,
xemu_saves,
xm6pro,
xroar,
)
+7
View File
@@ -77,6 +77,8 @@ LAUNCHERS = [
_record("setemu", "copies", ["user.sf2"]),
_record("caseemu", "fixed_name", ["mixed.rom"]),
_record("foldemu", "fixed_name", ["MIXED.rom"], case_insensitive=True),
_record("toolemu", "fixed_name", ["tool.exe"], search_dir="<emulatorLauncher folder>",
passes_to="exec"),
_record("labelemu", "core_option", ["labelled.rom (CDZ)"]),
]
@@ -160,6 +162,11 @@ class LauncherGapTest(unittest.TestCase):
)
return {(r["platform"], r["name"]): r for r in rows}
def test_a_program_of_the_launcher_itself_is_not_reported(self) -> None:
"""RetroBat runs its own xbox-iso-vfs.exe to mount an Xbox disc
image: part of the platform, no file to collect."""
self.assertNotIn("tool.exe", {name for _, name in self._rows()})
def test_a_held_name_is_not_reported(self) -> None:
names = {name for _, name in self._rows()}
self.assertNotIn("held.bin", names)
+144
View File
@@ -1103,6 +1103,150 @@ class FilesNeededTogether(unittest.TestCase):
)
XEMU_SAVES = """
public override ProcessStartInfo Generate(string system, string emulator, string core, string rom)
{
_chihiro = (emulator == "xemu-chihiro" || system == "chihiro");
if (!string.IsNullOrEmpty(AppConfig["saves"]) && Directory.Exists(AppConfig.GetFullPath("saves")))
{
string savePath = Path.Combine(AppConfig.GetFullPath("saves"), system);
if (!_chihiro)
{
if (!File.Exists(Path.Combine(savePath, "eeprom.bin")))
{
File.WriteAllBytes(Path.Combine(savePath, "eeprom.bin"), Properties.Resources.eeprom);
}
if (!File.Exists(Path.Combine(savePath, "xbox_hdd.qcow2")))
{
string zipFile = Path.Combine(savePath, "xbox_hdd.qcow2.zip");
File.WriteAllBytes(zipFile, Properties.Resources.xbox_hdd_qcow2);
Zip.Extract(zipFile, savePath);
File.Delete(zipFile);
}
}
}
}
private void SetupTOMLConfiguration(string eepromPath, string hddPath)
{
if (!string.IsNullOrEmpty(eepromPath))
ini.WriteValue("sys.files", "eeprom_path", "'" + eepromPath + "'");
if (!string.IsNullOrEmpty(hddPath))
ini.WriteValue("sys.files", "hdd_path", "'" + hddPath + "'");
}
private void SetupChihiroConfiguration()
{
string savePath = Path.Combine(AppConfig.GetFullPath("saves"), "chihiro");
if (!Directory.Exists(savePath)) try { Directory.CreateDirectory(savePath); } catch { }
if (Directory.Exists(savePath))
ini.WriteValue("chihiro.roms", "snapshot_store_path", "'" + Path.Combine(savePath, "chihiro_snapshots.qcow2") + "'");
}
private void SetupChihiroEeprom(string path)
{
string savePath = Path.Combine(AppConfig.GetFullPath("saves"), "chihiro");
_chihiroEeprom = Path.Combine(path, "chihiro_eeprom.bin");
_chihiroEepromSave = Path.Combine(savePath, "chihiro_eeprom.bin");
}
private void SaveChihiroEeprom()
{
if (File.Exists(_chihiroEeprom))
File.Copy(_chihiroEeprom, _chihiroEepromSave, true);
}
"""
CXBX = """
private string MountIso(string rom)
{
if (Path.GetExtension(rom).ToLowerInvariant() != ".iso")
return rom;
string xboxIsoVfsPath = Path.Combine(Path.GetDirectoryName(typeof(Installer).Assembly.Location), "xbox-iso-vfs.exe");
if (!File.Exists(xboxIsoVfsPath))
throw new ApplicationException("xbox-iso-vfs is required and is not installed");
string dokan = Environment.GetEnvironmentVariable("DokanLibrary2");
dokan = Path.Combine(dokan, "dokan2.dll");
if (!File.Exists(dokan))
{
throw new ApplicationException("Dokan 2 is required and is not installed");
}
}
public override ProcessStartInfo Generate(string system, string emulator, string core, string rom)
{
string path = null;
if ((core != null && core == "chihiro") || (emulator != null && emulator == "chihiro"))
{
path = AppConfig.GetFullPath("chihiro");
}
else if ((core != null && core == "chihiro-gun") || (emulator != null && emulator == "chihiro-gun"))
{
path = AppConfig.GetFullPath("chihiro-ds");
}
if (string.IsNullOrEmpty(path))
path = AppConfig.GetFullPath("cxbx-reloaded");
if (string.IsNullOrEmpty(path))
path = AppConfig.GetFullPath("cxbx-r");
using (var ini = IniFile.FromFile(Path.Combine(path, "settings.ini"), IniOptions.KeepEmptyValues))
{
}
if (system == "xbox")
{
string eeprom = Path.Combine(AppConfig.GetFullPath("cxbx-reloaded"), "EEPROM.bin");
if (!File.Exists(eeprom))
File.WriteAllBytes(eeprom, Properties.Resources.eeprom);
WriteXboxEEPROM(eeprom);
}
}
"""
class XboxFilesTheLauncherWrites(unittest.TestCase):
def test_xemu_keeps_its_eeprom_and_disk_below_saves(self):
records = rl.xemu_saves(_sources(**{f"{G}/Xemu.Generator.cs": XEMU_SAVES}))
self.assertEqual(
[(r["emulator"], r["kind"], r["names"], r["search_dir"], r["source_ref"])
for r in records],
[
("xemu", "fixed_name", ["eeprom.bin", "xbox_hdd.qcow2"], "saves/<system>",
f"{G}/Xemu.Generator.cs:3-19, 27-29"),
("xemu, xemu-chihiro", "copies", ["chihiro_eeprom.bin"], "saves/chihiro",
f"{G}/Xemu.Generator.cs:3, 40-51"),
("xemu, xemu-chihiro", "fixed_name", ["chihiro_snapshots.qcow2"], "saves/chihiro",
f"{G}/Xemu.Generator.cs:3, 34-37"),
],
)
self.assertIn("eeprom_path, hdd_path", records[0]["passes_to"])
self.assertIn('not (emulator == "xemu-chihiro" or system == "chihiro")',
records[0]["depends_on_option"])
def test_cxbx_runs_from_its_folder_and_gets_the_eeprom_there(self):
records = rl.cxbx(_sources(**{f"{G}/Cxbx.Generator.cs": CXBX}))
self.assertEqual(
[(r["emulator"], r["kind"], r["names"], r["search_dir"], r["source_ref"])
for r in records],
[
("chihiro", "passes_path", [], '<AppConfig.GetFullPath("chihiro")>',
f"{G}/Cxbx.Generator.cs:19-21, 31"),
("chihiro-gun", "passes_path", [], '<AppConfig.GetFullPath("chihiro-ds")>',
f"{G}/Cxbx.Generator.cs:23-25, 31"),
("cxbx-reloaded", "passes_path", [], '<AppConfig.GetFullPath("cxbx-reloaded")>',
f"{G}/Cxbx.Generator.cs:27-31"),
("chihiro, chihiro-gun, cxbx-reloaded", "fixed_name", ["xbox-iso-vfs.exe"],
"<emulatorLauncher folder>", f"{G}/Cxbx.Generator.cs:3-7"),
("chihiro, chihiro-gun, cxbx-reloaded", "fixed_name", ["dokan2.dll"],
"%DokanLibrary2%", f"{G}/Cxbx.Generator.cs:3-4, 8-13"),
("cxbx-reloaded", "fixed_name", ["EEPROM.bin"],
'<AppConfig.GetFullPath("cxbx-reloaded")>', f"{G}/Cxbx.Generator.cs:34-41"),
],
)
self.assertTrue(all(r["passes_to"].startswith(rl.CXBX_DATA_FOLDER) for r in records[:3]))
self.assertEqual(records[3]["passes_to"], "exec")
self.assertEqual(records[5]["depends_on_option"], 'system == "xbox"')
class HelpersAndCopies(unittest.TestCase):
def test_blastem_one_record_per_helper_call(self):
records = rl.blastem(_sources(**{f"{G}/Blastem.Generator.cs": BLASTEM}))