@@ -273,7 +273,7 @@ def __init__(self, fullpath, load_cache=False):
273273
274274 def _reset_masters (self ):
275275 #--Master Names/Order
276- self .masterNames = tuple (self .header . masters )
276+ self .masterNames = tuple (self .get_masters () )
277277 self .masterOrder = tuple () #--Reset to empty for now
278278
279279 def _file_changed (self , stat_tuple ):
@@ -335,6 +335,18 @@ def getStatus(self):
335335 else :
336336 return status
337337
338+ def get_masters (self ):
339+ """
340+ Returns the masters of this file as a list, if that operation makes any
341+ sense. For example, the masters of a mod are its plugin masters, while
342+ the masters of a save file are the plugins listed in its plugin list.
343+ If this operation does not make sense (e.g. on an archive), an
344+ AbstractError is raised.
345+
346+ :return: A list of the masters of this file, as paths.
347+ """
348+ raise AbstractError ()
349+
338350 # Backup stuff - beta, see #292 -------------------------------------------
339351 def getFileInfos (self ):
340352 """Return one of the FileInfos singletons depending on fileInfo type.
@@ -508,6 +520,9 @@ def setmtime(self, set_time=0, crc_changed=False):
508520 else :
509521 self .calculate_crc (recalculate = True )
510522
523+ def get_masters (self ):
524+ return self .header .masters
525+
511526 # Ghosting and ghosting related overrides ---------------------------------
512527 def do_update (self ):
513528 self .isGhost , old_ghost = not self ._abs_path .exists () and (
@@ -967,17 +982,14 @@ def listErrors(self):
967982
968983#------------------------------------------------------------------------------
969984from .save_headers import get_save_header_type , SaveFileHeader
970- from ._saves import PluggyFile
985+ from .cosaves import PluggyCosave
971986from . import cosaves
972987
973988class SaveInfo (FileInfo ):
974- _cosave_type = None # type: cosaves.ACoSaveFile
989+ # The xSE cosave that may come with this save file. Lazily initialized.
990+ _xse_cosave = None
975991
976- @property
977- def cosave_type (self ):
978- if self ._cosave_type is None :
979- SaveInfo ._cosave_type = cosaves .get_cosave_type (bush .game .fsName )
980- return self ._cosave_type
992+ def cosave_type (self ): return cosaves .get_cosave_type (bush .game .fsName )
981993
982994 def getFileInfos (self ): return saveInfos
983995
@@ -1013,26 +1025,29 @@ def write_masters(self):
10131025 oldMasters = [GPath (decode (x )) for x in oldMasters ]
10141026 self .abs_path .untemp ()
10151027 #--Cosaves
1016- masterMap = dict (
1017- (x , y ) for x , y in zip (oldMasters , self .header .masters ) if x != y )
1018- #--Pluggy file?
1019- pluggyPath = CoSaves .getPaths (self .abs_path )[0 ]
1020- if masterMap and pluggyPath .exists ():
1021- pluggy = PluggyFile (pluggyPath )
1022- pluggy .load ()
1023- pluggy .mapMasters (masterMap )
1024- pluggy .safeSave ()
1025- #--OBSE/SKSE file?
1026- cosave = self .get_cosave ()
1027- if cosave is not None :
1028- cosave .map_masters (masterMap )
1029- cosave .write_cosave_safe ()
1028+ master_map = dict ((x .s , y .s ) for x , y in
1029+ zip (oldMasters , self .header .masters ) if x != y )
1030+ if master_map :
1031+ #--Pluggy cosave?
1032+ if bush .game .has_standalone_pluggy :
1033+ pluggy_path = self .get_pluggy_cosave_path ()
1034+ if pluggy_path .isfile ():
1035+ pluggy_cosave = self .get_pluggy_cosave ()
1036+ pluggy_cosave .remap_plugins (master_map )
1037+ pluggy_cosave .write_cosave_safe ()
1038+ #--xSE cosave?
1039+ if bush .game .se .se_abbrev :
1040+ xse_path = self .get_xse_cosave_path ()
1041+ if xse_path is not None and xse_path .isfile ():
1042+ xse_cosave = self .get_xse_cosave ()
1043+ xse_cosave .remap_plugins (master_map )
1044+ xse_cosave .write_cosave_safe ()
10301045
10311046 def get_cosave_tags (self ):
10321047 """Return strings expressing whether cosaves exist and are correct."""
10331048 cPluggy , cObse = (u'' , u'' )
10341049 pluggy = self .name .root + u'.pluggy'
1035- obse = self .get_se_cosave_path ()
1050+ obse = self .get_xse_cosave_path ()
10361051 if pluggy .exists ():
10371052 cPluggy = u'XP' [abs (pluggy .mtime - self .mtime ) < 10 ]
10381053 if obse and obse .exists ():
@@ -1044,21 +1059,52 @@ def backup_paths(self, first=False):
10441059 save_paths .extend (CoSaves .get_new_paths (* save_paths [0 ]))
10451060 return save_paths
10461061
1047- def get_cosave (self ):
1048- """:rtype: cosaves.ACoSaveFile"""
1049- cosave_path = self .get_se_cosave_path ()
1050- if cosave_path is None : return None
1051- try :
1052- return self .cosave_type (cosave_path ) # type: cosaves.ACoSaveFile
1053- except (OSError , IOError , FileError ) as e :
1054- if isinstance (e , FileError ) or (
1055- isinstance (e , (OSError , IOError )) and e .errno != errno .ENOENT ):
1056- deprint (u'Failed to open %s' % cosave_path , traceback = True )
1057- return None
1058-
1059- def get_se_cosave_path (self ):
1062+ # TODO(inf) Turn into a property?
1063+ def get_xse_cosave (self ):
1064+ """:rtype: cosaves.xSECosave"""
1065+ if self ._xse_cosave is None :
1066+ cosave_path = self .get_xse_cosave_path ()
1067+ if cosave_path is None : return None
1068+ try :
1069+ cosave_constructor = self .cosave_type ()
1070+ self ._xse_cosave = cosave_constructor (cosave_path )
1071+ except (OSError , IOError , FileError ) as e :
1072+ if isinstance (e , FileError ) or (
1073+ isinstance (e , (OSError , IOError )) and e .errno != errno .ENOENT ):
1074+ deprint (u'Failed to open %s' % cosave_path , traceback = True )
1075+ return None
1076+ return self ._xse_cosave
1077+
1078+ def get_xse_cosave_path (self ):
10601079 if self .cosave_type is None : return None
1061- return self .getPath ().root + u'.' + self .cosave_type .signature .lower ()
1080+ return self .getPath ().root + bush .game .se .cosave_ext
1081+
1082+ def get_pluggy_cosave (self ):
1083+ cosave_path = self .get_pluggy_cosave_path ()
1084+ if cosave_path is not None :
1085+ try :
1086+ return PluggyCosave (cosave_path )
1087+ except (OSError , IOError , FileError ) as e :
1088+ if isinstance (e , FileError ) or (
1089+ isinstance (e , (OSError , IOError )) and e .errno != errno .ENOENT ):
1090+ deprint (u'Failed to open %s' % cosave_path , traceback = True )
1091+ return None
1092+
1093+ def get_pluggy_cosave_path (self ):
1094+ return self .getPath ().root + u'.pluggy'
1095+
1096+ def get_masters (self ):
1097+ if bush .game .has_esl :
1098+ xse_cosave_path = self .get_xse_cosave_path ()
1099+ if xse_cosave_path is not None and xse_cosave_path .isfile ():
1100+ # Make sure the cosave's masters are actually useful
1101+ xse_cosave = self .get_xse_cosave ()
1102+ if xse_cosave .has_accurate_master_list (True ):
1103+ return [GPath (master ) for master in
1104+ xse_cosave .get_master_list ()]
1105+ # Fall back on the regular masters - either the cosave is unnecessary,
1106+ # doesn't exist or isn't accurate
1107+ return self .header .masters
10621108
10631109#------------------------------------------------------------------------------
10641110class DataStore (DataDict ):
0 commit comments