"""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