diff --git a/Penumbra.GameData/Structs/CharacterArmor.cs b/Penumbra.GameData/Structs/CharacterArmor.cs index c61ac7ab..6b3f60a8 100644 --- a/Penumbra.GameData/Structs/CharacterArmor.cs +++ b/Penumbra.GameData/Structs/CharacterArmor.cs @@ -1,15 +1,14 @@ using System.Runtime.InteropServices; -namespace Penumbra.GameData.Structs -{ - [StructLayout( LayoutKind.Sequential, Pack = 1 )] - public readonly struct CharacterArmor - { - public readonly SetId Set; - public readonly byte Variant; - public readonly StainId Stain; +namespace Penumbra.GameData.Structs; - public override string ToString() - => $"{Set},{Variant},{Stain}"; - } +[StructLayout( LayoutKind.Sequential, Pack = 1 )] +public readonly struct CharacterArmor +{ + public readonly SetId Set; + public readonly byte Variant; + public readonly StainId Stain; + + public override string ToString() + => $"{Set},{Variant},{Stain}"; } \ No newline at end of file diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 1fa7b576..cc9c7e56 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -97,6 +97,10 @@ public interface IPenumbraApi : IPenumbraApiBase // Obtain a list of all installed mods. The first string is their directory name, the second string is their mod name. public IList< (string, string) > GetModList(); + // Obtain a base64 encoded, zipped json-string with a prepended version-byte of the current manipulations + // for the given collection associated with the character name, or the default collection. + public string GetMetaManipulations( string characterName ); + // ############## Mod Settings ################# diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index 30059913..9a9794b5 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -14,7 +14,6 @@ using OtterGui.Raii; using Penumbra.GameData.ByteString; using Penumbra.GameData.Enums; using Penumbra.Mods; -using Penumbra.Util; namespace Penumbra.Api; @@ -449,6 +448,14 @@ public class IpcTester : IDisposable ImGui.OpenPopup( "Ipc Data" ); } + DrawIntro(PenumbraIpc.LabelProviderGetMetaManipulations, "Meta Manipulations" ); + if( ImGui.Button( "Copy to Clipboard" ) ) + { + var base64 = _pi.GetIpcSubscriber< string, string >( PenumbraIpc.LabelProviderGetMetaManipulations ) + .InvokeFunc( _characterCollectionName ); + ImGui.SetClipboardText( base64 ); + } + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); using var p = ImRaii.Popup( "Ipc Data" ); if( p ) diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 2efeaaa9..e56f0b7a 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -443,6 +443,16 @@ public class PenumbraApi : IDisposable, IPenumbraApi }; } + public string GetMetaManipulations( string characterName ) + { + CheckInitialized(); + var collection = Penumbra.TempMods.Collections.TryGetValue( characterName, out var c ) + ? c + : Penumbra.CollectionManager.Character( characterName ); + var set = collection.MetaCache?.Manipulations ?? Array.Empty< MetaManipulation >(); + return Functions.ToCompressedBase64( set, MetaManipulation.CurrentVersion ); + } + internal bool HasTooltip => ChangedItemTooltip != null; diff --git a/Penumbra/Api/PenumbraIpc.cs b/Penumbra/Api/PenumbraIpc.cs index 600dcf9e..a60d63f4 100644 --- a/Penumbra/Api/PenumbraIpc.cs +++ b/Penumbra/Api/PenumbraIpc.cs @@ -326,12 +326,14 @@ public partial class PenumbraIpc public const string LabelProviderCurrentCollectionName = "Penumbra.GetCurrentCollectionName"; public const string LabelProviderDefaultCollectionName = "Penumbra.GetDefaultCollectionName"; public const string LabelProviderCharacterCollectionName = "Penumbra.GetCharacterCollectionName"; + public const string LabelProviderGetMetaManipulations = "Penumbra.GetMetaManipulations"; internal ICallGateProvider< IList< (string, string) > >? ProviderGetMods; internal ICallGateProvider< IList< string > >? ProviderGetCollections; internal ICallGateProvider< string >? ProviderCurrentCollectionName; internal ICallGateProvider< string >? ProviderDefaultCollectionName; internal ICallGateProvider< string, (string, bool) >? ProviderCharacterCollectionName; + internal ICallGateProvider< string, string >? ProviderGetMetaManipulations; private void InitializeDataProviders( DalamudPluginInterface pi ) { @@ -384,6 +386,16 @@ public partial class PenumbraIpc { PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" ); } + + try + { + ProviderGetMetaManipulations = pi.GetIpcProvider< string, string >( LabelProviderGetMetaManipulations ); + ProviderGetMetaManipulations.RegisterFunc( Api.GetMetaManipulations ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" ); + } } private void DisposeDataProviders() @@ -393,6 +405,7 @@ public partial class PenumbraIpc ProviderCurrentCollectionName?.UnregisterFunc(); ProviderDefaultCollectionName?.UnregisterFunc(); ProviderCharacterCollectionName?.UnregisterFunc(); + ProviderGetMetaManipulations?.UnregisterFunc(); } } diff --git a/Penumbra/Collections/ModCollection.cs b/Penumbra/Collections/ModCollection.cs index 1c913a11..0d485116 100644 --- a/Penumbra/Collections/ModCollection.cs +++ b/Penumbra/Collections/ModCollection.cs @@ -75,9 +75,9 @@ public partial class ModCollection => new(name, CurrentVersion, new Dictionary< string, ModSettings.SavedSettings >()); // Create a new temporary collection that does not save and has a negative index. - public static ModCollection CreateNewTemporary(string tag, string characterName) + public static ModCollection CreateNewTemporary( string tag, string characterName ) { - var collection = new ModCollection($"{tag}_{characterName}_temporary", Empty); + var collection = new ModCollection( $"{tag}_{characterName}", Empty ); collection.ModSettingChanged -= collection.SaveOnChange; collection.InheritanceChanged -= collection.SaveOnChange; collection.Index = ~Penumbra.TempMods.Collections.Count; diff --git a/Penumbra/Interop/Resolver/PathResolver.Material.cs b/Penumbra/Interop/Resolver/PathResolver.Material.cs index e3f1ab56..64e82ab7 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Material.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Material.cs @@ -8,6 +8,7 @@ using Penumbra.Collections; using Penumbra.GameData.ByteString; using Penumbra.GameData.Enums; using Penumbra.Interop.Structs; +using Penumbra.Util; namespace Penumbra.Interop.Resolver; @@ -56,7 +57,7 @@ public unsafe partial class PathResolver } // Check specifically for shpk and tex files whether we are currently in a material load. - private bool HandleMaterialSubFiles( ResourceType type, [NotNullWhen(true)] out ModCollection? collection ) + private bool HandleMaterialSubFiles( ResourceType type, [NotNullWhen( true )] out ModCollection? collection ) { if( _mtrlCollection != null && type is ResourceType.Tex or ResourceType.Shpk ) { @@ -81,7 +82,9 @@ public unsafe partial class PathResolver var lastUnderscore = split.LastIndexOf( ( byte )'_' ); var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString(); - if( Penumbra.CollectionManager.ByName( name, out var collection ) ) + if( Penumbra.TempMods.Collections.Values.FindFirst( c => string.Equals( c.Name, name, StringComparison.OrdinalIgnoreCase ), + out var collection ) + || Penumbra.CollectionManager.ByName( name, out collection ) ) { #if DEBUG PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path ); diff --git a/Penumbra/Meta/Manager/MetaManager.cs b/Penumbra/Meta/Manager/MetaManager.cs index a71e37b1..50404234 100644 --- a/Penumbra/Meta/Manager/MetaManager.cs +++ b/Penumbra/Meta/Manager/MetaManager.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using Penumbra.Collections; using Penumbra.Meta.Files; @@ -30,7 +31,7 @@ public partial class MetaManager : IDisposable } } - public bool TryGetValue( MetaManipulation manip, [NotNullWhen(true)] out IMod? mod ) + public bool TryGetValue( MetaManipulation manip, [NotNullWhen( true )] out IMod? mod ) { mod = manip.ManipulationType switch { @@ -53,6 +54,15 @@ public partial class MetaManager : IDisposable + Est.Manipulations.Count + Eqp.Manipulations.Count; + public MetaManipulation[] Manipulations + => Imc.Manipulations.Keys.Select( m => ( MetaManipulation )m ) + .Concat( Eqdp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) + .Concat( Cmp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) + .Concat( Gmp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) + .Concat( Est.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) + .Concat( Eqp.Manipulations.Keys.Select( m => ( MetaManipulation )m ) ) + .ToArray(); + public MetaManager( ModCollection collection ) => Imc = new MetaManagerImc( collection );