diff --git a/.gitattributes b/.gitattributes index 9e2b4945..1c544085 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,17 @@ launcher-rust/fixtures/** text eol=lf launcher-rust/assets/i18n.json text eol=lf # Snapshot references are images compared pixel by pixel: never touched as text. launcher-rust/snapshots/** binary +# The runner fixtures: text outputs are LF, every input and output that is bytes is never touched as text. +launcher-rust/fixtures/runner/**/*.json text eol=lf +launcher-rust/fixtures/runner/**/*.xml text eol=lf +launcher-rust/fixtures/runner/**/*.ini text eol=lf +launcher-rust/fixtures/runner/**/*.bin binary +launcher-rust/fixtures/runner/**/*.dds binary +launcher-rust/fixtures/runner/**/*.dxt1 binary +launcher-rust/fixtures/runner/**/*.dxt5 binary +launcher-rust/fixtures/runner/**/*.png binary +launcher-rust/fixtures/runner/**/*.bmp binary +launcher-rust/fixtures/runner/**/*.WAD binary +launcher-rust/fixtures/runner/**/*.prx binary +launcher-rust/fixtures/runner/**/*.sfo binary +launcher-rust/fixtures/runner/**/*.elf binary diff --git a/README.md b/README.md index a5fee56a..31c4390f 100644 --- a/README.md +++ b/README.md @@ -481,6 +481,7 @@ cargo test The page snapshots under `launcher-rust/snapshots/` are compared pixel by pixel on Windows only. `python -m unittest discover -s tests -p "test_launcher_parity_doc.py"` checks that every Python launcher test has its row in `docs/specs/2026-10-21-rust-launcher-parity.md`. +`tools/runner_fixtures.py` generates `launcher-rust/fixtures/runner/`, the inputs and the files the Python runner's modules write over them, for the Rust runner's parity tests. ### Windows C++ tests (MSYS2 CLANG64) diff --git a/launcher-rust/fixtures/runner/content/expected/full/content-profile.json b/launcher-rust/fixtures/runner/content/expected/full/content-profile.json new file mode 100644 index 00000000..72c1ed0a --- /dev/null +++ b/launcher-rust/fixtures/runner/content/expected/full/content-profile.json @@ -0,0 +1,13 @@ +{ + "title_id": "CUSA01623", + "sku": "full", + "sku_source": "explicit probe setting", + "user_params": [ + 13, + 0, + 7, + 4294967295 + ], + "mounted_addons": [], + "boot_attr": 0 +} diff --git a/launcher-rust/fixtures/runner/content/expected/full/content.bin b/launcher-rust/fixtures/runner/content/expected/full/content.bin new file mode 100644 index 00000000..9750054c Binary files /dev/null and b/launcher-rust/fixtures/runner/content/expected/full/content.bin differ diff --git a/launcher-rust/fixtures/runner/content/expected/trial/content-profile.json b/launcher-rust/fixtures/runner/content/expected/trial/content-profile.json new file mode 100644 index 00000000..f46a8b72 --- /dev/null +++ b/launcher-rust/fixtures/runner/content/expected/trial/content-profile.json @@ -0,0 +1,13 @@ +{ + "title_id": "CUSA01623", + "sku": "trial", + "sku_source": "explicit probe setting", + "user_params": [ + 13, + 0, + 7, + 4294967295 + ], + "mounted_addons": [], + "boot_attr": 0 +} diff --git a/launcher-rust/fixtures/runner/content/expected/trial/content.bin b/launcher-rust/fixtures/runner/content/expected/trial/content.bin new file mode 100644 index 00000000..e9b967d4 Binary files /dev/null and b/launcher-rust/fixtures/runner/content/expected/trial/content.bin differ diff --git a/launcher-rust/fixtures/runner/content/input/param.sfo b/launcher-rust/fixtures/runner/content/input/param.sfo new file mode 100644 index 00000000..34361ccc Binary files /dev/null and b/launcher-rust/fixtures/runner/content/input/param.sfo differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt1 b/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt1 new file mode 100644 index 00000000..2447e4d3 Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt1 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt5 b/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt5 new file mode 100644 index 00000000..34a4810d Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/alpha.dxt5 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/edges.dxt1 b/launcher-rust/fixtures/runner/dxt/expected/edges.dxt1 new file mode 100644 index 00000000..3010c7eb Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/edges.dxt1 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/edges.dxt5 b/launcher-rust/fixtures/runner/dxt/expected/edges.dxt5 new file mode 100644 index 00000000..661bad4e Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/edges.dxt5 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/flat.dxt1 b/launcher-rust/fixtures/runner/dxt/expected/flat.dxt1 new file mode 100644 index 00000000..d6d23e4a Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/flat.dxt1 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/flat.dxt5 b/launcher-rust/fixtures/runner/dxt/expected/flat.dxt5 new file mode 100644 index 00000000..1cf48edc Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/flat.dxt5 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt1 b/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt1 new file mode 100644 index 00000000..603eef05 Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt1 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt5 b/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt5 new file mode 100644 index 00000000..3a849cce Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/gradient.dxt5 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/noise.dxt1 b/launcher-rust/fixtures/runner/dxt/expected/noise.dxt1 new file mode 100644 index 00000000..69f9779b Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/noise.dxt1 differ diff --git a/launcher-rust/fixtures/runner/dxt/expected/noise.dxt5 b/launcher-rust/fixtures/runner/dxt/expected/noise.dxt5 new file mode 100644 index 00000000..fc5f455e Binary files /dev/null and b/launcher-rust/fixtures/runner/dxt/expected/noise.dxt5 differ diff --git a/launcher-rust/fixtures/runner/graphics/expected/good/environment.json b/launcher-rust/fixtures/runner/graphics/expected/good/environment.json new file mode 100644 index 00000000..a2697dee --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/good/environment.json @@ -0,0 +1,5 @@ +{ + "SPARTAN_DEFERRED_READBACK": "1", + "SPARTAN_STALE_READBACK": "1", + "SPARTAN_ASYNC_SHADERS": "1" +} diff --git a/launcher-rust/fixtures/runner/graphics/expected/good/graphics_state.json b/launcher-rust/fixtures/runner/graphics/expected/good/graphics_state.json new file mode 100644 index 00000000..ed8feee9 --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/good/graphics_state.json @@ -0,0 +1,10 @@ +{ + "last_good": { + "render_resolution": "1440p", + "engine_fps": "120", + "deferred_readback": "1", + "stale_readback": "1", + "async_shaders": "1" + }, + "attempt": false +} \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/graphics/expected/good/spartanport.ini b/launcher-rust/fixtures/runner/graphics/expected/good/spartanport.ini new file mode 100644 index 00000000..1350879c --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/good/spartanport.ini @@ -0,0 +1,2 @@ +render_resolution=1440p +engine_fps=120 diff --git a/launcher-rust/fixtures/runner/graphics/expected/reverted/environment.json b/launcher-rust/fixtures/runner/graphics/expected/reverted/environment.json new file mode 100644 index 00000000..a2697dee --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/reverted/environment.json @@ -0,0 +1,5 @@ +{ + "SPARTAN_DEFERRED_READBACK": "1", + "SPARTAN_STALE_READBACK": "1", + "SPARTAN_ASYNC_SHADERS": "1" +} diff --git a/launcher-rust/fixtures/runner/graphics/expected/reverted/graphics_state.json b/launcher-rust/fixtures/runner/graphics/expected/reverted/graphics_state.json new file mode 100644 index 00000000..ed8feee9 --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/reverted/graphics_state.json @@ -0,0 +1,10 @@ +{ + "last_good": { + "render_resolution": "1440p", + "engine_fps": "120", + "deferred_readback": "1", + "stale_readback": "1", + "async_shaders": "1" + }, + "attempt": false +} \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/graphics/expected/reverted/spartanport.ini b/launcher-rust/fixtures/runner/graphics/expected/reverted/spartanport.ini new file mode 100644 index 00000000..8681e6b1 --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/reverted/spartanport.ini @@ -0,0 +1,6 @@ +render_resolution=1440p +engine_fps=120 +restart_unconfirmed=0 +deferred_readback=1 +stale_readback=1 +async_shaders=1 diff --git a/launcher-rust/fixtures/runner/graphics/expected/tried/environment.json b/launcher-rust/fixtures/runner/graphics/expected/tried/environment.json new file mode 100644 index 00000000..a2697dee --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/tried/environment.json @@ -0,0 +1,5 @@ +{ + "SPARTAN_DEFERRED_READBACK": "1", + "SPARTAN_STALE_READBACK": "1", + "SPARTAN_ASYNC_SHADERS": "1" +} diff --git a/launcher-rust/fixtures/runner/graphics/expected/tried/graphics_state.json b/launcher-rust/fixtures/runner/graphics/expected/tried/graphics_state.json new file mode 100644 index 00000000..4d329057 --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/tried/graphics_state.json @@ -0,0 +1,10 @@ +{ + "last_good": { + "render_resolution": "1440p", + "engine_fps": "120", + "deferred_readback": "1", + "stale_readback": "1", + "async_shaders": "1" + }, + "attempt": true +} \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/graphics/expected/tried/spartanport.ini b/launcher-rust/fixtures/runner/graphics/expected/tried/spartanport.ini new file mode 100644 index 00000000..b23c9fbd --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/expected/tried/spartanport.ini @@ -0,0 +1,3 @@ +render_resolution=4K +engine_fps=240 +restart_unconfirmed=1 diff --git a/launcher-rust/fixtures/runner/graphics/input/edit.json b/launcher-rust/fixtures/runner/graphics/input/edit.json new file mode 100644 index 00000000..b0ca0d4c --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/input/edit.json @@ -0,0 +1,5 @@ +{ + "render_resolution": "4K", + "engine_fps": "240", + "restart_unconfirmed": "1" +} diff --git a/launcher-rust/fixtures/runner/graphics/input/graphics_state.json b/launcher-rust/fixtures/runner/graphics/input/graphics_state.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/input/graphics_state.json @@ -0,0 +1 @@ +{} diff --git a/launcher-rust/fixtures/runner/graphics/input/spartanport.ini b/launcher-rust/fixtures/runner/graphics/input/spartanport.ini new file mode 100644 index 00000000..1350879c --- /dev/null +++ b/launcher-rust/fixtures/runner/graphics/input/spartanport.ini @@ -0,0 +1,2 @@ +render_resolution=1440p +engine_fps=120 diff --git a/launcher-rust/fixtures/runner/icon/expected/window_icon.bmp b/launcher-rust/fixtures/runner/icon/expected/window_icon.bmp new file mode 100644 index 00000000..9349350d Binary files /dev/null and b/launcher-rust/fixtures/runner/icon/expected/window_icon.bmp differ diff --git a/launcher-rust/fixtures/runner/isa/expected/isa-rewrite.bin b/launcher-rust/fixtures/runner/isa/expected/isa-rewrite.bin new file mode 100644 index 00000000..8edb6d5f Binary files /dev/null and b/launcher-rust/fixtures/runner/isa/expected/isa-rewrite.bin differ diff --git a/launcher-rust/fixtures/runner/isa/expected/sites.json b/launcher-rust/fixtures/runner/isa/expected/sites.json new file mode 100644 index 00000000..62d3e890 --- /dev/null +++ b/launcher-rust/fixtures/runner/isa/expected/sites.json @@ -0,0 +1,122 @@ +[ + { + "offset": 0, + "length": 5, + "kind": "BLSR", + "width": 32, + "mode": "JMP", + "registers": { + "dst": 1, + "src1": 1, + "src2": 255, + "base": 255, + "index": 255 + }, + "mem_slot": 0, + "scale": 0, + "mem_flags": 0, + "disp": 0, + "original": "c4e270f3c9", + "relocated": "" + }, + { + "offset": 5, + "length": 4, + "kind": "MOVBE_STORE", + "width": 32, + "mode": "JMP_RELOCATE", + "registers": { + "dst": 255, + "src1": 1, + "src2": 255, + "base": 0, + "index": 255 + }, + "mem_slot": 1, + "scale": 1, + "mem_flags": 0, + "disp": 0, + "original": "0f38f108", + "relocated": "90" + }, + { + "offset": 10, + "length": 9, + "kind": "BEXTR", + "width": 32, + "mode": "JMP", + "registers": { + "dst": 0, + "src1": 255, + "src2": 1, + "base": 255, + "index": 255 + }, + "mem_slot": 2, + "scale": 1, + "mem_flags": 1, + "disp": 31, + "original": "c4e270f7050c000000", + "relocated": "" + }, + { + "offset": 19, + "length": 5, + "kind": "LZCNT", + "width": 16, + "mode": "JMP", + "registers": { + "dst": 0, + "src1": 1, + "src2": 255, + "base": 255, + "index": 255 + }, + "mem_slot": 0, + "scale": 0, + "mem_flags": 0, + "disp": 0, + "original": "66f30fbdc1", + "relocated": "" + }, + { + "offset": 24, + "length": 5, + "kind": "TZCNT", + "width": 64, + "mode": "JMP", + "registers": { + "dst": 0, + "src1": 1, + "src2": 255, + "base": 255, + "index": 255 + }, + "mem_slot": 0, + "scale": 0, + "mem_flags": 0, + "disp": 0, + "original": "f3480fbcc1", + "relocated": "" + }, + { + "offset": 29, + "length": 4, + "kind": "MOVBE_STORE", + "width": 32, + "mode": "INT3", + "registers": { + "dst": 255, + "src1": 1, + "src2": 255, + "base": 0, + "index": 255 + }, + "mem_slot": 1, + "scale": 1, + "mem_flags": 0, + "disp": 0, + "original": "0f38f108", + "relocated": "" + } +] diff --git a/launcher-rust/fixtures/runner/isa/input/boot-linked.bin b/launcher-rust/fixtures/runner/isa/input/boot-linked.bin new file mode 100644 index 00000000..4d2b8da2 Binary files /dev/null and b/launcher-rust/fixtures/runner/isa/input/boot-linked.bin differ diff --git a/launcher-rust/fixtures/runner/link/expected/boot-libc.bin b/launcher-rust/fixtures/runner/link/expected/boot-libc.bin new file mode 100644 index 00000000..22e7fa6f Binary files /dev/null and b/launcher-rust/fixtures/runner/link/expected/boot-libc.bin differ diff --git a/launcher-rust/fixtures/runner/link/expected/boot-linked.bin b/launcher-rust/fixtures/runner/link/expected/boot-linked.bin new file mode 100644 index 00000000..32752dbc Binary files /dev/null and b/launcher-rust/fixtures/runner/link/expected/boot-linked.bin differ diff --git a/launcher-rust/fixtures/runner/link/expected/libc-link.json b/launcher-rust/fixtures/runner/link/expected/libc-link.json new file mode 100644 index 00000000..b55954ee --- /dev/null +++ b/launcher-rust/fixtures/runner/link/expected/libc-link.json @@ -0,0 +1,37 @@ +{ + "base": "0x10000", + "size": 4096, + "sha256": "16bcdb5c0a28f581da21dc76c9e73ed1e8ec35398b85be38352691f4ff4cba33", + "init": "0x10010", + "bindings": 1, + "tls_module_id": 2, + "fs_loads_patched": 2, + "main_tls": { + "vaddr": 3072, + "filesz": 16, + "memsz": 48, + "align": 16 + }, + "tls_relocations": 1, + "tls_template_bytes": 16, + "tls_memory_bytes": 48, + "imports": [ + "bzQExy189ZI#q#q", + "AAAAAAAAAAA#q#q", + "DDDDDDDDDDD#q#q" + ], + "relocation_counts": { + "8": 1, + "16": 1, + "1": 1, + "6": 1, + "7": 1 + }, + "symbol_bindings": [ + { + "import_name": "bzQExy189ZI#q#q", + "address": "0x10010", + "kind": 1 + } + ] +} diff --git a/launcher-rust/fixtures/runner/link/expected/link.json b/launcher-rust/fixtures/runner/link/expected/link.json new file mode 100644 index 00000000..ac24c23a --- /dev/null +++ b/launcher-rust/fixtures/runner/link/expected/link.json @@ -0,0 +1,43 @@ +{ + "modules": [ + { + "file": "libc.prx", + "base": "0x10000", + "size": 4096, + "init": "0x10010", + "tls_address": "0x10500", + "tls_memsz": 48, + "tls_filesz": 16, + "tls_module": 2, + "tls_relocations": 1, + "exports": 3, + "sha256": "16bcdb5c0a28f581da21dc76c9e73ed1e8ec35398b85be38352691f4ff4cba33" + }, + { + "file": "libSceFios2.prx", + "base": "0x20000", + "size": 2048, + "init": "0x20000", + "tls_address": "0x0", + "tls_memsz": 0, + "tls_filesz": 0, + "tls_module": 0, + "tls_relocations": 0, + "exports": 1, + "sha256": "a7ae78118c4fcb8dd27129c2b8059fbcf2e0d685b4ad8b40038fa43f4d93e4f2" + } + ], + "bindings": 2, + "imports": 4, + "fs_loads_patched": 4, + "main_tls": { + "vaddr": 3072, + "filesz": 16, + "memsz": 48, + "align": 16 + }, + "unresolved_imports": [ + "AAAAAAAAAAA#libc", + "DDDDDDDDDDD#libc" + ] +} diff --git a/launcher-rust/fixtures/runner/link/input/boot.bin b/launcher-rust/fixtures/runner/link/input/boot.bin new file mode 100644 index 00000000..52118313 Binary files /dev/null and b/launcher-rust/fixtures/runner/link/input/boot.bin differ diff --git a/launcher-rust/fixtures/runner/link/input/eboot.bin b/launcher-rust/fixtures/runner/link/input/eboot.bin new file mode 100644 index 00000000..e1c1d5ed Binary files /dev/null and b/launcher-rust/fixtures/runner/link/input/eboot.bin differ diff --git a/launcher-rust/fixtures/runner/link/input/sce_module/libSceFios2.prx b/launcher-rust/fixtures/runner/link/input/sce_module/libSceFios2.prx new file mode 100644 index 00000000..c7cafc1e Binary files /dev/null and b/launcher-rust/fixtures/runner/link/input/sce_module/libSceFios2.prx differ diff --git a/launcher-rust/fixtures/runner/link/input/sce_module/libc.prx b/launcher-rust/fixtures/runner/link/input/sce_module/libc.prx new file mode 100644 index 00000000..d1ab1bb0 Binary files /dev/null and b/launcher-rust/fixtures/runner/link/input/sce_module/libc.prx differ diff --git a/launcher-rust/fixtures/runner/patches/expected/patch_status.json b/launcher-rust/fixtures/runner/patches/expected/patch_status.json new file mode 100644 index 00000000..f1652f37 --- /dev/null +++ b/launcher-rust/fixtures/runner/patches/expected/patch_status.json @@ -0,0 +1 @@ +{"sha256": "ffed585c6a2b513546eba7f283c45ad27382a8e31cf54fe03b4e1c0ca63195d5", "applied": ["Found", "Call", "Data"], "skipped": {"Missing": "signature not found"}} \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/patches/expected/patches-merged.xml b/launcher-rust/fixtures/runner/patches/expected/patches-merged.xml new file mode 100644 index 00000000..0366f3d0 --- /dev/null +++ b/launcher-rust/fixtures/runner/patches/expected/patches-merged.xml @@ -0,0 +1,44 @@ + + + + CUSA01623 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/patches/expected/patches.bin b/launcher-rust/fixtures/runner/patches/expected/patches.bin new file mode 100644 index 00000000..bdf3d8d6 Binary files /dev/null and b/launcher-rust/fixtures/runner/patches/expected/patches.bin differ diff --git a/launcher-rust/fixtures/runner/patches/input/eboot.bin b/launcher-rust/fixtures/runner/patches/input/eboot.bin new file mode 100644 index 00000000..9895ccd9 --- /dev/null +++ b/launcher-rust/fixtures/runner/patches/input/eboot.bin @@ -0,0 +1 @@ +synthetic eboot.bin of the patches case diff --git a/launcher-rust/fixtures/runner/patches/input/eboot.elf b/launcher-rust/fixtures/runner/patches/input/eboot.elf new file mode 100644 index 00000000..36bb9e2f Binary files /dev/null and b/launcher-rust/fixtures/runner/patches/input/eboot.elf differ diff --git a/launcher-rust/fixtures/runner/patches/input/mod.xml b/launcher-rust/fixtures/runner/patches/input/mod.xml new file mode 100644 index 00000000..087b4410 --- /dev/null +++ b/launcher-rust/fixtures/runner/patches/input/mod.xml @@ -0,0 +1,13 @@ + + + CUSA01623 + + + + + + + + + + \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/patches/input/param.sfo b/launcher-rust/fixtures/runner/patches/input/param.sfo new file mode 100644 index 00000000..331ffa86 Binary files /dev/null and b/launcher-rust/fixtures/runner/patches/input/param.sfo differ diff --git a/launcher-rust/fixtures/runner/patches/input/patches.xml b/launcher-rust/fixtures/runner/patches/input/patches.xml new file mode 100644 index 00000000..1d67a4be --- /dev/null +++ b/launcher-rust/fixtures/runner/patches/input/patches.xml @@ -0,0 +1,21 @@ + + + CUSA01623 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launcher-rust/fixtures/runner/prepare/expected/analysis.json b/launcher-rust/fixtures/runner/prepare/expected/analysis.json new file mode 100644 index 00000000..4d56ea28 --- /dev/null +++ b/launcher-rust/fixtures/runner/prepare/expected/analysis.json @@ -0,0 +1,142 @@ +{ + "source_sha256": "138b11e351ce338943e47f76805cfba5380996355ea098c97be4fd586002d96c", + "source_bytes": 5400, + "sfo": { + "APP_VER": "01.02", + "CONTENT_ID": "EP9000-CUSA01623_00-GOW3REMASTERED00", + "TITLE": "ゴッド・オブ・ウォーIII リマスタード", + "TITLE_ID": "CUSA01623", + "USER_DEFINED_PARAM_1": 13, + "USER_DEFINED_PARAM_2": 0, + "USER_DEFINED_PARAM_3": 7, + "USER_DEFINED_PARAM_4": 4294967295 + }, + "entry": "0x10", + "image_bytes": 4096, + "program_headers": [ + { + "type": 1, + "flags": 5, + "offset": 4096, + "vaddr": 0, + "paddr": 0, + "filesz": 2048, + "memsz": 2048, + "align": 16384 + }, + { + "type": 1, + "flags": 6, + "offset": 6144, + "vaddr": 2048, + "paddr": 2048, + "filesz": 2048, + "memsz": 2048, + "align": 16384 + }, + { + "type": 2, + "flags": 4, + "offset": 8192, + "vaddr": 0, + "paddr": 0, + "filesz": 192, + "memsz": 0, + "align": 8 + }, + { + "type": 1627389952, + "flags": 4, + "offset": 8448, + "vaddr": 0, + "paddr": 0, + "filesz": 344, + "memsz": 0, + "align": 8 + }, + { + "type": 1627389953, + "flags": 4, + "offset": 8960, + "vaddr": 128, + "paddr": 128, + "filesz": 0, + "memsz": 64, + "align": 8 + }, + { + "type": 7, + "flags": 4, + "offset": 7168, + "vaddr": 3072, + "paddr": 3072, + "filesz": 16, + "memsz": 48, + "align": 16 + } + ], + "self_segments": [ + { + "flags": "0x800", + "offset": 768, + "size": 2048 + }, + { + "flags": "0x100800", + "offset": 2816, + "size": 2048 + }, + { + "flags": "0x200800", + "offset": 4864, + "size": 192 + }, + { + "flags": "0x300800", + "offset": 5056, + "size": 344 + } + ], + "unavailable_metadata_headers": [], + "needed": [ + "libc.prx" + ], + "relocation_counts": { + "8": 1, + "6": 2, + "1": 1, + "7": 2 + }, + "import_count": 2, + "imports": [ + "bzQExy189ZI#q#q", + "AAAAAAAAAAA#q#q" + ], + "import_name_hints": { + "bzQExy189ZI#q#q": "_init_env" + }, + "libc_evidence": { + "sha256": "16bcdb5c0a28f581da21dc76c9e73ed1e8ec35398b85be38352691f4ff4cba33", + "init_env_is_ret": true, + "symbol": "bzQExy189ZI#C#A", + "address": "0x10", + "size": 1, + "bytes": "c3", + "ios_base_init": { + "symbol": "sqWytnhYdEg#C#A", + "address": "0x20", + "size": 3, + "code_sha256": "251447ee91a9067dcd6ab96703133f617565974cd6c4819021760c4688c91abf", + "name": "_ZNSt8ios_base4InitC1Ev" + } + }, + "bundled_modules": [ + "libc.prx" + ], + "resources": { + "PART1.WAD": 1, + "sound": 1 + }, + "resource_bytes": 132, + "status": "Prepared only; execution and Vulkan are tested separately." +} diff --git a/launcher-rust/fixtures/runner/prepare/expected/boot.bin b/launcher-rust/fixtures/runner/prepare/expected/boot.bin new file mode 100644 index 00000000..2f5242ca Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/expected/boot.bin differ diff --git a/launcher-rust/fixtures/runner/prepare/expected/eboot.elf b/launcher-rust/fixtures/runner/prepare/expected/eboot.elf new file mode 100644 index 00000000..4e828a14 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/expected/eboot.elf differ diff --git a/launcher-rust/fixtures/runner/prepare/expected/entry.bin b/launcher-rust/fixtures/runner/prepare/expected/entry.bin new file mode 100644 index 00000000..7a13f328 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/expected/entry.bin differ diff --git a/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/PART1.WAD b/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/PART1.WAD new file mode 100644 index 00000000..edf1f4a8 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/PART1.WAD differ diff --git a/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/sound/music.bin b/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/sound/music.bin new file mode 100644 index 00000000..eeb57607 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/input/dvdroot_ps4/sound/music.bin differ diff --git a/launcher-rust/fixtures/runner/prepare/input/eboot.bin b/launcher-rust/fixtures/runner/prepare/input/eboot.bin new file mode 100644 index 00000000..e1c1d5ed Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/input/eboot.bin differ diff --git a/launcher-rust/fixtures/runner/prepare/input/sce_module/libc.prx b/launcher-rust/fixtures/runner/prepare/input/sce_module/libc.prx new file mode 100644 index 00000000..d1ab1bb0 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/input/sce_module/libc.prx differ diff --git a/launcher-rust/fixtures/runner/prepare/input/sce_sys/param.sfo b/launcher-rust/fixtures/runner/prepare/input/sce_sys/param.sfo new file mode 100644 index 00000000..d6c3bc74 Binary files /dev/null and b/launcher-rust/fixtures/runner/prepare/input/sce_sys/param.sfo differ diff --git a/tests/test_runner_generators.py b/tests/test_runner_generators.py new file mode 100644 index 00000000..23ff3100 --- /dev/null +++ b/tests/test_runner_generators.py @@ -0,0 +1,58 @@ +"""The Rust runner's parity fixtures: what tools/runner_fixtures.py wrote from the Python modules.""" +import unittest + +from paths import ROOT + +FIXTURES = ROOT / 'launcher-rust/fixtures/runner' +DXT_NAMES = ('gradient', 'edges', 'alpha', 'noise', 'flat') + +# case -> (input files, expected files), relative to the case folder's input/ and expected/. +CASES = { + 'prepare': (['eboot.bin', 'sce_module/libc.prx', 'sce_sys/param.sfo', 'dvdroot_ps4/PART1.WAD'], + ['boot.bin', 'eboot.elf', 'entry.bin', 'analysis.json']), + 'link': (['boot.bin', 'eboot.bin', 'sce_module/libc.prx', 'sce_module/libSceFios2.prx'], + ['boot-libc.bin', 'libc-link.json', 'boot-linked.bin', 'link.json']), + 'isa': (['boot-linked.bin'], ['isa-rewrite.bin', 'sites.json']), + 'content': (['param.sfo'], ['full/content.bin', 'full/content-profile.json', + 'trial/content.bin', 'trial/content-profile.json']), + 'patches': (['eboot.elf', 'eboot.bin', 'param.sfo', 'patches.xml', 'mod.xml'], + ['patches-merged.xml', 'patches.bin', 'patch_status.json']), + 'graphics': (['spartanport.ini', 'graphics_state.json', 'edit.json'], + [f'{state}/{name}' for state in ('good', 'tried', 'reverted') + for name in ('spartanport.ini', 'graphics_state.json', 'environment.json')]), + 'dxt': ([f'{name}.png' for name in DXT_NAMES], + [f'{name}.{fmt}' for name in DXT_NAMES for fmt in ('dxt1', 'dxt5')]), + 'icon': ([], ['window_icon.bmp']), +} + + +class RunnerFixtures(unittest.TestCase): + def test_every_case_has_its_files(self): + for case, (inputs, expected) in CASES.items(): + for folder, names in (('input', inputs), ('expected', expected)): + for name in names: + path = FIXTURES / case / folder / name + self.assertTrue(path.is_file(), f'{case}/{folder}/{name}') + + def test_dxt_strips_are_whole_blocks(self): + for name in DXT_NAMES: + # 64 x 64 pixels are 16 x 16 blocks of 8 (DXT1) and 16 (DXT5) bytes. + dxt1 = (FIXTURES / 'dxt/expected' / f'{name}.dxt1').read_bytes() + dxt5 = (FIXTURES / 'dxt/expected' / f'{name}.dxt5').read_bytes() + self.assertEqual((len(dxt1) % 8, len(dxt1)), (0, 16 * 16 * 8), name) + self.assertEqual((len(dxt5) % 16, len(dxt5)), (0, 16 * 16 * 16), name) + + def test_content_profile_is_twenty_eight_bytes(self): + for sku in ('full', 'trial'): + data = (FIXTURES / 'content/expected' / sku / 'content.bin').read_bytes() + self.assertEqual(len(data), 28, sku) + self.assertEqual(data[:8], b'G3CONT01', sku) + + def test_isa_table_of_the_c_loader_fixture_is_reproduced(self): + # The isa input is a G3PROBE5 image holding isa_fixture.bin's code: its table is that file. + table = (FIXTURES / 'isa/expected/isa-rewrite.bin').read_bytes() + self.assertEqual(table, (ROOT / 'tests/data/isa_fixture.bin').read_bytes()) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/runner_fixtures.py b/tools/runner_fixtures.py new file mode 100644 index 00000000..61a60734 --- /dev/null +++ b/tools/runner_fixtures.py @@ -0,0 +1,492 @@ +"""One-off: runs the Python runner's own modules (scripts/*.py) over synthetic inputs and stores what they +write in launcher-rust/fixtures/runner//{input,expected}/, for the Rust runner's byte-for-byte parity tests. + +Run from anywhere: python tools/runner_fixtures.py (needs Pillow and iced_x86) + +Every case folder holds input/ (what the module reads) and expected/ (what it wrote). A game folder keeps its +own layout inside input/ (eboot.bin, sce_module/*.prx, sce_sys/param.sfo, dvdroot_ps4/...), so a test can +copy input/ as it is. Text outputs are written with LF; no expected file holds the temporary folder (the +generator stops when one does). + + prepare input: a synthetic eboot.bin SELF (two PT_LOAD, dynamic, library blob with imports and the four + relocation kinds, TLS) with sce_module/libc.prx (its _init_env is RET), sce_sys/param.sfo and a + dvdroot_ps4 folder. expected: boot.bin, eboot.elf, entry.bin, analysis.json (scripts/prepare.py). + link input: the same eboot.bin, libc.prx and libSceFios2.prx, and boot.bin from tests/test_probe.package + (a freshly prepared G3PROBE2 image with the eboot's imports). expected: boot-libc.bin and + libc-link.json (link_libc.py), boot-linked.bin and link.json (link_modules.py). + isa input: boot-linked.bin, a G3PROBE5 image (tests/test_isa_rewrite.boot_linked) whose one executable + segment is isa_rewrite.fixture_code(). expected: isa-rewrite.bin (equal to tests/data/isa_fixture.bin) + and sites.json, one entry per site: offset, length, kind and mode names, width, registers + (dst, src1, src2, base, index; 255 is none), mem_slot, scale, mem_flags, disp, original and + relocated bytes as hex. + content input: param.sfo with the four user-defined parameters. expected: full/ and trial/ with + content.bin and content-profile.json (content_profile.py). + patches input: eboot.elf holding the signatures of tests/test_patches.SignatureTests in its one PT_LOAD, + eboot.bin and param.sfo (CUSA01623, 01.02: the game folder patches.py looks the profile up in), + patches.xml (SignatureTests.XML with a TitleID so the mod file merges in) and mod.xml + (ModPatchTests.MOD). patches.py main() runs with --mod-patches mod.xml --extra "Found;Call;Missing;Data" + and SPARTAN_PATCHES_ONLY=1. expected: patches-merged.xml, patches.bin, patch_status.json (Missing is + skipped: its second signature is absent). + graphics input: spartanport.ini, graphics_state.json ({}) and edit.json, the keys written between the + launches. graphics_options.prepare runs three times, as in + test_unconfirmed_restart_is_tried_once_then_reverted; expected// holds the ini, + the state file and environment.json after each launch. + dxt input: gradient, edges, alpha, noise and flat, 64 x 64 RGBA PNGs (noise from a fixed seed). + expected: .dxt1 and .dxt5, the 4-row strips of glyphs.encode_region concatenated with + each DDS header (128 bytes) stripped (RGB for DXT1, RGBA for DXT5). + icon expected: window_icon.bmp, launcher/spartanport.png as run.py makes it (RGBA, 64 x 64 LANCZOS, BMP).""" +import contextlib +import io +import json +import os +import random +import shutil +import struct +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +FIXTURES = ROOT / 'launcher-rust' / 'fixtures' / 'runner' +WORK = Path(tempfile.mkdtemp(prefix='runner_fixtures_')) + +sys.path.insert(0, str(ROOT / 'tests')) +sys.path.insert(0, str(ROOT / 'scripts')) +import content_profile # noqa: E402 +import glyphs # noqa: E402 +import graphics_options # noqa: E402 +import isa_rewrite # noqa: E402 +import link_libc # noqa: E402 +import link_modules # noqa: E402 +import patches # noqa: E402 +import prepare as prepare_module # noqa: E402 +from PIL import Image # noqa: E402 +from test_isa_rewrite import boot_linked # noqa: E402 +from test_patches import ModPatchTests, SignatureTests # noqa: E402 +from test_probe import package # noqa: E402 + +FS_LOAD = bytes.fromhex('64488b042500000000') # mov rax, fs:[0]: what the linkers rewrite to GS +PARAMS = {'USER_DEFINED_PARAM_1': 13, 'USER_DEFINED_PARAM_2': 0, 'USER_DEFINED_PARAM_3': 7, + 'USER_DEFINED_PARAM_4': 0xffffffff} +EDIT = {'render_resolution': '4K', 'engine_fps': '240', 'restart_unconfirmed': '1'} + + +# -- Synthetic files ---------------------------------------------------------------------------------------- + +def sfo_bytes(values): + """A param.sfo (PSF 1.1): strings (0x204) and unsigned integers (0x404), keys sorted.""" + keys, data, entries = b'', b'', [] + for key, value in sorted(values.items()): + if isinstance(value, int): + fmt, raw, capacity = 0x404, struct.pack('', '\n CUSA01623', 1) + put(inputs / 'patches.xml', xml.encode()) + put(inputs / 'mod.xml', ModPatchTests.MOD.encode()) + + game = stage(WORK / 'patches-game', {'eboot.bin': (inputs / 'eboot.bin').read_bytes(), + 'sce_sys/param.sfo': (inputs / 'param.sfo').read_bytes()}) + out = WORK / 'patches-out' + put(out / 'eboot.elf', (inputs / 'eboot.elf').read_bytes()) + saved = patches.GAMES, sys.argv, os.environ.get('SPARTAN_PATCHES_ONLY') + patches.GAMES = [({'CUSA01623'}, WORK / 'patches.xml', '01.02', 'God of War III')] + put(WORK / 'patches.xml', xml.encode()) + os.environ['SPARTAN_PATCHES_ONLY'] = '1' + sys.argv = ['patches.py', '--out', str(out), '--game-dir', str(game), '--mod-patches', str(inputs / 'mod.xml'), + '--extra', 'Found;Call;Missing;Data'] + try: + quiet(patches.main) + finally: + patches.GAMES, sys.argv = saved[0], saved[1] + if saved[2] is None: + del os.environ['SPARTAN_PATCHES_ONLY'] + else: + os.environ['SPARTAN_PATCHES_ONLY'] = saved[2] + put(expected / 'patches-merged.xml', lf(out / 'patches-merged.xml')) + put(expected / 'patches.bin', (out / 'patches.bin').read_bytes()) + put(expected / 'patch_status.json', lf(out / 'patch_status.json')) + + +def case_graphics(): + inputs, expected = case_folder('graphics') + put(inputs / 'spartanport.ini', b'render_resolution=1440p\nengine_fps=120\n') + put(inputs / 'graphics_state.json', b'{}\n') + put(inputs / 'edit.json', text(EDIT).encode()) + work = WORK / 'graphics' + work.mkdir() + ini, state = work / 'spartanport.ini', work / 'graphics_state.json' + shutil.copyfile(inputs / 'spartanport.ini', ini) + shutil.copyfile(inputs / 'graphics_state.json', state) + for step in ('good', 'tried', 'reverted'): + if step == 'tried': + graphics_options.write_keys(ini, EDIT) # the in-game menu asking for a restart + options, _ = graphics_options.prepare(ini, state) + put(expected / step / 'spartanport.ini', lf(ini)) + put(expected / step / 'graphics_state.json', lf(state)) + put(expected / step / 'environment.json', text(graphics_options.environment(options)).encode()) + + +def test_images(): + """The five 64 x 64 RGBA test images.""" + size = 64 + noise = random.Random(20260101) + drawings = { + 'gradient': lambda x, y: (x * 4, y * 4, (x + y) * 2, 255), + 'edges': lambda x, y: ((255, 255, 255, 255) if ((x + 1) // 5 + (y + 3) // 7 + (x == y) * 2) % 2 == 0 + else (20, 30, 40, 255)), + 'alpha': lambda x, y: (230, 90 + y * 2, 40, x * 4), + 'noise': lambda x, y: tuple(noise.randrange(256) for _ in range(4)), + 'flat': lambda x, y: (200, 100, 50, 255), + } + images = {} + for name, draw in drawings.items(): + image = Image.new('RGBA', (size, size)) + image.putdata([draw(x, y) for y in range(size) for x in range(size)]) + images[name] = image + return images + + +def strips(image, fourcc): + """glyphs.encode_region's 4-row strips of the whole picture, DDS headers stripped, concatenated.""" + image = image.convert('RGBA' if fourcc == 'DXT5' else 'RGB') + width, height = image.size + encoded = b'' + for y in range(0, height, 4): + strip = io.BytesIO() + image.crop((0, y, width, min(y + 4, height))).save(strip, 'DDS', pixel_format=fourcc) + encoded += strip.getvalue()[128:] + return encoded + + +def encode_region_blocks(image, fourcc): + """The same bytes through glyphs.encode_region itself, over a texture holding just the top level.""" + block = 16 if fourcc == 'DXT5' else 8 + width, height = image.size + data = bytearray(128 + (width // 4) * (height // 4) * block) + struct.pack_into('