diff --git a/Penumbra.GameData/Structs/CharacterEquip.cs b/Penumbra.GameData/Structs/CharacterEquip.cs index 5e8489a1..c354cd8a 100644 --- a/Penumbra.GameData/Structs/CharacterEquip.cs +++ b/Penumbra.GameData/Structs/CharacterEquip.cs @@ -1,13 +1,9 @@ using System; using Penumbra.GameData.Enums; +using Penumbra.GameData.Util; namespace Penumbra.GameData.Structs; -public unsafe struct CharacterArmorData -{ - public fixed byte Data[40]; -} - public readonly unsafe struct CharacterEquip { public static readonly CharacterEquip Null = new(null); @@ -26,7 +22,6 @@ public readonly unsafe struct CharacterEquip public ref CharacterArmor this[ EquipSlot slot ] => ref _armor[ IndexOf( slot ) ]; - public ref CharacterArmor Head => ref _armor[ 0 ]; @@ -108,4 +103,12 @@ public readonly unsafe struct CharacterEquip _ => throw new ArgumentOutOfRangeException( nameof( slot ), slot, null ), }; } + + public void Write( IntPtr target ) + { + Functions.MemCpyUnchecked( ( void* )target, _armor, sizeof( CharacterArmor ) * 10 ); + } + + public bool Equals( CharacterEquip other ) + => Functions.MemCmpUnchecked( ( void* )_armor, ( void* )other._armor, sizeof( CharacterArmor ) * 10 ) == 0; } \ No newline at end of file diff --git a/Penumbra.GameData/Structs/CustomizeData.cs b/Penumbra.GameData/Structs/CustomizeData.cs index 0c7fcbf1..4bce24cf 100644 --- a/Penumbra.GameData/Structs/CustomizeData.cs +++ b/Penumbra.GameData/Structs/CustomizeData.cs @@ -17,7 +17,7 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData > } } - public void Write( void* target ) + public readonly void Write( void* target ) { fixed( byte* ptr = Data ) { @@ -25,14 +25,14 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData > } } - public CustomizeData Clone() + public readonly CustomizeData Clone() { var ret = new CustomizeData(); Write( ret.Data ); return ret; } - public bool Equals( CustomizeData other ) + public readonly bool Equals( CustomizeData other ) { fixed( byte* ptr = Data ) { @@ -40,6 +40,9 @@ public unsafe struct CustomizeData : IEquatable< CustomizeData > } } + public static bool Equals( CustomizeData* lhs, CustomizeData* rhs ) + => Functions.MemCmpUnchecked( lhs, rhs, Size ) == 0; + public override bool Equals( object? obj ) => obj is CustomizeData other && Equals( other ); diff --git a/Penumbra/Collections/CollectionManager.Active.cs b/Penumbra/Collections/CollectionManager.Active.cs index 8ab2c22b..81b3d7db 100644 --- a/Penumbra/Collections/CollectionManager.Active.cs +++ b/Penumbra/Collections/CollectionManager.Active.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using Penumbra.Mods; +using Penumbra.UI; namespace Penumbra.Collections; @@ -196,7 +197,7 @@ public partial class ModCollection var defaultIdx = GetIndexForCollectionName( defaultName ); if( defaultIdx < 0 ) { - PluginLog.Error( $"Last choice of Default Collection {defaultName} is not available, reset to {Empty.Name}." ); + PluginLog.Error( $"Last choice of {ConfigWindow.DefaultCollection} {defaultName} is not available, reset to {Empty.Name}." ); Default = Empty; configChanged = true; } @@ -210,7 +211,7 @@ public partial class ModCollection var currentIdx = GetIndexForCollectionName( currentName ); if( currentIdx < 0 ) { - PluginLog.Error( $"Last choice of Current Collection {currentName} is not available, reset to {DefaultCollection}." ); + PluginLog.Error( $"Last choice of {ConfigWindow.SelectedCollection} {currentName} is not available, reset to {DefaultCollection}." ); Current = DefaultName; configChanged = true; } diff --git a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs index 4fb934f4..bdcf1220 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using Dalamud.Hooking; using Dalamud.Logging; using Dalamud.Utility.Signatures; @@ -161,7 +162,9 @@ public unsafe partial class ResourceLoader return ret == null ? null : ret->Value; } - public delegate void ExtMapAction( ResourceCategory category, StdMap< uint, Pointer< StdMap< uint, Pointer< ResourceHandle > > > >* graph, int idx ); + public delegate void ExtMapAction( ResourceCategory category, StdMap< uint, Pointer< StdMap< uint, Pointer< ResourceHandle > > > >* graph, + int idx ); + public delegate void ResourceMapAction( uint ext, StdMap< uint, Pointer< ResourceHandle > >* graph ); public delegate void ResourceAction( uint crc32, ResourceHandle* graph ); @@ -223,9 +226,14 @@ public unsafe partial class ResourceLoader // Prevent resource management weirdness. private byte ResourceHandleDecRefDetour( ResourceHandle* handle ) { + if( handle == null ) + { + return 0; + } + if( handle->RefCount != 0 ) { - return _decRefHook!.Original( handle ); + return _decRefHook.Original( handle ); } PluginLog.Error( $"Caught decrease of Reference Counter for {handle->FileName} at 0x{( ulong )handle:X} below 0." ); diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 8ecb69ca..a5d4e5c8 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -79,9 +79,9 @@ public class Penumbra : IDalamudPlugin Backup.CreateBackup( pluginInterface.ConfigDirectory, PenumbraBackupFiles() ); Config = Configuration.Load(); - TempMods = new TempModManager(); - MetaFileManager = new MetaFileManager(); - ResourceLoader = new ResourceLoader( this ); + TempMods = new TempModManager(); + MetaFileManager = new MetaFileManager(); + ResourceLoader = new ResourceLoader( this ); ResourceLoader.EnableHooks(); ResourceLogger = new ResourceLogger( ResourceLoader ); ResidentResources = new ResidentResourceManager(); @@ -450,10 +450,10 @@ public class Penumbra : IDalamudPlugin c.AllConflicts.SelectMany( x => x ).Sum( x => x.HasPriority || !x.Solved ? 0 : x.Conflicts.Count ) ); sb.AppendLine( "**Collections**" ); - sb.AppendFormat( "> **`#Collections: `** {0}\n", CollectionManager.Count - 1 ); - sb.AppendFormat( "> **`Active Collections: `** {0}\n", CollectionManager.Count( c => c.HasCache ) ); - sb.AppendFormat( "> **`Default Collection: `** {0}\n", CollectionManager.Default.AnonymizedName); - sb.AppendFormat( "> **`Current Collection: `** {0}\n", CollectionManager.Current.AnonymizedName); + sb.AppendFormat( "> **`#Collections: `** {0}\n", CollectionManager.Count - 1 ); + sb.AppendFormat( "> **`Active Collections: `** {0}\n", CollectionManager.Count( c => c.HasCache ) ); + sb.AppendFormat( "> **`Base Collection: `** {0}\n", CollectionManager.Default.AnonymizedName ); + sb.AppendFormat( "> **`Selected Collection: `** {0}\n", CollectionManager.Current.AnonymizedName ); foreach( var type in CollectionTypeExtensions.Special ) { var collection = CollectionManager.ByType( type ); diff --git a/Penumbra/UI/ConfigWindow.DebugTab.cs b/Penumbra/UI/ConfigWindow.DebugTab.cs index 5ac267c3..081b0e79 100644 --- a/Penumbra/UI/ConfigWindow.DebugTab.cs +++ b/Penumbra/UI/ConfigWindow.DebugTab.cs @@ -87,9 +87,9 @@ public partial class ConfigWindow var manager = Penumbra.ModManager; PrintValue( "Penumbra Version", $"{Penumbra.Version} {DebugVersionString}" ); PrintValue( "Git Commit Hash", Penumbra.CommitHash ); - PrintValue( "Current Collection", Penumbra.CollectionManager.Current.Name ); + PrintValue( SelectedCollection, Penumbra.CollectionManager.Current.Name ); PrintValue( " has Cache", Penumbra.CollectionManager.Current.HasCache.ToString() ); - PrintValue( "Default Collection", Penumbra.CollectionManager.Default.Name ); + PrintValue( DefaultCollection, Penumbra.CollectionManager.Default.Name ); PrintValue( " has Cache", Penumbra.CollectionManager.Default.HasCache.ToString() ); PrintValue( "Mod Manager BasePath", manager.BasePath.Name ); PrintValue( "Mod Manager BasePath-Full", manager.BasePath.FullName ); diff --git a/Penumbra/UI/ConfigWindow.ModsTab.cs b/Penumbra/UI/ConfigWindow.ModsTab.cs index 5c9cb0e6..fbafb806 100644 --- a/Penumbra/UI/ConfigWindow.ModsTab.cs +++ b/Penumbra/UI/ConfigWindow.ModsTab.cs @@ -79,12 +79,12 @@ public partial class ConfigWindow private static void DrawDefaultCollectionButton( Vector2 width ) { - var name = $"Default Collection ({Penumbra.CollectionManager.Default.Name})"; + var name = $"{DefaultCollection} ({Penumbra.CollectionManager.Default.Name})"; var isCurrent = Penumbra.CollectionManager.Default == Penumbra.CollectionManager.Current; var isEmpty = Penumbra.CollectionManager.Default == ModCollection.Empty; - var tt = isCurrent ? "The current collection is already the configured default collection." - : isEmpty ? "The default collection is configured to be empty." - : "Set the current collection to the configured default collection."; + var tt = isCurrent ? $"The current collection is already the configured {DefaultCollection}." + : isEmpty ? $"The {DefaultCollection} is configured to be empty." + : $"Set the {SelectedCollection} to the configured {DefaultCollection}."; if( ImGuiUtil.DrawDisabledButton( name, width, tt, isCurrent || isEmpty ) ) { Penumbra.CollectionManager.SetCollection( Penumbra.CollectionManager.Default, CollectionType.Current ); diff --git a/Penumbra/UI/ConfigWindow.SettingsTab.General.cs b/Penumbra/UI/ConfigWindow.SettingsTab.General.cs index badf9f22..fe9983a6 100644 --- a/Penumbra/UI/ConfigWindow.SettingsTab.General.cs +++ b/Penumbra/UI/ConfigWindow.SettingsTab.General.cs @@ -59,27 +59,27 @@ public partial class ConfigWindow Dalamud.PluginInterface.UiBuilder.DisableGposeUiHide = !v; } ); ImGui.Dummy( _window._defaultSpace ); - Checkbox( "Use Special Collections in Character Window", - "Use the character collection for your character's name in your main character window, if it is set.", + Checkbox( $"Use {AssignedCollections} in Character Window", + "Use the character collection for your characters name or the Your Character collection in your main character window, if it is set.", Penumbra.Config.UseCharacterCollectionInMainWindow, v => Penumbra.Config.UseCharacterCollectionInMainWindow = v ); - Checkbox( "Use Special Collections in Adventurer Cards", + Checkbox( $"Use {AssignedCollections} in Adventurer Cards", "Use the appropriate character collection for the adventurer card you are currently looking at, based on the adventurer's name.", Penumbra.Config.UseCharacterCollectionsInCards, v => Penumbra.Config.UseCharacterCollectionsInCards = v ); - Checkbox( "Use Special Collections in Try-On Window", + Checkbox( $"Use {AssignedCollections} in Try-On Window", "Use the character collection for your character's name in your try-on, dye preview or glamour plate window, if it is set.", Penumbra.Config.UseCharacterCollectionInTryOn, v => Penumbra.Config.UseCharacterCollectionInTryOn = v ); - Checkbox( "Use Special Collections in Inspect Windows", + Checkbox( $"Use {AssignedCollections} in Inspect Windows", "Use the appropriate character collection for the character you are currently inspecting, based on their name.", Penumbra.Config.UseCharacterCollectionInInspect, v => Penumbra.Config.UseCharacterCollectionInInspect = v ); - Checkbox( "Use Special Collections based on Ownership", + Checkbox( $"Use {AssignedCollections} based on Ownership", "Use the owner's name to determine the appropriate character collection for mounts, companions and combat pets.", Penumbra.Config.UseOwnerNameForCharacterCollection, v => Penumbra.Config.UseOwnerNameForCharacterCollection = v ); Checkbox( "Prefer Named Collections over Ownership", - "If you have a character collection set to a specific name for a companion or combat pet, prefer this collection over the owner's collection.\n" + "If you have a character collection set to a specific name for a companion or combat pet, prefer this collection over the owners collection.\n" + "That is, if you have a 'Topaz Carbuncle' collection, it will use this one instead of the one for its owner.", Penumbra.Config.PreferNamedCollectionsOverOwners, v => Penumbra.Config.PreferNamedCollectionsOverOwners = v ); - Checkbox( "Use Default Collection for Housing Retainers", - "Housing Retainers use the name of their owner instead of their own, you can decide to let them use their owners character collection or the default collection.\n" + Checkbox( $"Use {DefaultCollection} for Housing Retainers", + $"Housing Retainers use the name of their owner instead of their own, you can decide to let them use their owners character collection or the {DefaultCollection}.\n" + "It is not possible to make them have their own collection, since they have no connection to their actual name.", Penumbra.Config.UseDefaultCollectionForRetainers, v => Penumbra.Config.UseDefaultCollectionForRetainers = v ); ImGui.Dummy( _window._defaultSpace ); diff --git a/Penumbra/UI/ConfigWindow.Tutorial.cs b/Penumbra/UI/ConfigWindow.Tutorial.cs index 6962a84e..ef75566d 100644 --- a/Penumbra/UI/ConfigWindow.Tutorial.cs +++ b/Penumbra/UI/ConfigWindow.Tutorial.cs @@ -11,6 +11,7 @@ public partial class ConfigWindow public const string SelectedCollection = "Selected Collection"; public const string DefaultCollection = "Base Collection"; public const string ActiveCollections = "Active Collections"; + public const string AssignedCollections = "Assigned Collections"; public const string GroupAssignment = "Group Assignment"; public const string CharacterGroups = "Character Groups"; public const string ConditionalGroup = "Group";