diff --git a/Penumbra/Import/Textures/TextureDrawer.cs b/Penumbra/Import/Textures/TextureDrawer.cs index bea28749..6d68efbd 100644 --- a/Penumbra/Import/Textures/TextureDrawer.cs +++ b/Penumbra/Import/Textures/TextureDrawer.cs @@ -101,24 +101,25 @@ public static class TextureDrawer } } - public sealed class PathSelectCombo : FilterComboCache<(string, bool)> + public sealed class PathSelectCombo : FilterComboCache<(string Path, bool Game, bool IsOnPlayer)> { private int _skipPrefix = 0; - public PathSelectCombo(TextureManager textures, ModEditor editor) - : base(() => CreateFiles(textures, editor), Penumbra.Log) + public PathSelectCombo(TextureManager textures, ModEditor editor, Func> getPlayerResources) + : base(() => CreateFiles(textures, editor, getPlayerResources), Penumbra.Log) { } - protected override string ToString((string, bool) obj) - => obj.Item1; + protected override string ToString((string Path, bool Game, bool IsOnPlayer) obj) + => obj.Path; protected override bool DrawSelectable(int globalIdx, bool selected) { - var (path, game) = Items[globalIdx]; + var (path, game, isOnPlayer) = Items[globalIdx]; bool ret; using (var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value(), game)) { - var equals = string.Equals(CurrentSelection.Item1, path, StringComparison.OrdinalIgnoreCase); + color.Push(ImGuiCol.Text, ColorId.HandledConflictMod.Value(), isOnPlayer); + var equals = string.Equals(CurrentSelection.Path, path, StringComparison.OrdinalIgnoreCase); var p = game ? $"--> {path}" : path[_skipPrefix..]; ret = ImGui.Selectable(p, selected) && !equals; } @@ -129,11 +130,16 @@ public static class TextureDrawer return ret; } - private static IReadOnlyList<(string, bool)> CreateFiles(TextureManager textures, ModEditor editor) - => editor.Files.Tex.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true)) + private static IReadOnlyList<(string Path, bool Game, bool IsOnPlayer)> CreateFiles(TextureManager textures, ModEditor editor, Func> getPlayerResources) + { + var playerResources = getPlayerResources(); + + return editor.Files.Tex.SelectMany(f => f.SubModUsage.Select(p => (p.Item2.ToString(), true)) .Prepend((f.File.FullName, false))) .Where(p => p.Item2 ? textures.GameFileExists(p.Item1) : File.Exists(p.Item1)) + .Select(p => (p.Item1, p.Item2, playerResources.Contains(p.Item1))) .ToList(); + } public bool Draw(string label, string tooltip, string current, int skipPrefix, out string newPath) { diff --git a/Penumbra/Mods/Editor/FileRegistry.cs b/Penumbra/Mods/Editor/FileRegistry.cs index 791778e3..a223b51e 100644 --- a/Penumbra/Mods/Editor/FileRegistry.cs +++ b/Penumbra/Mods/Editor/FileRegistry.cs @@ -10,6 +10,7 @@ public class FileRegistry : IEquatable public Utf8RelPath RelPath { get; private init; } public long FileSize { get; private init; } public int CurrentUsage; + public bool IsOnPlayer; public static bool FromFile(DirectoryInfo modPath, FileInfo file, [NotNullWhen(true)] out FileRegistry? registry) { @@ -26,6 +27,7 @@ public class FileRegistry : IEquatable RelPath = relPath, FileSize = file.Length, CurrentUsage = 0, + IsOnPlayer = false, }; return true; } diff --git a/Penumbra/UI/AdvancedWindow/FileEditor.cs b/Penumbra/UI/AdvancedWindow/FileEditor.cs index b84fa84c..c3a1c0dc 100644 --- a/Penumbra/UI/AdvancedWindow/FileEditor.cs +++ b/Penumbra/UI/AdvancedWindow/FileEditor.cs @@ -285,7 +285,9 @@ public class FileEditor : IDisposable where T : class, IWritable protected override bool DrawSelectable(int globalIdx, bool selected) { var file = Items[globalIdx]; - var ret = ImGui.Selectable(file.RelPath.ToString(), selected); + bool ret; + using (var c = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(), file.IsOnPlayer)) + ret = ImGui.Selectable(file.RelPath.ToString(), selected); if (ImGui.IsItemHovered()) { diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs index 48d617db..1ab1ed88 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.QuickImport.cs @@ -3,6 +3,7 @@ using ImGuiNET; using Lumina.Data; using OtterGui; using OtterGui.Raii; +using Penumbra.Api.Enums; using Penumbra.GameData.Files; using Penumbra.Interop.ResourceTree; using Penumbra.Mods; @@ -19,6 +20,25 @@ public partial class ModEditWindow private readonly Dictionary _quickImportWritables = new(); private readonly Dictionary<(Utf8GamePath, IWritable?), QuickImportAction> _quickImportActions = new(); + private HashSet GetPlayerResourcesOfType(ResourceType type) + { + var resources = ResourceTreeApiHelper.GetResourcesOfType(_resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly), type) + .Values + .SelectMany(resources => resources.Values) + .Select(resource => resource.Item1); + + return new(resources, StringComparer.OrdinalIgnoreCase); + } + + private IReadOnlyList PopulateIsOnPlayer(IReadOnlyList files, ResourceType type) + { + var playerResources = GetPlayerResourcesOfType(type); + foreach (var file in files) + file.IsOnPlayer = playerResources.Contains(file.File.ToPath()); + + return files; + } + private void DrawQuickImportTab() { using var tab = ImRaii.TabItem("Import from Screen"); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index 7171a0e2..ce2bb2a4 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -7,6 +7,7 @@ using Dalamud.Plugin.Services; using ImGuiNET; using OtterGui; using OtterGui.Raii; +using Penumbra.Api.Enums; using Penumbra.Collections.Manager; using Penumbra.Communication; using Penumbra.GameData.Enums; @@ -569,15 +570,15 @@ public partial class ModEditWindow : Window, IDisposable _fileDialog = fileDialog; _gameEvents = gameEvents; _materialTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Materials", ".mtrl", - () => _editor.Files.Mtrl, DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty, + () => PopulateIsOnPlayer(_editor.Files.Mtrl, ResourceType.Mtrl), DrawMaterialPanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, path, writable) => new MtrlTab(this, new MtrlFile(bytes), path, writable)); _modelTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Models", ".mdl", - () => _editor.Files.Mdl, DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, _, _) => new MdlFile(bytes)); + () => PopulateIsOnPlayer(_editor.Files.Mdl, ResourceType.Mdl), DrawModelPanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, _, _) => new MdlFile(bytes)); _shaderPackageTab = new FileEditor(this, gameData, config, _editor.Compactor, _fileDialog, "Shaders", ".shpk", - () => _editor.Files.Shpk, DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, + () => PopulateIsOnPlayer(_editor.Files.Shpk, ResourceType.Shpk), DrawShaderPackagePanel, () => _mod?.ModPath.FullName ?? string.Empty, (bytes, _, _) => new ShpkTab(_fileDialog, bytes)); _center = new CombinedTexture(_left, _right); - _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor); + _textureSelectCombo = new TextureDrawer.PathSelectCombo(textures, editor, () => GetPlayerResourcesOfType(ResourceType.Tex)); _resourceTreeFactory = resourceTreeFactory; _quickImportViewer = new ResourceTreeViewer(_config, resourceTreeFactory, changedItemDrawer, 2, OnQuickImportRefresh, DrawQuickImportActions);