Files
benilla-octo/Optional/sky-and-grading/tools/verify_client_chain.py
T
pkuzicandGPT-5.6 Sol (Codex) 67388625ec MONKEY (optional): add local sky and grading patch builder
Build Blizzard-derived sky assets only from the user's own CASC install, merge lighting DBCs from mounted winners, and pack a preserved patch-Z candidate. Include bilingual install and recovery guidance plus project-owned grading data.

Verified all 46 default stage payloads byte-identical to the reviewed stage; moderated WoW Forever LightIntBand byte-identical; band and mounted-chain checks pass on a hard-linked client copy. No Cargo command run.

Co-Authored-By: GPT-5.6 Sol (Codex)
2026-09-26 15:19:25 +01:00

112 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Verify every staged payload is the mounted winner in a candidate Data chain."""
from __future__ import annotations
import argparse
import hashlib
from pathlib import Path
from dbc_build import check_bands, indexed, parse_dbc
from mpq_tools import Storm, copies_for, find_stormlib
def digest(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def semantic_checks(payloads: dict[str, bytes]) -> None:
light, _, fields = parse_dbc(payloads["DBFilesClient\\Light.dbc"], "Light")
if fields != 12:
raise ValueError("Light.dbc is not 12 fields")
light_rows = indexed(light, "Light")
expected = {20: 1139, 539: 1139, 6: 1140, 65: 1140, 67: 1140, 14: 16}
for row_id, clear in expected.items():
if light_rows[row_id][7] != clear:
raise ValueError("Light[%d].Clear is not %d" % (row_id, clear))
params, _, fields = parse_dbc(
payloads["DBFilesClient\\LightParams.dbc"], "LightParams"
)
if fields != 9:
raise ValueError("LightParams.dbc is not 9 fields")
param_rows = indexed(params, "LightParams")
for row_id, skybox in ((1139, 8), (1140, 9), (269, 10)):
if param_rows[row_id][2] != skybox:
raise ValueError("LightParams[%d] skybox mismatch" % row_id)
if 1141 in param_rows:
raise ValueError("stray LightParams 1141")
check_bands(
payloads["DBFilesClient\\LightIntBand.dbc"],
payloads["DBFilesClient\\LightFloatBand.dbc"],
)
skies, strings, fields = parse_dbc(
payloads["DBFilesClient\\LightSkybox.dbc"], "LightSkybox"
)
if fields != 4 or not strings:
raise ValueError("LightSkybox.dbc is not the extended four-field layout")
sky_rows = indexed(skies, "LightSkybox")
for row_id in (8, 9, 10):
if row_id not in sky_rows:
raise ValueError("LightSkybox row %d is missing" % row_id)
grade, _, fields = parse_dbc(
payloads["DBFilesClient\\MonkeyZoneGrade.dbc"], "MonkeyZoneGrade"
)
if fields != 5 or len(grade) != 4:
raise ValueError("MonkeyZoneGrade must have four five-field rows")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("data_dir", type=Path)
parser.add_argument("--stage", type=Path, required=True)
parser.add_argument("--stormlib", type=Path)
parser.add_argument("--require-winner", default="patch-Z.mpq")
args = parser.parse_args()
storm = Storm(find_stormlib(args.stormlib))
expected = {
str(path.relative_to(args.stage)).replace("/", "\\"): path.read_bytes()
for path in sorted(args.stage.rglob("*"))
if path.is_file()
}
mounted = {}
failures = []
for member, wanted in expected.items():
copies = copies_for(storm, args.data_dir, member)
if not copies:
failures.append("missing %s" % member)
continue
archive, actual = copies[-1]
mounted[member] = actual
if args.require_winner.casefold() != "any" and (
archive.name.casefold() != args.require_winner.casefold()
):
failures.append("%s wins from %s" % (member, archive.name))
if actual != wanted:
failures.append(
"%s differs: %s != %s" % (member, digest(actual), digest(wanted))
)
required = {
"DBFilesClient\\Light.dbc",
"DBFilesClient\\LightParams.dbc",
"DBFilesClient\\LightIntBand.dbc",
"DBFilesClient\\LightFloatBand.dbc",
"DBFilesClient\\LightSkybox.dbc",
"DBFilesClient\\MonkeyZoneGrade.dbc",
}
if required <= set(mounted):
try:
semantic_checks(mounted)
except (KeyError, ValueError) as error:
failures.append("semantic check: %s" % error)
if failures:
print("FAIL")
for failure in failures:
print(" " + failure)
return 1
print("PASS: %d staged payloads are mounted winners" % len(expected))
return 0
if __name__ == "__main__":
raise SystemExit(main())