diff --git a/docs/hails-additions/01-iso-extraction.md b/docs/hails-additions/01-iso-extraction.md new file mode 100644 index 00000000..310539e0 --- /dev/null +++ b/docs/hails-additions/01-iso-extraction.md @@ -0,0 +1,56 @@ +# 1. ISO extraction argument order + +## Problem + +Selecting a Skate 3 ISO in setup failed with `Conversion failed. See ...\extract.log` +right after "Extracting your ISO". Setup deletes its temporary directory on +failure, so the log named in the message no longer exists. + +## Root cause + +`extract-xiso` (the pinned `build-202505152050` Windows release, v2.7.1) stops +parsing options at the first image path. Setup called it as + +``` +extract-xiso -x -d +``` + +so `-d` and `` were treated as two more images to extract. The tool +extracted the real ISO into the **current working directory** as +`\` (not the temporary directory), then failed on the "images": + +``` +open error: -d No such file or directory +``` + +and exited with code 1, which setup reports as a conversion failure. The +extraction itself was complete: all 103 files and 6,404,940,920 bytes matched +`extract-xiso -l`. + +## Change + +Pass options before the image, in both call sites: + +- `tools/asset_pipeline/customiser_setup.py` (`install`, used by the setup GUI) +- `tools/asset_pipeline/install.py` (`_install`) + +``` +extract-xiso -x -d +``` + +Note: `-d` does not create parent directories. Both call sites extract into a +subdirectory of an existing temporary/work directory, so this holds. + +## Verification + +- Reproduced the failure by running the tool with the original argument order + (stderr above, exit code 1, files written to the working directory). +- With the new order: exit code 0, all 103 files in the target directory, + byte total identical to the image listing, nothing written to the working + directory. + +## Notes for upstream + +- Upstream PR #9 (Linux/macOS port) contains the same fix, credited there to + PR #7 ("BSD getopt compatibility"). If #9 is merged first, this change is + already covered. diff --git a/tools/asset_pipeline/customiser_setup.py b/tools/asset_pipeline/customiser_setup.py index 518ddbb0..2efd0aec 100644 --- a/tools/asset_pipeline/customiser_setup.py +++ b/tools/asset_pipeline/customiser_setup.py @@ -175,7 +175,8 @@ def install(iso, base, game_exe, report, refresh=False): source = Path(temp)/'disc' extractor = core.dependency(base/'tools', 'extract-xiso', core.XISO_URL, core.XISO_SHA, report) with (Path(temp)/'extract.log').open('w') as log: - core.run([extractor, '-x', selected, '-d', source], log, report) + # extract-xiso stops parsing options at the first image path. + core.run([extractor, '-x', '-d', source, selected], log, report) else: source = source_directory(selected, require_core=not refresh) stage = core._install(iso, base, game_exe, report, game_root=source, refresh=refresh, diff --git a/tools/asset_pipeline/install.py b/tools/asset_pipeline/install.py index b199aa8c..56a9b0f4 100644 --- a/tools/asset_pipeline/install.py +++ b/tools/asset_pipeline/install.py @@ -300,7 +300,8 @@ def _install(iso,base,game_exe,report,game_root=None,refresh=False,finalize=None extractor=dependency(base/'tools','extract-xiso',XISO_URL,XISO_SHA,report) game_root=work/'disc' report('Extracting your ISO') - run([extractor,'-x',iso,'-d',game_root],log,report) + # extract-xiso stops parsing options at the first image path. + run([extractor,'-x','-d',game_root,iso],log,report) else:game_root=game_root.resolve() required_files=['default.xex'] if 'core' in groups:required_files += ['data/big/miscload.big','data/big/miscboot.big','data/big/db.big']