103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Copy loose rootfs assets with viewer-friendly extensions.
|
|
|
|
This preserves target files unchanged and writes renamed copies under assets/.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import shutil
|
|
import struct
|
|
from pathlib import Path
|
|
|
|
|
|
def sha256_file(path: Path) -> str:
|
|
h = hashlib.sha256()
|
|
with path.open("rb") as f:
|
|
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
|
h.update(chunk)
|
|
return h.hexdigest()
|
|
|
|
|
|
def safe_parts(path: Path) -> list[str]:
|
|
parts: list[str] = []
|
|
for part in path.parts:
|
|
if part in ("", ".", ".."):
|
|
continue
|
|
clean = re.sub(r"[^A-Za-z0-9._' -]+", "_", part).strip()
|
|
parts.append(clean or "_")
|
|
return parts
|
|
|
|
|
|
def detect_extension(data: bytes) -> str | None:
|
|
if data.startswith(b"\x89PNG\r\n\x1a\n"):
|
|
return ".png"
|
|
if data.startswith((b"\x00\x01\x00\x00", b"OTTO", b"ttcf")):
|
|
return ".ttf"
|
|
if len(data) >= 12 and data[4:8] == b"ftyp":
|
|
major = data[8:12]
|
|
return ".mov" if major == b"qt " else ".mp4"
|
|
return None
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--source", type=Path, default=Path("games/pokemon_pro/assets"))
|
|
parser.add_argument("--output", type=Path, default=Path("assets/openable/loose"))
|
|
parser.add_argument("--manifest", type=Path, default=Path("assets/loose_manifest.json"))
|
|
args = parser.parse_args()
|
|
|
|
copied = []
|
|
skipped = 0
|
|
for src in sorted(args.source.rglob("*")):
|
|
if not src.is_file() or src.name == "main.pck":
|
|
continue
|
|
with src.open("rb") as f:
|
|
head = f.read(64)
|
|
ext = detect_extension(head)
|
|
if ext is None:
|
|
skipped += 1
|
|
continue
|
|
rel = src.relative_to(args.source)
|
|
rel_parts = safe_parts(rel)
|
|
out = args.output.joinpath(*rel_parts)
|
|
if out.suffix.lower() != ext:
|
|
out = out.with_name(out.name + ext)
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(src, out)
|
|
copied.append(
|
|
{
|
|
"source": str(src),
|
|
"source_target_path": "/games/pokemon_pro/assets/" + str(rel),
|
|
"output": str(out),
|
|
"size": src.stat().st_size,
|
|
"sha256": sha256_file(src),
|
|
"detected_extension": ext,
|
|
}
|
|
)
|
|
|
|
args.manifest.parent.mkdir(parents=True, exist_ok=True)
|
|
args.manifest.write_text(
|
|
json.dumps(
|
|
{
|
|
"source_root": str(args.source),
|
|
"output_root": str(args.output),
|
|
"copied": len(copied),
|
|
"skipped_non_openable": skipped,
|
|
"entries": copied,
|
|
},
|
|
indent=2,
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
print(json.dumps({"copied": len(copied), "skipped_non_openable": skipped}, indent=2))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|