4 from pathlib import Path
7 def require(text: str, token: str, label: str) -> None:
9 raise SystemExit(f"FAIL: {label}: missing {token!r}")
13 parser = argparse.ArgumentParser()
14 parser.add_argument("--source-root", required=True)
15 args = parser.parse_args()
16 root = Path(args.source_root)
18 bridge_h = (root / "libfriidump/xbox_ref_bridge.h").read_text()
19 bridge_c = (root / "libfriidump/xbox_ref_bridge.c").read_text()
20 dumper_c = (root / "libfriidump/dumper.c").read_text()
21 utils_c = (root / "libfriidump/xbox_ref/utils.c").read_text()
22 friidump_c = (root / "src/friidump.c").read_text()
23 msvc_rsp = (root / "msvc32_friidump.rsp").read_text()
25 # Candidate18 removed the authRecovery parameter from the copied Windows
26 # helper but left three callers with a stale eighth argument. MSVC only
27 # warned (C4020), so enforce the exact seven-argument contract in source
28 # and make any future C4020 a production-build error.
30 re.finditer(r"DumpSectorRangeWithRetry\s*\((.*?)\)", utils_c, re.S)
32 if len(call_matches) < 2:
33 raise SystemExit("FAIL: DumpSectorRangeWithRetry declaration/calls not found")
34 for index, match in enumerate(call_matches[1:], start=1):
35 argument_count = match.group(1).count(",") + 1
36 if argument_count != 7:
38 f"FAIL: DumpSectorRangeWithRetry call {index} has "
39 f"{argument_count} arguments; expected 7"
41 require(msvc_rsp, "/we4020", "MSVC excess-argument warning is fatal")
43 require(bridge_h, "typedef int (*xbox_ref_cancel_func)", "bridge callback type")
44 require(bridge_h, "int cancelled;", "result cancellation state")
45 require(bridge_h, "uint32_t completed_sectors;", "result partial progress")
46 require(bridge_c, "cancel, cancel_data, result", "bridge callback handoff")
47 require(dumper_c, "dumper_xbox_ref_cancel_requested", "dumper adapter")
48 if dumper_c.count("dumper_xbox_ref_cancel_requested, dmp") < 2:
49 raise SystemExit("FAIL: callback is not passed to both copied Xbox ISO and XISO paths")
50 if utils_c.count("XboxDumpCancelRequested()") < 5:
51 raise SystemExit("FAIL: copied Xbox long-running paths do not contain enough cancellation checks")
52 require(utils_c, "result->completed_sectors =", "partial sector finalization")
55 "[HASH] Finalizing controlled partial Xbox output hashes",
56 "partial output hashing",
60 "result->cancelled &&",
61 "partial hash cancellation guard",
65 "CalculateFileHashes(filename",
66 "partial full-file hash calculation",
68 require(utils_c, "g_xbox_dump_cancel = NULL;", "callback cleanup")
69 require(friidump_c, "if (xbox_result.cancelled)", "native-report cancellation merge")
72 "g_validation_summary.expected_output_sectors",
73 "Xbox expected-output geometry summary",
77 "g_validation_summary.source_sectors",
78 "Xbox source geometry summary",
82 "g_validation_summary.region",
83 "Xbox region summary",
88 "Xbox title summary handoff",
92 "xbox_result.media_id",
93 "Xbox media-ID summary handoff",
96 print("Xbox copied-dumper cancellation, hashing, and summary contract: PASS")
100 if __name__ == "__main__":
101 raise SystemExit(main())