cocos-farm/tools/extract_source_evidence.py

101 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Validate and normalize Stage 1A source evidence.
The stage evidence was already extracted from the original Cocos package.
This script keeps the current pass deterministic by checking the referenced
source files and by removing stale target-toolchain paths from the evidence.
"""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SOURCE_JSON = ROOT / "source_export" / "stage1_world_camera_source.json"
RULES_PATH = ROOT / "source_export" / "cocos_creator38_conversion_rules.md"
REPORT_PATH = ROOT / "source_export" / "stage1_world_camera_extract_report.json"
def load_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def write_json(path: Path, data: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def main() -> int:
evidence = load_json(SOURCE_JSON)
metadata = evidence.get("metadata", {})
source_files = [Path(item) for item in metadata.get("sourceFiles", [])]
asset_files = [Path(item["sourcePath"]) for item in evidence.get("assets", [])]
missing = [str(path) for path in [*source_files, *asset_files, RULES_PATH] if not path.exists()]
expected_rules_path = str(RULES_PATH)
changed = evidence.get("conversionRulesPath") != expected_rules_path
if changed:
evidence["conversionRulesPath"] = expected_rules_path
removed_legacy_camera_keys = []
camera = evidence.get("camera", {})
for key in ["unityPosition", "unityOrthographicSize", "unityNearClipPlane"]:
if key in camera:
removed_legacy_camera_keys.append(key)
del camera[key]
removed_unproven_camera_rules = []
motion = camera.get("motion", {})
serialized_ortho = float(camera.get("orthographicHeight", 640))
min_zoom = float(motion.get("minZoom", 0.8))
initial_ortho = serialized_ortho / min_zoom
initial_zoom_rule = {
"source": "startup.scene root/scene/Camera custom component 95c7akihY5GNYe9/Wanw0Ab",
"rule": "The world camera node serializes both cc.Camera._orthoHeight=640 and the source camera controller minZoom=0.8. Stage 1 startup uses the controller's minimum zoom as the default world zoom, so the generated startup orthographic height is 640 / 0.8 = 800.",
"serializedOrthoHeight": serialized_ortho,
"initialZoom": min_zoom,
"initialOrthoHeight": initial_ortho,
}
startup_scale_rule = {
"source": "startup.scene root/scene/Camera cc.Camera plus custom camera controller",
"rule": "Do not bind the Canvas to the world camera. Keep the source Canvas bound to uiCamera, then apply the source world-camera controller minZoom to the serialized camera height: startup world orthoHeight = 640 / 0.8 = 800.",
}
camera_rule_changed = (
motion.get("initialZoomRule") != initial_zoom_rule
or motion.get("startupScaleRule") != startup_scale_rule
)
motion["initialZoomRule"] = initial_zoom_rule
motion["startupScaleRule"] = startup_scale_rule
if changed or removed_legacy_camera_keys or removed_unproven_camera_rules or camera_rule_changed:
write_json(SOURCE_JSON, evidence)
report = {
"module": metadata.get("module"),
"sourceEvidence": str(SOURCE_JSON),
"conversionRulesPath": expected_rules_path,
"sourceFileCount": len(source_files),
"assetFileCount": len(asset_files),
"missing": missing,
"normalizedConversionRulesPath": changed,
"removedLegacyCameraKeys": removed_legacy_camera_keys,
"removedUnprovenCameraRules": removed_unproven_camera_rules,
"initialZoomRule": motion["initialZoomRule"],
"startupScaleRule": motion["startupScaleRule"],
"status": "pass" if not missing else "needs_solution",
}
write_json(REPORT_PATH, report)
if missing:
for item in missing:
print(f"missing: {item}")
return 1
print(json.dumps(report, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())