Files
pgames-manager/backend/app/games/tomie/reader.py
T
mrleo1nidandClaude Sonnet 5.5 12ec82c7e6 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>
2026-10-25 11:35:23 +03:00

67 lines
3.2 KiB
Python

"""Tomie-specific parts of the Unity reader: ResourceManager actors and original variables."""
import json
from app.converter.compatibility import Issue
from app.games.hooks import ReaderHooks
from app.games.tomie.profile import PROFILE, extract_tables
class TomieHooks(ReaderHooks):
def begin(self, package, root):
package.profile = PROFILE
package.function_data = extract_tables(root)
def accept_duplicate_script(self, name):
# This pinned build contains an editor/addressable Init alongside the main
# corpus. The recovered port uses the main ResourceManager copy; it is read first.
return name == "Init"
def scan_object(self, package, obj, ctx):
kind = obj.type.name
if kind == "ResourceManager" and ctx.media:
self._actors(package, obj, ctx)
return True
if kind == "MonoBehaviour" and obj.peek_name() == "CustomVariablesConfiguration":
self._defaults(package, obj, ctx)
return True
return False
@staticmethod
def _actors(package, obj, ctx):
from UnityPy.classes import PPtr
actor_keys = set()
for key, pointer in obj.read_typetree()["m_Container"]:
if not key.lower().startswith("characters/"):
continue
name = key.lower().removeprefix("characters/")
actor_keys.add(name)
actor = PPtr(assetsfile=obj.assets_file, **pointer).deref()
# Unity exposes both the complete texture and its Sprite under the same
# Resources key. Naninovel's texture actor uses the complete texture,
# as does the recovered Godot port.
if actor.type.name == "Sprite":
continue
if actor.type.name != "Texture2D":
package.issues.append(Issue("unsupported_actor", "Не поддерживается тип персонажа Tomie", key))
continue
identity = f"{ctx.source.relative_to(ctx.root).as_posix()}:{actor.assets_file.name}:{actor.path_id}"
previous = package.actor_resources.get(name)
if previous and previous != identity:
package.issues.append(Issue("conflicting_actor", "Различающиеся изображения персонажа Tomie", key))
package.actor_resources[name] = identity
package.resources.append((ctx.source, actor))
for name in actor_keys - package.actor_resources.keys():
package.issues.append(Issue("missing_actor_texture", "Не найдена полная текстура персонажа Tomie", name))
@staticmethod
def _defaults(package, obj, ctx):
codec = ctx.codec(str(obj.assets_file.unity_version))
node = obj.serialized_type.node or codec.node("Elringus.Naninovel.Runtime", "Naninovel.CustomVariablesConfiguration")
config = obj.read_typetree(nodes=node)
defaults = {entry["name"]: json.loads(entry["value"]) for entry in config["PredefinedVariables"]}
if package.defaults and package.defaults != defaults:
package.issues.append(Issue("conflicting_defaults", "Различающиеся исходные переменные Naninovel"))
else:
package.defaults = defaults