mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 12:44:19 +01:00
Apply meta manipulation and load the corresponding files when encountering meta files.
This commit is contained in:
parent
739627b7c2
commit
db5ce7a2e4
5 changed files with 234 additions and 3 deletions
|
|
@ -40,6 +40,7 @@ namespace Penumbra.Models
|
||||||
meta.HasGroupWithConfig = meta.Groups.Count > 0
|
meta.HasGroupWithConfig = meta.Groups.Count > 0
|
||||||
&& meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1 );
|
&& meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
catch( Exception )
|
catch( Exception )
|
||||||
|
|
|
||||||
149
Penumbra/Mods/MetaManager.cs
Normal file
149
Penumbra/Mods/MetaManager.cs
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Lumina.Data.Files;
|
||||||
|
using Penumbra.Game;
|
||||||
|
using Penumbra.Hooks;
|
||||||
|
using Penumbra.Util;
|
||||||
|
using Penumbra.MetaData;
|
||||||
|
|
||||||
|
namespace Penumbra.Mods
|
||||||
|
{
|
||||||
|
public class MetaManager : IDisposable
|
||||||
|
{
|
||||||
|
private class FileInformation
|
||||||
|
{
|
||||||
|
public readonly object Data;
|
||||||
|
public bool Changed;
|
||||||
|
public FileInfo? CurrentFile;
|
||||||
|
|
||||||
|
public FileInformation( object data )
|
||||||
|
=> Data = data;
|
||||||
|
|
||||||
|
public void Write( DirectoryInfo dir )
|
||||||
|
{
|
||||||
|
byte[] data = Data switch
|
||||||
|
{
|
||||||
|
EqdpFile eqdp => eqdp.WriteBytes(),
|
||||||
|
EqpFile eqp => eqp.WriteBytes(),
|
||||||
|
GmpFile gmp => gmp.WriteBytes(),
|
||||||
|
EstFile est => est.WriteBytes(),
|
||||||
|
ImcFile imc => imc.WriteBytes(),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
DisposeFile( CurrentFile );
|
||||||
|
CurrentFile = TempFile.WriteNew( dir, data );
|
||||||
|
Changed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string TmpDirectory = "penumbrametatmp";
|
||||||
|
|
||||||
|
private readonly MetaDefaults _default;
|
||||||
|
private readonly DirectoryInfo _dir;
|
||||||
|
private readonly GameResourceManagement _resourceManagement;
|
||||||
|
private readonly Dictionary< GamePath, FileInfo > _resolvedFiles;
|
||||||
|
|
||||||
|
private readonly HashSet< MetaManipulation > _currentManipulations = new();
|
||||||
|
private readonly Dictionary< GamePath, FileInformation > _currentFiles = new();
|
||||||
|
|
||||||
|
private static void DisposeFile( FileInfo? file )
|
||||||
|
{
|
||||||
|
if( !( file?.Exists ?? false ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Could not delete temporary file \"{file.FullName}\":\n{e}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach( var file in _currentFiles )
|
||||||
|
{
|
||||||
|
_resolvedFiles.Remove( file.Key );
|
||||||
|
DisposeFile( file.Value.CurrentFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentManipulations.Clear();
|
||||||
|
_currentFiles.Clear();
|
||||||
|
ClearDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearDirectory()
|
||||||
|
{
|
||||||
|
if( _dir.Exists )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete( _dir.FullName, true );
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Could not clear temporary metafile directory \"{_dir.FullName}\":\n{e}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaManager( Dictionary< GamePath, FileInfo > resolvedFiles, DirectoryInfo modDir )
|
||||||
|
{
|
||||||
|
_resolvedFiles = resolvedFiles;
|
||||||
|
_default = Service< MetaDefaults >.Get();
|
||||||
|
_resourceManagement = Service< GameResourceManagement >.Get();
|
||||||
|
_dir = new DirectoryInfo( Path.Combine( modDir.FullName, TmpDirectory ) );
|
||||||
|
ClearDirectory();
|
||||||
|
Directory.CreateDirectory( _dir.FullName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNewFiles()
|
||||||
|
{
|
||||||
|
foreach( var kvp in _currentFiles.Where( kvp => kvp.Value.Changed ) )
|
||||||
|
{
|
||||||
|
kvp.Value.Write( _dir );
|
||||||
|
_resolvedFiles[ kvp.Key ] = kvp.Value.CurrentFile!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resourceManagement.ReloadPlayerResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyMod( MetaManipulation m )
|
||||||
|
{
|
||||||
|
if( !_currentManipulations.Add( m ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gamePath = m.CorrespondingFilename();
|
||||||
|
if( !_currentFiles.TryGetValue( gamePath, out var file ) )
|
||||||
|
{
|
||||||
|
file = new FileInformation( _default.CreateNewFile( m ) ?? throw new IOException() )
|
||||||
|
{
|
||||||
|
Changed = true,
|
||||||
|
CurrentFile = null
|
||||||
|
};
|
||||||
|
_currentFiles[ gamePath ] = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Changed |= m.Type switch
|
||||||
|
{
|
||||||
|
MetaType.Eqp => m.Apply( ( EqpFile )file.Data ),
|
||||||
|
MetaType.Eqdp => m.Apply( ( EqdpFile )file.Data ),
|
||||||
|
MetaType.Gmp => m.Apply( ( GmpFile )file.Data ),
|
||||||
|
MetaType.Est => m.Apply( ( EstFile )file.Data ),
|
||||||
|
MetaType.Imc => m.Apply( ( ImcFile )file.Data ),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Penumbra.Hooks;
|
using Penumbra.Hooks;
|
||||||
using Penumbra.Models;
|
using Penumbra.Models;
|
||||||
|
|
@ -13,6 +14,7 @@ namespace Penumbra.Mods
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
|
public readonly Dictionary< GamePath, FileInfo > ResolvedFiles = new();
|
||||||
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
public readonly Dictionary< GamePath, GamePath > SwappedFiles = new();
|
||||||
|
public MetaManager? MetaManipulations;
|
||||||
|
|
||||||
public ModCollection? Mods { get; set; }
|
public ModCollection? Mods { get; set; }
|
||||||
private DirectoryInfo? _basePath;
|
private DirectoryInfo? _basePath;
|
||||||
|
|
@ -47,12 +49,15 @@ namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
ResolvedFiles.Clear();
|
ResolvedFiles.Clear();
|
||||||
SwappedFiles.Clear();
|
SwappedFiles.Clear();
|
||||||
|
MetaManipulations?.Dispose();
|
||||||
|
|
||||||
if( Mods == null )
|
if( Mods == null )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaManipulations = new MetaManager( ResolvedFiles, _basePath! );
|
||||||
|
|
||||||
var changedSettings = false;
|
var changedSettings = false;
|
||||||
var registeredFiles = new Dictionary< GamePath, string >();
|
var registeredFiles = new Dictionary< GamePath, string >();
|
||||||
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) )
|
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) )
|
||||||
|
|
@ -63,10 +68,14 @@ namespace Penumbra.Mods
|
||||||
ProcessSwappedFiles( registeredFiles, mod, settings );
|
ProcessSwappedFiles( registeredFiles, mod, settings );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedSettings)
|
if( changedSettings )
|
||||||
|
{
|
||||||
Mods.Save();
|
Mods.Save();
|
||||||
|
}
|
||||||
|
|
||||||
Service<GameResourceManagement>.Get().ReloadPlayerResources();
|
MetaManipulations.WriteNewFiles();
|
||||||
|
|
||||||
|
Service< GameResourceManagement >.Get().ReloadPlayerResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessSwappedFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
private void ProcessSwappedFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
|
||||||
|
|
@ -94,7 +103,14 @@ namespace Penumbra.Mods
|
||||||
RelPath relativeFilePath = new( file, mod.ModBasePath );
|
RelPath relativeFilePath = new( file, mod.ModBasePath );
|
||||||
var (configChanged, gamePaths) = mod.Meta.GetFilesForConfig( relativeFilePath, settings );
|
var (configChanged, gamePaths) = mod.Meta.GetFilesForConfig( relativeFilePath, settings );
|
||||||
changedConfig |= configChanged;
|
changedConfig |= configChanged;
|
||||||
AddFiles( gamePaths, file, registeredFiles, mod );
|
if( file.Extension == ".meta" && gamePaths.Count > 0 )
|
||||||
|
{
|
||||||
|
AddManipulations( file, mod );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddFiles( gamePaths, file, registeredFiles, mod );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changedConfig;
|
return changedConfig;
|
||||||
|
|
@ -117,6 +133,20 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddManipulations( FileInfo file, ResourceMod mod )
|
||||||
|
{
|
||||||
|
if( !mod.MetaManipulations.TryGetValue( file, out var meta ) )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"{file.FullName} is a TexTools Meta File without meta information." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( var manipulation in meta.Manipulations )
|
||||||
|
{
|
||||||
|
MetaManipulations!.ApplyMod( manipulation );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ChangeModPriority( ModInfo info, bool up = false )
|
public void ChangeModPriority( ModInfo info, bool up = false )
|
||||||
{
|
{
|
||||||
Mods!.ReorderMod( info, up );
|
Mods!.ReorderMod( info, up );
|
||||||
|
|
@ -165,6 +195,7 @@ namespace Penumbra.Mods
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
MetaManipulations?.Dispose();
|
||||||
// _fileSystemWatcher?.Dispose();
|
// _fileSystemWatcher?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Penumbra.Importer;
|
||||||
using Penumbra.Models;
|
using Penumbra.Models;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
|
|
@ -19,16 +22,32 @@ namespace Penumbra.Mods
|
||||||
public DirectoryInfo ModBasePath { get; set; }
|
public DirectoryInfo ModBasePath { get; set; }
|
||||||
|
|
||||||
public List< FileInfo > ModFiles { get; } = new();
|
public List< FileInfo > ModFiles { get; } = new();
|
||||||
|
public Dictionary< FileInfo, TexToolsMeta > MetaManipulations { get; } = new();
|
||||||
|
|
||||||
public Dictionary< string, List< GamePath > > FileConflicts { get; } = new();
|
public Dictionary< string, List< GamePath > > FileConflicts { get; } = new();
|
||||||
|
|
||||||
|
|
||||||
public void RefreshModFiles()
|
public void RefreshModFiles()
|
||||||
{
|
{
|
||||||
|
FileConflicts.Clear();
|
||||||
ModFiles.Clear();
|
ModFiles.Clear();
|
||||||
|
MetaManipulations.Clear();
|
||||||
// we don't care about any _files_ in the root dir, but any folders should be a game folder/file combo
|
// we don't care about any _files_ in the root dir, but any folders should be a game folder/file combo
|
||||||
foreach( var file in ModBasePath.EnumerateDirectories()
|
foreach( var file in ModBasePath.EnumerateDirectories()
|
||||||
.SelectMany( dir => dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) ) )
|
.SelectMany( dir => dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) ) )
|
||||||
{
|
{
|
||||||
|
if( file.Extension == ".meta" )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MetaManipulations[ file ] = new TexToolsMeta( File.ReadAllBytes( file.FullName ) );
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
PluginLog.Error( $"Could not parse meta file {file.FullName}:\n{e}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ModFiles.Add( file );
|
ModFiles.Add( file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
Penumbra/Util/TempFile.cs
Normal file
31
Penumbra/Util/TempFile.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Penumbra.Util
|
||||||
|
{
|
||||||
|
public static class TempFile
|
||||||
|
{
|
||||||
|
public static FileInfo TempFileName( DirectoryInfo baseDir )
|
||||||
|
{
|
||||||
|
const uint maxTries = 15;
|
||||||
|
for( var i = 0; i < maxTries; ++i )
|
||||||
|
{
|
||||||
|
var name = Path.GetRandomFileName();
|
||||||
|
var path = new FileInfo( Path.Combine( baseDir.FullName, name ) );
|
||||||
|
if( !path.Exists )
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileInfo WriteNew( DirectoryInfo baseDir, byte[] data )
|
||||||
|
{
|
||||||
|
var fileName = TempFileName( baseDir );
|
||||||
|
File.WriteAllBytes( fileName.FullName, data );
|
||||||
|
fileName.Refresh();
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue