def __fixActive(acti, lord): # filter plugins not present in modInfos - this will disable corrupted too! actiFiltered = [x for x in acti if x in bosh.modInfos] #preserve acti order _removed = set(acti) - set(actiFiltered) if _removed: # take note as we may need to rewrite plugins txt msg = u'Those mods were present in plugins.txt but were not present ' \ u'in Data/ directory or were corrupted: ' + _pl(_removed) + u'\n' bosh.modInfos.selectedBad = _removed else: msg = u'' # again is below needed ? Apparently not with liblo 4 (acti is [Skyrim.esm, # Update.esm] on empty plugins.txt) - Keep it cause eventually (when liblo # is made to return actual contents of plugins.txt at all times) I may # need to correct this here addUpdateEsm = False if bush.game.fsName == 'Skyrim': skyrim = bolt.GPath(u'Skyrim.esm') if not skyrim in actiFiltered: actiFiltered.insert(0, skyrim) msg += u'Skyrim.esm not present in active mods' + u'\n' updateEsm = bolt.GPath(u'Update.esm') if updateEsm in lord and not updateEsm in actiFiltered: msg += (u'Update.esm not present in plugins.txt while present in ' u'Data folder') + u'\n' addUpdateEsm = True dexDict = {mod: index for index, mod in enumerate(lord)} # not needed for oblivion, for skyrim liblo will write plugins.txt in order # STILL restore for skyrim to warn on LO change if usingTxtFile(): actiSorted = actiFiltered[:] actiSorted.sort(key=dexDict.__getitem__) # all present in lord if actiFiltered != actiSorted: # were mods in an order that disagrees with lord ? msg += (u'Plugins.txt order of plugins (%s) differs from current ' u'load order (%s)') % (_pl(actiFiltered), _pl(actiSorted)) else: actiSorted = sorted(actiFiltered, key=dexDict.__getitem__) if addUpdateEsm: # insert after the last master (as does liblo) actiSorted.insert(_indexFirstEsp(actiSorted), updateEsm) # check if we have more than 256 active mods if len(actiSorted) > 255: msg += u'Plugins.txt contains more than 255 plugins - the following ' \ u'plugins will be deactivated: ' bosh.modInfos.selectedExtra = actiSorted[255:] msg += _pl(bosh.modInfos.selectedExtra) acti[:] = actiSorted[:255] # chop off extra if msg: ##: Notify user - maybe backup previous plugin txt ? bolt.deprint(u'Invalid Plugin txt corrected' + u'\n' + msg) SetActivePlugins(acti, lord, _fixed=True) return True # changes, saved return False # no changes, not saved
def _parse_plugins_txt_(path, mod_infos, _star): """Parse loadorder.txt and plugins.txt files with or without stars. Return two lists which are identical except when _star is True, whereupon the second list is the load order while the first the active plugins. In all other cases use the first list, which is either the list of active mods (when parsing plugins.txt) or the load order (when parsing loadorder.txt) :type path: bolt.Path :type mod_infos: bosh.ModInfos :type _star: bool :rtype: (list[bolt.Path], list[bolt.Path]) """ with path.open('r') as ins: #--Load Files active, modnames = [], [] for line in ins: # Oblivion/Skyrim saves the plugins.txt file in cp1252 format # It wont accept filenames in any other encoding modname = _re_plugins_txt_comment.sub('', line).strip() if not modname: continue # use raw strings below is_active = not _star or modname.startswith('*') if _star and is_active: modname = modname[1:] try: test = bolt.decode(modname, encoding='cp1252') except UnicodeError: bolt.deprint(u'%r failed to properly decode' % modname) continue if bolt.GPath(test) not in mod_infos: # The automatic encoding detector could have returned # an encoding it actually wasn't. Luckily, we # have a way to double check: modInfos.data for encoding in bolt.encodingOrder: try: test2 = unicode(modname, encoding) if bolt.GPath(test2) not in mod_infos: continue modname = bolt.GPath(test2) break except UnicodeError: pass else: modname = bolt.GPath(test) else: modname = bolt.GPath(test) modnames.append(modname) if is_active: active.append(modname) return active, modnames
def restore_ini(self): if self._timestamped_old is self.__unset: return # we did not move bash.ini if self._timestamped_old is not None: bolt.deprint(u'Restoring bash.ini') GPath(self._timestamped_old).copyTo(u'bash.ini') elif self._bash_ini_path: # remove bash.ini as it is the one from the backup bolt.GPath(u'bash.ini').remove()
class SkyrimSE(AsteriskGame): must_be_active_if_present = ( bolt.GPath(u'Update.esm'), bolt.GPath(u'Dawnguard.esm'), bolt.GPath(u'Hearthfires.esm'), bolt.GPath(u'Dragonborn.esm'), ) _ccc_filename = u'Skyrim.ccc' @property def remove_from_plugins_txt(self): return {bolt.GPath(u'Skyrim.esm')} | set( self.must_be_active_if_present) __dlc_spacing = 60 # in seconds def _fixed_order_plugins(self): """Return the semi fixed plugins after pinning them in correct order by timestamping them.""" # get existing add = [self.master_path] add.extend(x for x in self.must_be_active_if_present if x in self.mod_infos) # rewrite mtimes master_mtime = self.mod_infos[self.master_path].mtime update = bolt.GPath(u'Update.esm') for dlc in add[1:]: if dlc == update: master_mtime = self.mod_infos[update].mtime else: master_mtime += self.__dlc_spacing dlc_mtime = self.mod_infos[dlc].mtime if dlc_mtime != master_mtime: self.mod_infos[dlc].setmtime(master_mtime) bolt.deprint(u'Restamped %s from %s to %s' % (dlc, bolt.formatDate(dlc_mtime), bolt.formatDate(master_mtime))) return add
def exit_cleanup(): # Cleanup temp installers directory import tempfile tmpDir = bolt.GPath(tempfile.tempdir) for file_ in tmpDir.list(): if file_.cs.startswith(u'wryebash_'): file_ = tmpDir.join(file_) try: if file_.isdir(): file_.rmtree(safety=file_.stail) else: file_.remove() except: pass # make sure to flush the BashBugDump.log if _bugdump_handle is not None: _bugdump_handle.close() if bass.is_restarting: cli = cmd_line = bass.sys_argv # list of cli args try: if '--uac' in bass.sys_argv: ##: mostly untested - needs revamp import win32api if is_standalone: exe = cli[0] cli = cli[1:] else: exe = sys.executable exe = [u'%s', u'"%s"'][u' ' in exe] % exe cli = u' '.join([u'%s', u'"%s"'][u' ' in x] % x for x in cli) cmd_line = u'%s %s' % (exe, cli) win32api.ShellExecute(0, 'runas', exe, cli, None, True) return else: import subprocess cmd_line = (is_standalone and cli) or [sys.executable] + cli subprocess.Popen( cmd_line, # a list, no need to escape spaces close_fds=True) except Exception as error: print error print u'Error Attempting to Restart Wrye Bash!' print u'cmd line: %s' % (cmd_line, ) print raise
class Fallout4(AsteriskGame): must_be_active_if_present = ( bolt.GPath(u'DLCRobot.esm'), bolt.GPath(u'DLCworkshop01.esm'), bolt.GPath(u'DLCCoast.esm'), bolt.GPath(u'DLCWorkshop02.esm'), bolt.GPath(u'DLCWorkshop03.esm'), bolt.GPath(u'DLCNukaWorld.esm'), bolt.GPath(u'DLCUltraHighResolution.esm'), ) _ccc_filename = u'Fallout4.ccc' @property def remove_from_plugins_txt(self): return {bolt.GPath(u'Fallout4.esm')} | set( self.must_be_active_if_present)
def _fixed_order_plugins(self): """Return the semi fixed plugins after pinning them in correct order by timestamping them.""" # get existing add = [self.master_path] add.extend(x for x in self.must_be_active_if_present if x in self.mod_infos) # rewrite mtimes master_mtime = self.mod_infos[self.master_path].mtime update = bolt.GPath(u'Update.esm') for dlc in add[1:]: if dlc == update: master_mtime = self.mod_infos[update].mtime else: master_mtime += self.__dlc_spacing dlc_mtime = self.mod_infos[dlc].mtime if dlc_mtime != master_mtime: self.mod_infos[dlc].setmtime(master_mtime) bolt.deprint(u'Restamped %s from %s to %s' % (dlc, bolt.formatDate(dlc_mtime), bolt.formatDate(master_mtime))) return add
def remove_from_plugins_txt(self): return {bolt.GPath(u'Skyrim.esm')} | set( self.must_be_active_if_present)
class TextfileGame(Game): must_be_active_if_present = (bolt.GPath(u'Update.esm'), ) def __init__(self, mod_infos, plugins_txt_path, loadorder_txt_path): super(TextfileGame, self).__init__(mod_infos, plugins_txt_path) self.loadorder_txt_path = loadorder_txt_path self.mtime_loadorder_txt = 0 self.size_loadorder_txt = 0 def load_order_changed(self): # if active changed externally refetch load order to check for desync return self.active_changed() or (self.loadorder_txt_path.exists() and ( (self.size_loadorder_txt, self.mtime_loadorder_txt) != self.loadorder_txt_path.size_mtime())) def __update_lo_cache_info(self): self.size_loadorder_txt, self.mtime_loadorder_txt = \ self.loadorder_txt_path.size_mtime() @staticmethod def _must_update_active(deleted, reordered): return deleted or reordered def swap(self, old_path, new_path): super(TextfileGame, self).swap(old_path, new_path) # Save loadorder.txt inside the old (saves) directory if self.loadorder_txt_path.exists(): self.loadorder_txt_path.copyTo(old_path.join(u'loadorder.txt')) # Move the new loadorder.txt here for use move = new_path.join(u'loadorder.txt') if move.exists(): move.copyTo(self.loadorder_txt_path) self.loadorder_txt_path.mtime = time.time( ) # update mtime to trigger refresh # Abstract overrides ------------------------------------------------------ def _fetch_load_order(self, cached_load_order, cached_active): """Read data from loadorder.txt file. If loadorder.txt does not exist create it and try reading plugins.txt so the load order of the user is preserved (note it will create the plugins.txt if not existing). Additional mods should be added by caller who should anyway call _fix_load_order. If cached_active is passed, the relative order of mods will be corrected to match their relative order in cached_active. :type cached_active: tuple[bolt.Path] | list[bolt.Path]""" if not self.loadorder_txt_path.exists(): mods = cached_active or [] if cached_active is not None and not self.plugins_txt_path.exists( ): self._write_plugins_txt(cached_active, cached_active) bolt.deprint(u'Created %s based on cached info' % self.plugins_txt_path) elif cached_active is None and self.plugins_txt_path.exists(): mods = self._fetch_active_plugins() # will add Skyrim.esm self._persist_load_order(mods, mods) bolt.deprint(u'Created %s' % self.loadorder_txt_path) return mods #--Read file _acti, lo = self._parse_modfile(self.loadorder_txt_path) # handle desync with plugins txt if cached_active is not None: cached_active_copy = cached_active[:] active_in_lo = [x for x in lo if x in set(cached_active)] w = dict((x, i) for i, x in enumerate(lo)) while active_in_lo: for i, (ordered, current) in enumerate( zip(cached_active_copy, active_in_lo)): if ordered != current: for j, x in enumerate(active_in_lo[i:]): if x == ordered: break # x should be above ordered to = w[ordered] + 1 + j # make room w = dict((x, i if i < to else i + 1) for x, i in w.iteritems()) w[x] = to # bubble them up ! active_in_lo.remove(ordered) cached_active_copy = cached_active_copy[i + 1:] active_in_lo = active_in_lo[i:] break else: break fetched_lo = lo[:] lo.sort(key=w.get) if lo != fetched_lo: self._persist_load_order(lo, lo) bolt.deprint(u'Corrected %s (order of mods differed from ' u'their order in %s)' % (self.loadorder_txt_path, self.plugins_txt_path)) self.__update_lo_cache_info() return lo def _fetch_active_plugins(self): acti, _lo = self._parse_plugins_txt() if self.master_path in acti: acti.remove(self.master_path) self._write_plugins_txt(acti, acti) bolt.deprint(u'Removed %s from %s' % (self.master_path, self.plugins_txt_path)) acti.insert(0, self.master_path) return acti def _persist_load_order(self, lord, active): _write_plugins_txt_(self.loadorder_txt_path, lord, lord, _star=False) self.__update_lo_cache_info() def _persist_active_plugins(self, active, lord): # must chop off Skyrim.esm self._write_plugins_txt(active[1:], active[1:]) def _persist_if_changed(self, active, lord, previous_active, previous_lord): if previous_lord is None or previous_lord != lord: self._persist_load_order(lord, active) if previous_active is None or previous_active != active: self._persist_active_plugins(active, lord) # Validation overrides ---------------------------------------------------- @staticmethod def _check_active_order(acti, lord): dex_dict = {mod: index for index, mod in enumerate(lord)} old = acti[:] acti.sort(key=dex_dict.__getitem__) # all present in lord if acti != old: # active mods order that disagrees with lord ? return (u'Active list order of plugins (%s) differs from supplied ' u'load order (%s)') % (_pl(old), _pl(acti)) return u''