diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 49fa40db..dabe4207 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -1036,7 +1036,7 @@ public IReadOnlyDictionary> GetPla params ushort[] gameObjects) { var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType(); - var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUIData : 0); + var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); return Array.ConvertAll(gameObjects, obj => resDictionaries.TryGetValue(obj, out var resDict) ? resDict : null); @@ -1046,7 +1046,7 @@ public IReadOnlyDictionary> GetPla bool withUIData) { var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly - | (withUIData ? ResourceTreeFactory.Flags.WithUIData : 0)); + | (withUIData ? ResourceTreeFactory.Flags.WithUiData : 0)); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); return resDictionaries.AsReadOnly(); diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs index d788d0bd..2d094970 100644 --- a/Penumbra/Collections/ModCollection.Cache.Access.cs +++ b/Penumbra/Collections/ModCollection.Cache.Access.cs @@ -37,7 +37,7 @@ public void Remove(TemporaryMod tempMod) public IEnumerable ReverseResolvePath(FullPath path) => _cache?.ReverseResolvePath(path) ?? Array.Empty(); - public HashSet[] ReverseResolvePaths(string[] paths) + public HashSet[] ReverseResolvePaths(IReadOnlyCollection paths) => _cache?.ReverseResolvePaths(paths) ?? paths.Select(_ => new HashSet()).ToArray(); public FullPath? ResolvePath(Utf8GamePath path) diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index b6ce42d4..55893cab 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -278,7 +278,7 @@ static ushort GetTextureIndex(Material* mtrl, ushort texFlags, HashSet alr return node; } - internal List FilterGamePaths(List gamePaths) + internal List FilterGamePaths(IReadOnlyCollection gamePaths) { var filtered = new List(gamePaths.Count); foreach (var path in gamePaths) diff --git a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs index 33a8de0f..bd0138c4 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs @@ -84,11 +84,12 @@ private TreeBuildCache CreateTreeBuildCache(bool withCharacters) return null; var localPlayerRelated = cache.IsLocalPlayerRelated(character); - var (name, related) = GetCharacterName(character, cache); - var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId; - var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, networked, collectionResolveData.ModCollection.Name); + var (name, related) = GetCharacterName(character, cache); + var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId; + var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, + networked, collectionResolveData.ModCollection.Name); var globalContext = new GlobalResolveContext(_identifier.AwaitedService, cache, - ((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUIData) != 0); + ((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUiData) != 0); tree.LoadResources(globalContext); tree.FlatNodes.UnionWith(globalContext.Nodes.Values); tree.ProcessPostfix((node, _) => tree.FlatNodes.Add(node)); @@ -104,42 +105,47 @@ private TreeBuildCache CreateTreeBuildCache(bool withCharacters) private static void ResolveGamePaths(ResourceTree tree, ModCollection collection) { - var forwardSet = new HashSet(); - var reverseSet = new HashSet(); + var forwardDictionary = new Dictionary(); + var reverseDictionary = new Dictionary>(); foreach (var node in tree.FlatNodes) { if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty) - reverseSet.Add(node.FullPath.ToPath()); + reverseDictionary.TryAdd(node.FullPath.ToPath(), null!); else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1) - forwardSet.Add(node.GamePath); + forwardDictionary.TryAdd(node.GamePath, null); } - var forwardDictionary = forwardSet.ToDictionary(path => path, collection.ResolvePath); - var reverseArray = reverseSet.ToArray(); - var reverseResolvedArray = collection.ReverseResolvePaths(reverseArray); - var reverseDictionary = reverseArray.Zip(reverseResolvedArray).ToDictionary(pair => pair.First, pair => pair.Second); + foreach (var key in forwardDictionary.Keys) + forwardDictionary[key] = collection.ResolvePath(key); + + var reverseResolvedArray = collection.ReverseResolvePaths(reverseDictionary.Keys); + foreach (var (key, set) in reverseDictionary.Keys.Zip(reverseResolvedArray)) + reverseDictionary[key] = set; foreach (var node in tree.FlatNodes) { if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty) { - if (reverseDictionary.TryGetValue(node.FullPath.ToPath(), out var resolvedSet)) + if (!reverseDictionary.TryGetValue(node.FullPath.ToPath(), out var resolvedSet)) + continue; + + IReadOnlyCollection resolvedList = resolvedSet; + if (resolvedList.Count > 1) { - var resolvedList = resolvedSet.ToList(); - if (resolvedList.Count > 1) - { - var filteredList = node.ResolveContext!.FilterGamePaths(resolvedList); - if (filteredList.Count > 0) - resolvedList = filteredList; - } - if (resolvedList.Count != 1) - { - Penumbra.Log.Information($"Found {resolvedList.Count} game paths while reverse-resolving {node.FullPath} in {collection.Name}:"); - foreach (var gamePath in resolvedList) - Penumbra.Log.Information($"Game path: {gamePath}"); - } - node.PossibleGamePaths = resolvedList.ToArray(); + var filteredList = node.ResolveContext!.FilterGamePaths(resolvedList); + if (filteredList.Count > 0) + resolvedList = filteredList; } + + if (resolvedList.Count != 1) + { + Penumbra.Log.Debug( + $"Found {resolvedList.Count} game paths while reverse-resolving {node.FullPath} in {collection.Name}:"); + foreach (var gamePath in resolvedList) + Penumbra.Log.Debug($"Game path: {gamePath}"); + } + + node.PossibleGamePaths = resolvedList.ToArray(); } else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1) { @@ -237,7 +243,7 @@ private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.C public enum Flags { RedactExternalPaths = 1, - WithUIData = 2, + WithUiData = 2, LocalPlayerRelatedOnly = 4, WithOwnership = 8, } diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index 3901b431..39728cd4 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -11,7 +11,7 @@ public class ResourceTreeViewer { private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags = ResourceTreeFactory.Flags.RedactExternalPaths | - ResourceTreeFactory.Flags.WithUIData | + ResourceTreeFactory.Flags.WithUiData | ResourceTreeFactory.Flags.WithOwnership; private readonly Configuration _config;