mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-31 21:03:48 +01:00
Add option to disable disabling sound streaming.
This commit is contained in:
parent
ac2f2cf3b9
commit
e18fcafc51
4 changed files with 584 additions and 533 deletions
|
|
@ -12,364 +12,384 @@ using Penumbra.Mod;
|
|||
using Penumbra.Structs;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
// The ModCollectionCache contains all required temporary data to use a collection.
|
||||
// It will only be setup if a collection gets activated in any way.
|
||||
public class ModCollectionCache
|
||||
{
|
||||
// The ModCollectionCache contains all required temporary data to use a collection.
|
||||
// It will only be setup if a collection gets activated in any way.
|
||||
public class ModCollectionCache
|
||||
// Shared caches to avoid allocations.
|
||||
private static readonly BitArray FileSeen = new(256);
|
||||
private static readonly Dictionary< GamePath, Mod.Mod > RegisteredFiles = new(256);
|
||||
|
||||
public readonly Dictionary< string, Mod.Mod > AvailableMods = new();
|
||||
|
||||
private readonly SortedList< string, object? > _changedItems = new();
|
||||
public readonly Dictionary< GamePath, FullPath > ResolvedFiles = new();
|
||||
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
||||
public readonly HashSet< FullPath > MissingFiles = new();
|
||||
public readonly HashSet< ulong > Checksums = new();
|
||||
public readonly MetaManager MetaManipulations;
|
||||
|
||||
public IReadOnlyDictionary< string, object? > ChangedItems
|
||||
{
|
||||
// Shared caches to avoid allocations.
|
||||
private static readonly BitArray FileSeen = new( 256 );
|
||||
private static readonly Dictionary< GamePath, Mod.Mod > RegisteredFiles = new( 256 );
|
||||
|
||||
public readonly Dictionary< string, Mod.Mod > AvailableMods = new();
|
||||
|
||||
private readonly SortedList< string, object? > _changedItems = new();
|
||||
public readonly Dictionary< GamePath, FullPath > ResolvedFiles = new();
|
||||
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
||||
public readonly HashSet< FullPath > MissingFiles = new();
|
||||
public readonly HashSet< ulong > Checksums = new();
|
||||
public readonly MetaManager MetaManipulations;
|
||||
|
||||
public IReadOnlyDictionary< string, object? > ChangedItems
|
||||
get
|
||||
{
|
||||
get
|
||||
SetChangedItems();
|
||||
return _changedItems;
|
||||
}
|
||||
}
|
||||
|
||||
public ModCollectionCache( string collectionName, DirectoryInfo tempDir )
|
||||
=> MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir );
|
||||
|
||||
private static void ResetFileSeen( int size )
|
||||
{
|
||||
if( size < FileSeen.Length )
|
||||
{
|
||||
FileSeen.Length = size;
|
||||
FileSeen.SetAll( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSeen.SetAll( false );
|
||||
FileSeen.Length = size;
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateEffectiveFileList()
|
||||
{
|
||||
ResolvedFiles.Clear();
|
||||
SwappedFiles.Clear();
|
||||
MissingFiles.Clear();
|
||||
RegisteredFiles.Clear();
|
||||
_changedItems.Clear();
|
||||
|
||||
foreach( var mod in AvailableMods.Values
|
||||
.Where( m => m.Settings.Enabled )
|
||||
.OrderByDescending( m => m.Settings.Priority ) )
|
||||
{
|
||||
mod.Cache.ClearFileConflicts();
|
||||
AddFiles( mod );
|
||||
AddSwaps( mod );
|
||||
}
|
||||
|
||||
AddMetaFiles();
|
||||
Checksums.Clear();
|
||||
foreach( var file in ResolvedFiles )
|
||||
{
|
||||
Checksums.Add( file.Value.Crc64 );
|
||||
}
|
||||
}
|
||||
|
||||
private void SetChangedItems()
|
||||
{
|
||||
if( _changedItems.Count > 0 || ResolvedFiles.Count + SwappedFiles.Count + MetaManipulations.Count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Skip meta files because IMCs would result in far too many false-positive items,
|
||||
// since they are per set instead of per item-slot/item/variant.
|
||||
var metaFiles = MetaManipulations.Files.Select( p => p.Item1 ).ToHashSet();
|
||||
var identifier = GameData.GameData.GetIdentifier();
|
||||
foreach( var resolved in ResolvedFiles.Keys.Where( file => !metaFiles.Contains( file ) ) )
|
||||
{
|
||||
SetChangedItems();
|
||||
return _changedItems;
|
||||
identifier.Identify( _changedItems, resolved );
|
||||
}
|
||||
|
||||
foreach( var swapped in SwappedFiles.Keys )
|
||||
{
|
||||
identifier.Identify( _changedItems, swapped );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Unknown Error:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AddFiles( Mod.Mod mod )
|
||||
{
|
||||
ResetFileSeen( mod.Data.Resources.ModFiles.Count );
|
||||
// Iterate in reverse so that later groups take precedence before earlier ones.
|
||||
foreach( var group in mod.Data.Meta.Groups.Values.Reverse() )
|
||||
{
|
||||
switch( group.SelectionType )
|
||||
{
|
||||
case SelectType.Single:
|
||||
AddFilesForSingle( group, mod );
|
||||
break;
|
||||
case SelectType.Multi:
|
||||
AddFilesForMulti( group, mod );
|
||||
break;
|
||||
default: throw new InvalidEnumArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public ModCollectionCache( string collectionName, DirectoryInfo tempDir )
|
||||
=> MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir );
|
||||
AddRemainingFiles( mod );
|
||||
}
|
||||
|
||||
private static void ResetFileSeen( int size )
|
||||
private bool FilterFile( GamePath gamePath )
|
||||
{
|
||||
// If audio streaming is not disabled, replacing .scd files crashes the game,
|
||||
// so only add those files if it is disabled.
|
||||
if( !Penumbra.Config.DisableSoundStreaming
|
||||
&& gamePath.ToString().EndsWith( ".scd", StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
if( size < FileSeen.Length )
|
||||
{
|
||||
FileSeen.Length = size;
|
||||
FileSeen.SetAll( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSeen.SetAll( false );
|
||||
FileSeen.Length = size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CalculateEffectiveFileList()
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void AddFile( Mod.Mod mod, GamePath gamePath, FullPath file )
|
||||
{
|
||||
if( FilterFile( gamePath ) )
|
||||
{
|
||||
ResolvedFiles.Clear();
|
||||
SwappedFiles.Clear();
|
||||
MissingFiles.Clear();
|
||||
RegisteredFiles.Clear();
|
||||
_changedItems.Clear();
|
||||
|
||||
foreach( var mod in AvailableMods.Values
|
||||
.Where( m => m.Settings.Enabled )
|
||||
.OrderByDescending( m => m.Settings.Priority ) )
|
||||
{
|
||||
mod.Cache.ClearFileConflicts();
|
||||
AddFiles( mod );
|
||||
AddSwaps( mod );
|
||||
}
|
||||
|
||||
AddMetaFiles();
|
||||
Checksums.Clear();
|
||||
foreach( var file in ResolvedFiles )
|
||||
Checksums.Add( file.Value.Crc64 );
|
||||
return;
|
||||
}
|
||||
|
||||
private void SetChangedItems()
|
||||
if( !RegisteredFiles.TryGetValue( gamePath, out var oldMod ) )
|
||||
{
|
||||
if( _changedItems.Count > 0 || ResolvedFiles.Count + SwappedFiles.Count + MetaManipulations.Count == 0 )
|
||||
RegisteredFiles.Add( gamePath, mod );
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, gamePath );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, gamePath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMissingFile( FullPath file )
|
||||
{
|
||||
switch( file.Extension.ToLowerInvariant() )
|
||||
{
|
||||
case ".meta":
|
||||
case ".rgsp":
|
||||
return;
|
||||
default:
|
||||
MissingFiles.Add( file );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPathsForOption( Option option, Mod.Mod mod, bool enabled )
|
||||
{
|
||||
foreach( var (file, paths) in option.OptionFiles )
|
||||
{
|
||||
var fullPath = new FullPath( mod.Data.BasePath, file );
|
||||
var idx = mod.Data.Resources.ModFiles.IndexOf( f => f.Equals( fullPath ) );
|
||||
if( idx < 0 )
|
||||
{
|
||||
AddMissingFile( fullPath );
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
var registeredFile = mod.Data.Resources.ModFiles[ idx ];
|
||||
if( !registeredFile.Exists )
|
||||
{
|
||||
// Skip meta files because IMCs would result in far too many false-positive items,
|
||||
// since they are per set instead of per item-slot/item/variant.
|
||||
var metaFiles = MetaManipulations.Files.Select( p => p.Item1 ).ToHashSet();
|
||||
var identifier = GameData.GameData.GetIdentifier();
|
||||
foreach( var resolved in ResolvedFiles.Keys.Where( file => !metaFiles.Contains( file ) ) )
|
||||
{
|
||||
identifier.Identify( _changedItems, resolved );
|
||||
}
|
||||
|
||||
foreach( var swapped in SwappedFiles.Keys )
|
||||
{
|
||||
identifier.Identify( _changedItems, swapped );
|
||||
}
|
||||
AddMissingFile( registeredFile );
|
||||
continue;
|
||||
}
|
||||
catch( Exception e )
|
||||
|
||||
FileSeen.Set( idx, true );
|
||||
if( enabled )
|
||||
{
|
||||
PluginLog.Error( $"Unknown Error:\n{e}" );
|
||||
foreach( var path in paths )
|
||||
{
|
||||
AddFile( mod, path, registeredFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForSingle( OptionGroup singleGroup, Mod.Mod mod )
|
||||
{
|
||||
Debug.Assert( singleGroup.SelectionType == SelectType.Single );
|
||||
|
||||
private void AddFiles( Mod.Mod mod )
|
||||
if( !mod.Settings.Settings.TryGetValue( singleGroup.GroupName, out var setting ) )
|
||||
{
|
||||
ResetFileSeen( mod.Data.Resources.ModFiles.Count );
|
||||
// Iterate in reverse so that later groups take precedence before earlier ones.
|
||||
foreach( var group in mod.Data.Meta.Groups.Values.Reverse() )
|
||||
{
|
||||
switch( group.SelectionType )
|
||||
{
|
||||
case SelectType.Single:
|
||||
AddFilesForSingle( group, mod );
|
||||
break;
|
||||
case SelectType.Multi:
|
||||
AddFilesForMulti( group, mod );
|
||||
break;
|
||||
default: throw new InvalidEnumArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
AddRemainingFiles( mod );
|
||||
setting = 0;
|
||||
}
|
||||
|
||||
private void AddFile( Mod.Mod mod, GamePath gamePath, FullPath file )
|
||||
for( var i = 0; i < singleGroup.Options.Count; ++i )
|
||||
{
|
||||
if( !RegisteredFiles.TryGetValue( gamePath, out var oldMod ) )
|
||||
AddPathsForOption( singleGroup.Options[ i ], mod, setting == i );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForMulti( OptionGroup multiGroup, Mod.Mod mod )
|
||||
{
|
||||
Debug.Assert( multiGroup.SelectionType == SelectType.Multi );
|
||||
|
||||
if( !mod.Settings.Settings.TryGetValue( multiGroup.GroupName, out var setting ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Also iterate options in reverse so that later options take precedence before earlier ones.
|
||||
for( var i = multiGroup.Options.Count - 1; i >= 0; --i )
|
||||
{
|
||||
AddPathsForOption( multiGroup.Options[ i ], mod, ( setting & ( 1 << i ) ) != 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRemainingFiles( Mod.Mod mod )
|
||||
{
|
||||
for( var i = 0; i < mod.Data.Resources.ModFiles.Count; ++i )
|
||||
{
|
||||
if( FileSeen.Get( i ) )
|
||||
{
|
||||
RegisteredFiles.Add( gamePath, mod );
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = mod.Data.Resources.ModFiles[ i ];
|
||||
if( file.Exists )
|
||||
{
|
||||
AddFile( mod, file.ToGamePath( mod.Data.BasePath ), file );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, gamePath );
|
||||
MissingFiles.Add( file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMetaFiles()
|
||||
{
|
||||
foreach( var (gamePath, file) in MetaManipulations.Files )
|
||||
{
|
||||
if( RegisteredFiles.TryGetValue( gamePath, out var mod ) )
|
||||
{
|
||||
PluginLog.Warning(
|
||||
$"The meta manipulation file {gamePath} was already completely replaced by {mod.Data.Meta.Name}. This is probably a mistake. Using the custom file {file.FullName}." );
|
||||
}
|
||||
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSwaps( Mod.Mod mod )
|
||||
{
|
||||
foreach( var (key, value) in mod.Data.Meta.FileSwaps.Where( kvp => !FilterFile( kvp.Key ) ) )
|
||||
{
|
||||
if( !RegisteredFiles.TryGetValue( key, out var oldMod ) )
|
||||
{
|
||||
RegisteredFiles.Add( key, mod );
|
||||
SwappedFiles.Add( key, value );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, key );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, gamePath );
|
||||
oldMod.Cache.AddConflict( mod, key );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMissingFile( FullPath file )
|
||||
{
|
||||
switch( file.Extension.ToLowerInvariant() )
|
||||
{
|
||||
case ".meta":
|
||||
case ".rgsp":
|
||||
return;
|
||||
default:
|
||||
MissingFiles.Add( file );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPathsForOption( Option option, Mod.Mod mod, bool enabled )
|
||||
{
|
||||
foreach( var (file, paths) in option.OptionFiles )
|
||||
{
|
||||
var fullPath = new FullPath(mod.Data.BasePath, file);
|
||||
var idx = mod.Data.Resources.ModFiles.IndexOf( f => f.Equals(fullPath) );
|
||||
if( idx < 0 )
|
||||
{
|
||||
AddMissingFile( fullPath );
|
||||
continue;
|
||||
}
|
||||
|
||||
var registeredFile = mod.Data.Resources.ModFiles[ idx ];
|
||||
if( !registeredFile.Exists )
|
||||
{
|
||||
AddMissingFile( registeredFile );
|
||||
continue;
|
||||
}
|
||||
|
||||
FileSeen.Set( idx, true );
|
||||
if( enabled )
|
||||
{
|
||||
foreach( var path in paths )
|
||||
{
|
||||
AddFile( mod, path, registeredFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForSingle( OptionGroup singleGroup, Mod.Mod mod )
|
||||
{
|
||||
Debug.Assert( singleGroup.SelectionType == SelectType.Single );
|
||||
|
||||
if( !mod.Settings.Settings.TryGetValue( singleGroup.GroupName, out var setting ) )
|
||||
{
|
||||
setting = 0;
|
||||
}
|
||||
|
||||
for( var i = 0; i < singleGroup.Options.Count; ++i )
|
||||
{
|
||||
AddPathsForOption( singleGroup.Options[ i ], mod, setting == i );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForMulti( OptionGroup multiGroup, Mod.Mod mod )
|
||||
{
|
||||
Debug.Assert( multiGroup.SelectionType == SelectType.Multi );
|
||||
|
||||
if( !mod.Settings.Settings.TryGetValue( multiGroup.GroupName, out var setting ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Also iterate options in reverse so that later options take precedence before earlier ones.
|
||||
for( var i = multiGroup.Options.Count - 1; i >= 0; --i )
|
||||
{
|
||||
AddPathsForOption( multiGroup.Options[ i ], mod, ( setting & ( 1 << i ) ) != 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRemainingFiles( Mod.Mod mod )
|
||||
{
|
||||
for( var i = 0; i < mod.Data.Resources.ModFiles.Count; ++i )
|
||||
{
|
||||
if( FileSeen.Get( i ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = mod.Data.Resources.ModFiles[ i ];
|
||||
if( file.Exists )
|
||||
{
|
||||
AddFile( mod, file.ToGamePath( mod.Data.BasePath ), file );
|
||||
}
|
||||
else
|
||||
{
|
||||
MissingFiles.Add( file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMetaFiles()
|
||||
{
|
||||
foreach( var (gamePath, file) in MetaManipulations.Files )
|
||||
{
|
||||
if( RegisteredFiles.TryGetValue( gamePath, out var mod ) )
|
||||
{
|
||||
PluginLog.Warning(
|
||||
$"The meta manipulation file {gamePath} was already completely replaced by {mod.Data.Meta.Name}. This is probably a mistake. Using the custom file {file.FullName}." );
|
||||
}
|
||||
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSwaps( Mod.Mod mod )
|
||||
{
|
||||
foreach( var swap in mod.Data.Meta.FileSwaps )
|
||||
{
|
||||
if( !RegisteredFiles.TryGetValue( swap.Key, out var oldMod ) )
|
||||
{
|
||||
RegisteredFiles.Add( swap.Key, mod );
|
||||
SwappedFiles.Add( swap.Key, swap.Value );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, swap.Key );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, swap.Key );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddManipulations( Mod.Mod mod )
|
||||
{
|
||||
foreach( var manip in mod.Data.Resources.MetaManipulations.GetManipulationsForConfig( mod.Settings, mod.Data.Meta ) )
|
||||
{
|
||||
if( !MetaManipulations.TryGetValue( manip, out var oldMod ) )
|
||||
{
|
||||
MetaManipulations.ApplyMod( manip, mod );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, manip );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, manip );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMetaManipulations()
|
||||
{
|
||||
MetaManipulations.Reset( false );
|
||||
|
||||
foreach( var mod in AvailableMods.Values.Where( m => m.Settings.Enabled && m.Data.Resources.MetaManipulations.Count > 0 ) )
|
||||
{
|
||||
mod.Cache.ClearMetaConflicts();
|
||||
AddManipulations( mod );
|
||||
}
|
||||
|
||||
MetaManipulations.WriteNewFiles();
|
||||
}
|
||||
|
||||
public void RemoveMod( DirectoryInfo basePath )
|
||||
{
|
||||
if( AvailableMods.TryGetValue( basePath.Name, out var mod ) )
|
||||
{
|
||||
AvailableMods.Remove( basePath.Name );
|
||||
if( mod.Settings.Enabled )
|
||||
{
|
||||
CalculateEffectiveFileList();
|
||||
if( mod.Data.Resources.MetaManipulations.Count > 0 )
|
||||
{
|
||||
UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PriorityComparer : IComparer< Mod.Mod >
|
||||
{
|
||||
public int Compare( Mod.Mod? x, Mod.Mod? y )
|
||||
=> ( x?.Settings.Priority ?? 0 ).CompareTo( y?.Settings.Priority ?? 0 );
|
||||
}
|
||||
|
||||
private static readonly PriorityComparer Comparer = new();
|
||||
|
||||
public void AddMod( ModSettings settings, ModData data, bool updateFileList = true )
|
||||
{
|
||||
if( !AvailableMods.TryGetValue( data.BasePath.Name, out var existingMod ) )
|
||||
{
|
||||
var newMod = new Mod.Mod( settings, data );
|
||||
AvailableMods[ data.BasePath.Name ] = newMod;
|
||||
|
||||
if( updateFileList && settings.Enabled )
|
||||
{
|
||||
CalculateEffectiveFileList();
|
||||
if( data.Resources.MetaManipulations.Count > 0 )
|
||||
{
|
||||
UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FullPath? GetCandidateForGameFile( GamePath gameResourcePath )
|
||||
{
|
||||
if( !ResolvedFiles.TryGetValue( gameResourcePath, out var candidate ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( candidate.FullName.Length >= 260 || !candidate.Exists )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public GamePath? GetSwappedFilePath( GamePath gameResourcePath )
|
||||
=> SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
|
||||
|
||||
public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath )
|
||||
=> GetCandidateForGameFile( gameResourcePath )?.FullName.Replace( '\\', '/' ) ?? GetSwappedFilePath( gameResourcePath ) ?? null;
|
||||
}
|
||||
|
||||
private void AddManipulations( Mod.Mod mod )
|
||||
{
|
||||
foreach( var manip in mod.Data.Resources.MetaManipulations.GetManipulationsForConfig( mod.Settings, mod.Data.Meta ) )
|
||||
{
|
||||
if( !MetaManipulations.TryGetValue( manip, out var oldMod ) )
|
||||
{
|
||||
MetaManipulations.ApplyMod( manip, mod );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, manip );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, manip );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMetaManipulations()
|
||||
{
|
||||
MetaManipulations.Reset( false );
|
||||
|
||||
foreach( var mod in AvailableMods.Values.Where( m => m.Settings.Enabled && m.Data.Resources.MetaManipulations.Count > 0 ) )
|
||||
{
|
||||
mod.Cache.ClearMetaConflicts();
|
||||
AddManipulations( mod );
|
||||
}
|
||||
|
||||
MetaManipulations.WriteNewFiles();
|
||||
}
|
||||
|
||||
public void RemoveMod( DirectoryInfo basePath )
|
||||
{
|
||||
if( AvailableMods.TryGetValue( basePath.Name, out var mod ) )
|
||||
{
|
||||
AvailableMods.Remove( basePath.Name );
|
||||
if( mod.Settings.Enabled )
|
||||
{
|
||||
CalculateEffectiveFileList();
|
||||
if( mod.Data.Resources.MetaManipulations.Count > 0 )
|
||||
{
|
||||
UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PriorityComparer : IComparer< Mod.Mod >
|
||||
{
|
||||
public int Compare( Mod.Mod? x, Mod.Mod? y )
|
||||
=> ( x?.Settings.Priority ?? 0 ).CompareTo( y?.Settings.Priority ?? 0 );
|
||||
}
|
||||
|
||||
private static readonly PriorityComparer Comparer = new();
|
||||
|
||||
public void AddMod( ModSettings settings, ModData data, bool updateFileList = true )
|
||||
{
|
||||
if( !AvailableMods.TryGetValue( data.BasePath.Name, out var existingMod ) )
|
||||
{
|
||||
var newMod = new Mod.Mod( settings, data );
|
||||
AvailableMods[ data.BasePath.Name ] = newMod;
|
||||
|
||||
if( updateFileList && settings.Enabled )
|
||||
{
|
||||
CalculateEffectiveFileList();
|
||||
if( data.Resources.MetaManipulations.Count > 0 )
|
||||
{
|
||||
UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FullPath? GetCandidateForGameFile( GamePath gameResourcePath )
|
||||
{
|
||||
if( !ResolvedFiles.TryGetValue( gameResourcePath, out var candidate ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( candidate.FullName.Length >= 260 || !candidate.Exists )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public GamePath? GetSwappedFilePath( GamePath gameResourcePath )
|
||||
=> SwappedFiles.TryGetValue( gameResourcePath, out var swappedPath ) ? swappedPath : null;
|
||||
|
||||
public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath )
|
||||
=> GetCandidateForGameFile( gameResourcePath )?.FullName.Replace( '\\', '/' ) ?? GetSwappedFilePath( gameResourcePath ) ?? null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue