mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 12:49:03 -05:00
fix: judge a declared slot by the platform's copy
--declared-only settled each slot on its first declaration before the cores narrowed the list, and the declared event judged the core copy served in its place. Both now read the declaration whose systems remain, as the platform checks it.
This commit is contained in:
1 parent
5869da409b
commit
ea48a54329
4 files changed
+129
-20
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "b23c6da4ee131f898c25540bd53677ea4112964fd00b209e4fe85dfdba8427d1"
|
||||
$defaultInstallSha256 = "5fedfbf308ba04a174ce0682380a28150b6fe5eab514a605e3b318193ce37c7a"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+77
-18
@@ -665,17 +665,45 @@ def _emit_check(
|
||||
bytes_needed=sum(entry.get("size", 0) for entry in needed),
|
||||
data_missing=data_missing, not_installable=not_installable,
|
||||
)
|
||||
own = [entry for entry in files if entry.get("cores") is None]
|
||||
own_needed = [entry for entry in own if id(entry) not in present]
|
||||
own_wrong = sum(1 for entry in own if id(entry) in wrong)
|
||||
# The copy the platform itself checks at each of its slots, which is
|
||||
# what --declared-only checks: a core's copy served in its place is
|
||||
# judged against the platform's.
|
||||
own_states = []
|
||||
for entry in files:
|
||||
if entry.get("cores") is not None:
|
||||
continue
|
||||
copy = declared_copy(entry)
|
||||
if copy is entry or copy.get("sha1") == entry.get("sha1"):
|
||||
state = ("present" if id(entry) in present
|
||||
else "wrong_hash" if id(entry) in wrong else "missing")
|
||||
else:
|
||||
state = _declared_state(copy, entry, bios_path)
|
||||
own_states.append((entry["dest"], copy.get("size", 0), state))
|
||||
EVENTS.emit(
|
||||
"declared", platform=plat, files=len(own), present=len(own) - len(own_needed),
|
||||
missing=len(own_needed) - own_wrong, wrong_hash=own_wrong,
|
||||
bytes_needed=sum(entry.get("size", 0) for entry in own_needed),
|
||||
dests=[entry["dest"] for entry in own],
|
||||
"declared", platform=plat, files=len(own_states),
|
||||
present=sum(1 for _d, _s, state in own_states if state == "present"),
|
||||
missing=sum(1 for _d, _s, state in own_states if state == "missing"),
|
||||
wrong_hash=sum(1 for _d, _s, state in own_states if state == "wrong_hash"),
|
||||
bytes_needed=sum(size for _d, size, state in own_states if state != "present"),
|
||||
dests=[dest for dest, _s, _state in own_states],
|
||||
)
|
||||
|
||||
|
||||
def _declared_state(copy: dict, entry: dict, bios_path: Path) -> str:
|
||||
"""present, missing or wrong_hash: the file at entry's place against copy."""
|
||||
try:
|
||||
path = _entry_path(bios_path, entry)
|
||||
except ValueError:
|
||||
return "missing"
|
||||
if not path.is_file():
|
||||
return "missing" if not path.exists() else "wrong_hash"
|
||||
try:
|
||||
verdict = _classify_local(copy, path)
|
||||
except OSError:
|
||||
return "wrong_hash"
|
||||
return "present" if verdict == "up_to_date" else "wrong_hash"
|
||||
|
||||
|
||||
def leave_out_game_data(
|
||||
files: list[dict], omitted: list[dict]
|
||||
) -> tuple[list[dict], list[dict], list[dict]]:
|
||||
@@ -697,17 +725,42 @@ def keep_declared(files: list[dict], omitted: list[dict]) -> tuple[list[dict], l
|
||||
|
||||
A slot a core contradicts gets the platform's copy, and no core copy
|
||||
stays behind as an alternative: narrowing by cores afterwards would
|
||||
otherwise put a core's file back where a dropped system's was.
|
||||
otherwise put a core's file back where a dropped system's was. Which of
|
||||
the platform's declarations answers is left to that narrowing, as
|
||||
without --declared-only: the first whose systems remain.
|
||||
"""
|
||||
def own(entries: list[dict]) -> list[dict]:
|
||||
return [
|
||||
{k: v for k, v in _settle_slot(entry, set()).items() if k != "alternatives"}
|
||||
for entry in entries if entry.get("cores") is None
|
||||
]
|
||||
result = []
|
||||
for entry in entries:
|
||||
if entry.get("cores") is not None:
|
||||
continue
|
||||
kept = {k: v for k, v in _settle_slot(entry, set()).items()
|
||||
if k not in ("alternatives", "declared_copy")}
|
||||
declared = entry.get("declared_choices") or []
|
||||
if len(declared) > 1:
|
||||
kept["declared_choices"] = declared
|
||||
result.append(kept)
|
||||
return result
|
||||
|
||||
return own(files), own(omitted)
|
||||
|
||||
|
||||
def declared_copy(entry: dict) -> dict:
|
||||
"""The copy the platform's own list checks at a platform file's slot.
|
||||
|
||||
A narrowing that serves a core's copy there records the platform's
|
||||
answer beside it, and an entry not yet settled answers by its first
|
||||
declaration. Otherwise the entry is the platform's copy itself.
|
||||
"""
|
||||
recorded = entry.get("declared_copy")
|
||||
if recorded is not None:
|
||||
return recorded
|
||||
declared = entry.get("declared_choices")
|
||||
if declared:
|
||||
return declared[0]
|
||||
return entry
|
||||
|
||||
|
||||
def _left_out_lines(left_out: list[dict], every: bool) -> list[str]:
|
||||
"""What --no-game-data left out, named per category, ten each unless every."""
|
||||
lines = []
|
||||
@@ -3428,22 +3481,28 @@ def _settle_slot(
|
||||
declared = entry.get("declared_choices")
|
||||
if declared is None:
|
||||
return entry
|
||||
platform_copy = next(
|
||||
(c for c in declared
|
||||
if kept_system is None or any(kept_system(s) for s in c["systems"])),
|
||||
declared[0],
|
||||
)
|
||||
copy = next(
|
||||
(c for c in entry.get("slot_choices", []) if chosen.intersection(c["cores"])),
|
||||
None,
|
||||
)
|
||||
if copy is None or "sha1" not in copy:
|
||||
copy = next(
|
||||
(c for c in declared
|
||||
if kept_system is None or any(kept_system(s) for s in c["systems"])),
|
||||
declared[0],
|
||||
)
|
||||
copy = platform_copy
|
||||
kept = {
|
||||
k: v for k, v in entry.items()
|
||||
if k not in _COPY_FIELDS and k not in ("slot_choices", "declared_choices")
|
||||
if k not in _COPY_FIELDS
|
||||
and k not in ("slot_choices", "declared_choices", "declared_copy")
|
||||
}
|
||||
kept.update((k, copy[k]) for k in _COPY_FIELDS if k in copy and k != "cores")
|
||||
kept["cores"] = None
|
||||
if copy is not platform_copy and copy.get("sha1") != platform_copy.get("sha1"):
|
||||
# What the platform checks here, for the `declared` event.
|
||||
kept["declared_copy"] = {k: platform_copy[k] for k in _COPY_FIELDS
|
||||
if k in platform_copy and k != "cores"}
|
||||
return kept
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="b23c6da4ee131f898c25540bd53677ea4112964fd00b209e4fe85dfdba8427d1"
|
||||
DEFAULT_INSTALL_SHA256="5fedfbf308ba04a174ce0682380a28150b6fe5eab514a605e3b318193ce37c7a"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -288,6 +288,56 @@ class APlatformFileSettledByCoresAndSystems(unittest.TestCase):
|
||||
self.assertEqual(self.served(["dsi"]), "c")
|
||||
self.assertEqual(self.served(["nds"]), "b")
|
||||
|
||||
def test_declared_only_keeps_the_declaration_whose_system_remains(self):
|
||||
"""--declared-only settled the slot on the first declaration before
|
||||
the cores narrowed it: on a target without melonds it checked the
|
||||
nintendo-dsi copy, the platform the nintendo-ds one."""
|
||||
manifest = self.manifest()
|
||||
for names, letter in ((["nds"], "b"), (["dsi"], "c"), (["melonds"], "c"),
|
||||
(["desmume"], "b")):
|
||||
with self.subTest(names=names):
|
||||
files, omitted = install.keep_declared(manifest["files"], [])
|
||||
files, _ = install.narrow_to_cores(manifest, names, files, omitted)
|
||||
self.assertEqual(files[0]["sha1"][0], letter)
|
||||
|
||||
def test_the_declared_event_judges_the_copy_the_platform_checks(self):
|
||||
"""melonds' copy is served at the slot, the platform checks its own:
|
||||
the event and a --declared-only check give the same answer."""
|
||||
import hashlib
|
||||
|
||||
tmp = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
self.addCleanup(shutil.rmtree, tmp, True)
|
||||
payloads = {letter: letter.encode() * 3 for letter in "abc"}
|
||||
manifest = self.manifest()
|
||||
entry = manifest["files"][0]
|
||||
|
||||
def real(copy: dict, letter: str) -> None:
|
||||
copy.update(sha1=hashlib.sha1(payloads[letter]).hexdigest(),
|
||||
sha256=hashlib.sha256(payloads[letter]).hexdigest(), size=3)
|
||||
|
||||
real(entry, "a")
|
||||
real(entry["slot_choices"][1], "a")
|
||||
real(entry["declared_choices"][0], "c")
|
||||
real(entry["declared_choices"][1], "b")
|
||||
(tmp / "dsi_bios9.bin").write_bytes(payloads["c"])
|
||||
|
||||
def declared_event(files: list[dict]) -> dict:
|
||||
_todo, fine, wrong = install.check_local(files, tmp)
|
||||
with mock.patch.object(install.EVENTS, "emit") as emit:
|
||||
install._emit_check("retroarch", manifest, tmp, files, fine, wrong, 0, 0)
|
||||
return next(call.kwargs for call in emit.call_args_list
|
||||
if call.args[0] == "declared")
|
||||
|
||||
for names in (None, ["melonds"], ["desmume", "melonds"]):
|
||||
with self.subTest(names=names):
|
||||
served = manifest["files"]
|
||||
own, _ = install.keep_declared(manifest["files"], [])
|
||||
if names is not None:
|
||||
served, _ = install.narrow_to_cores(manifest, names)
|
||||
own, _ = install.narrow_to_cores(manifest, names, own, [])
|
||||
self.assertEqual(declared_event(served), declared_event(own))
|
||||
self.assertEqual(declared_event(own)["present"], 1)
|
||||
|
||||
def test_the_boundary_checks_every_copy(self):
|
||||
install._validate_manifest(self.manifest(), "retroarch")
|
||||
bad_choices = (
|
||||
|
||||
Reference in new issue
Block a user