Files
pgames-manager/backend/app/converter/preflight.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

79 lines
4.2 KiB
Python

"""Read-only compatibility check, shared by uploads, CLI and the converter."""
import shutil
import subprocess
import tempfile
import zipfile
from pathlib import Path
from app.converter.archive import extract
from app.converter.compatibility import CompatibilityError, Issue, detect
def probe_archive(source: Path, filename: str) -> dict:
"""Cheap distribution check before queueing: list entries, never extract media."""
if zipfile.is_zipfile(source):
with zipfile.ZipFile(source) as archive:
entries = [entry.filename.replace("\\", "/") for entry in archive.infolist()]
else:
seven = shutil.which("7zz") or shutil.which("7z")
if not seven:
from app.converter.archive import ArchiveError
raise ArchiveError("Для проверки этого архива нужен 7-Zip")
try:
process = subprocess.run([seven, "l", "-slt", "-ba", "-sccUTF-8", str(source)],
capture_output=True, encoding="utf-8", errors="replace", timeout=120)
except subprocess.TimeoutExpired:
from app.converter.archive import ArchiveError
raise ArchiveError("Превышено время чтения списка файлов архива") from None
if process.returncode:
from app.converter.archive import ArchiveError
raise ArchiveError("Не удалось прочитать архив: " + (process.stderr or process.stdout)[-500:])
entries = [line[7:].replace("\\", "/") for line in process.stdout.splitlines() if line.startswith("Path = ")]
from app.converter.compatibility import IGNORED
entries = [entry.lower() for entry in entries
if not any(part.lower() in IGNORED for part in Path(entry).parts)]
engines = set()
if any("naninovel" in entry.rsplit("/", 1)[-1] and entry.endswith(".dll") for entry in entries):
engines.add("naninovel")
elif any("_data/managed/" in entry or "_data/il2cpp_data/" in entry or entry.endswith("_data/data.unity3d") for entry in entries):
engines.add("unity")
if any(entry.rsplit("/", 1)[-1] == "data.win" for entry in entries):
engines.add("gamemaker")
if any(entry.endswith((".pck", "/project.godot")) or entry == "project.godot" for entry in entries):
engines.add("godot")
if any(entry.endswith((".rgss3a", ".rgss2a", ".rgssad", "/rpg_core.js", "/rmmz_core.js")) for entry in entries):
engines.add("rpgmaker")
if any(entry.endswith((".rpy", ".rpyc", ".rpa")) and "renpy/" not in entry for entry in entries):
engines.add("renpy")
if len(engines) != 1:
raise CompatibilityError("ambiguous" if engines else "unknown", [Issue(
"multiple_engines" if engines else "unknown_engine",
"В архиве несколько движков: " + ", ".join(sorted(engines)) if engines else
"Движок не распознан; нужен полный дистрибутив игры")])
engine = engines.pop()
from app.games.registry import all_profiles
if engine not in {profile.engine for profile in all_profiles()}:
raise CompatibilityError(engine, [Issue("unsupported_engine", f"Движок {engine} пока не поддерживается")])
return {"engine": engine, "scriptsChecked": False}
def inspect_directory(root: Path, *, work: Path | None = None) -> dict:
from app.games.registry import select
detection = detect(root)
profile = select(detection.engine, detection.root)
if profile is None:
raise CompatibilityError(detection.engine, [Issue(
"unsupported_engine", f"Для движка {detection.engine} нет профиля импорта")])
return profile.inspect(detection.root, work)
def inspect_archive(source: Path, filename: str) -> dict:
if source.is_dir():
return inspect_directory(source)
with tempfile.TemporaryDirectory(prefix="pgames-preflight-") as directory:
root = Path(directory)
probe = probe_archive(source, filename)
extract(source, filename, root, only_scripts=probe["engine"] == "renpy")
return inspect_directory(root, work=root / "_inspection")