refactor: move game-specific import logic into profiles; unblock Tomie import
- Add app/games: GameProfile base, registry, ReaderHooks and renpy/naninovel/ tomie profiles. pipeline, preflight, detect and mods dispatch via the registry - Move tomie_* converter modules to app/games/tomie and split out reader hooks, import, mod validation and known defects - Tolerate the 31 authoring defects of the pinned Tomie build as report warnings (exact list, per-script counts); any new defect still blocks import - Frontend: runtime registry (runtimes.ts) with lazy Tomie launcher - Add profile/registry tests and docs/GAME_PROFILES.md; update IMPORT.md Full media export of the original Tomie build is not yet verified. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bbbcc6a5d8
commit
12ec82c7e6
30 files changed
+700
-196
No files matched your search
@@ -0,0 +1,80 @@
|
||||
"""Tomie import: validated native command lists in editable PGVN modules."""
|
||||
import json
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from app.blobs import put_bytes
|
||||
from app.converter import locales
|
||||
from app.converter.nani_package import read_package
|
||||
from app.games.tomie.assets import export_media
|
||||
from app.games.tomie.format import RUNTIME, modules, prepare
|
||||
from app.games.tomie.reader import TomieHooks
|
||||
|
||||
|
||||
def inspect_tomie(root: Path, work: Path | None = None) -> dict:
|
||||
if work is None:
|
||||
with tempfile.TemporaryDirectory(prefix="pgames-tomie-") as directory:
|
||||
return inspect_tomie(root, Path(directory))
|
||||
package = read_package(root, work, media=True, hooks=TomieHooks())
|
||||
try:
|
||||
native = prepare(package)
|
||||
return {"engine": "naninovel", "profile": package.profile, "compatible": True,
|
||||
"scripts": len(native["scripts"]), "textResources": len(package.texts),
|
||||
"commands": sum(len(script["commands"]) for script in native["scripts"].values()),
|
||||
"warnings": _warnings(native)}
|
||||
finally:
|
||||
package.close()
|
||||
|
||||
|
||||
def _warnings(native: dict) -> dict:
|
||||
"""Known authoring defects of the original build, grouped by message."""
|
||||
result: dict[str, int] = {}
|
||||
for defect in native.get("known_defects", []):
|
||||
text = f"{defect['message']} ({defect['location']})"
|
||||
result[text] = result.get(text, 0) + 1
|
||||
return result
|
||||
|
||||
|
||||
def convert_tomie(root: Path, work: Path, progress):
|
||||
progress("Чтение сценариев Tomie и проверка совместимости")
|
||||
package = read_package(root, work / "unity", media=True, hooks=TomieHooks())
|
||||
try:
|
||||
return convert_package(root, work, progress, package)
|
||||
finally:
|
||||
package.close()
|
||||
|
||||
|
||||
def convert_package(root: Path, work: Path, progress, package):
|
||||
from app.converter.pipeline import Result, anchors
|
||||
|
||||
native = prepare(package) # all script checks precede media decoding/blob writes
|
||||
known = _warnings(native)
|
||||
native.pop("known_defects", None)
|
||||
scripts = modules(native, package.texts)
|
||||
files, media, counts = export_media(package, root, work, progress)
|
||||
|
||||
def store(path, value):
|
||||
raw = json.dumps(value, ensure_ascii=False, separators=(",", ":")).encode()
|
||||
digest, size = put_bytes(raw, ".json")
|
||||
files[path] = {"hash": digest, "size": size}
|
||||
|
||||
resources = {}
|
||||
for tid, record in package.texts.items():
|
||||
resources.setdefault(record["collection"], {}).setdefault(record["key"], []).append({"tid": tid, **record})
|
||||
for path, module in scripts.items():
|
||||
module["anchors"] = anchors(module["labels"])
|
||||
store(path, module)
|
||||
game = {"format": "pgvn/1", "engine": "naninovel", "runtime": RUNTIME, "title": root.name,
|
||||
"resolution": [1920, 1080], "entry": "nani:" + native["start"]["script"], "scripts": list(scripts),
|
||||
"characters": {}, "images": {}, "transforms": {}, "transitions": {}, "audio": {}, "files": {},
|
||||
"defaults": [], "init": [], "ui": {}, "sourceLang": locales.detect_language(
|
||||
{path: module["labels"] for path, module in scripts.items()}) or "und", "locales": {},
|
||||
"naniResources": resources,
|
||||
"tomie": {**{key: value for key, value in native.items() if key != "scripts"}, "media": media}}
|
||||
store("game.json", game)
|
||||
report = {"engine": "naninovel", "profile": package.profile, "compatible": True, "format": "pgvn/1",
|
||||
"runtime": RUNTIME, "scripts": len(package.scripts), "textResources": len(package.texts),
|
||||
"media": counts, "warnings": [{"message": text, "count": count} for text, count in known.items()],
|
||||
"knownDefects": sum(known.values())}
|
||||
return Result({"format": "pgvn/1", "files": files}, report, root.name, None,
|
||||
sum(entry["size"] for entry in files.values()))
|
||||
Reference in new issue
Block a user