feat: add native Tomie adapter and strict import compatibility checks

This commit is contained in:
mrleo1nid committed 2026-10-25 11:02:52 +03:00
1 parent c8d83b3fe7
commit 57a4edcbfd
45 files changed
+2985 -71

No files matched your search

+68 -9
View File
@@ -16,12 +16,22 @@ def inspect_naninovel(root: Path, *, work: Path | None = None) -> dict:
with tempfile.TemporaryDirectory(prefix="pgames-naninovel-") as directory:
return inspect_naninovel(root, work=Path(directory))
package = read_package(root, work, media=True)
data, entry, _ = compile_package(package)
resolve_media(package, data)
return {"engine": "naninovel", "compatible": True, "scripts": len(package.scripts),
"textResources": len(package.texts), "entry": entry,
"commands": sum(len(line["commands"]) for script in package.scripts.values() for line in script["lines"]),
"warnings": dict(data.warnings)}
try:
if package.profile:
from app.converter.tomie_format import prepare
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": {}}
data, entry, _ = compile_package(package)
resolve_media(package, data)
return {"engine": "naninovel", "compatible": True, "scripts": len(package.scripts),
"textResources": len(package.texts), "entry": entry,
"commands": sum(len(line["commands"]) for script in package.scripts.values() for line in script["lines"]),
"warnings": dict(data.warnings)}
finally:
package.close()
def resolve_media(package, data):
@@ -99,10 +109,20 @@ def export_media(package, data, root: Path, work: Path, progress):
def convert_naninovel(root: Path, work: Path, progress):
from app.converter.pipeline import Result, anchors
progress("Чтение сценариев Naninovel и проверка совместимости")
package = read_package(root, work / "unity", media=True)
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
if package.profile:
return _convert_tomie(root, work, progress, package)
data, entry, expressions = compile_package(package)
# No media is decoded or written to the blob store before the script gate.
files, aliases = export_media(package, data, root, work, progress)
@@ -119,14 +139,53 @@ def convert_naninovel(root: Path, work: Path, progress):
store(logical, {"file": path, "labels": labels, "anchors": anchors(labels)})
scripts.append(logical)
source = locales.detect_language(data.modules) or "und"
resources = {}
for tid, record in package.texts.items():
resources.setdefault(record["collection"], {}).setdefault(record["key"], []).append({"tid": tid, **record})
game = {"format": "pgvn/1", "engine": "naninovel", "title": root.name,
"resolution": [1920, 1080], "entry": entry, "scripts": scripts,
"characters": data.characters, "images": data.images, "transforms": {},
"transitions": {}, "audio": {}, "files": aliases, "defaults": [], "init": [],
"ui": {}, "sourceLang": source, "locales": {}, "naniExpressions": expressions}
"ui": {}, "sourceLang": source, "locales": {}, "naniExpressions": expressions,
"naniResources": resources}
store("game.json", game)
total = sum(entry["size"] for entry in files.values())
report = {"engine": "naninovel", "compatible": True, "format": "pgvn/1",
"scripts": len(package.scripts), "textResources": len(package.texts),
"media": len(aliases), "warnings": []}
return Result({"format": "pgvn/1", "files": files}, report, root.name, None, total)
def _convert_tomie(root, work, progress, package):
from app.converter.pipeline import Result, anchors
from app.converter.tomie_format import RUNTIME, modules, prepare
from app.converter.tomie_assets import export_media as export_tomie_media
native = prepare(package) # all script checks precede media decoding/blob writes
scripts = modules(native, package.texts)
files, media, counts = export_tomie_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": []}
return Result({"format": "pgvn/1", "files": files}, report, root.name, None,
sum(entry["size"] for entry in files.values()))