This commit is contained in:
Ottermandias 2023-03-26 12:37:22 +02:00
parent e33f49e097
commit ef9022a746
12 changed files with 205 additions and 204 deletions

View file

@ -1253,7 +1253,7 @@ public class IpcTester : IDisposable
.FirstOrDefault()
?? "Unknown";
if (ImGui.Button($"Save##{collection.Name}"))
Mod.TemporaryMod.SaveTempCollection(_modManager, collection, character);
TemporaryMod.SaveTempCollection(_modManager, collection, character);
ImGuiUtil.DrawTableColumn(collection.Name);
ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString());
@ -1271,7 +1271,7 @@ public class IpcTester : IDisposable
using var table = ImRaii.Table("##modTree", 5);
void PrintList(string collectionName, IReadOnlyList<Mod.TemporaryMod> list)
void PrintList(string collectionName, IReadOnlyList<TemporaryMod> list)
{
foreach (var mod in list)
{

View file

@ -31,7 +31,7 @@ public class TempCollectionManager : IDisposable
_communicator.TemporaryGlobalModChange.Event -= OnGlobalModChange;
}
private void OnGlobalModChange(Mod.TemporaryMod mod, bool created, bool removed)
private void OnGlobalModChange(TemporaryMod mod, bool created, bool removed)
=> TempModManager.OnGlobalModChange(_customCollections.Values, mod, created, removed);
public int Count

View file

@ -20,8 +20,8 @@ public class TempModManager : IDisposable
{
private readonly CommunicatorService _communicator;
private readonly Dictionary<ModCollection, List<Mod.TemporaryMod>> _mods = new();
private readonly List<Mod.TemporaryMod> _modsForAllCollections = new();
private readonly Dictionary<ModCollection, List<TemporaryMod>> _mods = new();
private readonly List<TemporaryMod> _modsForAllCollections = new();
public TempModManager(CommunicatorService communicator)
{
@ -34,10 +34,10 @@ public class TempModManager : IDisposable
_communicator.CollectionChange.Event -= OnCollectionChange;
}
public IReadOnlyDictionary<ModCollection, List<Mod.TemporaryMod>> Mods
public IReadOnlyDictionary<ModCollection, List<TemporaryMod>> Mods
=> _mods;
public IReadOnlyList<Mod.TemporaryMod> ModsForAllCollections
public IReadOnlyList<TemporaryMod> ModsForAllCollections
=> _modsForAllCollections;
public RedirectResult Register(string tag, ModCollection? collection, Dictionary<Utf8GamePath, FullPath> dict,
@ -74,7 +74,7 @@ public class TempModManager : IDisposable
}
// Apply any new changes to the temporary mod.
private void ApplyModChange(Mod.TemporaryMod mod, ModCollection? collection, bool created, bool removed)
private void ApplyModChange(TemporaryMod mod, ModCollection? collection, bool created, bool removed)
{
if (collection != null)
{
@ -92,7 +92,7 @@ public class TempModManager : IDisposable
/// <summary>
/// Apply a mod change to a set of collections.
/// </summary>
public static void OnGlobalModChange(IEnumerable<ModCollection> collections, Mod.TemporaryMod mod, bool created, bool removed)
public static void OnGlobalModChange(IEnumerable<ModCollection> collections, TemporaryMod mod, bool created, bool removed)
{
if (removed)
foreach (var c in collections)
@ -104,9 +104,9 @@ public class TempModManager : IDisposable
// Find or create a mod with the given tag as name and the given priority, for the given collection (or all collections).
// Returns the found or created mod and whether it was newly created.
private Mod.TemporaryMod GetOrCreateMod(string tag, ModCollection? collection, int priority, out bool created)
private TemporaryMod GetOrCreateMod(string tag, ModCollection? collection, int priority, out bool created)
{
List<Mod.TemporaryMod> list;
List<TemporaryMod> list;
if (collection == null)
{
list = _modsForAllCollections;
@ -117,14 +117,14 @@ public class TempModManager : IDisposable
}
else
{
list = new List<Mod.TemporaryMod>();
list = new List<TemporaryMod>();
_mods.Add(collection, list);
}
var mod = list.Find(m => m.Priority == priority && m.Name == tag);
if (mod == null)
{
mod = new Mod.TemporaryMod()
mod = new TemporaryMod()
{
Name = tag,
Priority = priority,

View file

@ -92,7 +92,7 @@ public partial class ModCollection
_modManager.ModPathChanged -= OnModPathChange;
}
private void OnGlobalModChange(Mod.TemporaryMod mod, bool created, bool removed)
private void OnGlobalModChange(TemporaryMod mod, bool created, bool removed)
=> TempModManager.OnGlobalModChange(_collections, mod, created, removed);
// Returns true if the name is not empty, it is not the name of the empty collection

View file

@ -44,7 +44,7 @@ public partial class ModCollection
=> CalculateEffectiveFileList(this == Penumbra.CollectionManager.Default);
// Handle temporary mods for this collection.
public void Apply(Mod.TemporaryMod tempMod, bool created)
public void Apply(TemporaryMod tempMod, bool created)
{
if (created)
_cache?.AddMod(tempMod, tempMod.TotalManipulations > 0);
@ -52,7 +52,7 @@ public partial class ModCollection
_cache?.ReloadMod(tempMod, tempMod.TotalManipulations > 0);
}
public void Remove(Mod.TemporaryMod tempMod)
public void Remove(TemporaryMod tempMod)
{
_cache?.RemoveMod(tempMod, tempMod.TotalManipulations > 0);
}

View file

@ -192,7 +192,7 @@ public partial class ModCollection
// Add all forced redirects.
foreach( var tempMod in Penumbra.TempMods.ModsForAllCollections.Concat(
Penumbra.TempMods.Mods.TryGetValue( _collection, out var list ) ? list : Array.Empty< Mod.TemporaryMod >() ) )
Penumbra.TempMods.Mods.TryGetValue( _collection, out var list ) ? list : Array.Empty< TemporaryMod >() ) )
{
AddMod( tempMod, false );
}

View file

@ -39,7 +39,7 @@ public class ModDataEditor
mod.Description = description ?? mod.Description;
mod.Version = version ?? mod.Version;
mod.Website = website ?? mod.Website;
_saveService.ImmediateSave(new ModMeta(mod));
_saveService.ImmediateSave(new Mod.ModMeta(mod));
}
public ModDataChangeType LoadLocalData(Mod mod)
@ -96,7 +96,7 @@ public class ModDataEditor
}
if (save)
_saveService.QueueSave(new ModData(mod));
_saveService.QueueSave(new Mod.ModData(mod));
return changes;
}
@ -161,7 +161,7 @@ public class ModDataEditor
if (Mod.Migration.Migrate(mod, json))
{
changes |= ModDataChangeType.Migration;
_saveService.ImmediateSave(new ModMeta(mod));
_saveService.ImmediateSave(new Mod.ModMeta(mod));
}
}
@ -189,7 +189,7 @@ public class ModDataEditor
var oldName = mod.Name;
mod.Name = newName;
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Name, mod, oldName.Text);
}
@ -199,7 +199,7 @@ public class ModDataEditor
return;
mod.Author = newAuthor;
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Author, mod, null);
}
@ -209,7 +209,7 @@ public class ModDataEditor
return;
mod.Description = newDescription;
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Description, mod, null);
}
@ -219,7 +219,7 @@ public class ModDataEditor
return;
mod.Version = newVersion;
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Version, mod, null);
}
@ -229,7 +229,7 @@ public class ModDataEditor
return;
mod.Website = newWebsite;
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Website, mod, null);
}
@ -245,7 +245,7 @@ public class ModDataEditor
return;
mod.Favorite = state;
_saveService.QueueSave(new ModData(mod));
_saveService.QueueSave(new Mod.ModData(mod));
;
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null);
}
@ -256,7 +256,7 @@ public class ModDataEditor
return;
mod.Note = newNote;
_saveService.QueueSave(new ModData(mod));
_saveService.QueueSave(new Mod.ModData(mod));
;
_communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null);
}
@ -281,10 +281,10 @@ public class ModDataEditor
}
if (flags.HasFlag(ModDataChangeType.ModTags))
_saveService.QueueSave(new ModMeta(mod));
_saveService.QueueSave(new Mod.ModMeta(mod));
if (flags.HasFlag(ModDataChangeType.LocalTags))
_saveService.QueueSave(new ModData(mod));
_saveService.QueueSave(new Mod.ModData(mod));
if (flags != 0)
_communicatorService.ModDataChanged.Invoke(flags, mod, null);
@ -306,57 +306,4 @@ public class ModDataEditor
Penumbra.Log.Error($"Could not move local data file {oldFile} to {newFile}:\n{e}");
}
}
private readonly struct ModMeta : ISavable
{
private readonly Mod _mod;
public ModMeta(Mod mod)
=> _mod = mod;
public string ToFilename(FilenameService fileNames)
=> fileNames.ModMetaPath(_mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(Mod.FileVersion), JToken.FromObject(_mod.FileVersion) },
{ nameof(Mod.Name), JToken.FromObject(_mod.Name) },
{ nameof(Mod.Author), JToken.FromObject(_mod.Author) },
{ nameof(Mod.Description), JToken.FromObject(_mod.Description) },
{ nameof(Mod.Version), JToken.FromObject(_mod.Version) },
{ nameof(Mod.Website), JToken.FromObject(_mod.Website) },
{ nameof(Mod.ModTags), JToken.FromObject(_mod.ModTags) },
};
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
jObject.WriteTo(jWriter);
}
}
private readonly struct ModData : ISavable
{
private readonly Mod _mod;
public ModData(Mod mod)
=> _mod = mod;
public string ToFilename(FilenameService fileNames)
=> fileNames.LocalDataFile(_mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(Mod.FileVersion), JToken.FromObject(_mod.FileVersion) },
{ nameof(Mod.ImportDate), JToken.FromObject(_mod.ImportDate) },
{ nameof(Mod.LocalTags), JToken.FromObject(_mod.LocalTags) },
{ nameof(Mod.Note), JToken.FromObject(_mod.Note) },
{ nameof(Mod.Favorite), JToken.FromObject(_mod.Favorite) },
};
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
jObject.WriteTo(jWriter);
}
}
}

View file

@ -3,7 +3,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Mods;
@ -49,4 +51,29 @@ public sealed partial class Mod
return type;
}
internal readonly struct ModData : ISavable
{
private readonly Mod _mod;
public ModData(Mod mod)
=> _mod = mod;
public string ToFilename(FilenameService fileNames)
=> fileNames.LocalDataFile(_mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(FileVersion), JToken.FromObject(_mod.FileVersion) },
{ nameof(ImportDate), JToken.FromObject(_mod.ImportDate) },
{ nameof(LocalTags), JToken.FromObject(_mod.LocalTags) },
{ nameof(Note), JToken.FromObject(_mod.Note) },
{ nameof(Favorite), JToken.FromObject(_mod.Favorite) },
};
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
jObject.WriteTo(jWriter);
}
}
}

View file

@ -1,9 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Mods;
public sealed partial class Mod : IMod
{
public static readonly TemporaryMod ForcedFiles = new()
@ -13,15 +18,42 @@ public sealed partial class Mod : IMod
Priority = int.MaxValue,
};
public const uint CurrentFileVersion = 3;
public uint FileVersion { get; internal set; } = CurrentFileVersion;
public LowerString Name { get; internal set; } = "New Mod";
public LowerString Author { get; internal set; } = LowerString.Empty;
public string Description { get; internal set; } = string.Empty;
public string Version { get; internal set; } = string.Empty;
public string Website { get; internal set; } = string.Empty;
public IReadOnlyList< string > ModTags { get; internal set; } = Array.Empty< string >();
public const uint CurrentFileVersion = 3;
public uint FileVersion { get; internal set; } = CurrentFileVersion;
public LowerString Name { get; internal set; } = "New Mod";
public LowerString Author { get; internal set; } = LowerString.Empty;
public string Description { get; internal set; } = string.Empty;
public string Version { get; internal set; } = string.Empty;
public string Website { get; internal set; } = string.Empty;
public IReadOnlyList<string> ModTags { get; internal set; } = Array.Empty<string>();
public override string ToString()
=> Name.Text;
}
internal readonly struct ModMeta : ISavable
{
private readonly Mod _mod;
public ModMeta(Mod mod)
=> _mod = mod;
public string ToFilename(FilenameService fileNames)
=> fileNames.ModMetaPath(_mod);
public void Save(StreamWriter writer)
{
var jObject = new JObject
{
{ nameof(FileVersion), JToken.FromObject(_mod.FileVersion) },
{ nameof(Name), JToken.FromObject(_mod.Name) },
{ nameof(Author), JToken.FromObject(_mod.Author) },
{ nameof(Description), JToken.FromObject(_mod.Description) },
{ nameof(Version), JToken.FromObject(_mod.Version) },
{ nameof(Website), JToken.FromObject(_mod.Website) },
{ nameof(ModTags), JToken.FromObject(_mod.ModTags) },
};
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
jObject.WriteTo(jWriter);
}
}
}

View file

@ -1,111 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods;
public sealed partial class Mod
{
public class TemporaryMod : IMod
{
public LowerString Name { get; init; } = LowerString.Empty;
public int Index { get; init; } = -2;
public int Priority { get; init; } = int.MaxValue;
public int TotalManipulations
=> Default.Manipulations.Count;
public ISubMod Default
=> _default;
public IReadOnlyList< IModGroup > Groups
=> Array.Empty< IModGroup >();
public IEnumerable< ISubMod > AllSubMods
=> new[] { Default };
private readonly SubMod _default;
public TemporaryMod()
=> _default = new SubMod( this );
public void SetFile( Utf8GamePath gamePath, FullPath fullPath )
=> _default.FileData[ gamePath ] = fullPath;
public bool SetManipulation( MetaManipulation manip )
=> _default.ManipulationData.Remove( manip ) | _default.ManipulationData.Add( manip );
public void SetAll( Dictionary< Utf8GamePath, FullPath > dict, HashSet< MetaManipulation > manips )
{
_default.FileData = dict;
_default.ManipulationData = manips;
}
public static void SaveTempCollection( Mod.Manager modManager, ModCollection collection, string? character = null )
{
DirectoryInfo? dir = null;
try
{
dir = Creator.CreateModFolder( Penumbra.ModManager.BasePath, collection.Name );
var fileDir = Directory.CreateDirectory( Path.Combine( dir.FullName, "files" ) );
modManager.DataEditor.CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Name} for {character ?? "Unknown Character"}.", null, null );
var mod = new Mod( dir );
var defaultMod = mod._default;
foreach( var (gamePath, fullPath) in collection.ResolvedFiles )
{
if( gamePath.Path.EndsWith( ".imc"u8 ) )
{
continue;
}
var targetPath = fullPath.Path.FullName;
if( fullPath.Path.Name.StartsWith( '|' ) )
{
targetPath = targetPath.Split( '|', 3, StringSplitOptions.RemoveEmptyEntries ).Last();
}
if( Path.IsPathRooted(targetPath) )
{
var target = Path.Combine( fileDir.FullName, Path.GetFileName(targetPath) );
File.Copy( targetPath, target, true );
defaultMod.FileData[ gamePath ] = new FullPath( target );
}
else
{
defaultMod.FileSwapData[ gamePath ] = new FullPath(targetPath);
}
}
foreach( var manip in collection.MetaCache?.Manipulations ?? Array.Empty< MetaManipulation >() )
{
defaultMod.ManipulationData.Add( manip );
}
mod.SaveDefaultMod();
modManager.AddMod( dir );
Penumbra.Log.Information( $"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}." );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Could not save temporary collection {collection.Name} to permanent Mod:\n{e}" );
if( dir != null && Directory.Exists( dir.FullName ) )
{
try
{
Directory.Delete( dir.FullName, true );
}
catch
{
// ignored
}
}
}
}
}
}

View file

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods;
public class TemporaryMod : IMod
{
public LowerString Name { get; init; } = LowerString.Empty;
public int Index { get; init; } = -2;
public int Priority { get; init; } = int.MaxValue;
public int TotalManipulations
=> Default.Manipulations.Count;
public ISubMod Default
=> _default;
public IReadOnlyList< IModGroup > Groups
=> Array.Empty< IModGroup >();
public IEnumerable< ISubMod > AllSubMods
=> new[] { Default };
private readonly Mod.SubMod _default;
public TemporaryMod()
=> _default = new Mod.SubMod( this );
public void SetFile( Utf8GamePath gamePath, FullPath fullPath )
=> _default.FileData[ gamePath ] = fullPath;
public bool SetManipulation( MetaManipulation manip )
=> _default.ManipulationData.Remove( manip ) | _default.ManipulationData.Add( manip );
public void SetAll( Dictionary< Utf8GamePath, FullPath > dict, HashSet< MetaManipulation > manips )
{
_default.FileData = dict;
_default.ManipulationData = manips;
}
public static void SaveTempCollection( Mod.Manager modManager, ModCollection collection, string? character = null )
{
DirectoryInfo? dir = null;
try
{
dir = Mod.Creator.CreateModFolder( Penumbra.ModManager.BasePath, collection.Name );
var fileDir = Directory.CreateDirectory( Path.Combine( dir.FullName, "files" ) );
modManager.DataEditor.CreateMeta( dir, collection.Name, character ?? Penumbra.Config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Name} for {character ?? "Unknown Character"}.", null, null );
var mod = new Mod( dir );
var defaultMod = (Mod.SubMod) mod.Default;
foreach( var (gamePath, fullPath) in collection.ResolvedFiles )
{
if( gamePath.Path.EndsWith( ".imc"u8 ) )
{
continue;
}
var targetPath = fullPath.Path.FullName;
if( fullPath.Path.Name.StartsWith( '|' ) )
{
targetPath = targetPath.Split( '|', 3, StringSplitOptions.RemoveEmptyEntries ).Last();
}
if( Path.IsPathRooted(targetPath) )
{
var target = Path.Combine( fileDir.FullName, Path.GetFileName(targetPath) );
File.Copy( targetPath, target, true );
defaultMod.FileData[ gamePath ] = new FullPath( target );
}
else
{
defaultMod.FileSwapData[ gamePath ] = new FullPath(targetPath);
}
}
foreach( var manip in collection.MetaCache?.Manipulations ?? Array.Empty< MetaManipulation >() )
defaultMod.ManipulationData.Add( manip );
mod.SaveDefaultMod();
modManager.AddMod( dir );
Penumbra.Log.Information( $"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Name}." );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Could not save temporary collection {collection.Name} to permanent Mod:\n{e}" );
if( dir != null && Directory.Exists( dir.FullName ) )
{
try
{
Directory.Delete( dir.FullName, true );
}
catch
{
// ignored
}
}
}
}
}

View file

@ -20,7 +20,7 @@ public class CommunicatorService : IDisposable
/// <item>Parameter is whether the mod was newly created.</item>
/// <item>Parameter is whether the mod was deleted.</item>
/// </list> </summary>
public readonly EventWrapper<Mod.TemporaryMod, bool, bool> TemporaryGlobalModChange = new(nameof(TemporaryGlobalModChange));
public readonly EventWrapper<TemporaryMod, bool, bool> TemporaryGlobalModChange = new(nameof(TemporaryGlobalModChange));
/// <summary> <list type="number">
/// <item>Parameter is the type of change. </item>