mirror of
https://github.com/sal063/AC6_recomp
synced 2026-08-31 01:50:24 -04:00
Add 60fps cutscene clamp for in-engine cinematics
Suspend the FPS unlock while a demo-manager Exec (DD sub_82184460 / EM sub_821856F8) ticks, so the frame-locked IngameCinematics Sequencer plays at native ~30fps instead of double speed. Adds ac6_cutscene_clamp CVar (default on).
This commit is contained in:
+15
-55
@@ -3,54 +3,11 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MAGIC_EXT = {
|
||||
"FHM ": ".fhm", "NTXR": ".ntxr", "NDXR": ".ndxr", "NSXR": ".nsxr",
|
||||
"MDLP": ".mdlp", "PLAD": ".plad", "MATE": ".mate", "NFIC": ".nfic",
|
||||
"NFH\x00": ".nfh", "CAPT": ".capt", "Scen": ".scen", "ACE6": ".ace6",
|
||||
"RIFF": ".wav", "SWG\x00": ".swg",
|
||||
}
|
||||
|
||||
|
||||
def parse_fhm(blob: bytes):
|
||||
"""Return list of (index, offset, child_bytes) or None if not an FHM."""
|
||||
if len(blob) < 0x1C or blob[:4] != b"FHM ":
|
||||
return None
|
||||
count = struct.unpack_from(">I", blob, 0x10)[0]
|
||||
if count == 0 or count > 100000:
|
||||
return []
|
||||
offs_base = 0x14
|
||||
size_base = offs_base + count * 4
|
||||
if size_base + count * 4 > len(blob):
|
||||
return []
|
||||
offsets = [struct.unpack_from(">I", blob, offs_base + i * 4)[0] for i in range(count)]
|
||||
sizes = [struct.unpack_from(">I", blob, size_base + i * 4)[0] for i in range(count)]
|
||||
out = []
|
||||
for i, (off, sz) in enumerate(zip(offsets, sizes)):
|
||||
if off == 0 or off >= len(blob):
|
||||
continue
|
||||
end = off + sz
|
||||
if end > len(blob) or end <= off:
|
||||
nxt = offsets[i + 1] if i + 1 < count else len(blob)
|
||||
end = min(nxt, len(blob))
|
||||
if end > off:
|
||||
out.append((i, off, blob[off:end]))
|
||||
return out
|
||||
|
||||
|
||||
def magic_of(blob: bytes) -> str:
|
||||
return blob[:4].decode("latin-1") if len(blob) >= 4 else ""
|
||||
|
||||
|
||||
def ext_for(blob: bytes) -> str:
|
||||
return MAGIC_EXT.get(magic_of(blob), ".bin")
|
||||
|
||||
|
||||
def safe_tag(magic: str) -> str:
|
||||
return "".join(c if c.isalnum() else "_" for c in magic) or "raw"
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from ac6_fhm import ext_for_blob, magic_of, parse_fhm, safe_tag
|
||||
|
||||
|
||||
def unpack(blob: bytes, out_dir: Path, root: Path, depth: int, max_depth: int) -> list[dict]:
|
||||
@@ -59,15 +16,18 @@ def unpack(blob: bytes, out_dir: Path, root: Path, depth: int, max_depth: int) -
|
||||
return []
|
||||
recs = []
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
for idx, off, child in children:
|
||||
magic = magic_of(child)
|
||||
name = f"{idx:04d}_{safe_tag(magic)}{ext_for(child)}"
|
||||
for child in children:
|
||||
magic = child.magic
|
||||
name = f"{child.index:04d}_{safe_tag(magic)}{ext_for_blob(child.data)}"
|
||||
path = out_dir / name
|
||||
path.write_bytes(child)
|
||||
rec = {"index": idx, "offset": off, "size": len(child), "magic": magic,
|
||||
"path": str(path.relative_to(root)).replace("\\", "/")}
|
||||
if depth < max_depth and child[:4] == b"FHM ":
|
||||
nested = unpack(child, out_dir / f"{idx:04d}_FHM", root, depth + 1, max_depth)
|
||||
path.write_bytes(child.data)
|
||||
rec = {"index": child.index, "offset": child.offset,
|
||||
"declared_size": child.declared_size, "size": child.size,
|
||||
"magic": magic, "path": str(path.relative_to(root)).replace("\\", "/")}
|
||||
if child.notes:
|
||||
rec["parser_notes"] = child.notes
|
||||
if depth < max_depth and child.data[:4] == b"FHM ":
|
||||
nested = unpack(child.data, out_dir / f"{child.index:04d}_FHM", root, depth + 1, max_depth)
|
||||
if nested:
|
||||
rec["children"] = nested
|
||||
recs.append(rec)
|
||||
@@ -97,7 +57,7 @@ def main() -> int:
|
||||
stem = src.stem.split(".")[0]
|
||||
if blob[:4] != b"FHM ":
|
||||
# Top-level non-FHM (raw entry): copy through as a leaf.
|
||||
dst = out_root / f"{stem}{ext_for(blob)}"
|
||||
dst = out_root / f"{stem}{ext_for_blob(blob)}"
|
||||
dst.write_bytes(blob)
|
||||
manifest.append({"source": src.name, "kind": "leaf", "magic": magic_of(blob),
|
||||
"path": str(dst.relative_to(out_root)).replace("\\", "/")})
|
||||
|
||||
Reference in New Issue
Block a user