mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 15:43:45 -06:00
feat: settle a file only when the run uses it
hle_fallback needs the file to change the run: Beetle PSX hashes every file of its folder, so opened is not used. PCSX-ReARMed hides the BIOS logo by default: its BIOS load line stands for the bios stage. A check runs again only when the oracle entries of its content and core change, and the rules run on every pass from the kept records.
This commit is contained in:
1 parent
a17406edae
commit
70c3bbd853
8 files changed
+111
-21
No files matched your search
+30
-6
@@ -903,6 +903,7 @@ class Campaign:
|
||||
self.store = ContentStore(self)
|
||||
self.content = load_content(paths.content_file)
|
||||
self.oracle_sha = file_sha256(paths.oracle) if paths.oracle.is_file() else ""
|
||||
self.oracle_data = core_probe.load_oracle(paths.oracle)[0]
|
||||
self.deadline = 0.0
|
||||
self.current: Pass | None = None
|
||||
self.dirty_at_start: set[str] = set()
|
||||
@@ -1055,9 +1056,14 @@ class Campaign:
|
||||
|
||||
def remember(self, emulator: str, key: str, result: ProbeResult, core: Core | None,
|
||||
digest: str) -> None:
|
||||
judged = None
|
||||
if result.record and result.record["inputs"].get("content"):
|
||||
judged = [result.record["inputs"]["content"]["sha1"], result.record["core"]["library_name"]]
|
||||
entry = {"status": result.status, "at": now_iso(), "profile_digest": digest,
|
||||
"core_sha256": core.sha256 if core else None,
|
||||
"record_version": mr.RECORD_VERSION, "oracle_sha256": self.oracle_sha}
|
||||
"record_version": mr.RECORD_VERSION, "oracle_sha256": self.oracle_digest(judged)}
|
||||
if judged:
|
||||
entry["oracle_key"] = judged
|
||||
if result.reason:
|
||||
entry["reason"] = result.reason
|
||||
if result.log:
|
||||
@@ -1073,9 +1079,27 @@ class Campaign:
|
||||
self.state["checks"].setdefault(emulator, {})[key] = {
|
||||
"status": PENDING, "reason": reason, "at": now_iso(), "profile_digest": digest}
|
||||
|
||||
def oracle_digest(self, key: list[str] | None) -> str:
|
||||
"""What a check's verdicts depend on in the oracle: the entries of its
|
||||
content and core, else the whole file. Another core's references
|
||||
never make a check run again."""
|
||||
if not key:
|
||||
return self.oracle_sha
|
||||
return mr.value_sha1(core_probe.oracle_for(self.oracle_data, *key))
|
||||
|
||||
def needed(self, emulator: str, key: str, core: Core, digest: str) -> bool:
|
||||
return should_run(self.state["checks"].get(emulator, {}).get(key), core.sha256, digest,
|
||||
self.oracle_sha)
|
||||
previous = self.state["checks"].get(emulator, {}).get(key)
|
||||
judged = (previous or {}).get("oracle_key")
|
||||
if previous and not judged and previous.get("record"):
|
||||
record = self.read_record(emulator, previous["record"]) or {}
|
||||
if (record.get("inputs") or {}).get("content"):
|
||||
judged = [record["inputs"]["content"]["sha1"], record["core"]["library_name"]]
|
||||
previous["oracle_key"] = judged
|
||||
# Judged against the entries the record kept, not the file it named.
|
||||
kept = {k: v for k, v in (record["inputs"].get("oracle") or {}).items()
|
||||
if k not in ("file", "sha256")}
|
||||
previous["oracle_sha256"] = mr.value_sha1({"bios_patterns": [], **kept})
|
||||
return should_run(previous, core.sha256, digest, self.oracle_digest(judged))
|
||||
|
||||
def check(self, emulator: str, key: str, core: Core, digest: str, argv: list[str],
|
||||
log_name: str) -> dict | None:
|
||||
@@ -1134,8 +1158,8 @@ class Campaign:
|
||||
keys[f"negative:{dest}"] = self.check_key(core, content, f"negative:{dest}")
|
||||
if self.settings.bench:
|
||||
keys["bench"] = self.check_key(core, content, "bench")
|
||||
if not any(self.needed(emulator, key, core, digest) for key in keys.values()):
|
||||
return
|
||||
# The rules run on every pass, from the records kept when nothing
|
||||
# needs to run again: a change of the table applies without a run.
|
||||
try:
|
||||
folder = self.store.get(content)
|
||||
load = load_file(folder, content.load)
|
||||
@@ -1174,7 +1198,7 @@ class Campaign:
|
||||
for label in labels[3:]:
|
||||
self.log(f"{emulator}: {rules.rule_region(rules.run_of(record, label), present, entry, content.region)}")
|
||||
if dest in self.opened(files_record, 0) or dest in self.opened(record, 0):
|
||||
verdicts = rules.rule_need(none, present, entry)
|
||||
verdicts = rules.rule_need(none, present, entry, rules.read(none, present, zeros, half))
|
||||
validation = rules.rule_validation(zeros, half, none, present, entry)
|
||||
if validation:
|
||||
verdicts.append(validation)
|
||||
|
||||
@@ -11,7 +11,7 @@ BIOS at all, `present` with the file alone, and wrong files staged under its
|
||||
name (zeros of its size, its first half, another region's dump).
|
||||
|
||||
runs field settled
|
||||
none boots hle_fallback true
|
||||
none boots, the file changes the run hle_fallback true
|
||||
none fails, present boots required true
|
||||
hle_fallback false
|
||||
zeros refused, present boots validation a content check
|
||||
@@ -123,9 +123,20 @@ def agrees(entry: dict, verdict: Verdict) -> bool:
|
||||
return current(entry, verdict.field) == verdict.value
|
||||
|
||||
|
||||
def rule_need(none: Run, present: Run, entry: dict) -> list[Verdict]:
|
||||
"""No BIOS at all against the file alone: required and hle_fallback."""
|
||||
def read(none: Run, *runs: Run) -> bool:
|
||||
"""True when the file changes the run: alone or as a wrong file, one
|
||||
variant ends otherwise than the run with no BIOS, or reaches other
|
||||
stages. A core can open a file without using it for this content (a
|
||||
scan that hashes every file of the folder)."""
|
||||
return any(run.verdict != none.verdict or run.stages != none.stages for run in runs if run)
|
||||
|
||||
|
||||
def rule_need(none: Run, present: Run, entry: dict, used: bool = True) -> list[Verdict]:
|
||||
"""No BIOS at all against the file alone: required and hle_fallback. A
|
||||
file the content never uses settles nothing."""
|
||||
name = entry["name"]
|
||||
if none.verdict == BOOTS and not used:
|
||||
return []
|
||||
if none.verdict == BOOTS:
|
||||
out = [Verdict("boots_without_file", "hle_fallback", True,
|
||||
f"the core boots the content with no BIOS at all, {name} included",
|
||||
|
||||
+11
-3
@@ -1173,8 +1173,9 @@ DEFAULT_ORACLE = ROOT / "tests" / "campaign" / "oracle.yml"
|
||||
|
||||
def oracle_for(data: dict, content_sha1: str | None, library_name: str | None) -> dict:
|
||||
"""The oracle entries that judge one content on one core: its reference
|
||||
frames, the error patterns every core shares and the core's own, and the
|
||||
core's known error screens."""
|
||||
frames, the error patterns every core shares and the core's own, the
|
||||
core's known error screens, and the log lines by which it says it loaded
|
||||
the console's own BIOS."""
|
||||
core = (data.get("cores") or {}).get(library_name or "") or {}
|
||||
references: list[dict] = []
|
||||
for item in data.get("content") or ():
|
||||
@@ -1185,7 +1186,9 @@ def oracle_for(data: dict, content_sha1: str | None, library_name: str | None) -
|
||||
patterns = [p["pattern"] for p in (data.get("error_patterns") or ())]
|
||||
patterns += [p["pattern"] for p in core.get("error_patterns") or ()]
|
||||
screens = [{"sha1": s["sha1"], "shows": s["shows"]} for s in core.get("error_screens") or ()]
|
||||
return {"references": references, "error_patterns": patterns, "error_screens": screens}
|
||||
loaded = [p["pattern"] for p in core.get("bios_patterns") or ()]
|
||||
return {"references": references, "error_patterns": patterns, "error_screens": screens,
|
||||
"bios_patterns": loaded}
|
||||
|
||||
|
||||
def sampled(rep: dict) -> list[tuple[int, str]]:
|
||||
@@ -1218,6 +1221,11 @@ def judge(rep: dict, oracle: dict) -> tuple[str, dict]:
|
||||
references = {r["sha1"]: r for r in oracle.get("references") or ()}
|
||||
reached = [(frame, digest) for frame, digest in frames if digest in references]
|
||||
stages = list(dict.fromkeys(references[d]["stage"] for _, d in reached))
|
||||
# A core that hides the BIOS screens by default still says it loaded one.
|
||||
if "bios" not in stages and any(re.search(pattern, line["text"])
|
||||
for pattern in oracle.get("bios_patterns") or ()
|
||||
for line in rep.get("logs") or ()):
|
||||
stages.insert(0, "bios")
|
||||
game = [(frame, digest) for frame, digest in reached if references[digest]["stage"] == "game"]
|
||||
if game:
|
||||
frame, digest = game[0]
|
||||
|
||||
Reference in new issue
Block a user