From 2377985df712a7d037c3cd68dba851a8c60b9761 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:16:29 +0100 Subject: [PATCH 1/5] docs: read the dsperate font check from code prepare_dsi_hle refuses a TWLFontTable.dat under 256 bytes and reads its layout from bytes 0x84 and 0x86, warning on a mismatch. No hash is checked, so the three retail NAND sha1s go, and one entry states the size floor and the three layouts. --- emulators/dsperate.yml | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/emulators/dsperate.yml b/emulators/dsperate.yml index ba5534c8c..d0b9066c9 100644 --- a/emulators/dsperate.yml +++ b/emulators/dsperate.yml @@ -140,34 +140,12 @@ files: system: nintendo-dsi required: false hle_fallback: true - size: 863296 - sha1: "c1101d7c9224acfa0350b5fc62da97d4c6d36141" + min_size: 256 + validation: [size] config_key: "paths.dsi_font" - description: "DSi system font (Japan, Americas, Europe, Australia)" - note: "Used for DSiWare run without a NAND. Absent, DSperate's own font table is generated. ROCKNIX's ini leaves the key unset. A file under 256 bytes is refused. No hash is checked, only the layout bytes 0x84 and 0x86, and a table whose layout differs from the title's region draws a warning. This is the normal layout (0x84 = 3, 0x86 = 0), read from nand:/sys/TWLFontTable.dat of a retail DSi NAND dump. The FAT16 partition is AES-CTR encrypted with the normal key ROL128((KeyX XOR KeyY) + 0xFFFEFB4E295902582A680F5F1A4F3E79, 42), where KeyX is built from the console ID in the nocash footer and KeyY is 0x0AB9DC76 0xBD4DC4D3 0x202DDD1D 0xE1A00005. The counter is the first 16 bytes of SHA-1 of the footer's eMMC CID, and each 16-byte block is byte-reversed before and after AES. The USA, Europe, Japan and Australia consoles, four different console IDs, give the same bytes." - source_ref: "src/core/nds_dsi.cpp:310-327, src/core/io/dsi_font.cpp:38-43, src/core/io/dsi_nand_fs.cpp:786-800, src/core/io/dsi_aes.cpp:38-46, src/frontend/sdl/main.cpp:1774-1778" - - - name: "TWLFontTable.dat" - system: nintendo-dsi - required: false - hle_fallback: true - size: 581664 - sha1: "5170aff7e9766baadbbf2943c41f5634e82aaf2d" - config_key: "paths.dsi_font" - description: "DSi system font (China)" - note: "The Chinese layout (0x84 = 9, 0x86 = 4), which Chinese DSiWare needs to find its fonts. Read from nand:/sys/TWLFontTable.dat of a retail Chinese DSi NAND dump, decrypted as for the normal table." - source_ref: "src/core/nds_dsi.cpp:313-320, src/core/io/dsi_font.cpp:38-43, src/core/io/dsi_nand_fs.cpp:786-800, src/core/io/dsi_aes.cpp:38-46" - - - name: "TWLFontTable.dat" - system: nintendo-dsi - required: false - hle_fallback: true - size: 162688 - sha1: "f0a37b073eb0208d3a54c9a6bb94292dbec1192a" - config_key: "paths.dsi_font" - description: "DSi system font (Korea)" - note: "The Korean layout (0x84 = 9, 0x86 = 5), which Korean DSiWare needs to find its fonts. Read from nand:/sys/TWLFontTable.dat of a retail Korean DSi NAND dump, decrypted as for the normal table." - source_ref: "src/core/nds_dsi.cpp:313-320, src/core/io/dsi_font.cpp:38-43, src/core/io/dsi_nand_fs.cpp:786-800, src/core/io/dsi_aes.cpp:38-46" + description: "DSi system font" + note: "Used for DSiWare run without a NAND. A path that names no file falls back to DSperate's own tables, linked into the binary. ROCKNIX's ini leaves the key unset. A file under 256 bytes stops the session. No hash is checked. The layout is read from bytes 0x84 and 0x86: 3 and 0 for the normal table (Japan, Americas, Europe, Australia), 9 and 4 for the Chinese one, 9 and 5 for the Korean one. Chinese and Korean titles need their own layout and every other title the normal one. Another layout only draws a warning, and the title does not find its fonts." + source_ref: "src/core/nds_dsi.cpp:311-323, src/core/io/dsi_font.cpp:38-43, src/frontend/sdl/main.cpp:1774-1778" - name: "usrcheat.dat" system: nintendo-ds From eb75c2d1f565e2bba0797119a0bff20ac8f3b94c Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:19:16 +0100 Subject: [PATCH 2/5] docs: read uqm content checks from the engine The engine checks no hash: only the Windows installer compares the packages.nsh MD5s, so validation and verification go. The content pack stays required from LoadKernel, which exits without a .rmp index at the content root and names the pack. --- emulators/uqm.yml | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/emulators/uqm.yml b/emulators/uqm.yml index 3fd73e99c..3d351956a 100644 --- a/emulators/uqm.yml +++ b/emulators/uqm.yml @@ -14,7 +14,6 @@ version_basis: display_name: "The Ur-Quan Masters" cores: [uqm] systems: [ur-quan-masters, star-control-2] -verification: md5 notes: | The Ur-Quan Masters (UQM) is the open-source port of Toys for Bob's @@ -26,16 +25,18 @@ notes: | prepareContentDir() in src/options.c:136 searches for content/ relative to the executable (default locations are CONTENTDIR, "", "content", "../../content", while macOS also probes the app bundle). A - sentinel file content/version must exist (lines 138-150) or the game + sentinel file content/version must exist (lines 138-182) or the game logs "Could not find content" and exits. - mountContentDir() (src/options.c:329) mounts content/ at the uio root - and then auto-mounts every .uqm file found under content/packages/ as - an overlay archive (lines 354-359). The engine itself does not treat - individual .uqm packages as mandatory: the fatal check is the - content/ tree. In practice content/packages/uqm-0.8.0-content.uqm is - "Core Data" in the Windows installer (uqm-installer.nsi:289-306, - SectionIn 1 2 3 4 6) and the game is unplayable without it. + mountContentDir() (src/options.c:330-361) mounts content/ at the uio + root and then mounts every .zip or .uqm file under content/packages/ + below it (line 357, mountDirZips at 463-489). The archive name is not + read. LoadKernel then needs at least one .rmp index at the content + root (src/uqm/setup.c:113-115), and without one the game logs "Could + not load basic content", names uqm-0.8.0-content.uqm and exits + (src/uqm/starcon.c:194-204). The content pack carries uqm.rmp and + base/. A content/ tree that already holds them unpacked passes the + same check, as the tree at this pin does. 3DO music (uqm-0.8.0-3domusic.uqm) and voice overs (uqm-0.8.0-voice.uqm) are optional sections (installer lines 315, @@ -50,9 +51,10 @@ notes: | (uqm.key, menu.key) are shipped verbatim in the source repository and are not external data, so they are not listed here. - MD5 and size values come from build/win32_install/packages.nsh, - autogenerated by procpkgs.sh at release time. Sizes are the KiB - values the NSIS installer uses with AddSize. + The engine checks no hash. The MD5 values come from + build/win32_install/packages.nsh, autogenerated by procpkgs.sh at + release time, and only the Windows installer compares them, offering + a fresh download on a mismatch (uqm-installer.nsi:132-190). files: - name: uqm-0.8.0-content.uqm @@ -62,10 +64,9 @@ files: required: true installed_on: [retropie] md5: "6cbc9d51fa63e07c0f4d1d061136d816" - validation: [md5] description: "UQM core game data archive" - note: "Published apart from the source release. The RetroPie scriptmodule downloads it into content/packages." - source_ref: "sc2/build/win32_install/packages.nsh:3-5; sc2/build/win32_install/uqm-installer.nsi:289-306" + note: "Published apart from the source release. Carries uqm.rmp and base/: without a .rmp index at the content root the game exits with \"Could not load basic content\". Any .zip or .uqm in content/packages is mounted, the name is not read. The RetroPie scriptmodule downloads it into content/packages." + source_ref: "sc2/src/uqm/starcon.c:194-204, sc2/src/uqm/setup.c:113-115, sc2/src/options.c:354-359, sc2/src/options.c:463-489, sc2/src/options.c:491-515, sc2/build/win32_install/packages.nsh:3-5" - name: uqm-0.8.0-3domusic.uqm path: uqm/content/addons/uqm-0.8.0-3domusic.uqm @@ -74,7 +75,6 @@ files: required: false installed_on: [retropie] md5: "9e5801d45ca12028b486cdeb83568c02" - validation: [md5] description: "UQM 3DO music track pack" note: "The RetroPie scriptmodule downloads it into content/addons." source_ref: "sc2/build/win32_install/packages.nsh:6-8; sc2/build/win32_install/uqm-installer.nsi:315-327, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" @@ -86,7 +86,6 @@ files: required: false installed_on: [retropie] md5: "fc89f77d7b66c2669abca6c157f5259a" - validation: [md5] description: "UQM alien-race voice-over pack" note: "The RetroPie scriptmodule downloads it into content/addons." source_ref: "sc2/build/win32_install/packages.nsh:9-11; sc2/build/win32_install/uqm-installer.nsi:329-341, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" @@ -97,7 +96,6 @@ files: category: game_data required: false md5: "09f242d8d72166d1d5ccbd3d99c93e7d" - validation: [md5] description: "Precursor Remix disc 1 (modern remixes)" source_ref: "sc2/build/win32_install/packages.nsh:12-14; sc2/build/win32_install/uqm-installer.nsi:345-358, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" @@ -107,7 +105,6 @@ files: category: game_data required: false md5: "fbc8bdcb709939d559d8c7216ad15cc2" - validation: [md5] description: "Precursor Remix disc 2" source_ref: "sc2/build/win32_install/packages.nsh:15-17, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" @@ -117,7 +114,6 @@ files: category: game_data required: false md5: "5ccc6d4ac301ae98e172ac6835dcdead" - validation: [md5] description: "Precursor Remix disc 3" source_ref: "sc2/build/win32_install/packages.nsh:18-20, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" @@ -127,6 +123,5 @@ files: category: game_data required: false md5: "3fb63f4ac514343ed4b4b5b194c413fb" - validation: [md5] description: "Precursor Remix disc 4-1" source_ref: "sc2/build/win32_install/packages.nsh:21-23, sc2/src/options.c:393-402, 518-551, sc2/src/uqm/setup.c:118-132" From d9aaa9bdba381aa599861c6c98ad1c9c1151ef69 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:23:08 +0100 Subject: [PATCH 3/5] docs: split xemu system files by build The standalone runs without the MCPX ROM and the disk image, queuing an error, and stops only without a flash ROM: vl.c clears autostart. bootrom_path, flashrom_path and hdd_path have no default, so their names are labels and the standalone entries lose path. The core still refuses to load without the MCPX ROM or the flash. --- emulators/xemu.yml | 69 +++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/emulators/xemu.yml b/emulators/xemu.yml index 55f44153d..b61eff341 100644 --- a/emulators/xemu.yml +++ b/emulators/xemu.yml @@ -99,47 +99,67 @@ notes: | files: - name: mcpx_1.0.bin - path: "xemu/mcpx_1.0.bin" - standalone_path: mcpx_1.0.bin - required: true + mode: standalone + required: false size: 512 validation: [size] config_key: sys.files.bootrom_path + found_by: content description: "MCPX boot ROM" note: >- Passed as the bootrom machine property and read over the last 512 bytes of the flash image, which the machine maps as a writable overlay at the - top of the address space. qemu_init drops the path with an error when - the file cannot be opened or is not 512 bytes, and the machine then runs - the flash alone. The core reads system/xemu/mcpx_1.0.bin, else - mcpx_1.0.bin at the root of the system directory, and refuses to load - when neither is there. + top of the address space. bootrom_path has no default, so the name is a + label. qemu_init drops a path that cannot be opened or is not 512 bytes + and queues an error, and the machine then runs the flash alone, as it + does when the path is empty. + source_ref: "system/vl.c:2983-3006, hw/xbox/xbox.c:143-171, hw/xbox/xbox.c:468-470, config_spec.yml:362-366" + + - name: mcpx_1.0.bin + mode: libretro + path: "xemu/mcpx_1.0.bin" + required: true + size: 512 + validation: [size] + description: "MCPX boot ROM" + note: >- + Read from system/xemu/mcpx_1.0.bin, else mcpx_1.0.bin at the root of + the system directory. The core refuses to load when neither is there. source_ref: - standalone: "system/vl.c:2983-3006, hw/xbox/xbox.c:143-171, hw/xbox/xbox.c:468-470" libretro: "ui/libretro.c:3370-3415, ui/libretro.c:3418-3427" - name: Complex_4627.bin - path: "xemu/Complex_4627.bin" - standalone_path: Complex_4627.bin - aliases: [Complex_4627v1.03.bin] + mode: standalone required: true size: [262144, 524288, 1048576] validation: [size] size_note: "Any size that is a positive multiple of 64 KiB is mapped, and the image is mirrored across the 16 MB flash window" config_key: sys.files.flashrom_path + found_by: content description: "Xbox flash ROM image" note: >- - Passed as -bios and opened through qemu_find_file. An image whose size - is not a multiple of 65536 bytes counts as a failed load, like a missing - file: the 256 KB region is filled with 0xFF and the machine starts - anyway. An unreadable path clears autostart in the standalone, which - takes any file the settings name. The core tries - Complex_4627v1.03.bin, then Complex_4627.bin, in system/xemu/ and - then at the root of the system directory, and refuses to load when - none is there. + Passed as -bios and opened through qemu_find_file. flashrom_path has no + default, so the name is a label. A path that cannot be opened, an empty + one included, queues an error and clears autostart, so the machine does + not start. An image whose size is not a multiple of 65536 bytes counts + as a failed load in the machine: the 256 KB region is filled with 0xFF. + source_ref: "system/vl.c:2527-2535, system/vl.c:3041-3053, hw/xbox/xbox.c:63-123, config_spec.yml:362-366" + + - name: Complex_4627.bin + mode: libretro + path: "xemu/Complex_4627.bin" + aliases: [Complex_4627v1.03.bin] + required: true + size: [262144, 524288, 1048576] + validation: [size] + size_note: "Any size that is a positive multiple of 64 KiB is mapped, and the image is mirrored across the 16 MB flash window" + description: "Xbox flash ROM image" + note: >- + The core tries Complex_4627v1.03.bin, then Complex_4627.bin, in + system/xemu/ and then at the root of the system directory, and refuses + to load when none is there. source_ref: - standalone: "system/vl.c:3041-3053, hw/xbox/xbox.c:63-123" - libretro: "ui/libretro.c:3370-3415, ui/libretro.c:3429-3436" + libretro: "ui/libretro.c:3370-3415, ui/libretro.c:3429-3436, hw/xbox/xbox.c:63-123" - name: xbox_eeprom.bin path: "xemu/xbox_eeprom.bin" @@ -167,8 +187,7 @@ files: - name: xbox_hdd.qcow2 mode: standalone - path: xbox_hdd.qcow2 - required: true + required: false installed_on: [batocera, emudeck, recalbox, retrodeck, rocknix] config_key: sys.files.hdd_path found_by: content @@ -176,7 +195,7 @@ files: note: >- Attached as the IDE drive at index 0 with locked=on when the path is set and the file opens. An unreadable file only queues an error and an empty - path attaches no drive. Snapshots are stored inside the image. + path attaches no drive: the machine starts either way. Snapshots are stored inside the image. hdd_path has no default, so the name is a label: it is the file of the formatted image the project publishes apart from the xemu release (xemu-hdd-image 1.0, xbox_hdd.qcow2.zip), which carries its own From 3d662cbd15e3432efe52a2d83b423304d5efe3a8 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:25:10 +0100 Subject: [PATCH 4/5] docs: read the triforce font load from code LoadFileToIPL reads a standalone font with no size check, keeping what fits in the IPL image. The sizes stay as the IPL dump fonts' lengths and the notes say so. No HLE stands in for a missing font: the IPL read raises an alert and the game runs on, so hle_fallback goes. --- emulators/triforce.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/emulators/triforce.yml b/emulators/triforce.yml index a1c90f9e7..52bb92546 100644 --- a/emulators/triforce.yml +++ b/emulators/triforce.yml @@ -279,21 +279,23 @@ files: the byte-swapped data (Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:100-102). # -- GameCube fonts -- - # Sys/GC/ only. Skipped when an IPL dump is available, the fonts are then read - # out of the dump at 0x1aff00 and 0x1fcf00 instead. + # Sys/GC/ only. With Load IPL dump on and a GC//IPL.bin found, the + # fonts are read out of the dump at 0x1aff00 and 0x1fcf00 instead. - name: "font_western.bin" path: "Sys/GC/font_western.bin" description: "Windows-1252 font used by GameCube and Wii text rendering" size: 9589 required: false installed_on: [retrobat] - hle_fallback: true - source_ref: "source Source/Core/Common/CommonPaths.h:142, source Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131, 203-247" + source_ref: "source Source/Core/Common/CommonPaths.h:142, source Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131, 161-176, 203-247, 330-344" note: > - Size is the official font length the code reads from an IPL dump, 0x2575 - (Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:234-236). A standalone file is - read without any check. The fork's tree carries no font, RetroBat's - dolphin-triforce package installs the free replacement here. + Read without any size check into the IPL image at 0x1fcf00, keeping at + most 0x3100 bytes. The size is the IPL dump font's, 0x2575, the length + the code reads out of a dump (EXI_DeviceIPL.cpp:234-236). The fork's + tree carries no font, and RetroBat's dolphin-triforce package installs + Dolphin's free replacement here. Without a font a game that reads it + gets an alert that text may be missing or the game may crash, and runs + on. - name: "font_japanese.bin" path: "Sys/GC/font_japanese.bin" @@ -301,9 +303,15 @@ files: size: 303693 required: false installed_on: [retrobat] - hle_fallback: true - source_ref: "source Source/Core/Common/CommonPaths.h:143, source Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130, 203-247" - note: "Size is the official font length read from an IPL dump, 0x4a24d. The fork's tree carries no font, RetroBat's dolphin-triforce package installs the free replacement here." + source_ref: "source Source/Core/Common/CommonPaths.h:143, source Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130, 161-176, 203-247, 330-344" + note: > + Read without any size check into the IPL image at 0x1aff00, keeping at + most 0x50100 bytes. The size is the IPL dump font's, 0x4a24d, the length + the code reads out of a dump (EXI_DeviceIPL.cpp:234-236). The fork's + tree carries no font, and RetroBat's dolphin-triforce package installs + Dolphin's free replacement here. Without a font a game that reads it + gets an alert that text may be missing or the game may crash, and runs + on. # -- GBA BIOS -- - name: "gba_bios.bin" From a22d9fd499a78e95453e833ba6753b2fe24f29f8 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:27:48 +0100 Subject: [PATCH 5/5] docs: add the dsperate-headless dsi boot blob dsperate-headless reads a 0x154-byte console data blob through --dsi-boot and stops on any other size. The flag has no default, so the name is a label. Without it the bytes come from the NAND's TWLCFG and HWINFO files, are generated for DSiWare run without a NAND, or stay zero in card mode. --- emulators/dsperate.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/emulators/dsperate.yml b/emulators/dsperate.yml index d0b9066c9..cf175c2a9 100644 --- a/emulators/dsperate.yml +++ b/emulators/dsperate.yml @@ -136,6 +136,16 @@ files: note: "Read only by --dsi-mode and NAND title shortcuts. DSiWare run from the game list does without it. Opened read-only, rejected without the \"DSi eMMC CID/CPU\" footer." source_ref: "src/frontend/sdl/main.cpp:1153-1166, src/frontend/sdl/main.cpp:1331-1337, src/core/nds_dsi.cpp:112-118, src/core/io/dsi_sd.cpp:105-138" + - name: "dsi_boot.bin" + system: nintendo-dsi + required: false + size: 340 + validation: [size] + found_by: content + description: "DSi console data blob" + note: "Read only by dsperate-headless, through --dsi-boot, which has no default, so the name is a label. A file of any size other than 0x154 bytes stops the run. The blob is TWLCFG bytes 0x88-0x1AF, HWINFO_N bytes 0x88-0x9B and HWINFO_S bytes 0x88-0x9F, copied to 0x02000400, 0x02000600 and 0x02FFFD68 when a DSi title starts. Without it the same bytes are read from shared1/TWLCFG0.dat or TWLCFG1.dat (the higher save count at byte 0x81) and sys/HWINFO_N.dat and sys/HWINFO_S.dat of an attached NAND, generated for DSiWare run without a NAND, and left zero in card mode." + source_ref: "src/frontend/headless/main.cpp:304, src/frontend/headless/main.cpp:386, src/frontend/headless/main.cpp:508, src/core/nds_dsi.cpp:107-111, src/core/nds_dsi.cpp:294, src/core/nds_dsi.cpp:340-343, src/core/nds_dsi.cpp:454-464, src/core/io/dsi_nand_launch.cpp:147-167, src/core/io/dsi_nand_synth.cpp:114-120" + - name: "TWLFontTable.dat" system: nintendo-dsi required: false