diff --git a/install.ps1 b/install.ps1 index 3e0fa3b13..cf7abb20c 100644 --- a/install.ps1 +++ b/install.ps1 @@ -16,7 +16,7 @@ # and Invoke-WebRequest throws on a cut transfer before the hash is read: # the site's build rewrites this line there (scripts/sitebootstrap.py). $mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that." - $defaultInstallSha256 = "02b1a66c6f9d63122c8064005ec6ae7c7504820c28e5027a5894aef209be7aa5" + $defaultInstallSha256 = "57b285ce8de68f8ece0d1adb9f3b7a7fc8cda5ab7004c3601d5ef357353ab69e" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index a1956f4d5..89a945fab 100644 --- a/install.py +++ b/install.py @@ -12168,16 +12168,35 @@ def _copy_reason(entry: dict, copy: LocalCopy, bios_path: Path, known: set[str]) return _place_reason(entry, copy.path, copy.index, expected, known)[0] +def _twin_in_place(entry: dict, files: list[dict], bios_path: Path) -> bool: + """Whether the name a case variant shares with another entry, but for + the case (bios.col and BIOS.col), is in place.""" + folded = str(entry.get("dest", "")).casefold() + for twin in files: + if twin is entry or twin.get("case_variant") or \ + str(twin.get("dest", "")).casefold() != folded: + continue + try: + if _is_file(_entry_path(bios_path, twin)): + return True + except ValueError: + continue + return False + + def _found_elsewhere( entry: dict, index: FolderIndex, bios_path: Path, files: list[dict], known: set[str], zipped: _ZippedLookup | None = None, ) -> tuple[str, str]: """(reason, folder too deep) for an entry missing at its place.""" dest = str(entry.get("dest", "")) - if entry.get("case_variant"): + if entry.get("case_variant") and _twin_in_place(entry, files, bios_path): # A pack is read on Windows and macOS too, where two names that # differ only by case are one file: it carries one of the pair. Moving # the other copy here would take it from the core that opens it. + # Only its twin in place says a pack was used: on an empty folder the + # note named a pack never downloaded and pushed the BIOS files the + # user looked for into "... and N more". return ("a second name the pack leaves out: Windows and macOS see it " "as the same file. Run the command without --check to add it"), "" name = dest.rsplit("/", 1)[-1] diff --git a/install.sh b/install.sh index 83346b4a8..5e88c6d10 100755 --- a/install.sh +++ b/install.sh @@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins # curl or wget stop on a cut transfer before the hash is read: the site's # build rewrites this line there (scripts/sitebootstrap.py). MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that." -DEFAULT_INSTALL_SHA256="02b1a66c6f9d63122c8064005ec6ae7c7504820c28e5027a5894aef209be7aa5" +DEFAULT_INSTALL_SHA256="57b285ce8de68f8ece0d1adb9f3b7a7fc8cda5ab7004c3601d5ef357353ab69e" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/install/launchers/index.json b/install/launchers/index.json index 9bf791423..7d57d3af8 100644 --- a/install/launchers/index.json +++ b/install/launchers/index.json @@ -27,8 +27,8 @@ { "dest": "roms/ports/retrobios/install.py", "repo_path": "install.py", - "size": 1096845, - "sha256": "02b1a66c6f9d63122c8064005ec6ae7c7504820c28e5027a5894aef209be7aa5", + "size": 1097676, + "sha256": "57b285ce8de68f8ece0d1adb9f3b7a7fc8cda5ab7004c3601d5ef357353ab69e", "executable": false }, { @@ -71,8 +71,8 @@ "muos": { "archive": { "dest": "ARCHIVE/RetroBIOS.muxapp", - "size": 1150472, - "sha256": "b3261f3b02e6e4e14bbcba07d9f6f03b7b43e5e66bf20c770d4c68e8cb6bd277" + "size": 1151303, + "sha256": "3df4d9dd45f830736e40d80be6ff589f22c91bff93ea00570918ef53a072e826" }, "members": [ { @@ -92,8 +92,8 @@ { "name": "RetroBIOS/install.py", "repo_path": "install.py", - "size": 1096845, - "sha256": "02b1a66c6f9d63122c8064005ec6ae7c7504820c28e5027a5894aef209be7aa5", + "size": 1097676, + "sha256": "57b285ce8de68f8ece0d1adb9f3b7a7fc8cda5ab7004c3601d5ef357353ab69e", "executable": false }, { @@ -166,8 +166,8 @@ { "dest": ".config/modules/retrobios/install.py", "repo_path": "install.py", - "size": 1096845, - "sha256": "02b1a66c6f9d63122c8064005ec6ae7c7504820c28e5027a5894aef209be7aa5", + "size": 1097676, + "sha256": "57b285ce8de68f8ece0d1adb9f3b7a7fc8cda5ab7004c3601d5ef357353ab69e", "executable": false }, { diff --git a/tests/test_install_offline.py b/tests/test_install_offline.py index d066b105d..04b3ab018 100644 --- a/tests/test_install_offline.py +++ b/tests/test_install_offline.py @@ -179,14 +179,30 @@ class CheckNamesWhyAFileIsRefused(Scratch): self.skipTest("this filesystem does not keep case") data = json.loads(self.list.read_text()) twin = next(f for f in data["files"] if f["dest"] == "gba_bios.bin") - data["case_variants"] = [{**twin, "dest": "twin/gba_bios.bin"}] + data["case_variants"] = [{**twin, "dest": "GBA_BIOS.BIN"}] self.list.write_text(json.dumps(data)) self.complete() out = self.check() - self.assertIn("missing: twin/gba_bios.bin", out) + self.assertIn("missing: GBA_BIOS.BIN", out) self.assertIn("a second name the pack leaves out", out) self.assertNotIn("wrong folder: it is at gba_bios.bin", out) + def test_a_case_twin_is_plain_missing_when_no_pack_was_used(self): + """On an empty folder every case variant came first, uncapped, with + a note on a pack never downloaded: the BIOS files the user looked + for sat in "... and N more missing".""" + probe = self.root / "CaseProbe" + probe.write_text("x") + if (self.root / "caseprobe").exists(): + self.skipTest("this filesystem does not keep case") + data = json.loads(self.list.read_text()) + twin = next(f for f in data["files"] if f["dest"] == "gba_bios.bin") + data["case_variants"] = [{**twin, "dest": "GBA_BIOS.BIN"}] + self.list.write_text(json.dumps(data)) + out = self.check() + self.assertIn("missing: GBA_BIOS.BIN", out) + self.assertNotIn("a second name the pack leaves out", out) + def test_a_nested_bios_folder_is_named(self): for name, data in PAYLOAD.items(): self.place(f"system/{name}", data)