Files
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

51 lines
2.1 KiB
Python

"""Game profiles: everything specific to one engine or one concrete game build.
The shared pipeline (upload, queue, storage, mods, player shell) only talks to
this interface. A game that needs its own importer, validator or runtime adds a
profile in ``app/games`` and registers it; nothing else has to change.
"""
from __future__ import annotations
from collections.abc import Callable
from pathlib import Path
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from app.converter.pipeline import Result
Progress = Callable[[str], None]
class GameProfile:
"""Base class. Subclasses override what differs from a plain engine import."""
#: Stable identifier stored in reports, e.g. ``renpy`` or ``tomie/expansion-1.3801``.
id: str = ""
#: Engine family from ``compatibility.detect`` this profile applies to.
engine: str = ""
#: ``game.json`` ``runtime`` value for games played by a dedicated player (None = shared player).
runtime: str | None = None
#: True for profiles pinned to one concrete build; they win over the engine's generic profile.
specific: bool = False
#: True when ``pipeline.convert_archive`` itself performs the conversion (Ren'Py).
converts_in_pipeline: bool = False
def claims(self, root: Path) -> bool:
"""Whether this profile recognises the game at ``root`` (cheap, no media decoding)."""
return True
def inspect(self, root: Path, work: Path | None = None) -> dict[str, Any]:
"""Read-only compatibility check; raise ``CompatibilityError`` to refuse the game."""
raise NotImplementedError
def convert(self, root: Path, work: Path, progress: Progress) -> Result:
"""Full import into a PGVN package."""
raise NotImplementedError
def validate_mod(self, game: dict[str, Any], scripts: dict[str, Any], manifest: dict[str, Any]) -> None:
"""Extra checks for edited mods of games using this profile; raise ``ValueError``."""
def unity_hooks(self) -> Any | None:
"""Hooks for the shared Unity/Naninovel reader (see ``app.games.hooks``)."""
return None