11
2- from PyQt6 .QtCore import QFileInfo
2+ from PyQt6 .QtCore import QDir
33import mobase
4+
5+ from pathlib import Path
6+ from ..basic_features .basic_save_game_info import BasicGameSaveGame
7+ from ..basic_features .basic_local_savegames import BasicLocalSavegames
8+ from ..steam_utils import find_steam_path
49from ..basic_game import BasicGame
510
6- class FLiMLModContentChecker ( modbase .ModDataChecker ):
7- def __init__ (self ) :
11+ class FantasyLifeIModDataChecker ( mobase .ModDataChecker ):
12+ def __init__ (self : mobase . ModDataChecker ) -> None :
813 super ().__init__ ()
914
10- def dataLooksValid (
11- self , filetree : mobase .IFileTree
12- ) -> mobase .ModDataChecker .CheckReturn :
13- for e in filetree :
14- if isinstance (e , mobase .IFileTree ) and e .exists (
15- "mod.json" , mobase .IFileTree .FILE
16- ):
17- return mobase .ModDataChecker .VALID
18-
15+ def dataLooksValid (self : mobase .ModDataChecker , filetree : mobase .IFileTree ) -> mobase .ModDataChecker .CheckReturn :
16+ if filetree .exists ("Game" , mobase .IFileTree .DIRECTORY ):
17+ return mobase .ModDataChecker .INVALID
18+
19+ if filetree .exists ("Mods" , mobase .IFileTree .DIRECTORY ) or filetree .exists ("Paks" , mobase .IFileTree .DIRECTORY ):
20+ return mobase .ModDataChecker .VALID
21+
1922 return mobase .ModDataChecker .INVALID
2023
21- class FantasyLifeI (BasicGame ):
24+
25+ class FantasyLifeI (BasicGame ,
26+ mobase .IPluginFileMapper ,
27+ mobase .IPluginInstallerSimple
28+ ):
29+
2230 Name = "Fantasy Life I Support Plugin"
2331 Author = "AmeliaCute"
24- Version = "0.2.0 "
32+ Version = "0.2.1 "
2533
26- GameName = "Fantasy Life i: The Girl who Steals Time "
34+ GameName = "FANTASY LIFE i "
2735 GameShortName = "fantasylifei"
2836 GameNexusName = "fantasylifeithegirlwhostealstime"
2937 GameValidShortNames = ["fli" ]
30-
38+
39+ GameDataPath = "Game/Content/"
3140 GameBinary = "Game/Binaries/Win64/NFL1-Win64-Shipping.exe"
32- GameLauncher = "NFL1.exe"
33-
3441 GameSteamId = 2993780
35- GameDataPath = "Mods"
42+
43+ GameSupportURL = (
44+ r"https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/"
45+ "Game:-Fantasy-Life-I:-The-Girl-Who-Steals-Time"
46+ )
47+
48+ def __init__ (self ):
49+ BasicGame .__init__ (self )
50+ mobase .IPluginFileMapper .__init__ (self )
51+ mobase .IPluginInstallerSimple .__init__ (self )
3652
37- def init (self , organizer : mobase .IOrganizer ):
53+ def init (self , organizer : mobase .IOrganizer ) -> bool :
3854 super ().init (organizer )
39- self ._register_feature (FLiMLModContentChecker )
55+ self ._register_feature (FantasyLifeIModDataChecker ())
56+ self ._register_feature (BasicLocalSavegames (self .savesDirectory ()))
4057 return True
4158
4259 def executables (self ):
4360 return [
4461 mobase .ExecutableInfo (
45- "Fantasy Life i" , QFileInfo ( self .gameDirectory (), "Game/Binaries/Win64/NFL1-Win64-Shipping.exe" )
62+ "Fantasy Life i" , self .GameBinary
4663 ),
4764 ]
4865
66+ ## SAVE
67+
68+ # credit to game.darkestdungeon.py
69+ @staticmethod
70+ def getCloudSaveDirectory () -> str | None :
71+ steamPath = find_steam_path ()
72+ if steamPath is None :
73+ return None
74+
75+ userData = steamPath .joinpath ("userdata" )
76+ for child in userData .iterdir ():
77+ name = child .name
78+ try :
79+ int (name )
80+ except ValueError :
81+ continue
82+
83+ cloudSaves = child .joinpath ("2993780/remote" )
84+ if cloudSaves .exists () and cloudSaves .is_dir ():
85+ return str (cloudSaves )
86+ return None
87+
88+ def savesDirectory (self ) -> QDir :
89+ return QDir (self .getCloudSaveDirectory ())
90+
91+ def listSaves (self , folder : QDir ) -> list [mobase .ISaveGame ]:
92+ saves : list [Path ] = []
93+ for path in Path (folder .absolutePath ()).glob ("*.bin" ):
94+ saves .append (path )
95+
96+ ##TODO: need a proper implementation
97+ return [BasicGameSaveGame (path ) for path in saves ]
98+
99+ ## MAPPING
100+
101+ def exeDirectory (self ) -> QDir : return QDir (QDir (self .gameDirectory ()).filePath ("Game/Binaries/Win64" ))
102+
103+ def mappings (self ) -> list [mobase .Mapping ]:
104+ return [
105+ mobase .Mapping ("*.dll" , self .exeDirectory ().absolutePath (), False , True ),
106+ ]
107+
108+ ## INSTALLER
109+
110+ _PAK_EXTENSIONS = ('.pak' , '.ucas' , '.utoc' )
111+
112+ def priority (self ) -> int :
113+ return 150
114+
115+ def isArchiveSupported (self , tree : mobase .IFileTree ) -> bool :
116+ for entry in tree :
117+ if entry .name ().lower ().endswith (self ._PAK_EXTENSIONS ): return True
118+
119+ if tree .exists ("Mod.json" , mobase .IFileTree .FILE ): return True
120+ return False
121+
122+ def install (self , name : mobase .GuessedString , tree : mobase .IFileTree , version : str , nexus_id : int ) -> mobase .InstallResult :
123+
124+ for entry in list (tree ):
125+ if entry .name ().lower ().endswith (self ._PAK_EXTENSIONS ):
126+ paks_dir = QDir ("." ).filePath ("Paks" )
127+ target_dir = QDir (paks_dir ).filePath ("~mods/" )
128+ QDir ().mkpath (target_dir )
129+ tree .move (entry , target_dir )
130+
131+ if tree .exists ("Mod.json" , mobase .IFileTree .FILE ):
132+ mods_dir = QDir ("." ).filePath ("Mods" )
133+ target_dir = QDir (mods_dir ).filePath (f"{ name .__str__ ()} /" )
134+ QDir ().mkpath (target_dir )
135+
136+ for entry in list (tree ): tree .move (entry , target_dir )
137+
138+ return mobase .InstallResult .SUCCESS
0 commit comments