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>
This commit is contained in:
1 parent
bbbcc6a5d8
commit
12ec82c7e6
30 files changed
+700
-196
No files matched your search
@@ -0,0 +1,63 @@
|
||||
"""Authoring defects of the pinned Tomie build that the importer tolerates.
|
||||
|
||||
These are mistakes in the original scripts (jumps to labels that do not exist,
|
||||
branches without EndIf, assignments without a valid operator). The original
|
||||
engine fails at the same place; so does the PGames player, which throws an
|
||||
explicit error when execution reaches one (see nani-runtime.ts). They are listed
|
||||
as warnings in the import report instead of blocking the whole game.
|
||||
|
||||
The list is exact: a key is (issue code, message, script) and the number is the
|
||||
maximum count. Anything new or more frequent - for example from an edited mod -
|
||||
is still refused. Do not add entries to make an import pass; add them only after
|
||||
confirming that the defect exists in the original build.
|
||||
"""
|
||||
from collections import Counter
|
||||
|
||||
from app.converter.compatibility import Issue
|
||||
|
||||
KNOWN_DEFECTS: dict[tuple[str, str, str], int] = {
|
||||
('invalid_branch', 'BeginIf не имеет завершающего EndIf', 'EP_Slum'): 1,
|
||||
('invalid_branch', 'BeginIf не имеет завершающего EndIf', 'QuestHub'): 1,
|
||||
('invalid_branch', 'BeginIf не имеет завершающего EndIf', 'System'): 1,
|
||||
('invalid_branch', 'Else не имеет завершающего EndIf', 'EP_Slum'): 1,
|
||||
('invalid_branch', 'Else не имеет завершающего EndIf', 'QuestHub'): 1,
|
||||
('invalid_branch', 'ElseIf не имеет завершающего EndIf', 'EP_Slum'): 1,
|
||||
('missing_target', 'Не найден переход AB_Studio.SkillUp', 'AB_Studio'): 1,
|
||||
('missing_target', 'Не найден переход B_Beggar.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_Exercise.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_Harassment.endStreet', 'B_Harassment'): 1,
|
||||
('missing_target', 'Не найден переход B_Job.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_Model.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_Police.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_R_Roaming_Normal.DemandSexByGroup', 'B_R_Roaming_Normal'): 1,
|
||||
('missing_target', 'Не найден переход B_Rest.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход B_Study.Start', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход Common.Failed', 'B_Approached'): 1,
|
||||
('missing_target', 'Не найден переход Common.StatP', 'Common_Date'): 1,
|
||||
('missing_target', 'Не найден переход E_VacationWithMari.DayPassVacation', 'DicePlaySub'): 1,
|
||||
('missing_target', 'Не найден переход E_VacationWithMari.DayPassVacation', 'E_VacationWithMari'): 2,
|
||||
('missing_target', 'Не найден переход Laptop.Cam_END', 'Laptop'): 1,
|
||||
('missing_target', 'Не найден переход Laptop.SexPain_Defloration', 'Laptop'): 1,
|
||||
('missing_target', 'Не найден переход LoanShark.End', 'LoanShark'): 1,
|
||||
('missing_target', 'Не найден переход MessagePlay.End', 'MessagePlay'): 1,
|
||||
('missing_target', 'Не найден переход ReturnToHome.return', 'ReturnToHome'): 1,
|
||||
('missing_target', 'Не найден переход XEnding.Koman', 'QuestHub'): 1,
|
||||
('missing_target', 'Не найден переход XEnding.Suicide_Continue', 'XEnding'): 1,
|
||||
('unsupported_choice', 'Не поддерживается присваивание Naninovel: iCorruption+1.5', 'AppInswitter'): 1,
|
||||
('unsupported_choice', 'Не поддерживается присваивание Naninovel: iWillpower-5', 'EP_Slum'): 1,
|
||||
('unsupported_choice', 'Не поддерживается присваивание Naninovel: temp==2', 'B_HighStreet'): 1,
|
||||
}
|
||||
|
||||
|
||||
def split_known(issues: list[Issue]) -> tuple[list[Issue], list[Issue]]:
|
||||
"""Return (blocking, known) issues. Each key is waived up to its recorded count."""
|
||||
left = Counter(KNOWN_DEFECTS)
|
||||
blocking, known = [], []
|
||||
for issue in issues:
|
||||
key = (issue.code, issue.message, issue.location.split(":")[0])
|
||||
if left[key] > 0:
|
||||
left[key] -= 1
|
||||
known.append(issue)
|
||||
else:
|
||||
blocking.append(issue)
|
||||
return blocking, known
|
||||
Reference in new issue
Block a user