diff --git a/Penumbra.Api b/Penumbra.Api index 0a970295..c27a0600 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 0a970295b2398683b1e49c46fd613541e2486210 +Subproject commit c27a06004138f2ec80ccdb494bb6ddf6d39d2165 diff --git a/Penumbra/Api/Api/ModsApi.cs b/Penumbra/Api/Api/ModsApi.cs index 55f1e259..78c62953 100644 --- a/Penumbra/Api/Api/ModsApi.cs +++ b/Penumbra/Api/Api/ModsApi.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json.Linq; using OtterGui.Compression; using OtterGui.Services; using Penumbra.Api.Enums; @@ -34,8 +33,12 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable { switch (type) { - case ModPathChangeType.Deleted when oldDirectory != null: ModDeleted?.Invoke(oldDirectory.Name); break; - case ModPathChangeType.Added when newDirectory != null: ModAdded?.Invoke(newDirectory.Name); break; + case ModPathChangeType.Deleted when oldDirectory != null: + ModDeleted?.Invoke(oldDirectory.Name); + break; + case ModPathChangeType.Added when newDirectory != null: + ModAdded?.Invoke(newDirectory.Name); + break; case ModPathChangeType.Moved when newDirectory != null && oldDirectory != null: ModMoved?.Invoke(oldDirectory.Name, newDirectory.Name); break; @@ -43,9 +46,7 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable } public void Dispose() - { - _communicator.ModPathChanged.Unsubscribe(OnModPathChanged); - } + => _communicator.ModPathChanged.Unsubscribe(OnModPathChanged); public Dictionary GetModList() => _modManager.ToDictionary(m => m.ModPath.Name, m => m.Name.Text); @@ -108,18 +109,6 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable public event Action? ModAdded; public event Action? ModMoved; - public event Action? CreatingPcp - { - add => _communicator.PcpCreation.Subscribe(value!, PcpCreation.Priority.ModsApi); - remove => _communicator.PcpCreation.Unsubscribe(value!); - } - - public event Action? ParsingPcp - { - add => _communicator.PcpParsing.Subscribe(value!, PcpParsing.Priority.ModsApi); - remove => _communicator.PcpParsing.Unsubscribe(value!); - } - public (PenumbraApiEc, string, bool, bool) GetModPath(string modDirectory, string modName) { if (!_modManager.TryGetMod(modDirectory, modName, out var mod) diff --git a/Penumbra/Api/Api/PenumbraApi.cs b/Penumbra/Api/Api/PenumbraApi.cs index 9e7eb964..7ca41324 100644 --- a/Penumbra/Api/Api/PenumbraApi.cs +++ b/Penumbra/Api/Api/PenumbraApi.cs @@ -17,7 +17,7 @@ public class PenumbraApi( UiApi ui) : IDisposable, IApiService, IPenumbraApi { public const int BreakingVersion = 5; - public const int FeatureVersion = 11; + public const int FeatureVersion = 10; public void Dispose() { diff --git a/Penumbra/Api/IpcProviders.cs b/Penumbra/Api/IpcProviders.cs index 0c80626f..7dcee375 100644 --- a/Penumbra/Api/IpcProviders.cs +++ b/Penumbra/Api/IpcProviders.cs @@ -54,8 +54,6 @@ public sealed class IpcProviders : IDisposable, IApiService IpcSubscribers.ModDeleted.Provider(pi, api.Mods), IpcSubscribers.ModAdded.Provider(pi, api.Mods), IpcSubscribers.ModMoved.Provider(pi, api.Mods), - IpcSubscribers.CreatingPcp.Provider(pi, api.Mods), - IpcSubscribers.ParsingPcp.Provider(pi, api.Mods), IpcSubscribers.GetModPath.Provider(pi, api.Mods), IpcSubscribers.SetModPath.Provider(pi, api.Mods), IpcSubscribers.GetChangedItems.Provider(pi, api.Mods), diff --git a/Penumbra/Collections/CollectionAutoSelector.cs b/Penumbra/Collections/CollectionAutoSelector.cs index f6e6bf72..68dac914 100644 --- a/Penumbra/Collections/CollectionAutoSelector.cs +++ b/Penumbra/Collections/CollectionAutoSelector.cs @@ -59,15 +59,8 @@ public sealed class CollectionAutoSelector : IService, IDisposable return; var collection = _resolver.PlayerCollection(); - if (collection.Identity.Id == Guid.Empty) - { - Penumbra.Log.Debug($"Not setting current collection because character has no mods assigned."); - } - else - { - Penumbra.Log.Debug($"Setting current collection to {collection.Identity.Identifier} through automatic collection selection."); - _collections.SetCollection(collection, CollectionType.Current); - } + Penumbra.Log.Debug($"Setting current collection to {collection.Identity.Identifier} through automatic collection selection."); + _collections.SetCollection(collection, CollectionType.Current); } diff --git a/Penumbra/Communication/ModPathChanged.cs b/Penumbra/Communication/ModPathChanged.cs index efe59482..1e4f8d36 100644 --- a/Penumbra/Communication/ModPathChanged.cs +++ b/Penumbra/Communication/ModPathChanged.cs @@ -3,7 +3,6 @@ using Penumbra.Api; using Penumbra.Api.Api; using Penumbra.Mods; using Penumbra.Mods.Manager; -using Penumbra.Services; namespace Penumbra.Communication; @@ -21,14 +20,11 @@ public sealed class ModPathChanged() { public enum Priority { - /// - PcpService = int.MinValue, - /// - ApiMods = int.MinValue + 1, + ApiMods = int.MinValue, /// - ApiModSettings = int.MinValue + 1, + ApiModSettings = int.MinValue, /// EphemeralConfig = -500, diff --git a/Penumbra/Communication/PcpCreation.cs b/Penumbra/Communication/PcpCreation.cs deleted file mode 100644 index cb11b3c3..00000000 --- a/Penumbra/Communication/PcpCreation.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Newtonsoft.Json.Linq; -using OtterGui.Classes; - -namespace Penumbra.Communication; - -/// -/// Triggered when the character.json file for a .pcp file is written. -/// -/// Parameter is the JObject that gets written to file. -/// Parameter is the object index of the game object this is written for. -/// -/// -public sealed class PcpCreation() : EventWrapper(nameof(PcpCreation)) -{ - public enum Priority - { - /// - ModsApi = int.MinValue, - } -} diff --git a/Penumbra/Communication/PcpParsing.cs b/Penumbra/Communication/PcpParsing.cs deleted file mode 100644 index 95b78951..00000000 --- a/Penumbra/Communication/PcpParsing.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Newtonsoft.Json.Linq; -using OtterGui.Classes; - -namespace Penumbra.Communication; - -/// -/// Triggered when the character.json file for a .pcp file is parsed and applied. -/// -/// Parameter is parsed JObject that contains the data. -/// Parameter is the identifier of the created mod. -/// Parameter is the GUID of the created collection. -/// -/// -public sealed class PcpParsing() : EventWrapper(nameof(PcpParsing)) -{ - public enum Priority - { - /// - ModsApi = int.MinValue, - } -} diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index d9a9f5fe..8c50dad7 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -68,8 +68,6 @@ public class Configuration : IPluginConfiguration, ISavable, IService public bool HideMachinistOffhandFromChangedItems { get; set; } = true; public bool DefaultTemporaryMode { get; set; } = false; public bool EnableCustomShapes { get; set; } = true; - public bool DisablePcpHandling { get; set; } = false; - public bool AllowPcpIpc { get; set; } = true; public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; public ChangedItemMode ChangedItemDisplay { get; set; } = ChangedItemMode.GroupedCollapsed; public int OptionGroupCollapsibleMin { get; set; } = 5; @@ -90,7 +88,6 @@ public class Configuration : IPluginConfiguration, ISavable, IService public bool OpenFoldersByDefault { get; set; } = false; public int SingleGroupRadioMax { get; set; } = 2; public string DefaultImportFolder { get; set; } = string.Empty; - public string PcpFolderName { get; set; } = "PCP"; public string QuickMoveFolder1 { get; set; } = string.Empty; public string QuickMoveFolder2 { get; set; } = string.Empty; public string QuickMoveFolder3 { get; set; } = string.Empty; diff --git a/Penumbra/Import/TexToolsImport.cs b/Penumbra/Import/TexToolsImport.cs index 8e4fea41..fed06573 100644 --- a/Penumbra/Import/TexToolsImport.cs +++ b/Penumbra/Import/TexToolsImport.cs @@ -119,7 +119,7 @@ public partial class TexToolsImporter : IDisposable // Puts out warnings if extension does not correspond to data. private DirectoryInfo VerifyVersionAndImport(FileInfo modPackFile) { - if (modPackFile.Extension.ToLowerInvariant() is ".pmp" or ".pcp" or ".zip" or ".7z" or ".rar") + if (modPackFile.Extension.ToLowerInvariant() is ".pmp" or ".zip" or ".7z" or ".rar") return HandleRegularArchive(modPackFile); using var zfs = modPackFile.OpenRead(); diff --git a/Penumbra/Mods/Manager/ModDataEditor.cs b/Penumbra/Mods/Manager/ModDataEditor.cs index ffa73b76..fc4fdadc 100644 --- a/Penumbra/Mods/Manager/ModDataEditor.cs +++ b/Penumbra/Mods/Manager/ModDataEditor.cs @@ -36,7 +36,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic /// Create the file containing the meta information about a mod from scratch. public void CreateMeta(DirectoryInfo directory, string? name, string? author, string? description, string? version, - string? website, params string[] tags) + string? website) { var mod = new Mod(directory); mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString(name); @@ -44,7 +44,6 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic mod.Description = description ?? mod.Description; mod.Version = version ?? mod.Version; mod.Website = website ?? mod.Website; - mod.ModTags = tags; saveService.ImmediateSaveSync(new ModMeta(mod)); } diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 3a7bd105..1bb2a073 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -32,12 +32,12 @@ public partial class ModCreator( public readonly Configuration Config = config; /// Creates directory and files necessary for a new mod without adding it to the manager. - public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "", string? author = null, params string[] tags) + public DirectoryInfo? CreateEmptyMod(DirectoryInfo basePath, string newName, string description = "", string? author = null) { try { var newDir = CreateModFolder(basePath, newName, Config.ReplaceNonAsciiOnImport, true); - dataEditor.CreateMeta(newDir, newName, author ?? Config.DefaultModAuthor, description, "1.0", string.Empty, tags); + dataEditor.CreateMeta(newDir, newName, author ?? Config.DefaultModAuthor, description, "1.0", string.Empty); CreateDefaultFiles(newDir); return newDir; } diff --git a/Penumbra/Services/CommunicatorService.cs b/Penumbra/Services/CommunicatorService.cs index 35f15e9e..5d745419 100644 --- a/Penumbra/Services/CommunicatorService.cs +++ b/Penumbra/Services/CommunicatorService.cs @@ -81,12 +81,6 @@ public class CommunicatorService : IDisposable, IService /// public readonly ResolvedFileChanged ResolvedFileChanged = new(); - /// - public readonly PcpCreation PcpCreation = new(); - - /// - public readonly PcpParsing PcpParsing = new(); - public void Dispose() { CollectionChange.Dispose(); @@ -111,7 +105,5 @@ public class CommunicatorService : IDisposable, IService ChangedItemClick.Dispose(); SelectTab.Dispose(); ResolvedFileChanged.Dispose(); - PcpCreation.Dispose(); - PcpParsing.Dispose(); } } diff --git a/Penumbra/Services/PcpService.cs b/Penumbra/Services/PcpService.cs deleted file mode 100644 index 73c61cdb..00000000 --- a/Penumbra/Services/PcpService.cs +++ /dev/null @@ -1,293 +0,0 @@ -using Dalamud.Game.ClientState.Objects.Types; -using FFXIVClientStructs.FFXIV.Client.Game.Object; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using OtterGui.Classes; -using OtterGui.Services; -using Penumbra.Collections; -using Penumbra.Collections.Manager; -using Penumbra.Communication; -using Penumbra.GameData.Actors; -using Penumbra.GameData.Interop; -using Penumbra.GameData.Structs; -using Penumbra.Interop.PathResolving; -using Penumbra.Interop.ResourceTree; -using Penumbra.Meta.Manipulations; -using Penumbra.Mods; -using Penumbra.Mods.Groups; -using Penumbra.Mods.Manager; -using Penumbra.Mods.SubMods; -using Penumbra.String.Classes; - -namespace Penumbra.Services; - -public class PcpService : IApiService, IDisposable -{ - public const string Extension = ".pcp"; - - private readonly Configuration _config; - private readonly SaveService _files; - private readonly ResourceTreeFactory _treeFactory; - private readonly ObjectManager _objectManager; - private readonly ActorManager _actors; - private readonly FrameworkManager _framework; - private readonly CollectionResolver _collectionResolver; - private readonly CollectionManager _collections; - private readonly ModCreator _modCreator; - private readonly ModExportManager _modExport; - private readonly CommunicatorService _communicator; - private readonly SHA1 _sha1 = SHA1.Create(); - private readonly ModFileSystem _fileSystem; - private readonly ModManager _mods; - - public PcpService(Configuration config, - SaveService files, - ResourceTreeFactory treeFactory, - ObjectManager objectManager, - ActorManager actors, - FrameworkManager framework, - CollectionManager collections, - CollectionResolver collectionResolver, - ModCreator modCreator, - ModExportManager modExport, - CommunicatorService communicator, - ModFileSystem fileSystem, - ModManager mods) - { - _config = config; - _files = files; - _treeFactory = treeFactory; - _objectManager = objectManager; - _actors = actors; - _framework = framework; - _collectionResolver = collectionResolver; - _collections = collections; - _modCreator = modCreator; - _modExport = modExport; - _communicator = communicator; - _fileSystem = fileSystem; - _mods = mods; - - _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.PcpService); - } - - public void CleanPcpMods() - { - var mods = _mods.Where(m => m.ModTags.Contains("PCP")).ToList(); - Penumbra.Log.Information($"[PCPService] Deleting {mods.Count} mods containing the tag PCP."); - foreach (var mod in mods) - _mods.DeleteMod(mod); - } - - public void CleanPcpCollections() - { - var collections = _collections.Storage.Where(c => c.Identity.Name.StartsWith("PCP/")).ToList(); - Penumbra.Log.Information($"[PCPService] Deleting {collections.Count} mods containing the tag PCP."); - foreach (var collection in collections) - _collections.Storage.Delete(collection); - } - - private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory) - { - if (type is not ModPathChangeType.Added || _config.DisablePcpHandling || newDirectory is null) - return; - - try - { - var file = Path.Combine(newDirectory.FullName, "character.json"); - if (!File.Exists(file)) - { - // First version had collection.json, changed. - var oldFile = Path.Combine(newDirectory.FullName, "collection.json"); - Penumbra.Log.Information("[PCPService] Renaming old PCP file from collection.json to character.json."); - if (File.Exists(oldFile)) - File.Move(oldFile, file, true); - else - return; - } - - Penumbra.Log.Information($"[PCPService] Found a PCP file for {mod.Name}, applying."); - var text = File.ReadAllText(file); - var jObj = JObject.Parse(text); - var identifier = _actors.FromJson(jObj["Actor"] as JObject); - if (!identifier.IsValid) - return; - - if (jObj["Collection"]?.ToObject() is not { } collectionName) - return; - - var name = $"PCP/{collectionName}"; - if (!_collections.Storage.AddCollection(name, null)) - return; - - var collection = _collections.Storage[^1]; - _collections.Editor.SetModState(collection, mod, true); - - var identifierGroup = _collections.Active.Individuals.GetGroup(identifier); - _collections.Active.SetCollection(collection, CollectionType.Individual, identifierGroup); - if (_fileSystem.TryGetValue(mod, out var leaf)) - { - try - { - var folder = _fileSystem.FindOrCreateAllFolders(_config.PcpFolderName); - _fileSystem.Move(leaf, folder); - } - catch - { - // ignored. - } - } - - if (_config.AllowPcpIpc) - _communicator.PcpParsing.Invoke(jObj, mod.Identifier, collection.Identity.Id); - } - catch (Exception ex) - { - Penumbra.Log.Error($"Error reading the character.json file from {mod.Identifier}:\n{ex}"); - } - } - - public void Dispose() - => _communicator.ModPathChanged.Unsubscribe(OnModPathChange); - - public async Task<(bool, string)> CreatePcp(ObjectIndex objectIndex, string note = "", CancellationToken cancel = default) - { - try - { - Penumbra.Log.Information($"[PCPService] Creating PCP file for game object {objectIndex.Index}."); - var (identifier, tree, collection) = await _framework.Framework.RunOnFrameworkThread(() => - { - var (actor, identifier) = CheckActor(objectIndex); - cancel.ThrowIfCancellationRequested(); - unsafe - { - var collection = _collectionResolver.IdentifyCollection((GameObject*)actor.Address, true); - if (!collection.Valid || !collection.ModCollection.HasCache) - throw new Exception($"Actor {identifier} has no mods applying, nothing to do."); - - cancel.ThrowIfCancellationRequested(); - if (_treeFactory.FromCharacter(actor, 0) is not { } tree) - throw new Exception($"Unable to fetch modded resources for {identifier}."); - - return (identifier.CreatePermanent(), tree, collection); - } - }); - cancel.ThrowIfCancellationRequested(); - var time = DateTime.Now; - var modDirectory = CreateMod(identifier, note, time); - await CreateDefaultMod(modDirectory, collection.ModCollection, tree, cancel); - await CreateCollectionInfo(modDirectory, objectIndex, identifier, note, time, cancel); - var file = ZipUp(modDirectory); - return (true, file); - } - catch (Exception ex) - { - return (false, ex.Message); - } - } - - private static string ZipUp(DirectoryInfo directory) - { - var fileName = directory.FullName + Extension; - ZipFile.CreateFromDirectory(directory.FullName, fileName, CompressionLevel.Optimal, false); - directory.Delete(true); - return fileName; - } - - private async Task CreateCollectionInfo(DirectoryInfo directory, ObjectIndex index, ActorIdentifier actor, string note, DateTime time, - CancellationToken cancel = default) - { - var jObj = new JObject - { - ["Version"] = 1, - ["Actor"] = actor.ToJson(), - ["Mod"] = directory.Name, - ["Collection"] = note.Length > 0 ? $"{actor.ToName()}: {note}" : actor.ToName(), - ["Time"] = time, - ["Note"] = note, - }; - if (note.Length > 0) - cancel.ThrowIfCancellationRequested(); - if (_config.AllowPcpIpc) - await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(jObj, index.Index)); - var filePath = Path.Combine(directory.FullName, "character.json"); - await using var file = File.Open(filePath, File.Exists(filePath) ? FileMode.Truncate : FileMode.CreateNew); - await using var stream = new StreamWriter(file); - await using var json = new JsonTextWriter(stream); - json.Formatting = Formatting.Indented; - await jObj.WriteToAsync(json, cancel); - } - - private DirectoryInfo CreateMod(ActorIdentifier actor, string note, DateTime time) - { - var directory = _modExport.ExportDirectory; - directory.Create(); - var actorName = actor.ToName(); - var authorName = _actors.GetCurrentPlayer().ToName(); - var suffix = note.Length > 0 - ? note - : time.ToString("yyyy-MM-ddTHH\\:mm", CultureInfo.InvariantCulture); - var modName = $"{actorName} - {suffix}"; - var description = $"On-Screen Data for {actorName} as snapshotted on {time}."; - return _modCreator.CreateEmptyMod(directory, modName, description, authorName, "PCP") - ?? throw new Exception($"Unable to create mod {modName} in {directory.FullName}."); - } - - private async Task CreateDefaultMod(DirectoryInfo modDirectory, ModCollection collection, ResourceTree tree, - CancellationToken cancel = default) - { - var subDirectory = modDirectory.CreateSubdirectory("files"); - var subMod = new DefaultSubMod(null!); - foreach (var node in tree.FlatNodes) - { - cancel.ThrowIfCancellationRequested(); - var gamePath = node.GamePath; - var fullPath = node.FullPath; - if (fullPath.IsRooted) - { - var hash = await _sha1.ComputeHashAsync(File.OpenRead(fullPath.FullName), cancel).ConfigureAwait(false); - cancel.ThrowIfCancellationRequested(); - var name = Convert.ToHexString(hash) + fullPath.Extension; - var newFile = Path.Combine(subDirectory.FullName, name); - if (!File.Exists(newFile)) - File.Copy(fullPath.FullName, newFile); - subMod.Files.TryAdd(gamePath, new FullPath(newFile)); - } - else if (gamePath.Path != fullPath.InternalName) - { - subMod.FileSwaps.TryAdd(gamePath, fullPath); - } - } - - cancel.ThrowIfCancellationRequested(); - subMod.Manipulations = new MetaDictionary(collection.MetaCache); - - var saveGroup = new ModSaveGroup(modDirectory, subMod, _config.ReplaceNonAsciiOnImport); - var filePath = _files.FileNames.OptionGroupFile(modDirectory.FullName, -1, string.Empty, _config.ReplaceNonAsciiOnImport); - cancel.ThrowIfCancellationRequested(); - await using var fileStream = File.Open(filePath, File.Exists(filePath) ? FileMode.Truncate : FileMode.CreateNew); - await using var writer = new StreamWriter(fileStream); - saveGroup.Save(writer); - } - - private (ICharacter Actor, ActorIdentifier Identifier) CheckActor(ObjectIndex objectIndex) - { - var actor = _objectManager[objectIndex]; - if (!actor.Valid) - throw new Exception($"No Actor at index {objectIndex} found."); - - if (!actor.Identifier(_actors, out var identifier)) - throw new Exception($"Could not create valid identifier for actor at index {objectIndex}."); - - if (!actor.IsCharacter) - throw new Exception($"Actor {identifier} at index {objectIndex} is not a valid character."); - - if (!actor.Model.Valid) - throw new Exception($"Actor {identifier} at index {objectIndex} has no model."); - - if (_objectManager.Objects.CreateObjectReference(actor.Address) is not ICharacter character) - throw new Exception($"Actor {identifier} at index {objectIndex} could not be converted to ICharacter"); - - return (character, identifier); - } -} diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs index a2309343..440baa2f 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs @@ -1,19 +1,15 @@ -using Dalamud.Bindings.ImGui; using Dalamud.Interface; -using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.Utility; -using OtterGui; -using OtterGui.Classes; -using OtterGui.Extensions; +using Dalamud.Bindings.ImGui; using OtterGui.Raii; +using OtterGui; using OtterGui.Text; using Penumbra.Api.Enums; -using Penumbra.GameData.Structs; using Penumbra.Interop.ResourceTree; using Penumbra.Services; -using Penumbra.String; using Penumbra.UI.Classes; -using static System.Net.Mime.MediaTypeNames; +using Penumbra.String; +using OtterGui.Extensions; namespace Penumbra.UI.AdvancedWindow; @@ -25,13 +21,12 @@ public class ResourceTreeViewer( int actionCapacity, Action onRefresh, Action drawActions, - CommunicatorService communicator, - PcpService pcpService) + CommunicatorService communicator) { private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags = ResourceTreeFactory.Flags.RedactExternalPaths | ResourceTreeFactory.Flags.WithUiData | ResourceTreeFactory.Flags.WithOwnership; - private readonly HashSet _unfolded = []; + private readonly HashSet _unfolded = []; private readonly Dictionary _filterCache = []; @@ -39,7 +34,6 @@ public class ResourceTreeViewer( private ChangedItemIconFlag _typeFilter = ChangedItemFlagExtensions.AllFlags; private string _nameFilter = string.Empty; private string _nodeFilter = string.Empty; - private string _note = string.Empty; private Task? _task; @@ -89,28 +83,7 @@ public class ResourceTreeViewer( using var id = ImRaii.PushId(index); - ImUtf8.TextFrameAligned($"Collection: {(incognito.IncognitoMode ? tree.AnonymizedCollectionName : tree.CollectionName)}"); - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Export Character Pack"u8, - "Note that this recomputes the current data of the actor if it still exists, and does not use the cached data."u8)) - { - pcpService.CreatePcp((ObjectIndex)tree.GameObjectIndex, _note).ContinueWith(t => - { - - var (success, text) = t.Result; - - if (success) - Penumbra.Messager.NotificationMessage($"Created {text}.", NotificationType.Success, false); - else - Penumbra.Messager.NotificationMessage(text, NotificationType.Error, false); - }); - _note = string.Empty; - } - - ImUtf8.SameLineInner(); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - ImUtf8.InputText("##note"u8, ref _note, "Export note..."u8); - + ImGui.TextUnformatted($"Collection: {(incognito.IncognitoMode ? tree.AnonymizedCollectionName : tree.CollectionName)}"); using var table = ImRaii.Table("##ResourceTree", actionCapacity > 0 ? 4 : 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); @@ -290,8 +263,7 @@ public class ResourceTreeViewer( using var group = ImUtf8.Group(); using (var color = ImRaii.PushColor(ImGuiCol.Text, (hasMod ? ColorId.NewMod : ColorId.DisabledMod).Value())) { - ImUtf8.Selectable(modName, false, ImGuiSelectableFlags.AllowItemOverlap, - new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); + ImUtf8.Selectable(modName, false, ImGuiSelectableFlags.AllowItemOverlap, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); } ImGui.SameLine(); @@ -300,8 +272,7 @@ public class ResourceTreeViewer( } else { - ImGui.Selectable(resourceNode.FullPath.ToPath(), false, ImGuiSelectableFlags.AllowItemOverlap, - new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); + ImGui.Selectable(resourceNode.FullPath.ToPath(), false, ImGuiSelectableFlags.AllowItemOverlap, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight)); } if (ImGui.IsItemClicked()) @@ -394,10 +365,9 @@ public class ResourceTreeViewer( private static string GetPathStatusDescription(ResourceNode.PathStatus status) => status switch { - ResourceNode.PathStatus.External => "The actual path to this file is unavailable, because it is managed by external tools.", - ResourceNode.PathStatus.NonExistent => - "The actual path to this file is unavailable, because it seems to have been moved or deleted since it was loaded.", - _ => "The actual path to this file is unavailable.", + ResourceNode.PathStatus.External => "The actual path to this file is unavailable, because it is managed by external tools.", + ResourceNode.PathStatus.NonExistent => "The actual path to this file is unavailable, because it seems to have been moved or deleted since it was loaded.", + _ => "The actual path to this file is unavailable.", }; [Flags] diff --git a/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs index ac06fe1a..10a4aea2 100644 --- a/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs +++ b/Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs @@ -9,9 +9,8 @@ public class ResourceTreeViewerFactory( ResourceTreeFactory treeFactory, ChangedItemDrawer changedItemDrawer, IncognitoService incognito, - CommunicatorService communicator, - PcpService pcpService) : IService + CommunicatorService communicator) : IService { public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action drawActions) - => new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator, pcpService); + => new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator); } diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index 3f3c82aa..16ff7b41 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -126,7 +126,6 @@ public sealed class ModFileSystemSelector : FileSystemSelector + "Mod Packs{.ttmp,.ttmp2,.pmp},TexTools Mod Packs{.ttmp,.ttmp2},Penumbra Mod Packs{.pmp},Archives{.zip,.7z,.rar}", (s, f) => { if (!s) return; @@ -446,7 +445,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector _config.AlwaysOpenDefaultImport = v); - Checkbox("Handle PCP Files", - "When encountering specific mods, usually but not necessarily denoted by a .pcp file ending, Penumbra will automatically try to create an associated collection and assign it to a specific character for this mod package. This can turn this behaviour off if unwanted.", - !_config.DisablePcpHandling, v => _config.DisablePcpHandling = !v); - - var active = _config.DeleteModModifier.IsActive(); - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Delete all PCP Mods"u8, "Deletes all mods tagged with 'PCP' from the mod list."u8, disabled: !active)) - _pcpService.CleanPcpMods(); - if (!active) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Delete all PCP Collections"u8, "Deletes all collections whose name starts with 'PCP/' from the collection list."u8, disabled: !active)) - _pcpService.CleanPcpCollections(); - if (!active) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking."); - - Checkbox("Allow Other Plugins Access to PCP Handling", - "When creating or importing PCP files, other plugins can add and interpret their own data to the character.json file.", - _config.AllowPcpIpc, v => _config.AllowPcpIpc = v); DrawDefaultModImportPath(); DrawDefaultModAuthor(); DrawDefaultModImportFolder(); - DrawPcpFolder(); DrawDefaultModExportPath(); } @@ -733,21 +712,6 @@ public class SettingsTab : ITab, IUiService "Set the default Penumbra mod folder to place newly imported mods into.\nLeave blank to import into Root."); } - /// Draw input for the default folder to sort put newly imported mods into. - private void DrawPcpFolder() - { - var tmp = _config.PcpFolderName; - ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X); - if (ImUtf8.InputText("##pcpFolder"u8, ref tmp)) - _config.PcpFolderName = tmp; - - if (ImGui.IsItemDeactivatedAfterEdit()) - _config.Save(); - - ImGuiUtil.LabeledHelpMarker("Default PCP Organizational Folder", - "The folder any penumbra character packs are moved to on import.\nLeave blank to import into Root."); - } - /// Draw all settings pertaining to advanced editing of mods. private void DrawModEditorSettings() @@ -1091,7 +1055,7 @@ public class SettingsTab : ITab, IUiService if (ImGui.Button("Show Changelogs", new Vector2(width, 0))) _penumbra.ForceChangelogOpen(); - ImGui.SetCursorPos(new Vector2(xPos, 5 * ImGui.GetFrameHeightWithSpacing())); + ImGui.SetCursorPos(new Vector2(xPos, 5 * ImGui.GetFrameHeightWithSpacing())); CustomGui.DrawKofiPatreonButton(Penumbra.Messager, new Vector2(width, 0)); } diff --git a/repo.json b/repo.json index 48d5b97f..a452dc94 100644 --- a/repo.json +++ b/repo.json @@ -6,7 +6,7 @@ "Description": "Runtime mod loader and manager.", "InternalName": "Penumbra", "AssemblyVersion": "1.5.0.6", - "TestingAssemblyVersion": "1.5.0.8", + "TestingAssemblyVersion": "1.5.0.6", "RepoUrl": "https://github.com/xivdev/Penumbra", "ApplicableVersion": "any", "DalamudApiLevel": 13, @@ -19,7 +19,7 @@ "LoadRequiredState": 2, "LoadSync": true, "DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip", - "DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/testing_1.5.0.8/Penumbra.zip", + "DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip", "DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/1.5.0.6/Penumbra.zip", "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" }