Skip to content

Commit

Permalink
bosh/_mergeability.py:
Browse files Browse the repository at this point in the history
The essential diff is:

@@ -3122,3 +3122,3 @@ def rescanMergeable(self,names,progress,doCBash=None):
                 try:
-                    canMerge = is_mergeable(fileInfo)
+                    canMerge = is_mergeable(fileInfo, self)
                 except Exception as e:
@@ -4196,3 +4196,3 @@ def _is_mergeable_no_load(modInfo, verbose):
     #-- Check to make sure NoMerge tag not in tags - if in tags don't show up as mergeable.
-    tags = modInfos[modInfo.name].getBashTags()
+    tags = modInfo.getBashTags()
     if u'NoMerge' in tags:
@@ -4219,3 +4219,3 @@ def pbash_mergeable_no_load(modInfo, verbose):

-def isPBashMergeable(modInfo,verbose=True):
+def isPBashMergeable(modInfo, minfos, verbose=True):
     """Returns True or error message indicating whether specified mod is mergeable."""
@@ -4255,3 +4255,3 @@ def isPBashMergeable(modInfo,verbose=True):
     if newblocks: reasons += u'\n.    '+_(u'New record(s) in block(s): ')+u', '.join(sorted(newblocks))+u'.'
-    dependent = [name.s for name, info in modInfos.iteritems()
+    dependent = [name.s for name, info in minfos.iteritems()
                  if info.header.author != u'BASHED PATCH'
@@ -4268,3 +4268,3 @@ def cbash_mergeable_no_load(modInfo, verbose):

-def _modIsMergeableLoad(modInfo,verbose):
+def _modIsMergeableLoad(modInfo, minfos, verbose):
     """Check if mod is mergeable, loading it and taking into account the
@@ -4272,3 +4272,3 @@ def _modIsMergeableLoad(modInfo,verbose):
     allowMissingMasters = {u'Filter', u'IIM', u'InventOnly'}
-    tags = modInfos[modInfo.name].getBashTags()
+    tags = modInfo.getBashTags()
     reasons = []
@@ -4287,3 +4287,3 @@ def _modIsMergeableLoad(modInfo,verbose):
             if not tags & allowMissingMasters:
-                if master not in modInfos:
+                if master not in minfos:
                     if not verbose: return False
@@ -4311,5 +4311,5 @@ def _modIsMergeableLoad(modInfo,verbose):
                     reasons.append(u'\n.    '+_(u'New record(s) in block(s): %s.') % u', '.join(sorted(newblocks)))
-        dependent = [name.s for name, info in modInfos.iteritems()
+        dependent = [name.s for name, info in minfos.iteritems()
             if info.header.author != u'BASHED PATCH' and
-            modInfo.name in info.header.masters and name not in modInfos.mergeable]
+            modInfo.name in info.header.masters and name not in minfos.mergeable]
         if dependent:
@@ -4321,3 +4321,3 @@ def _modIsMergeableLoad(modInfo,verbose):
 # noinspection PySimplifyBooleanCheck
-def isCBashMergeable(modInfo,verbose=True):
+def isCBashMergeable(modInfo, minfos, verbose=True):
     """Returns True or error message indicating whether specified mod is mergeable."""
@@ -4329,3 +4329,3 @@ def isCBashMergeable(modInfo,verbose=True):
     if verbose:
-        loadreasons = _modIsMergeableLoad(modInfo, verbose)
+        loadreasons = _modIsMergeableLoad(modInfo, minfos, verbose)
         reasons = []
@@ -4339,3 +4339,3 @@ def isCBashMergeable(modInfo,verbose=True):
         if canmerge == True:
-            return _modIsMergeableLoad(modInfo, verbose)
+            return _modIsMergeableLoad(modInfo, minfos, verbose)
         return False

Meaning I added a ModInfos parameter to rip this out of bosh.
Tmp file hence the underscore - probably belongs to patcher.
Will be worked on a bit in next few commits.

Under #201.
  • Loading branch information
Utumno committed Feb 10, 2017
1 parent b3fdcad commit 7e63c19
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 168 deletions.
171 changes: 3 additions & 168 deletions Mopy/bash/bosh/__init__.py
Expand Up @@ -45,6 +45,7 @@
from itertools import imap
from operator import attrgetter

from ._mergeability import isPBashMergeable, isCBashMergeable
from .mods_metadata import ConfigHelpers
from .. import bass, bolt, balt, bush, env, load_order, archives
from .. import patcher # for configIsCBash()
Expand All @@ -57,7 +58,7 @@
from ..bolt import decode, encode
from ..brec import MreRecord, ModReader, ModError, ModWriter, getObjectIndex, \
getFormIndices
from ..cint import ObCollection, CBashApi
from ..cint import CBashApi
from ..parsers import LoadFactory, ModFile

#--Settings
Expand Down Expand Up @@ -3182,7 +3183,7 @@ def rescanMergeable(self,names,progress,doCBash=None):
canMerge = False
else:
try:
canMerge = is_mergeable(fileInfo)
canMerge = is_mergeable(fileInfo, self)
except Exception as e:
# deprint (_(u"Error scanning mod %s (%s)") % (fileName, e))
# canMerge = False #presume non-mergeable.
Expand Down Expand Up @@ -4229,172 +4230,6 @@ def renamePlayer(self,newName):
saveFile.setRecord(npc.getTuple(fid,version))
saveFile.safeSave()

# Mergeability ----------------------------------------------------------------
##: belong to patcher/patch_files (?) but used in modInfos - cyclic imports
def _is_mergeable_no_load(modInfo, verbose):
reasons = []
if modInfo.isEsm():
if not verbose: return False
reasons.append(u'\n. '+_(u'Is esm.'))
#--Bashed Patch
if modInfo.header.author == u'BASHED PATCH':
if not verbose: return False
reasons.append(u'\n. '+_(u'Is Bashed Patch.'))
#--Bsa / voice?
if modInfo.isMod() and tuple(modInfo.hasResources()) != (False,False):
if not verbose: return False
hasBsa, hasVoices = modInfo.hasResources()
if hasBsa:
reasons.append(u'\n. '+_(u'Has BSA archive.'))
if hasVoices:
reasons.append(u'\n. '+_(u'Has associated voice directory (Sound\\Voice\\%s).') % modInfo.name.s)
#-- Check to make sure NoMerge tag not in tags - if in tags don't show up as mergeable.
tags = modInfos[modInfo.name].getBashTags()
if u'NoMerge' in tags:
if not verbose: return False
reasons.append(u'\n. '+_(u"Has 'NoMerge' tag."))
if reasons: return reasons
return True

def pbash_mergeable_no_load(modInfo, verbose):
reasons = _is_mergeable_no_load(modInfo, verbose)
if isinstance(reasons, list):
reasons = u''.join(reasons)
elif not reasons:
return False # non verbose mode
else: # True
reasons = u''
#--Missing Strings Files?
if modInfo.isMissingStrings():
if not verbose: return False
reasons += u'\n. '+_(u'Missing String Translation Files (Strings\\%s_%s.STRINGS, etc).') % (
modInfo.name.sbody, oblivionIni.get_ini_language())
if reasons: return reasons
return True

def isPBashMergeable(modInfo,verbose=True):
"""Returns True or error message indicating whether specified mod is mergeable."""
reasons = pbash_mergeable_no_load(modInfo, verbose)
if isinstance(reasons, unicode):
pass
elif not reasons:
return False # non verbose mode
else: # True
reasons = u''
#--Load test
mergeTypes = set([recClass.classType for recClass in bush.game.mergeClasses])
modFile = ModFile(modInfo, LoadFactory(False, *mergeTypes))
try:
modFile.load(True,loadStrings=False)
except ModError as error:
if not verbose: return False
reasons += u'\n. %s.' % error
#--Skipped over types?
if modFile.topsSkipped:
if not verbose: return False
reasons += u'\n. '+_(u'Unsupported types: ')+u', '.join(sorted(modFile.topsSkipped))+u'.'
#--Empty mod
elif not modFile.tops:
if not verbose: return False
reasons += u'\n. '+ u'Empty mod.'
#--New record
lenMasters = len(modFile.tes4.masters)
newblocks = []
for type,block in modFile.tops.iteritems():
for record in block.getActiveRecords():
if record.fid >> 24 >= lenMasters:
if record.flags1.deleted: continue #if new records exist but are deleted just skip em.
if not verbose: return False
newblocks.append(type)
break
if newblocks: reasons += u'\n. '+_(u'New record(s) in block(s): ')+u', '.join(sorted(newblocks))+u'.'
dependent = [name.s for name, info in modInfos.iteritems()
if info.header.author != u'BASHED PATCH'
if modInfo.name in info.header.masters]
if dependent:
if not verbose: return False
reasons += u'\n. '+_(u'Is a master of mod(s): ')+u', '.join(sorted(dependent))+u'.'
if reasons: return reasons
return True

def cbash_mergeable_no_load(modInfo, verbose):
"""Check if mod is mergeable without taking into account the rest of mods"""
return _is_mergeable_no_load(modInfo, verbose)

def _modIsMergeableLoad(modInfo,verbose):
"""Check if mod is mergeable, loading it and taking into account the
rest of mods."""
allowMissingMasters = {u'Filter', u'IIM', u'InventOnly'}
tags = modInfos[modInfo.name].getBashTags()
reasons = []

#--Load test
with ObCollection(ModsPath=dirs['mods'].s) as Current:
#MinLoad, InLoadOrder, AddMasters, TrackNewTypes, SkipAllRecords
modFile = Current.addMod(modInfo.getPath().stail, Flags=0x00002129)
Current.load()

missingMasters = []
nonActiveMasters = []
masters = modFile.TES4.masters
for master in masters:
master = GPath(master)
if not tags & allowMissingMasters:
if master not in modInfos:
if not verbose: return False
missingMasters.append(master.s)
elif not load_order.cached_is_active(master):
if not verbose: return False
nonActiveMasters.append(master.s)
#--masters not present in mod list?
if len(missingMasters):
if not verbose: return False
reasons.append(u'\n. '+_(u'Masters missing: ')+u'\n * %s' % (u'\n * '.join(sorted(missingMasters))))
if len(nonActiveMasters):
if not verbose: return False
reasons.append(u'\n. '+_(u'Masters not active: ')+u'\n * %s' % (u'\n * '.join(sorted(nonActiveMasters))))
#--Empty mod
if modFile.IsEmpty():
if not verbose: return False
reasons.append(u'\n. '+_(u'Empty mod.'))
#--New record
else:
if not tags & allowMissingMasters:
newblocks = modFile.GetNewRecordTypes()
if newblocks:
if not verbose: return False
reasons.append(u'\n. '+_(u'New record(s) in block(s): %s.') % u', '.join(sorted(newblocks)))
dependent = [name.s for name, info in modInfos.iteritems()
if info.header.author != u'BASHED PATCH' and
modInfo.name in info.header.masters and name not in modInfos.mergeable]
if dependent:
if not verbose: return False
reasons.append(u'\n. '+_(u'Is a master of non-mergeable mod(s): %s.') % u', '.join(sorted(dependent)))
if reasons: return reasons
return True

# noinspection PySimplifyBooleanCheck
def isCBashMergeable(modInfo,verbose=True):
"""Returns True or error message indicating whether specified mod is mergeable."""
if modInfo.name.s == u"Oscuro's_Oblivion_Overhaul.esp":
if verbose: return u'\n. ' + _(
u'Marked non-mergeable at request of mod author.')
return False
canmerge = cbash_mergeable_no_load(modInfo, verbose)
if verbose:
loadreasons = _modIsMergeableLoad(modInfo, verbose)
reasons = []
if canmerge != True:
reasons = canmerge
if loadreasons != True:
reasons.extend(loadreasons)
if reasons: return u''.join(reasons)
return True
else:
if canmerge == True:
return _modIsMergeableLoad(modInfo, verbose)
return False

# Initialization --------------------------------------------------------------
from ..env import get_personal_path, get_local_app_data_path

Expand Down
195 changes: 195 additions & 0 deletions Mopy/bash/bosh/_mergeability.py
@@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
#
# GPL License and Copyright Notice ============================================
# This file is part of Wrye Bash.
#
# Wrye Bash is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Wrye Bash is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Wrye Bash; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Wrye Bash copyright (C) 2005-2009 Wrye, 2010-2015 Wrye Bash Team
# https://github.com/wrye-bash
#
# =============================================================================
"""Tmp module to get mergeability stuff out of bosh.__init__.py."""
from .. import bass, bush
from ..bolt import GPath
from ..brec import ModError
from ..cint import ObCollection
from ..load_order import cached_is_active
from ..parsers import ModFile, LoadFactory

def _is_mergeable_no_load(modInfo, verbose):
reasons = []
if modInfo.isEsm():
if not verbose: return False
reasons.append(u'\n. '+_(u'Is esm.'))
#--Bashed Patch
if modInfo.header.author == u'BASHED PATCH':
if not verbose: return False
reasons.append(u'\n. '+_(u'Is Bashed Patch.'))
#--Bsa / voice?
if modInfo.isMod() and tuple(modInfo.hasResources()) != (False,False):
if not verbose: return False
hasBsa, hasVoices = modInfo.hasResources()
if hasBsa:
reasons.append(u'\n. '+_(u'Has BSA archive.'))
if hasVoices:
reasons.append(u'\n. '+_(u'Has associated voice directory (Sound\\Voice\\%s).') % modInfo.name.s)
#-- Check to make sure NoMerge tag not in tags - if in tags don't show up as mergeable.
tags = modInfo.getBashTags()
if u'NoMerge' in tags:
if not verbose: return False
reasons.append(u'\n. '+_(u"Has 'NoMerge' tag."))
if reasons: return reasons
return True

def pbash_mergeable_no_load(modInfo, verbose):
reasons = _is_mergeable_no_load(modInfo, verbose)
if isinstance(reasons, list):
reasons = u''.join(reasons)
elif not reasons:
return False # non verbose mode
else: # True
reasons = u''
#--Missing Strings Files?
if modInfo.isMissingStrings():
if not verbose: return False
from . import oblivionIni
reasons += u'\n. '+_(u'Missing String Translation Files (Strings\\%s_%s.STRINGS, etc).') % (
modInfo.name.sbody, oblivionIni.get_ini_language())
if reasons: return reasons
return True

def isPBashMergeable(modInfo, minfos, verbose=True):
"""Returns True or error message indicating whether specified mod is mergeable."""
reasons = pbash_mergeable_no_load(modInfo, verbose)
if isinstance(reasons, unicode):
pass
elif not reasons:
return False # non verbose mode
else: # True
reasons = u''
#--Load test
mergeTypes = set([recClass.classType for recClass in bush.game.mergeClasses])
modFile = ModFile(modInfo, LoadFactory(False, *mergeTypes))
try:
modFile.load(True,loadStrings=False)
except ModError as error:
if not verbose: return False
reasons += u'\n. %s.' % error
#--Skipped over types?
if modFile.topsSkipped:
if not verbose: return False
reasons += u'\n. '+_(u'Unsupported types: ')+u', '.join(sorted(modFile.topsSkipped))+u'.'
#--Empty mod
elif not modFile.tops:
if not verbose: return False
reasons += u'\n. '+ u'Empty mod.'
#--New record
lenMasters = len(modFile.tes4.masters)
newblocks = []
for type,block in modFile.tops.iteritems():
for record in block.getActiveRecords():
if record.fid >> 24 >= lenMasters:
if record.flags1.deleted: continue #if new records exist but are deleted just skip em.
if not verbose: return False
newblocks.append(type)
break
if newblocks: reasons += u'\n. '+_(u'New record(s) in block(s): ')+u', '.join(sorted(newblocks))+u'.'
dependent = [name.s for name, info in minfos.iteritems()
if info.header.author != u'BASHED PATCH'
if modInfo.name in info.header.masters]
if dependent:
if not verbose: return False
reasons += u'\n. '+_(u'Is a master of mod(s): ')+u', '.join(sorted(dependent))+u'.'
if reasons: return reasons
return True

def cbash_mergeable_no_load(modInfo, verbose):
"""Check if mod is mergeable without taking into account the rest of mods"""
return _is_mergeable_no_load(modInfo, verbose)

def _modIsMergeableLoad(modInfo, minfos, verbose):
"""Check if mod is mergeable, loading it and taking into account the
rest of mods."""
allowMissingMasters = {u'Filter', u'IIM', u'InventOnly'}
tags = modInfo.getBashTags()
reasons = []

#--Load test
with ObCollection(ModsPath=bass.dirs['mods'].s) as Current:
#MinLoad, InLoadOrder, AddMasters, TrackNewTypes, SkipAllRecords
modFile = Current.addMod(modInfo.getPath().stail, Flags=0x00002129)
Current.load()

missingMasters = []
nonActiveMasters = []
masters = modFile.TES4.masters
for master in masters:
master = GPath(master)
if not tags & allowMissingMasters:
if master not in minfos:
if not verbose: return False
missingMasters.append(master.s)
elif not cached_is_active(master):
if not verbose: return False
nonActiveMasters.append(master.s)
#--masters not present in mod list?
if len(missingMasters):
if not verbose: return False
reasons.append(u'\n. '+_(u'Masters missing: ')+u'\n * %s' % (u'\n * '.join(sorted(missingMasters))))
if len(nonActiveMasters):
if not verbose: return False
reasons.append(u'\n. '+_(u'Masters not active: ')+u'\n * %s' % (u'\n * '.join(sorted(nonActiveMasters))))
#--Empty mod
if modFile.IsEmpty():
if not verbose: return False
reasons.append(u'\n. '+_(u'Empty mod.'))
#--New record
else:
if not tags & allowMissingMasters:
newblocks = modFile.GetNewRecordTypes()
if newblocks:
if not verbose: return False
reasons.append(u'\n. '+_(u'New record(s) in block(s): %s.') % u', '.join(sorted(newblocks)))
dependent = [name.s for name, info in minfos.iteritems()
if info.header.author != u'BASHED PATCH' and
modInfo.name in info.header.masters and name not in minfos.mergeable]
if dependent:
if not verbose: return False
reasons.append(u'\n. '+_(u'Is a master of non-mergeable mod(s): %s.') % u', '.join(sorted(dependent)))
if reasons: return reasons
return True

# noinspection PySimplifyBooleanCheck
def isCBashMergeable(modInfo, minfos, verbose=True):
"""Returns True or error message indicating whether specified mod is mergeable."""
if modInfo.name.s == u"Oscuro's_Oblivion_Overhaul.esp":
if verbose: return u'\n. ' + _(
u'Marked non-mergeable at request of mod author.')
return False
canmerge = cbash_mergeable_no_load(modInfo, verbose)
if verbose:
loadreasons = _modIsMergeableLoad(modInfo, minfos, verbose)
reasons = []
if canmerge != True:
reasons = canmerge
if loadreasons != True:
reasons.extend(loadreasons)
if reasons: return u''.join(reasons)
return True
else:
if canmerge == True:
return _modIsMergeableLoad(modInfo, minfos, verbose)
return False

0 comments on commit 7e63c19

Please sign in to comment.