Load cutscenes directly from JSONC at runtime, fix asset bundling staleness
Cutscenes now parse their authored .jsonc straight into the runtime cutsceneitem_t/pool representation via yyjson, instead of going through a separate Python-compiled DCTS binary format - removes the whole build/compile step and the byte-format contract between the Python encoder and the C decoder, at the cost of a (still tiny, one-time) parse per cutscene load. Also fixes dusk.dsk going stale after a build: the old custom_command depended on a CMake-configure-time file glob, which only re-detects added/removed assets on the next configure and could miss edits entirely. tools.asset.pack now always runs and decides for itself (via a small manifest) whether anything actually needs repacking, so asset changes are never missed regardless of add/edit/remove. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -35,9 +35,20 @@ Usage:
|
||||
<assets_dir> as the zip entry name, to the stored archive if its
|
||||
relative path matches any --stored pattern (fnmatch, default:
|
||||
"locale/*"), otherwise to the compressed archive.
|
||||
|
||||
Called unconditionally on every CMake build (see the DUSK_ASSETS_BUILT
|
||||
target) rather than gated behind file-level build-system dependencies -
|
||||
CMake's CONFIGURE_DEPENDS glob only notices added/removed files on the
|
||||
NEXT configure, so a plain custom_command DEPENDS list can miss edits to
|
||||
existing files or lag a build behind on adds/removes. Instead this script
|
||||
itself decides whether anything actually needs repacking: it writes a
|
||||
manifest of every input file's (mtime, size) next to the output on each
|
||||
successful pack, and skips the real work if a fresh manifest compares
|
||||
equal to it and the output still exists.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
import zipfile
|
||||
@@ -60,6 +71,26 @@ def build_zip_blob(root, relative_paths, compression):
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def manifest_path(output_path):
|
||||
return output_path + '.manifest.json'
|
||||
|
||||
|
||||
def compute_manifest(input_dir, relative_paths, stored_patterns):
|
||||
entries = {}
|
||||
for relative_path in relative_paths:
|
||||
st = os.stat(os.path.join(input_dir, relative_path))
|
||||
entries[relative_path] = [st.st_mtime_ns, st.st_size]
|
||||
return {'stored_patterns': sorted(stored_patterns), 'files': entries}
|
||||
|
||||
|
||||
def load_manifest(path):
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def pack(input_dir, output_path, stored_patterns):
|
||||
relative_paths = []
|
||||
for dirpath, _dirnames, filenames in os.walk(input_dir):
|
||||
@@ -69,6 +100,12 @@ def pack(input_dir, output_path, stored_patterns):
|
||||
os.path.relpath(full_path, input_dir).replace(os.sep, '/')
|
||||
)
|
||||
|
||||
manifest = compute_manifest(input_dir, relative_paths, stored_patterns)
|
||||
manifest_file = manifest_path(output_path)
|
||||
if os.path.isfile(output_path) and load_manifest(manifest_file) == manifest:
|
||||
print(f'{output_path} is up to date, skipping pack')
|
||||
return
|
||||
|
||||
stored_paths = [
|
||||
path for path in relative_paths
|
||||
if any(fnmatch.fnmatch(path, pattern) for pattern in stored_patterns)
|
||||
@@ -102,6 +139,9 @@ def pack(input_dir, output_path, stored_patterns):
|
||||
f.write(compressed_blob)
|
||||
f.write(stored_blob)
|
||||
|
||||
with open(manifest_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(manifest, f)
|
||||
|
||||
print(
|
||||
f'Wrote {output_path}: {len(compressed_paths)} compressed file(s) '
|
||||
f'({len(compressed_blob)} bytes), {len(stored_paths)} stored file(s) '
|
||||
|
||||
Reference in New Issue
Block a user