- 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>
67 lines
3.2 KiB
Python
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
|