Files
mrleo1nidandClaude Sonnet 5.5 f49ee90477 feat: browser access (allowed_origins, CORS with Private Network Access), job cancel, Tomie/Unity adapters, benchmark
Includes earlier uncommitted work: Tomie and Unity adapters, benchmark, structured output, call metrics.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 18:59:28 +03:00

54 lines
2.4 KiB
Python

"""Extract Naninovel DefaultUI TextAsset from Tomie into this workspace.
Only reads the game. Writes a JSON translation input and a source manifest.
Requires: python -m pip install -e ".[unity]"
"""
import argparse
import hashlib
import json
from pathlib import Path
from local_translator.naninovel import parse_dictionary
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("game", type=Path)
parser.add_argument("--output", type=Path, default=Path("data/tomie/input.json"))
parser.add_argument("--limit", type=int, default=15, help="0 extracts the full DefaultUI dictionary")
args = parser.parse_args()
if args.limit < 0:
parser.error("limit must be nonnegative")
try:
import UnityPy
except ImportError:
parser.error('Install optional extractor: python -m pip install -e ".[unity]"')
asset_path = args.game.resolve() / "TomieWGM_Data" / "data.unity3d"
output = args.output.resolve()
if output.is_relative_to(args.game.resolve()):
parser.error("Choose an output outside the game directory")
environment = UnityPy.load(str(asset_path))
assets = [obj for obj in environment.objects if obj.type.name == "TextAsset" and obj.read().m_Name == "DefaultUI"]
if len(assets) != 1:
parser.error(f"Expected exactly one DefaultUI TextAsset, found {len(assets)}")
obj = assets[0]
script = obj.read().m_Script
if isinstance(script, bytes):
script = script.decode("utf-8-sig")
entries = parse_dictionary(script)
selected = entries[:args.limit] if args.limit else entries
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps([{"id": row["id"], "text": row["text"]} for row in selected], ensure_ascii=False, indent=2), encoding="utf-8")
manifest = {
"game": str(args.game.resolve()), "source_file": str(asset_path), "asset_name": "DefaultUI",
"path_id": obj.path_id, "serialized_file": obj.assets_file.name,
"script_sha256": hashlib.sha256(script.encode("utf-8")).hexdigest(),
"total_entries": len(entries), "selected_entries": selected,
"note": "Read-only extraction. No patching, font replacement or changes to game files.",
}
output.with_suffix(".manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"Extracted {len(selected)}/{len(entries)} UI strings to {output}")
if __name__ == "__main__":
main()