@@ -31,23 +31,33 @@ def moveOverwriteMerge(self, source: str, destination: str):
3131 self .moveOverwriteMerge (s_item , d_item )
3232 os .rmdir (source )
3333
34+ def readManifestId (self , manifest_path : str ) -> str | None :
35+ try :
36+ with open (manifest_path , encoding = "utf-8" ) as manifest_file :
37+ mod_data = json .load (manifest_file )
38+ except (OSError , json .JSONDecodeError ):
39+ return None
40+
41+ mod_id = mod_data .get ("id" )
42+ if isinstance (mod_id , str ) and mod_id :
43+ return mod_id
44+ return None
45+
3446 def fixInstalledMod (self , mod : mobase .IModInterface ):
3547 if not self .needsNameFix :
3648 return
3749 GameSMMPath = getattr (self .organizer .managedGame (), "GameSMMPath" , "" )
3850 filetree : mobase .IFileTree = mod .fileTree ()
3951 fixed = False
40- if filetree .exists (
41- GameSMMPath + "/Mods/FOLDERNAME" , mobase .IFileTree .DIRECTORY
42- ):
52+ foldername_path = GameSMMPath + "/Mods/FOLDERNAME"
53+ if filetree .exists (foldername_path , mobase .IFileTree .DIRECTORY ):
4354 path = mod .absolutePath ()
44- json_path = os .path .join (
45- path , GameSMMPath + "/Mods/FOLDERNAME/manifest.json"
46- )
47- mod_data = json .load (open (json_path , encoding = "utf-8" ))
48- modname = mod_data ["id" ]
49- old_path = os .path .join (path , GameSMMPath + "/Mods/FOLDERNAME" )
50- new_path = os .path .join (path , GameSMMPath + f"/Mods/{ modname } " )
55+ json_path = os .path .join (path , foldername_path , "manifest.json" )
56+ modname = self .readManifestId (json_path )
57+ if not modname :
58+ return
59+ old_path = os .path .join (path , foldername_path )
60+ new_path = os .path .join (path , GameSMMPath , "Mods" , modname )
5161 self .moveOverwriteMerge (old_path , new_path )
5262 fixed = True
5363 if not fixed :
@@ -57,30 +67,26 @@ def fixInstalledMod(self, mod: mobase.IModInterface):
5767 def dataLooksValid (
5868 self , filetree : mobase .IFileTree
5969 ) -> mobase .ModDataChecker .CheckReturn :
70+ validList = {"simple mod framework" }
71+ for e in filetree :
72+ if isinstance (e , mobase .IFileTree ) and e .isDir ():
73+ if e .name ().casefold () not in validList :
74+ return mobase .ModDataChecker .FIXABLE
6075 if filetree .exists ("Simple Mod Framework" , mobase .IFileTree .DIRECTORY ):
6176 return mobase .ModDataChecker .VALID
6277 return mobase .ModDataChecker .FIXABLE
6378
64- def fileExistsInNextSubDir (self , filetree : mobase .IFileTree , name : str ):
65- for branch in filetree :
66- if isinstance (branch , mobase .IFileTree ):
67- for e in branch :
68- if e .name () == name :
69- return True
70- return False
71-
7279 def allMoveTo (
7380 self , sourcetree : mobase .IFileTree , targettree : mobase .IFileTree , toMoveTo : str
74- ):
81+ ) -> bool :
7582 entriesToMove : list [mobase .FileTreeEntry ] = []
76- retVal = 0
7783 for e in sourcetree :
7884 entriesToMove .append (e )
7985 for e in entriesToMove :
8086 targettree .move (e , toMoveTo , mobase .IFileTree .MERGE )
81- retVal = 1
82- targettree .remove (sourcetree )
83- return retVal
87+ if sourcetree is not targettree :
88+ targettree .remove (sourcetree )
89+ return bool ( entriesToMove )
8490
8591 def firstTree (self , filetree : mobase .IFileTree ) -> mobase .IFileTree | None :
8692 for e in filetree :
@@ -90,22 +96,28 @@ def firstTree(self, filetree: mobase.IFileTree) -> mobase.IFileTree | None:
9096
9197 def fix (self , filetree : mobase .IFileTree ) -> mobase .IFileTree | None :
9298 GameSMMPath = getattr (self .organizer .managedGame (), "GameSMMPath" , "" )
93- treefixed = 0
99+
94100 if filetree .exists ("manifest.json" , mobase .IFileTree .FILE ):
95- treefixed = self .allMoveTo (
96- filetree , filetree , GameSMMPath + "/Mods/FOLDERNAME/"
97- )
98- if treefixed == 1 :
101+ if self .allMoveTo (
102+ filetree ,
103+ filetree ,
104+ GameSMMPath + "/Mods/FOLDERNAME/" ,
105+ ):
99106 self .needsNameFix = True
107+
100108 elif len (filetree ) == 1 :
101- firsttreelayer : mobase .IFileTree | None = self .firstTree (filetree )
102- if firsttreelayer is not None :
103- if firsttreelayer .exists ("manifest.json" , mobase .IFileTree .FILE ):
104- treefixed = self .allMoveTo (
105- firsttreelayer , filetree , GameSMMPath + "/Mods/FOLDERNAME/"
106- )
107- if treefixed == 1 :
108- self .needsNameFix = True
109+ firsttreelayer = self .firstTree (filetree )
110+
111+ if firsttreelayer is not None and firsttreelayer .exists (
112+ "manifest.json" , mobase .IFileTree .FILE
113+ ):
114+ if self .allMoveTo (
115+ firsttreelayer ,
116+ filetree ,
117+ GameSMMPath + "/Mods/FOLDERNAME/" ,
118+ ):
119+ self .needsNameFix = True
120+
109121 return filetree
110122
111123
@@ -129,72 +141,84 @@ def init(self, organizer: mobase.IOrganizer) -> bool:
129141
130142 def updateSmmMeta (self , mods : dict [str , mobase .ModState ]):
131143 SMM_Path = os .path .join (self .dataDirectory ().absolutePath (), self .GameSMMPath )
132- SMM_Config_Json = SMM_Path + "/config.json"
144+ SMM_Config_Json = os .path .join (SMM_Path , "config.json" )
145+
146+ if not os .path .exists (SMM_Config_Json ):
147+ return None
148+
133149 for key , value in mods .items ():
134- key = self ._organizer .modList ().getMod (key )
135- tree = key .fileTree ()
150+ mod = self ._organizer .modList ().getMod (key )
151+ tree = mod .fileTree ()
136152 subtree = tree .find (self .GameSMMPath + "/Mods" , mobase .IFileTree .DIRECTORY )
137- if isinstance (subtree , mobase .IFileTree ):
138- for e in subtree :
139- if isinstance (e , mobase .IFileTree ):
140- if e .exists ("manifest.json" , mobase .IFileTree .FILE ):
141- json_path = (
142- key .absolutePath () + "/" + e .path () + "/manifest.json"
143- )
144- mod_data = json .load (open (json_path , encoding = "utf-8" ))
145- modname = mod_data ["id" ]
146- if value == 35 :
147- with open (SMM_Config_Json , "r" ) as config_json :
148- config_json_content = config_json .read ()
149- config_json .close ()
150- good_code = '"knownMods": []'
151- if good_code in config_json_content :
152- bad_code = "{runtimePath:'..\\ Runtime',retailPath:'..\\ Retail',skipIntro:false,outputToSeparateDirectory:false,loadOrder:[''],modOptions:{},outputConfigToAppDataOnDeploy:true,knownMods:[''],developerMode:false,reportErrors:false}"
153- config_json_content = bad_code
154- if modname not in config_json_content :
155- substr = "knownMods:["
156- config_json_content = config_json_content .replace (
157- substr , substr + "'" + modname + "',"
158- )
159- substr = "loadOrder:["
160- config_json_content = config_json_content .replace (
161- substr , substr + "'" + modname + "',"
162- )
163- substr = ",],modOptions"
164- config_json_content = config_json_content .replace (
165- substr , "],modOptions"
166- )
167- substr = ",],developer"
168- config_json_content = config_json_content .replace (
169- substr , "],developer"
170- )
171- with open (SMM_Config_Json , "w" ) as config_json :
172- config_json .write (config_json_content )
173- config_json .close ()
174- return None
175- if value == 33 :
176- with open (SMM_Config_Json , "r" ) as config_json :
177- config_json_content = config_json .read ()
178- config_json .close ()
179- if modname in config_json_content :
180- config_json_content = config_json_content .replace (
181- "'" + modname + "'," , ""
182- )
183- config_json_content = config_json_content .replace (
184- ",," , ","
185- )
186- substr = ",],modOptions"
187- config_json_content = config_json_content .replace (
188- substr , "],modOptions"
189- )
190- substr = ",],developer"
191- config_json_content = config_json_content .replace (
192- substr , "],developer"
193- )
194- with open (SMM_Config_Json , "w" ) as config_json :
195- config_json .write (config_json_content )
196- config_json .close ()
197- return None
153+ if not isinstance (subtree , mobase .IFileTree ):
154+ continue
155+
156+ for e in subtree :
157+ if not isinstance (e , mobase .IFileTree ):
158+ continue
159+ if not e .exists ("manifest.json" , mobase .IFileTree .FILE ):
160+ continue
161+
162+ json_path = os .path .join (mod .absolutePath (), e .path (), "manifest.json" )
163+ try :
164+ with open (json_path , encoding = "utf-8" ) as manifest_file :
165+ mod_data = json .load (manifest_file )
166+ except (OSError , json .JSONDecodeError ):
167+ continue
168+
169+ modname = mod_data .get ("id" )
170+ if not isinstance (modname , str ) or not modname :
171+ continue
172+
173+ try :
174+ with open (SMM_Config_Json , "r" , encoding = "utf-8" ) as config_json :
175+ config_json_content = config_json .read ()
176+ except OSError :
177+ return None
178+
179+ good_code = '"knownMods": []'
180+ if good_code in config_json_content :
181+ config_json_content = "{runtimePath:'..\\ Runtime',retailPath:'..\\ Retail',skipIntro:false,outputToSeparateDirectory:false,loadOrder:[''],modOptions:{},outputConfigToAppDataOnDeploy:true,knownMods:[''],developerMode:false,reportErrors:false}"
182+
183+ quoted_modname = "'" + modname + "'"
184+ changed = False
185+ if value == mobase .ModState .ACTIVE :
186+ if quoted_modname not in config_json_content :
187+ substr = "knownMods:["
188+ config_json_content = config_json_content .replace (
189+ substr , substr + quoted_modname + ","
190+ )
191+ substr = "loadOrder:["
192+ config_json_content = config_json_content .replace (
193+ substr , substr + quoted_modname + ","
194+ )
195+ changed = True
196+ else :
197+ old_content = config_json_content
198+ config_json_content = config_json_content .replace (
199+ quoted_modname + "," , ""
200+ )
201+ config_json_content = config_json_content .replace (
202+ "," + quoted_modname , ""
203+ )
204+ changed = config_json_content != old_content
205+
206+ if changed :
207+ config_json_content = config_json_content .replace (",," , "," )
208+ config_json_content = config_json_content .replace (
209+ ",],modOptions" , "],modOptions"
210+ )
211+ config_json_content = config_json_content .replace (
212+ ",],developer" , "],developer"
213+ )
214+ try :
215+ with open (
216+ SMM_Config_Json , "w" , encoding = "utf-8"
217+ ) as config_json :
218+ config_json .write (config_json_content )
219+ except OSError :
220+ return None
221+ return None
198222
199223 def executables (self ):
200224 return [
0 commit comments