Update to Luna state.
Some checks failed
.NET Build / build (push) Has been cancelled

This commit is contained in:
Ottermandias 2025-09-17 18:05:07 +02:00
parent c5d09d7cd1
commit 924c9b9f7e
48 changed files with 1323 additions and 1271 deletions

View file

@ -59,9 +59,9 @@ csharp_space_between_method_declaration_parameter_list_parentheses=false
csharp_space_between_parentheses=false
csharp_space_between_square_brackets=false
csharp_style_namespace_declarations= file_scoped:suggestion
csharp_style_var_elsewhere=true:suggestion
csharp_style_var_for_built_in_types=true:suggestion
csharp_style_var_when_type_is_apparent=true:suggestion
csharp_style_var_elsewhere= true:suggestion
csharp_style_var_for_built_in_types= true:suggestion
csharp_style_var_when_type_is_apparent= true:suggestion
csharp_using_directive_placement= outside_namespace:silent
dotnet_diagnostic.bc40000.severity=warning
dotnet_diagnostic.bc400005.severity=warning
@ -348,16 +348,16 @@ dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities=*
dotnet_naming_symbols.type_parameters_symbols.applicable_kinds=type_parameter
dotnet_separate_import_directive_groups=false
dotnet_sort_system_directives_first=true
dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:suggestion
dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:suggestion
dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:suggestion
dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion
dotnet_style_predefined_type_for_member_access=true:suggestion
dotnet_style_qualification_for_event=false:suggestion
dotnet_style_qualification_for_field=false:suggestion
dotnet_style_qualification_for_method=false:suggestion
dotnet_style_qualification_for_property=false:suggestion
dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators= never_if_unnecessary:suggestion
dotnet_style_parentheses_in_other_binary_operators= never_if_unnecessary:suggestion
dotnet_style_parentheses_in_relational_binary_operators= never_if_unnecessary:suggestion
dotnet_style_predefined_type_for_locals_parameters_members= true:suggestion
dotnet_style_predefined_type_for_member_access= true:suggestion
dotnet_style_qualification_for_event= false:suggestion
dotnet_style_qualification_for_field= false:suggestion
dotnet_style_qualification_for_method= false:suggestion
dotnet_style_qualification_for_property= false:suggestion
dotnet_style_require_accessibility_modifiers= always:suggestion
file_header_template=
# ReSharper properties
@ -3588,6 +3588,40 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_expression_bodied_properties = true:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_prefer_simple_property_accessors = true:suggestion
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_prefer_static_anonymous_function = true:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
[*.{cshtml,htm,html,proto,razor}]
indent_style=tab
@ -3635,3 +3669,10 @@ dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
insert_final_newline = true
end_of_line = lf
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
dotnet_style_readonly_field = true:suggestion
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent

2
Luna

@ -1 +1 @@
Subproject commit f39b514304d8f30030310f75d83f99385105c271
Subproject commit 15636cda90725e6af7071512cf9873dd273570fc

@ -1 +1 @@
Subproject commit 2183dc78c46dd9b72fd2d95370e1bf223eff93a3
Subproject commit 3104cfeefbdf0dd616a5886e07d1b615db15afd4

View file

@ -40,7 +40,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
if (!c.Active.Current.ChangedItems.TryGetValue(s, out var d))
return [];
return d.Item1.Select(m => (m is Mod mod ? mod.Identifier : string.Empty, m.Name.Text)).ToArray();
return d.Item1.Select(m => (m is Mod mod ? mod.Identifier : string.Empty, m.Name)).ToArray();
};
}

View file

@ -58,7 +58,7 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
}
public Dictionary<string, string> GetModList()
=> _modManager.ToDictionary(m => m.ModPath.Name, m => m.Name.Text);
=> _modManager.ToDictionary(m => m.ModPath.Name, m => m.Name);
public PenumbraApiEc InstallMod(string modFilePackagePath)
{

View file

@ -1,14 +1,10 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Plugin;
using ImSharp;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Api.Enums;
using Penumbra.Api.IpcSubscribers;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Data;
using ImGuiClip = OtterGui.ImGuiClip;
namespace Penumbra.Api.IpcTester;
@ -28,89 +24,110 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
public void Draw()
{
using var _ = ImRaii.TreeNode("Collections");
using var _ = Im.Tree.Node("Collections"u8);
if (!_)
return;
ImGuiUtil.GenericEnumCombo("Collection Type", 200, _type, out _type, t => ((CollectionType)t).ToName());
ImGui.InputInt("Object Index##Collections", ref _objectIdx, 0, 0);
ImGuiUtil.GuidInput("Collection Id##Collections", "Collection Identifier...", string.Empty, ref _collectionId, ref _collectionIdString);
ImGui.Checkbox("Allow Assignment Creation", ref _allowCreation);
ImGui.SameLine();
ImGui.Checkbox("Allow Assignment Deletion", ref _allowDeletion);
using var table = ImRaii.Table(string.Empty, 4, ImGuiTableFlags.SizingFixedFit);
if (!table)
return;
IpcTester.DrawIntro("Last Return Code", _returnCode.ToString());
if (_oldCollection != null)
ImGui.TextUnformatted(!_oldCollection.HasValue ? "Created" : _oldCollection.ToString());
IpcTester.DrawIntro(GetCollectionsByIdentifier.Label, "Collection Identifier");
var collectionList = new GetCollectionsByIdentifier(pi).Invoke(_collectionIdString);
if (collectionList.Count == 0)
Im.Input.Scalar("Object Index##Collections"u8, ref _objectIdx);
if (_collectionId.HasValue)
{
DrawCollection(null);
if (ImEx.GuidInput("Collection Id##Collections"u8, $"{_collectionId.Value}", out var id))
_collectionId = id;
}
else
{
DrawCollection(collectionList[0]);
if (ImEx.GuidInput("Collection Id##Collections"u8, "Collection Identifier..."u8, out var id))
_collectionId = id;
}
Im.Checkbox("Allow Assignment Creation"u8, ref _allowCreation);
Im.Line.Same();
Im.Checkbox("Allow Assignment Deletion"u8, ref _allowDeletion);
using var table = Im.Table.Begin("table"u8, 4, TableFlags.SizingFixedFit);
if (!table)
return;
table.DrawColumn("Last Return Code"u8);
table.DrawColumn($"{_returnCode}");
if (_oldCollection is not null)
Im.Text(!_oldCollection.HasValue ? "Created" : _oldCollection.ToString()!);
table.DrawColumn(GetCollectionsByIdentifier.Label);
table.DrawColumn("Collection Identifier"u8);
var collectionList = new GetCollectionsByIdentifier(pi).Invoke(_collectionIdString);
if (collectionList.Count == 0)
{
DrawCollection(table, null);
}
else
{
DrawCollection(table, collectionList[0]);
foreach (var pair in collectionList.Skip(1))
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.TableNextColumn();
DrawCollection(pair);
table.NextRow();
table.NextColumn();
table.NextColumn();
table.NextColumn();
DrawCollection(table, pair);
}
}
IpcTester.DrawIntro(GetCollection.Label, "Current Collection");
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Current));
table.DrawColumn(GetCollection.Label);
table.DrawColumn("Current Collection"u8);
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Current));
IpcTester.DrawIntro(GetCollection.Label, "Default Collection");
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Default));
table.DrawColumn(GetCollection.Label);
table.DrawColumn("Default Collection"u8);
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Default));
IpcTester.DrawIntro(GetCollection.Label, "Interface Collection");
DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Interface));
table.DrawColumn(GetCollection.Label);
table.DrawColumn("Interface Collection"u8);
DrawCollection(table, new GetCollection(pi).Invoke(ApiCollectionType.Interface));
IpcTester.DrawIntro(GetCollection.Label, "Special Collection");
DrawCollection(new GetCollection(pi).Invoke(_type));
table.DrawColumn(GetCollection.Label);
table.DrawColumn("Special Collection"u8);
DrawCollection(table, new GetCollection(pi).Invoke(_type));
IpcTester.DrawIntro(GetCollections.Label, "Collections");
table.DrawColumn(GetCollections.Label);
table.DrawColumn("Collections"u8);
DrawCollectionPopup();
if (ImGui.Button("Get##Collections"))
if (Im.Button("Get##Collections"u8))
{
_collections = new GetCollections(pi).Invoke();
ImGui.OpenPopup("Collections");
Im.Popup.Open("Collections"u8);
}
IpcTester.DrawIntro(GetCollectionForObject.Label, "Get Object Collection");
table.DrawColumn(GetCollectionForObject.Label);
table.DrawColumn("Get Object Collection"u8);
var (valid, individual, effectiveCollection) = new GetCollectionForObject(pi).Invoke(_objectIdx);
DrawCollection(effectiveCollection);
ImGui.SameLine();
ImGui.TextUnformatted($"({(valid ? "Valid" : "Invalid")} Object{(individual ? ", Individual Assignment)" : ")")}");
DrawCollection(table, effectiveCollection);
Im.Line.Same();
Im.Text($"({(valid ? "Valid" : "Invalid")} Object{(individual ? ", Individual Assignment)" : ")")}");
IpcTester.DrawIntro(SetCollection.Label, "Set Special Collection");
if (ImGui.Button("Set##SpecialCollection"))
table.DrawColumn(SetCollection.Label);
table.DrawColumn("Set Special Collection"u8);
if (Im.Button("Set##SpecialCollection"u8))
(_returnCode, _oldCollection) =
new SetCollection(pi).Invoke(_type, _collectionId.GetValueOrDefault(Guid.Empty), _allowCreation, _allowDeletion);
ImGui.TableNextColumn();
if (ImGui.Button("Remove##SpecialCollection"))
table.NextColumn();
if (Im.Button("Remove##SpecialCollection"u8))
(_returnCode, _oldCollection) = new SetCollection(pi).Invoke(_type, null, _allowCreation, _allowDeletion);
IpcTester.DrawIntro(SetCollectionForObject.Label, "Set Object Collection");
if (ImGui.Button("Set##ObjectCollection"))
table.DrawColumn(SetCollectionForObject.Label);
table.DrawColumn("Set Object Collection"u8);
if (Im.Button("Set##ObjectCollection"u8))
(_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, _collectionId.GetValueOrDefault(Guid.Empty),
_allowCreation, _allowDeletion);
ImGui.TableNextColumn();
if (ImGui.Button("Remove##ObjectCollection"))
table.NextColumn();
if (Im.Button("Remove##ObjectCollection"u8))
(_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, null, _allowCreation, _allowDeletion);
IpcTester.DrawIntro(GetChangedItemsForCollection.Label, "Changed Item List");
table.DrawColumn(GetChangedItemsForCollection.Label);
table.DrawColumn("Changed Item List"u8);
DrawChangedItemPopup();
if (ImGui.Button("Get##ChangedItems"))
if (Im.Button("Get##ChangedItems"u8))
{
var items = new GetChangedItemsForCollection(pi).Invoke(_collectionId.GetValueOrDefault(Guid.Empty));
_changedItems = items.Select(kvp =>
@ -118,67 +135,70 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : Luna.IUiService
var (type, id) = kvp.Value.ToApiObject();
return (kvp.Key, type, id);
}).ToArray();
ImGui.OpenPopup("Changed Item List");
Im.Popup.Open("Changed Item List"u8);
}
}
private void DrawChangedItemPopup()
{
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
using var p = ImRaii.Popup("Changed Item List");
Im.Window.SetNextSize(ImEx.ScaledVector(500));
using var p = Im.Popup.Begin("Changed Item List"u8);
if (!p)
return;
using (var table = ImRaii.Table("##ChangedItems", 3, ImGuiTableFlags.SizingFixedFit))
using (var table = Im.Table.Begin("##ChangedItems"u8, 3, TableFlags.SizingFixedFit))
{
if (table)
ImGuiClip.ClippedDraw(_changedItems, t =>
{
using var clipper = new Im.ListClipper(_changedItems.Length, Im.Style.TextHeightWithSpacing);
foreach (var item in clipper.Iterate(_changedItems))
{
ImGuiUtil.DrawTableColumn(t.Item1);
ImGuiUtil.DrawTableColumn(t.Item2.ToString());
ImGuiUtil.DrawTableColumn(t.Item3.ToString());
}, ImGui.GetTextLineHeightWithSpacing());
table.DrawColumn(item.Item1);
table.DrawColumn($"{item.Item2}");
table.DrawColumn($"{item.Item3}");
}
}
}
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
ImGui.CloseCurrentPopup();
if (Im.Button("Close"u8, -Vector2.UnitX) || !Im.Window.Focused())
Im.Popup.CloseCurrent();
}
private void DrawCollectionPopup()
{
ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500));
using var p = ImRaii.Popup("Collections");
Im.Window.SetNextSize(ImEx.ScaledVector(500));
using var p = Im.Popup.Begin("Collections"u8);
if (!p)
return;
using (var t = ImRaii.Table("collections", 2, ImGuiTableFlags.SizingFixedFit))
using (var t = Im.Table.Begin("collections"u8, 2, TableFlags.SizingFixedFit))
{
if (t)
foreach (var collection in _collections)
{
ImGui.TableNextColumn();
DrawCollection((collection.Key, collection.Value));
t.NextColumn();
DrawCollection(t, (collection.Key, collection.Value));
}
}
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
ImGui.CloseCurrentPopup();
if (Im.Button("Close"u8, -Vector2.UnitX) || !Im.Window.Focused())
Im.Popup.CloseCurrent();
}
private static void DrawCollection((Guid Id, string Name)? collection)
private static void DrawCollection(Im.TableDisposable table, (Guid Id, string Name)? collection)
{
if (collection == null)
{
ImGui.TextUnformatted("<Unassigned>");
ImGui.TableNextColumn();
Im.Text("<Unassigned>"u8);
table.NextColumn();
return;
}
ImGui.TextUnformatted(collection.Value.Name);
ImGui.TableNextColumn();
using (ImRaii.PushFont(UiBuilder.MonoFont))
Im.Text(collection.Value.Name);
table.NextColumn();
using (Im.Font.PushMono())
{
ImGuiUtil.CopyOnClickSelectable(collection.Value.Id.ToString());
ImEx.CopyOnClickSelectable($"{collection.Value.Id}");
}
}
}

View file

@ -1,5 +1,6 @@
using Dalamud.Plugin.Services;
using Dalamud.Bindings.ImGui;
using ImSharp;
using Penumbra.Api.Api;
namespace Penumbra.Api.IpcTester;
@ -54,13 +55,13 @@ public class IpcTester(
}
}
internal static void DrawIntro(string label, string info)
internal static void DrawIntro(Utf8StringHandler<LabelStringHandlerBuffer> label, Utf8StringHandler<TextStringHandlerBuffer> info)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.TextUnformatted(label);
Im.Text(ref label);
ImGui.TableNextColumn();
ImGui.TextUnformatted(info);
Im.Text(ref info);
ImGui.TableNextColumn();
}

View file

@ -282,7 +282,7 @@ public class TemporaryIpcTester(
foreach (var mod in list)
{
ImGui.TableNextColumn();
ImGui.TextUnformatted(mod.Name.Text);
ImGui.TextUnformatted(mod.Name);
ImGui.TableNextColumn();
ImGui.TextUnformatted(mod.Priority.ToString());
ImGui.TableNextColumn();

View file

@ -3,7 +3,6 @@ using Dalamud.Interface.ImGuiNotification;
using Luna;
using Newtonsoft.Json;
using OtterGui.Filesystem;
using OtterGui.Widgets;
using Penumbra.Import.Structs;
using Penumbra.Interop.Services;
using Penumbra.Mods;
@ -23,6 +22,7 @@ public record PcpSettings
public bool AllowIpc { get; set; } = true;
public bool DisableHandling { get; set; } = false;
public string FolderName { get; set; } = "PCP";
public string PcpExtension { get; set; } = ".pcp";
}
[Serializable]
@ -93,19 +93,19 @@ public class Configuration : IPluginConfiguration, ISavable, IService
[JsonProperty(Order = int.MaxValue)]
public ISortMode<Mod> SortMode = ISortMode<Mod>.FoldersFirst;
public bool OpenFoldersByDefault { get; set; } = false;
public int SingleGroupRadioMax { get; set; } = 2;
public string DefaultImportFolder { get; set; } = string.Empty;
public string QuickMoveFolder1 { get; set; } = string.Empty;
public string QuickMoveFolder2 { get; set; } = string.Empty;
public string QuickMoveFolder3 { get; set; } = string.Empty;
public OtterGui.Classes.DoubleModifier DeleteModModifier { get; set; } = new(OtterGui.Classes.ModifierHotkey.Control, OtterGui.Classes.ModifierHotkey.Shift);
public OtterGui.Classes.DoubleModifier IncognitoModifier { get; set; } = new(OtterGui.Classes.ModifierHotkey.Control);
public bool PrintSuccessfulCommandsToChat { get; set; } = true;
public bool AutoDeduplicateOnImport { get; set; } = true;
public bool AutoReduplicateUiOnImport { get; set; } = true;
public bool UseFileSystemCompression { get; set; } = true;
public bool EnableHttpApi { get; set; } = true;
public bool OpenFoldersByDefault { get; set; } = false;
public int SingleGroupRadioMax { get; set; } = 2;
public string DefaultImportFolder { get; set; } = string.Empty;
public string QuickMoveFolder1 { get; set; } = string.Empty;
public string QuickMoveFolder2 { get; set; } = string.Empty;
public string QuickMoveFolder3 { get; set; } = string.Empty;
public DoubleModifier DeleteModModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
public DoubleModifier IncognitoModifier { get; set; } = new(ModifierHotkey.Control);
public bool PrintSuccessfulCommandsToChat { get; set; } = true;
public bool AutoDeduplicateOnImport { get; set; } = true;
public bool AutoReduplicateUiOnImport { get; set; } = true;
public bool UseFileSystemCompression { get; set; } = true;
public bool EnableHttpApi { get; set; } = true;
public bool MigrateImportedModelsToV6 { get; set; } = true;
public bool MigrateImportedMaterialsToLegacy { get; set; } = true;

View file

@ -1,7 +1,7 @@
namespace Penumbra;
namespace Penumbra;
public class DebugConfiguration
{
public static bool WriteImcBytesToLog = false;
public static bool UseSkinMaterialProcessing = true;
}
}

View file

@ -3,16 +3,14 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
using Penumbra.Util;
namespace Penumbra.Interop.Hooks.ResourceLoading;
public unsafe class FileReadService : IDisposable, Luna.IRequiredService
{
public FileReadService(PerformanceTracker performance, ResourceManagerService resourceManager, IGameInteropProvider interop)
public FileReadService(ResourceManagerService resourceManager, IGameInteropProvider interop)
{
_resourceManager = resourceManager;
_performance = performance;
interop.InitializeFromAttributes(this);
if (!HookOverrides.Instance.ResourceLoading.ReadSqPack)
_readSqPackHook.Enable();
@ -49,7 +47,6 @@ public unsafe class FileReadService : IDisposable, Luna.IRequiredService
_readSqPackHook.Dispose();
}
private readonly PerformanceTracker _performance;
private readonly ResourceManagerService _resourceManager;
private delegate byte ReadSqPackPrototype(nint resourceManager, SeFileDescriptor* pFileDesc, int priority, bool isSync);
@ -59,7 +56,6 @@ public unsafe class FileReadService : IDisposable, Luna.IRequiredService
private byte ReadSqPackDetour(nint resourceManager, SeFileDescriptor* fileDescriptor, int priority, bool isSync)
{
using var performance = _performance.Measure(PerformanceType.ReadSqPack);
byte? ret = null;
_lastFileThreadResourceManager.Value = resourceManager;
ReadSqPack?.Invoke(fileDescriptor, ref priority, ref isSync, ref ret);

View file

@ -8,21 +8,18 @@ using Penumbra.Interop.SafeHandles;
using Penumbra.Interop.Structs;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.Util;
using CSResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
namespace Penumbra.Interop.Hooks.ResourceLoading;
public unsafe class ResourceService : IDisposable, Luna.IRequiredService
{
private readonly PerformanceTracker _performance;
private readonly ResourceManagerService _resourceManager;
private readonly ThreadLocal<Utf8GamePath> _currentGetResourcePath = new(() => Utf8GamePath.Empty);
public ResourceService(PerformanceTracker performance, ResourceManagerService resourceManager, IGameInteropProvider interop)
public ResourceService(ResourceManagerService resourceManager, IGameInteropProvider interop)
{
_performance = performance;
_resourceManager = resourceManager;
interop.InitializeFromAttributes(this);
_incRefHook = interop.HookFromAddress<ResourceHandlePrototype>(
@ -108,7 +105,6 @@ public unsafe class ResourceService : IDisposable, Luna.IRequiredService
private ResourceHandle* GetResourceHandler(bool isSync, ResourceManager* resourceManager, ResourceCategory* categoryId,
ResourceType* resourceType, int* resourceHash, byte* path, GetResourceParameters* pGetResParams, byte isUnk, nint unk8, uint unk9)
{
using var performance = _performance.Measure(PerformanceType.GetResourceHandler);
if (!Utf8GamePath.FromPointer(path, MetaDataComputation.CiCrc32, out var gamePath))
{
Penumbra.Log.Error("[ResourceService] Could not create GamePath from resource path.");

View file

@ -10,7 +10,6 @@ using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using Penumbra.String;
using Penumbra.Util;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
@ -18,7 +17,6 @@ using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
namespace Penumbra.Interop.PathResolving;
public sealed unsafe class CollectionResolver(
PerformanceTracker performance,
IdentifiedCollectionCache cache,
IClientState clientState,
ObjectManager objects,
@ -38,7 +36,6 @@ public sealed unsafe class CollectionResolver(
/// </summary>
public ModCollection PlayerCollection()
{
using var performance1 = performance.Measure(PerformanceType.IdentifyCollection);
var gameObject = objects[0];
if (!gameObject.Valid)
return collectionManager.Active.ByType(CollectionType.Yourself)
@ -55,8 +52,6 @@ public sealed unsafe class CollectionResolver(
/// <summary> Identify the correct collection for a game object. </summary>
public ResolveData IdentifyCollection(GameObject* gameObject, bool useCache)
{
using var t = performance.Measure(PerformanceType.IdentifyCollection);
if (gameObject == null)
return collectionManager.Active.Default.ToResolveData();

View file

@ -5,13 +5,11 @@ using Penumbra.Collections.Manager;
using Penumbra.Interop.Hooks.ResourceLoading;
using Penumbra.Interop.Processing;
using Penumbra.String.Classes;
using Penumbra.Util;
namespace Penumbra.Interop.PathResolving;
public class PathResolver : IDisposable, Luna.IService
{
private readonly PerformanceTracker _performance;
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
private readonly ResourceLoader _loader;
@ -23,11 +21,10 @@ public class PathResolver : IDisposable, Luna.IService
private readonly CollectionResolver _collectionResolver;
private readonly GamePathPreProcessService _preprocessor;
public PathResolver(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ResourceLoader loader,
public PathResolver(Configuration config, CollectionManager collectionManager, ResourceLoader loader,
SubfileHelper subfileHelper, PathState pathState, MetaState metaState, CollectionResolver collectionResolver, GameState gameState,
GamePathPreProcessService preprocessor)
{
_performance = performance;
_config = config;
_collectionManager = collectionManager;
_subfileHelper = subfileHelper;
@ -75,10 +72,6 @@ public class PathResolver : IDisposable, Luna.IService
// always use the default resolver for now,
// except that common/font is conceptually more UI.
ResourceCategory.Common => path.Path.StartsWith("common/font"u8) ? ResolveUi(path) : DefaultResolver(path),
ResourceCategory.BgCommon => DefaultResolver(path),
ResourceCategory.Bg => DefaultResolver(path),
ResourceCategory.Cut => DefaultResolver(path),
ResourceCategory.Music => DefaultResolver(path),
_ => DefaultResolver(path),
}
};
@ -99,7 +92,6 @@ public class PathResolver : IDisposable, Luna.IService
public (FullPath?, ResolveData) Resolve(Utf8GamePath gamePath, ResourceType type)
{
using var performance = _performance.Measure(PerformanceType.CharacterResolver);
// Check if the path was marked for a specific collection,
// or if it is a file loaded by a material, and if we are currently in a material load,
// or if it is a face decal path and the current mod collection is set.

View file

@ -1,4 +1,3 @@
using OtterGui.Classes;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
@ -15,7 +14,7 @@ public record struct AppliedModData(
public interface IMod
{
LowerString Name { get; }
public string Name { get; }
public int Index { get; }
public ModPriority Priority { get; }

View file

@ -479,8 +479,8 @@ public class ModMerger : IDisposable, IService
private void OnSelectionChange(in ModSelection.Arguments arguments)
{
if (OptionGroupName == "Merges" && OptionName.Length == 0 || OptionName == arguments.OldSelection?.Name.Text)
OptionName = arguments.NewSelection?.Name.Text ?? string.Empty;
if (OptionGroupName == "Merges" && OptionName.Length == 0 || OptionName == arguments.OldSelection?.Name)
OptionName = arguments.NewSelection?.Name ?? string.Empty;
if (MergeToMod == arguments.NewSelection)
MergeToMod = null;

View file

@ -1,5 +1,4 @@
using Dalamud.Utility;
using OtterGui.Classes;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.GameData.Structs;
@ -39,8 +38,8 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
string? website, params string[] tags)
{
var mod = new Mod(directory);
mod.Name = name.IsNullOrEmpty() ? mod.Name : new LowerString(name);
mod.Author = author != null ? new LowerString(author) : mod.Author;
mod.Name = name.IsNullOrEmpty() ? mod.Name : name;
mod.Author = author ?? mod.Author;
mod.Description = description ?? mod.Description;
mod.Version = version ?? mod.Version;
mod.Website = website ?? mod.Website;
@ -50,13 +49,13 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
public void ChangeModName(Mod mod, string newName)
{
if (mod.Name.Text == newName)
if (mod.Name == newName)
return;
var oldName = mod.Name;
mod.Name = newName;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Name, mod, oldName.Text));
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Name, mod, oldName));
}
public void ChangeModAuthor(Mod mod, string newAuthor)
@ -97,7 +96,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Website = newWebsite;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Website, mod, null));
}
}
public void ChangeRequiredFeatures(Mod mod, FeatureFlags flags)
{

View file

@ -3,6 +3,7 @@ using Luna;
using OtterGui.Filesystem;
using Penumbra.Communication;
using Penumbra.Services;
using FileSystemChangeType = OtterGui.Filesystem.FileSystemChangeType;
namespace Penumbra.Mods.Manager;
@ -82,9 +83,9 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, ISer
if (!arguments.Type.HasFlag(ModDataChangeType.Name) || arguments.OldName == null || !TryGetValue(arguments.Mod, out var leaf))
return;
var old = arguments.OldName.FixName();
if (old == leaf.Name || leaf.Name.IsDuplicateName(out var baseName, out _) && baseName == old)
RenameWithDuplicates(leaf, arguments.Mod.Name.Text);
var old = Extensions.FixName(arguments.OldName);
if (old == leaf.Name || Extensions.IsDuplicateName(leaf.Name, out var baseName, out _) && baseName == old)
RenameWithDuplicates(leaf, arguments.Mod.Name);
}
// Update the filesystem if a mod has been added or removed.
@ -107,7 +108,7 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, ISer
NotificationType.Warning);
}
CreateDuplicateLeaf(parent, arguments.Mod.Name.Text, arguments.Mod);
CreateDuplicateLeaf(parent, arguments.Mod.Name, arguments.Mod);
break;
case ModPathChangeType.Deleted:
if (TryGetValue(arguments.Mod, out var leaf))
@ -128,7 +129,7 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, ISer
=> mod.ModPath.Name;
private static string ModToName(Mod mod)
=> mod.Name.Text.FixName();
=> Extensions.FixName(mod.Name);
// Return whether a mod has a custom path or is just a numbered default path.
public static bool ModHasDefaultPath(Mod mod, string fullPath)

View file

@ -9,7 +9,7 @@ public class ModCombo(Func<IReadOnlyList<Mod>> generator) : FilterComboCache<Mod
=> Items[globalIndex].Name.Contains(filter);
protected override string ToString(Mod obj)
=> obj.Name.Text;
=> obj.Name;
}
public class ModStorage : IReadOnlyList<Mod>
@ -60,7 +60,7 @@ public class ModStorage : IReadOnlyList<Mod>
/// Mods are removed when they are deleted or when they are toggled in any collection.
/// Also gets cleared on mod rediscovery.
/// </summary>
private readonly HashSet<Mod> _newMods = new();
private readonly HashSet<Mod> _newMods = [];
public bool IsNew(Mod mod)
=> _newMods.Contains(mod);

View file

@ -1,144 +1,143 @@
using Luna;
using OtterGui.Classes;
using Penumbra.GameData.Data;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.String.Classes;
namespace Penumbra.Mods;
[Flags]
public enum FeatureFlags : ulong
{
None = 0,
Atch = 1ul << 0,
Shp = 1ul << 1,
Atr = 1ul << 2,
Invalid = 1ul << 62,
}
public sealed class Mod : IMod
{
public static readonly TemporaryMod ForcedFiles = new()
{
Name = "Forced Files",
Index = -1,
Priority = ModPriority.MaxValue,
};
// Main Data
public DirectoryInfo ModPath { get; internal set; }
public string Identifier
=> Index >= 0 ? ModPath.Name : Name;
public int Index { get; internal set; } = -1;
public bool IsTemporary
=> Index < 0;
/// <summary>Unused if Index is less than 0 but used for special temporary mods.</summary>
public ModPriority Priority
=> ModPriority.Default;
IReadOnlyList<IModGroup> IMod.Groups
=> Groups;
internal Mod(DirectoryInfo modPath)
{
ModPath = modPath;
Default = new DefaultSubMod(this);
}
public override string ToString()
=> Name.Text;
// Meta Data
public LowerString Name { get; internal set; } = "New Mod";
public LowerString Author { get; internal set; } = LowerString.Empty;
public string Description { get; internal set; } = string.Empty;
public string Version { get; internal set; } = string.Empty;
public string Website { get; internal set; } = string.Empty;
public string Image { get; internal set; } = string.Empty;
public IReadOnlyList<string> ModTags { get; internal set; } = [];
public HashSet<CustomItemId> DefaultPreferredItems { get; internal set; } = [];
public FeatureFlags RequiredFeatures { get; internal set; } = 0;
// Local Data
public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
public IReadOnlyList<string> LocalTags { get; internal set; } = [];
public string Note { get; internal set; } = string.Empty;
public HashSet<CustomItemId> PreferredChangedItems { get; internal set; } = [];
public bool Favorite { get; internal set; }
// Options
public readonly DefaultSubMod Default;
public readonly List<IModGroup> Groups = [];
/// <summary> Compute the required feature flags for this mod. </summary>
public FeatureFlags ComputeRequiredFeatures()
{
var flags = FeatureFlags.None;
foreach (var option in AllDataContainers)
{
if (option.Manipulations.Atch.Count > 0)
flags |= FeatureFlags.Atch;
if (option.Manipulations.Atr.Count > 0)
flags |= FeatureFlags.Atr;
if (option.Manipulations.Shp.Count > 0)
flags |= FeatureFlags.Shp;
}
return flags;
}
public AppliedModData GetData(ModSettings? settings = null)
{
if (settings is not { Enabled: true })
return AppliedModData.Empty;
var dictRedirections = new Dictionary<Utf8GamePath, FullPath>(TotalFileCount);
var setManips = new MetaDictionary();
foreach (var (groupIndex, group) in Groups.Index().Reverse().OrderByDescending(g => g.Item.Priority))
{
var config = settings.Settings[groupIndex];
group.AddData(config, dictRedirections, setManips);
}
Default.AddTo(dictRedirections, setManips);
return new AppliedModData(dictRedirections, setManips);
}
public IEnumerable<IModDataContainer> AllDataContainers
=> Groups.SelectMany(o => o.DataContainers).Prepend(Default);
public List<FullPath> FindUnusedFiles()
{
var modFiles = AllDataContainers.SelectMany(o => o.Files)
.Select(p => p.Value)
.ToHashSet();
return ModPath.EnumerateDirectories()
.Where(d => !d.IsHidden())
.SelectMany(FileExtensions.EnumerateNonHiddenFiles)
.Select(f => new FullPath(f))
.Where(f => !modFiles.Contains(f))
.ToList();
}
// Cache
public readonly SortedList<string, IIdentifiedObjectData> ChangedItems = new();
public string LowerChangedItemsString { get; internal set; } = string.Empty;
public string AllTagsLower { get; internal set; } = string.Empty;
public int TotalFileCount { get; internal set; }
public int TotalSwapCount { get; internal set; }
public int TotalManipulations { get; internal set; }
public ushort LastChangedItemsUpdate { get; internal set; }
public bool HasOptions { get; internal set; }
}
using Luna;
using Penumbra.GameData.Data;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.String.Classes;
namespace Penumbra.Mods;
[Flags]
public enum FeatureFlags : ulong
{
None = 0,
Atch = 1ul << 0,
Shp = 1ul << 1,
Atr = 1ul << 2,
Invalid = 1ul << 62,
}
public sealed class Mod : IMod
{
public static readonly TemporaryMod ForcedFiles = new()
{
Name = "Forced Files",
Index = -1,
Priority = ModPriority.MaxValue,
};
// Main Data
public DirectoryInfo ModPath { get; internal set; }
public string Identifier
=> Index >= 0 ? ModPath.Name : Name;
public int Index { get; internal set; } = -1;
public bool IsTemporary
=> Index < 0;
/// <summary>Unused if Index is less than 0 but used for special temporary mods.</summary>
public ModPriority Priority
=> ModPriority.Default;
IReadOnlyList<IModGroup> IMod.Groups
=> Groups;
internal Mod(DirectoryInfo modPath)
{
ModPath = modPath;
Default = new DefaultSubMod(this);
}
public override string ToString()
=> Name;
// Meta Data
public string Name { get; internal set; } = "New Mod";
public string Author { get; internal set; } = string.Empty;
public string Description { get; internal set; } = string.Empty;
public string Version { get; internal set; } = string.Empty;
public string Website { get; internal set; } = string.Empty;
public string Image { get; internal set; } = string.Empty;
public IReadOnlyList<string> ModTags { get; internal set; } = [];
public HashSet<CustomItemId> DefaultPreferredItems { get; internal set; } = [];
public FeatureFlags RequiredFeatures { get; internal set; } = 0;
// Local Data
public long ImportDate { get; internal set; } = DateTimeOffset.UnixEpoch.ToUnixTimeMilliseconds();
public IReadOnlyList<string> LocalTags { get; internal set; } = [];
public string Note { get; internal set; } = string.Empty;
public HashSet<CustomItemId> PreferredChangedItems { get; internal set; } = [];
public bool Favorite { get; internal set; }
// Options
public readonly DefaultSubMod Default;
public readonly List<IModGroup> Groups = [];
/// <summary> Compute the required feature flags for this mod. </summary>
public FeatureFlags ComputeRequiredFeatures()
{
var flags = FeatureFlags.None;
foreach (var option in AllDataContainers)
{
if (option.Manipulations.Atch.Count > 0)
flags |= FeatureFlags.Atch;
if (option.Manipulations.Atr.Count > 0)
flags |= FeatureFlags.Atr;
if (option.Manipulations.Shp.Count > 0)
flags |= FeatureFlags.Shp;
}
return flags;
}
public AppliedModData GetData(ModSettings? settings = null)
{
if (settings is not { Enabled: true })
return AppliedModData.Empty;
var dictRedirections = new Dictionary<Utf8GamePath, FullPath>(TotalFileCount);
var setManips = new MetaDictionary();
foreach (var (groupIndex, group) in Groups.Index().Reverse().OrderByDescending(g => g.Item.Priority))
{
var config = settings.Settings[groupIndex];
group.AddData(config, dictRedirections, setManips);
}
Default.AddTo(dictRedirections, setManips);
return new AppliedModData(dictRedirections, setManips);
}
public IEnumerable<IModDataContainer> AllDataContainers
=> Groups.SelectMany(o => o.DataContainers).Prepend(Default);
public List<FullPath> FindUnusedFiles()
{
var modFiles = AllDataContainers.SelectMany(o => o.Files)
.Select(p => p.Value)
.ToHashSet();
return ModPath.EnumerateDirectories()
.Where(d => !d.IsHidden())
.SelectMany(FileExtensions.EnumerateNonHiddenFiles)
.Select(f => new FullPath(f))
.Where(f => !modFiles.Contains(f))
.ToList();
}
// Cache
public readonly SortedList<string, IIdentifiedObjectData> ChangedItems = new();
public string LowerChangedItemsString { get; internal set; } = string.Empty;
public string AllTagsLower { get; internal set; } = string.Empty;
public int TotalFileCount { get; internal set; }
public int TotalSwapCount { get; internal set; }
public int TotalManipulations { get; internal set; }
public ushort LastChangedItemsUpdate { get; internal set; }
public bool HasOptions { get; internal set; }
}

View file

@ -23,7 +23,7 @@ public partial class ModCreator(
Configuration config,
ModDataEditor dataEditor,
MetaFileManager metaFileManager,
GamePathParser gamePathParser) : Luna.IService
GamePathParser gamePathParser) : IService
{
public const FeatureFlags SupportedFeatures = FeatureFlags.Atch | FeatureFlags.Shp | FeatureFlags.Atr;
public readonly Configuration Config = config;
@ -139,7 +139,7 @@ public partial class ModCreator(
name = "_";
var newModFolderBase = NewOptionDirectory(outDirectory, name, onlyAscii);
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
var newModFolder = FileSystemUtility.ObtainUniqueFile(newModFolderBase.FullName);
if (newModFolder.Length == 0)
throw new IOException("Could not create mod folder: too many folders of the same name exist.");
@ -236,7 +236,7 @@ public partial class ModCreator(
public static DirectoryInfo? NewSubFolderName(DirectoryInfo parentFolder, string subFolderName, bool onlyAscii)
{
var newModFolderBase = NewOptionDirectory(parentFolder, subFolderName, onlyAscii);
var newModFolder = newModFolderBase.FullName.ObtainUniqueFile();
var newModFolder = FileSystemUtility.ObtainUniqueFile(newModFolderBase.FullName);
return newModFolder.Length == 0 ? null : new DirectoryInfo(newModFolder);
}

View file

@ -1,4 +1,3 @@
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.Interop.PathResolving;
using Penumbra.Meta.Manipulations;
@ -14,7 +13,7 @@ namespace Penumbra.Mods;
public class TemporaryMod : IMod
{
public LowerString Name { get; init; } = LowerString.Empty;
public string Name { get; init; } = string.Empty;
public int Index { get; init; } = -2;
public ModPriority Priority { get; init; } = ModPriority.MaxValue;

View file

@ -22,7 +22,8 @@ namespace Penumbra.Services;
public class PcpService : IApiService, IDisposable
{
public const string Extension = ".pcp";
public string Extension
=> _config.PcpSettings.PcpExtension;
private readonly Configuration _config;
private readonly SaveService _files;
@ -160,7 +161,7 @@ public class PcpService : IApiService, IDisposable
public void Dispose()
=> _communicator.ModPathChanged.Unsubscribe(OnModPathChange);
public async Task<(bool, string)> CreatePcp(ObjectIndex objectIndex, string note = "", CancellationToken cancel = default)
public async Task<(bool, string)> CreatePcp(ObjectIndex objectIndex, string? modPath, string note = "", CancellationToken cancel = default)
{
try
{
@ -188,7 +189,7 @@ public class PcpService : IApiService, IDisposable
var modDirectory = CreateMod(identifier, note, time);
await CreateDefaultMod(modDirectory, meta, tree, cancel);
await CreateCollectionInfo(modDirectory, objectIndex, identifier, note, time, cancel);
var file = ZipUp(modDirectory);
var file = ZipUp(modDirectory, modPath, Extension);
return (true, file);
}
catch (Exception ex)
@ -197,12 +198,15 @@ public class PcpService : IApiService, IDisposable
}
}
private static string ZipUp(DirectoryInfo directory)
private static string ZipUp(DirectoryInfo directory, string? path, string extension)
{
var fileName = directory.FullName + Extension;
ZipFile.CreateFromDirectory(directory.FullName, fileName, CompressionLevel.Optimal, false);
if (path is null)
path = directory.FullName + extension;
else if (Path.GetExtension(path.AsSpan()).IsEmpty)
path += extension;
ZipFile.CreateFromDirectory(directory.FullName, path, CompressionLevel.Optimal, false);
directory.Delete(true);
return fileName;
return path;
}
private async Task CreateCollectionInfo(DirectoryInfo directory, ObjectIndex index, ActorIdentifier actor, string note, DateTime time,
@ -220,7 +224,8 @@ public class PcpService : IApiService, IDisposable
if (note.Length > 0)
cancel.ThrowIfCancellationRequested();
if (_config.PcpSettings.AllowIpc)
await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(new PcpCreation.Arguments(jObj, index.Index, directory.FullName)));
await _framework.Framework.RunOnFrameworkThread(()
=> _communicator.PcpCreation.Invoke(new PcpCreation.Arguments(jObj, index.Index, directory.FullName)));
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);
@ -233,17 +238,22 @@ public class PcpService : IApiService, IDisposable
{
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 actorName = actor.ToName();
var modName = ModName(actorName, note, time);
var authorName = _actors.GetCurrentPlayer().ToName();
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}.");
}
public static string ModName(string actorName, string note, DateTime time)
{
var suffix = note.Length > 0
? note
: time.ToString("yyyy-MM-ddTHH_mm", CultureInfo.InvariantCulture);
return $"{actorName} - {suffix}";
}
private async Task CreateDefaultMod(DirectoryInfo modDirectory, MetaDictionary meta, ResourceTree tree,
CancellationToken cancel = default)
{

View file

@ -75,9 +75,9 @@ public class ItemSwapTab : IDisposable, ITab, IUiService
if (mod == _mod && settings == _modSettings)
return;
var oldDefaultName = $"{_mod?.Name.Text ?? "Unknown"} (Swapped)";
var oldDefaultName = $"{_mod?.Name ?? "Unknown"} (Swapped)";
if (_newModName.Length == 0 || oldDefaultName == _newModName)
_newModName = $"{mod.Name.Text} (Swapped)";
_newModName = $"{mod.Name} (Swapped)";
_mod = mod;
_modSettings = settings;
@ -163,7 +163,7 @@ public class ItemSwapTab : IDisposable, ITab, IUiService
: null;
var ret = base.DrawSelectable(globalIdx, selected);
if (inCollection.Count > 0)
ImUtf8.HoverTooltip(string.Join('\n', inCollection.Select(m => m.Name.Text)));
ImUtf8.HoverTooltip(string.Join('\n', inCollection.Select(m => m.Name)));
return ret;
}
@ -305,7 +305,7 @@ public class ItemSwapTab : IDisposable, ITab, IUiService
private string OriginalAuthor()
{
if (_mod!.Author.IsEmpty || _mod!.Author.Text is "TexTools User" or DefaultTexToolsData.Author)
if (_mod!.Author.Length is 0 || _mod!.Author is "TexTools User" or DefaultTexToolsData.Author)
return ".";
return $" by {_mod!.Author}.";
@ -313,11 +313,11 @@ public class ItemSwapTab : IDisposable, ITab, IUiService
private string CreateAuthor()
{
if (_mod!.Author.IsEmpty)
if (_mod!.Author.Length is 0)
return _config.DefaultModAuthor;
if (_mod!.Author.Text == _config.DefaultModAuthor)
if (_mod!.Author == _config.DefaultModAuthor)
return _config.DefaultModAuthor;
if (_mod!.Author.Text is "TexTools User" or DefaultTexToolsData.Author)
if (_mod!.Author is "TexTools User" or DefaultTexToolsData.Author)
return _config.DefaultModAuthor;
if (_config.DefaultModAuthor is DefaultTexToolsData.Author)
return _mod!.Author;

View file

@ -1,7 +1,7 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using Luna;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.Widget.Editors;

View file

@ -8,7 +8,6 @@ using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
using Penumbra.UI.Classes;
using Notification = OtterGui.Classes.Notification;
namespace Penumbra.UI.AdvancedWindow;

View file

@ -1,7 +1,7 @@
using Dalamud.Interface;
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Mods.Editor;
@ -15,7 +15,7 @@ public partial class ModEditWindow
{
private readonly HashSet<FileRegistry> _selectedFiles = new(256);
private readonly HashSet<Utf8GamePath> _cutPaths = [];
private LowerString _fileFilter = LowerString.Empty;
private string _fileFilter = string.Empty;
private bool _showGamePaths = true;
private string _gamePathEdit = string.Empty;
private int _fileIdx = -1;
@ -23,12 +23,12 @@ public partial class ModEditWindow
private int _folderSkip;
private bool _overviewMode;
private LowerString _fileOverviewFilter1 = LowerString.Empty;
private LowerString _fileOverviewFilter2 = LowerString.Empty;
private LowerString _fileOverviewFilter3 = LowerString.Empty;
private string _fileOverviewFilter1 = string.Empty;
private string _fileOverviewFilter2 = string.Empty;
private string _fileOverviewFilter3 = string.Empty;
private bool CheckFilter(FileRegistry registry)
=> _fileFilter.IsEmpty || registry.File.FullName.Contains(_fileFilter.Lower, StringComparison.OrdinalIgnoreCase);
=> _fileFilter.Length is 0 || registry.File.FullName.Contains(_fileFilter, StringComparison.OrdinalIgnoreCase);
private bool CheckFilter((int, FileRegistry) p)
=> CheckFilter(p.Item2);
@ -105,9 +105,9 @@ public partial class ModEditWindow
}
bool Filter((string, string, string, uint) data)
=> _fileOverviewFilter1.IsContained(data.Item1)
&& _fileOverviewFilter2.IsContained(data.Item2)
&& _fileOverviewFilter3.IsContained(data.Item3);
=> data.Item1.Contains(_fileOverviewFilter1, StringComparison.OrdinalIgnoreCase)
&& data.Item2.Contains(_fileOverviewFilter2, StringComparison.OrdinalIgnoreCase)
&& data.Item3.Contains(_fileOverviewFilter3, StringComparison.OrdinalIgnoreCase);
var end = ImGuiClip.FilteredClippedDraw(files, skips, Filter, DrawLine);
ImGuiClip.DrawEndDummy(end, height);
@ -252,7 +252,7 @@ public partial class ModEditWindow
}
}
private void PrintGamePath(int i, int j, FileRegistry registry, IModDataContainer subMod, Utf8GamePath gamePath)
private void PrintGamePath(int i, int j, FileRegistry registry, IModDataContainer _, Utf8GamePath gamePath)
{
using var id = ImRaii.PushId(j);
ImGui.TableNextColumn();
@ -290,16 +290,16 @@ public partial class ModEditWindow
{
ImGui.SameLine();
ImGui.SetCursorPosX(pos);
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
ImUtf8.HoverTooltip("The game path and the file do not have the same extension."u8);
}
}
private void PrintNewGamePath(int i, FileRegistry registry, IModDataContainer subMod)
private void PrintNewGamePath(int i, FileRegistry registry, IModDataContainer _)
{
var tmp = _fileIdx == i && _pathIdx == -1 ? _gamePathEdit : string.Empty;
var pos = ImGui.GetCursorPosX() - ImGui.GetFrameHeight();
@ -333,9 +333,9 @@ public partial class ModEditWindow
{
ImGui.SameLine();
ImGui.SetCursorPosX(pos);
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGuiUtil.TextColored(0xFF00B0B0, FontAwesomeIcon.ExclamationCircle.ToIconString());
}
ImUtf8.HoverTooltip("The game path and the file do not have the same extension."u8);
@ -404,7 +404,7 @@ public partial class ModEditWindow
private void DrawFileManagementNormal()
{
ImGui.SetNextItemWidth(250 * UiHelpers.Scale);
LowerString.InputWithHint("##filter", "Filter paths...", ref _fileFilter, Utf8GamePath.MaxGamePathLength);
Im.Input.Text("##filter"u8, ref _fileFilter, "Filter paths..."u8);
ImGui.SameLine();
ImGui.Checkbox("Show Game Paths", ref _showGamePaths);
ImGui.SameLine();
@ -437,12 +437,12 @@ public partial class ModEditWindow
var width = ImGui.GetContentRegionAvail().X / 8;
ImGui.SetNextItemWidth(width * 3);
LowerString.InputWithHint("##fileFilter", "Filter file...", ref _fileOverviewFilter1, Utf8GamePath.MaxGamePathLength);
Im.Input.Text("##fileFilter"u8, ref _fileOverviewFilter1, "Filter file..."u8);
ImGui.SameLine();
ImGui.SetNextItemWidth(width * 3);
LowerString.InputWithHint("##pathFilter", "Filter path...", ref _fileOverviewFilter2, Utf8GamePath.MaxGamePathLength);
Im.Input.Text("##pathFilter"u8, ref _fileOverviewFilter2, "Filter path..."u8);
ImGui.SameLine();
ImGui.SetNextItemWidth(width * 2);
LowerString.InputWithHint("##optionFilter", "Filter option...", ref _fileOverviewFilter3, Utf8GamePath.MaxGamePathLength);
Im.Input.Text("##optionFilter"u8, ref _fileOverviewFilter3, "Filter option..."u8);
}
}

View file

@ -1,7 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Lumina.Data.Parsing;
using Luna.Widgets;
using Luna;
using OtterGui;
using OtterGui.Custom;
using OtterGui.Raii;

View file

@ -1,6 +1,6 @@
using Dalamud.Utility;
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.ShaderStructs;
using Penumbra.GameData.Interop;

View file

@ -26,7 +26,6 @@ using Penumbra.String.Classes;
using Penumbra.UI.AdvancedWindow.Materials;
using Penumbra.UI.AdvancedWindow.Meta;
using Penumbra.UI.Classes;
using Penumbra.Util;
using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor;
namespace Penumbra.UI.AdvancedWindow;
@ -35,7 +34,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
{
private const string WindowBaseLabel = "###SubModEdit";
private readonly PerformanceTracker _performance;
private readonly ModEditor _editor;
private readonly Configuration _config;
private readonly ItemSwapTab _itemSwapTab;
@ -129,8 +127,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
if (IsLoading)
return;
using var performance = _performance.Measure(PerformanceType.UiAdvancedWindow);
var sb = new StringBuilder(256);
var redirections = 0;
@ -195,8 +191,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
public override void Draw()
{
using var performance = _performance.Measure(PerformanceType.UiAdvancedWindow);
if (!_config.Ephemeral.AdvancedEditingOpen)
{
_config.Ephemeral.AdvancedEditingOpen = true;
@ -620,7 +614,7 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
return ret;
}
public ModEditWindow(PerformanceTracker performance, FileDialogService fileDialog, ItemSwapTab itemSwapTab, IDataManager gameData,
public ModEditWindow(FileDialogService fileDialog, ItemSwapTab itemSwapTab, IDataManager gameData,
Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager,
ActiveCollections activeCollections, ModMergeTab modMergeTab,
CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager,
@ -629,7 +623,6 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
MtrlTabFactory mtrlTabFactory, ModSelection selection)
: base(WindowBaseLabel)
{
_performance = performance;
_itemSwapTab = itemSwapTab;
_gameData = gameData;
_config = config;

View file

@ -58,11 +58,11 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
if (size - textSize < minComboSize)
{
ImUtf8.Text("selected mod"u8, ColorId.FolderLine.Value());
ImUtf8.HoverTooltip(modMerger.MergeFromMod!.Name.Text);
ImUtf8.HoverTooltip(modMerger.MergeFromMod!.Name);
}
else
{
ImUtf8.Text(modMerger.MergeFromMod!.Name.Text, ColorId.FolderLine.Value());
ImUtf8.Text(modMerger.MergeFromMod!.Name, ColorId.FolderLine.Value());
}
ImGui.SameLine(0, 0);
@ -121,7 +121,7 @@ public class ModMergeTab(ModMerger modMerger) : Luna.IUiService
private void DrawCombo(float width)
{
_modCombo.Draw("##ModSelection", _modCombo.CurrentSelection?.Name.Text ?? "Select the target Mod...", string.Empty, width,
_modCombo.Draw("##ModSelection", _modCombo.CurrentSelection?.Name ?? "Select the target Mod...", string.Empty, width,
ImGui.GetTextLineHeight());
modMerger.MergeToMod = _modCombo.CurrentSelection;
}

View file

@ -103,9 +103,8 @@ public class ResourceTreeViewer(
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 =>
pcpService.CreatePcp((ObjectIndex)tree.GameObjectIndex, null, _note).ContinueWith(t =>
{
var (success, text) = t.Result;
if (success)
@ -116,6 +115,27 @@ public class ResourceTreeViewer(
_note = string.Empty;
}
ImUtf8.SameLineInner();
if (ImUtf8.ButtonEx("Export To..."u8,
"Note that this recomputes the current data of the actor if it still exists, and does not use the cached data."u8))
fileDialog.OpenSavePicker("Export PCP...", $"Penumbra Mod Packs{{.pcp,.pmp}},{config.PcpSettings.PcpExtension},Any File{{.*}}", PcpService.ModName(tree.Name, _note, DateTime.Now),
config.PcpSettings.PcpExtension,
(selected, path) =>
{
if (!selected)
return;
pcpService.CreatePcp((ObjectIndex)tree.GameObjectIndex, path, _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;
}, config.ExportDirectory, false);
ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
ImUtf8.InputText("##note"u8, ref _note, "Export note..."u8);
@ -434,7 +454,7 @@ public class ResourceTreeViewer(
_writableCache.Add(resourceNode.FullPath, writable);
}
if (ImUtf8.IconButton(FontAwesomeIcon.Save, "Export this file."u8, buttonSize,
resourceNode.FullPath.FullName.Length is 0 || writable is null))
{
@ -442,7 +462,8 @@ public class ResourceTreeViewer(
var ext = resourceNode.PossibleGamePaths.Length == 1
? Path.GetExtension(resourceNode.GamePath.ToString())
: Path.GetExtension(fullPathStr);
fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr), ext,
fileDialog.OpenSavePicker($"Export {Path.GetFileName(fullPathStr)} to...", ext, Path.GetFileNameWithoutExtension(fullPathStr),
ext,
(success, name) =>
{
if (!success)
@ -458,7 +479,7 @@ public class ResourceTreeViewer(
}
}, null, false);
}
drawActions(resourceNode, writable, new Vector2(frameHeight));
}
}
@ -524,7 +545,7 @@ public class ResourceTreeViewer(
Visible = 1,
DescendentsOnly = 2,
}
private record RawFileWritable(string Path) : IWritable
{
public bool Valid

View file

@ -6,7 +6,6 @@ using Dalamud.Utility;
using Dalamud.Bindings.ImGui;
using Lumina.Data.Files;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Enums;
@ -87,10 +86,10 @@ public class ChangedItemDrawer : IDisposable, Luna.IUiService
}
/// <summary> Check if a changed item should be drawn based on its category. </summary>
public bool FilterChangedItem(string name, IIdentifiedObjectData data, LowerString filter)
public bool FilterChangedItem(string name, IIdentifiedObjectData data, string filter)
=> (_config.Ephemeral.ChangedItemFilter == ChangedItemFlagExtensions.AllFlags
|| _config.Ephemeral.ChangedItemFilter.HasFlag(data.GetIcon().ToFlag()))
&& (filter.IsEmpty || !data.IsFilteredOut(name, filter.Text));
&& (filter.Length is 0 || !data.IsFilteredOut(name, filter));
/// <summary> Draw the icon corresponding to the category of a changed item. </summary>
public void DrawCategoryIcon(IIdentifiedObjectData data, float height)

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Bindings.ImGui;
using Luna.Widgets;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
@ -10,6 +9,7 @@ using Penumbra.Services;
using Penumbra.UI.Classes;
using Penumbra.UI.Tabs;
using Penumbra.Util;
using Window = Dalamud.Interface.Windowing.Window;
namespace Penumbra.UI;
@ -17,19 +17,17 @@ public sealed class ConfigWindow : Window, Luna.IUiService
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly Configuration _config;
private readonly PerformanceTracker _tracker;
private readonly ValidityChecker _validityChecker;
private Penumbra? _penumbra;
private ConfigTabBar _configTabs = null!;
private string? _lastException;
public ConfigWindow(PerformanceTracker tracker, IDalamudPluginInterface pi, Configuration config, ValidityChecker checker,
public ConfigWindow(IDalamudPluginInterface pi, Configuration config, ValidityChecker checker,
TutorialService tutorial)
: base(GetLabel(checker))
{
_pluginInterface = pi;
_config = config;
_tracker = tracker;
_validityChecker = checker;
RespectCloseHotkey = true;
@ -68,7 +66,6 @@ public sealed class ConfigWindow : Window, Luna.IUiService
public override void Draw()
{
using var timer = _tracker.Measure(PerformanceType.UiMainWindow);
UiHelpers.SetupCommonSizes();
try
{

View file

@ -221,12 +221,12 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
using var c = ImRaii.PushColor(ImGuiCol.Text, state.Color.Tinted(state.Tint))
.Push(ImGuiCol.HeaderHovered, 0x4000FFFF, leaf.Value.Favorite);
using var id = ImUtf8.PushId(leaf.Value.Index);
ImUtf8.TreeNode(leaf.Value.Name.Text, flags).Dispose();
ImUtf8.TreeNode(leaf.Value.Name, flags).Dispose();
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
{
_modManager.SetKnown(leaf.Value);
var (setting, collection) = _collectionManager.Active.Current.GetActualSettings(leaf.Value.Index);
if (_config.DeleteModModifier.ForcedModifier(new OtterGui.Classes.DoubleModifier(OtterGui.Classes.ModifierHotkey.Control, OtterGui.Classes.ModifierHotkey.Shift)).IsActive())
if (_config.DeleteModModifier.ForcedModifier(new DoubleModifier(ModifierHotkey.Control, ModifierHotkey.Shift)).IsActive())
{
// Delete temporary settings if they exist, regardless of mode, or set to inheriting if none exist.
if (_collectionManager.Active.Current.GetTempSettings(leaf.Value.Index) is not null)
@ -397,7 +397,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
private void RenameMod(ModFileSystem.Leaf leaf)
{
ImGui.Separator();
var currentName = leaf.Value.Name.Text;
var currentName = leaf.Value.Name;
if (ImGui.IsWindowAppearing())
ImGui.SetKeyboardFocusHere(0);
ImUtf8.Text("Rename Mod:"u8);
@ -411,7 +411,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
}
private void DeleteModButton(Vector2 size)
=> DeleteSelectionButton(size, _config.DeleteModModifier, "mod", "mods", _modManager.DeleteMod);
=> DeleteSelectionButton(size, Unsafe.BitCast<DoubleModifier, OtterGui.Classes.DoubleModifier>(_config.DeleteModModifier), "mod", "mods", _modManager.DeleteMod);
private void AddHelpButton(Vector2 size)
{
@ -471,7 +471,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
ImUtf8.BulletText("Middle-click a mod to disable it if it is enabled or enable it if it is disabled."u8);
indent.Push();
ImUtf8.BulletText(
$"Holding {_config.DeleteModModifier.ForcedModifier(new OtterGui.Classes.DoubleModifier(OtterGui.Classes.ModifierHotkey.Control, OtterGui.Classes.ModifierHotkey.Shift))} while middle-clicking lets it inherit, discarding settings.");
$"Holding {_config.DeleteModModifier.ForcedModifier(new DoubleModifier(ModifierHotkey.Control, ModifierHotkey.Shift))} while middle-clicking lets it inherit, discarding settings.");
indent.Pop(1);
ImUtf8.BulletText("Right-click a mod to enter its sort order, which is its name by default, possibly with a duplicate number."u8);
indent.Push();

View file

@ -2,7 +2,6 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Services;
using OtterGui.Text;
using OtterGui.Widgets;
@ -118,7 +117,7 @@ public class ModPanelChangedItemsTab(
{
foreach (var (s, i) in _lastSelected.ChangedItems)
{
if (drawer.FilterChangedItem(s, i, LowerString.Empty))
if (drawer.FilterChangedItem(s, i, string.Empty))
Data.Add(Container.Single(s, i));
}
@ -132,7 +131,7 @@ public class ModPanelChangedItemsTab(
if (i is not IdentifiedItem item)
continue;
if (!drawer.FilterChangedItem(s, item, LowerString.Empty))
if (!drawer.FilterChangedItem(s, item, string.Empty))
continue;
if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p))
@ -168,7 +167,7 @@ public class ModPanelChangedItemsTab(
if (i is IdentifiedItem)
continue;
if (!drawer.FilterChangedItem(s, i, LowerString.Empty))
if (!drawer.FilterChangedItem(s, i, string.Empty))
continue;
while (sortedTmpIdx < sortedTmp.Length
@ -291,7 +290,7 @@ public class ModPanelChangedItemsTab(
if (ImUtf8.IconButton(FontAwesomeIcon.Star, "Prefer displaying this item instead of the current primary item.\n\nRight-click for more options."u8, _buttonSize,
false, _starColor))
dataEditor.AddPreferredItem(selector.Selected!, item.Item.Id, false, true);
using var context = ImUtf8.PopupContextItem("StarContext"u8, ImGuiPopupFlags.MouseButtonRight);
using var context = ImUtf8.PopupContextItem("StarContext"u8);
if (!context)
return;
@ -335,7 +334,7 @@ public class ModPanelChangedItemsTab(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int idx)
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int _)
{
ImGui.TableNextColumn();
using var indent = ImRaii.PushIndent(1, obj.Child);

View file

@ -59,7 +59,7 @@ public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSy
var mod = selector.Selected!;
foreach (var (index, conflict) in collectionManager.Active.Current.Conflicts(mod).Where(c => !c.Mod2.Priority.IsHidden)
.OrderByDescending(GetPriority)
.ThenBy(c => c.Mod2.Name.Lower).Index())
.ThenBy(c => c.Mod2.Name, StringComparer.OrdinalIgnoreCase).Index())
{
using var id = ImRaii.PushId(index);
DrawConflictRow(conflict, priorityWidth, buttonSize);
@ -71,7 +71,7 @@ public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSy
ImGui.TableNextColumn();
using var c = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderLine.Value());
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(selector.Selected!.Name.Text);
ImGui.TextUnformatted(selector.Selected!.Name);
ImGui.TableNextColumn();
var actualSettings = collectionManager.Active.Current.GetActualSettings(selector.Selected!.Index).Settings!;
var priority = actualSettings.Priority.Value;
@ -102,7 +102,7 @@ public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSy
private void DrawConflictSelectable(ModConflicts conflict)
{
ImGui.AlignTextToFramePadding();
if (ImGui.Selectable(conflict.Mod2.Name.Text) && conflict.Mod2 is Mod otherMod)
if (ImGui.Selectable(conflict.Mod2.Name) && conflict.Mod2 is Mod otherMod)
selector.SelectByValue(otherMod);
var hovered = ImGui.IsItemHovered();
var rightClicked = ImGui.IsItemClicked(ImGuiMouseButton.Right);

View file

@ -80,8 +80,8 @@ public class ModPanelHeader : IDisposable
// Author
if (_mod.Author != _modAuthor)
{
var author = _mod.Author.IsEmpty ? string.Empty : $"by {_mod.Author}";
_modAuthor = _mod.Author.Text;
var author = _mod.Author.Length is 0 ? string.Empty : $"by {_mod.Author}";
_modAuthor = _mod.Author;
_modAuthorWidth = ImGui.CalcTextSize(author).X;
_secondRowWidth = _modAuthorWidth + _modWebsiteButtonWidth + ImGui.GetStyle().ItemSpacing.X;
}

View file

@ -97,18 +97,18 @@ public sealed class ModSearchStringSplitter : SearchStringSplitter<ModSearchType
var fullName = folder.FullName();
return Forced.All(i => MatchesName(i, folder.Name, fullName, false))
&& !Negated.Any(i => MatchesName(i, folder.Name, fullName, true))
&& (General.Count == 0 || General.Any(i => MatchesName(i, folder.Name, fullName, false)));
&& (General.Count is 0 || General.Any(i => MatchesName(i, folder.Name, fullName, false)));
}
protected override bool Matches(Entry entry, ModFileSystem.Leaf leaf)
=> entry.Type switch
{
ModSearchType.Default => leaf.FullName().AsSpan().Contains(entry.Needle, StringComparison.OrdinalIgnoreCase)
|| leaf.Value.Name.Lower.AsSpan().Contains(entry.Needle, StringComparison.Ordinal),
|| leaf.Value.Name.AsSpan().Contains(entry.Needle, StringComparison.OrdinalIgnoreCase),
ModSearchType.ChangedItem => leaf.Value.LowerChangedItemsString.AsSpan().Contains(entry.Needle, StringComparison.Ordinal),
ModSearchType.Tag => leaf.Value.AllTagsLower.AsSpan().Contains(entry.Needle, StringComparison.Ordinal),
ModSearchType.Name => leaf.Value.Name.Lower.AsSpan().Contains(entry.Needle, StringComparison.Ordinal),
ModSearchType.Author => leaf.Value.Author.Lower.AsSpan().Contains(entry.Needle, StringComparison.Ordinal),
ModSearchType.Name => leaf.Value.Name.AsSpan().Contains(entry.Needle, StringComparison.OrdinalIgnoreCase),
ModSearchType.Author => leaf.Value.Author.AsSpan().Contains(entry.Needle, StringComparison.OrdinalIgnoreCase),
ModSearchType.Category => leaf.Value.ChangedItems.Any(p
=> ((p.Value?.Icon.ToFlag() ?? ChangedItemIconFlag.Unknown) & entry.IconFlagFilter) != 0),
_ => true,
@ -117,14 +117,14 @@ public sealed class ModSearchStringSplitter : SearchStringSplitter<ModSearchType
protected override bool MatchesNone(ModSearchType type, bool negated, ModFileSystem.Leaf haystack)
=> type switch
{
ModSearchType.Author when negated => !haystack.Value.Author.IsEmpty,
ModSearchType.Author => haystack.Value.Author.IsEmpty,
ModSearchType.Author when negated => haystack.Value.Author.Length > 0,
ModSearchType.Author => haystack.Value.Author.Length is 0,
ModSearchType.ChangedItem when negated => haystack.Value.LowerChangedItemsString.Length > 0,
ModSearchType.ChangedItem => haystack.Value.LowerChangedItemsString.Length == 0,
ModSearchType.ChangedItem => haystack.Value.LowerChangedItemsString.Length is 0,
ModSearchType.Tag when negated => haystack.Value.AllTagsLower.Length > 0,
ModSearchType.Tag => haystack.Value.AllTagsLower.Length == 0,
ModSearchType.Tag => haystack.Value.AllTagsLower.Length is 0,
ModSearchType.Category when negated => haystack.Value.ChangedItems.Count > 0,
ModSearchType.Category => haystack.Value.ChangedItems.Count == 0,
ModSearchType.Category => haystack.Value.ChangedItems.Count is 0,
_ => true,
};

View file

@ -70,7 +70,7 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor,
{
using var id = ImRaii.PushId(i++);
var (icon, text) = path is ModFileSystem.Leaf l
? (FontAwesomeIcon.FileCircleMinus, l.Value.Name.Text)
? (FontAwesomeIcon.FileCircleMinus, l.Value.Name)
: (FontAwesomeIcon.FolderMinus, string.Empty);
ImGui.TableNextColumn();
if (ImUtf8.IconButton(icon, "Remove from selection."u8, sizeType))
@ -112,7 +112,7 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor,
? _tag.Length == 0
? "No tag specified."
: $"All mods selected already contain the tag \"{_tag}\", either locally or as mod data."
: $"Add the tag \"{_tag}\" to {_addMods.Count} mods as a local tag:\n\n\t{string.Join("\n\t", _addMods.Select(m => m.Name.Text))}";
: $"Add the tag \"{_tag}\" to {_addMods.Count} mods as a local tag:\n\n\t{string.Join("\n\t", _addMods.Select(m => m.Name))}";
ImUtf8.SameLineInner();
if (ImUtf8.ButtonEx(label, tooltip, width, _addMods.Count == 0))
foreach (var mod in _addMods)
@ -125,7 +125,7 @@ public class MultiModPanel(ModFileSystemSelector selector, ModDataEditor editor,
? _tag.Length == 0
? "No tag specified."
: $"No selected mod contains the tag \"{_tag}\" locally."
: $"Remove the local tag \"{_tag}\" from {_removeMods.Count} mods:\n\n\t{string.Join("\n\t", _removeMods.Select(m => m.Item1.Name.Text))}";
: $"Remove the local tag \"{_tag}\" from {_removeMods.Count} mods:\n\n\t{string.Join("\n\t", _removeMods.Select(m => m.Item1.Name))}";
ImUtf8.SameLineInner();
if (ImUtf8.ButtonEx(label, tooltip, width, _removeMods.Count == 0))
foreach (var (mod, index) in _removeMods)

View file

@ -1,4 +1,4 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Collections;
using Penumbra.Enums;
using Penumbra.Interop;

View file

@ -1,7 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Luna;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Table;
using OtterGui.Text;

View file

@ -1,6 +1,6 @@
using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
@ -25,8 +25,8 @@ public class ChangedItemsTab(
public ReadOnlySpan<byte> Label
=> "Changed Items"u8;
private LowerString _changedItemFilter = LowerString.Empty;
private LowerString _changedItemModFilter = LowerString.Empty;
private string _changedItemFilter = string.Empty;
private string _changedItemModFilter = string.Empty;
private Vector2 _buttonSize;
public void DrawContent()
@ -66,17 +66,17 @@ public class ChangedItemsTab(
- 450 * UiHelpers.Scale
- ImGui.GetStyle().ItemSpacing.X;
ImGui.SetNextItemWidth(450 * UiHelpers.Scale);
LowerString.InputWithHint("##changedItemsFilter", "Filter Item...", ref _changedItemFilter, 128);
Im.Input.Text("##changedItemsFilter"u8, ref _changedItemFilter, "Filter Item..."u8);
ImGui.SameLine();
ImGui.SetNextItemWidth(varWidth);
LowerString.InputWithHint("##changedItemsModFilter", "Filter Mods...", ref _changedItemModFilter, 128);
Im.Input.Text("##changedItemsModFilter"u8, ref _changedItemModFilter, "Filter Mods..."u8);
return varWidth;
}
/// <summary> Apply the current filters. </summary>
private bool FilterChangedItem(KeyValuePair<string, (Luna.SingleArray<IMod>, IIdentifiedObjectData)> item)
=> drawer.FilterChangedItem(item.Key, item.Value.Item2, _changedItemFilter)
&& (_changedItemModFilter.IsEmpty || item.Value.Item1.Any(m => m.Name.Contains(_changedItemModFilter)));
&& (_changedItemModFilter.Length is 0 || item.Value.Item1.Any(m => m.Name.Contains(_changedItemModFilter, StringComparison.OrdinalIgnoreCase)));
/// <summary> Draw a full column for a changed item. </summary>
private void DrawChangedItemColumn(KeyValuePair<string, (Luna.SingleArray<IMod>, IIdentifiedObjectData)> item)
@ -101,7 +101,7 @@ public class ChangedItemsTab(
return;
var first = mods[0];
if (ImUtf8.Selectable(first.Name.Text, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 })
if (ImUtf8.Selectable(first.Name, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 })
&& ImGui.GetIO().KeyCtrl
&& first is Mod mod)
communicator.SelectTab.Invoke(new SelectTab.Arguments(TabType.Mods, mod));

View file

@ -1,6 +1,5 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
@ -10,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using ImSharp;
using Luna;
using Microsoft.Extensions.DependencyInjection;
using OtterGui;
@ -31,7 +31,6 @@ using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.UI.Classes;
using Penumbra.Util;
using static OtterGui.Raii.ImRaii;
using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
using ImGuiClip = OtterGui.ImGuiClip;
@ -43,7 +42,6 @@ using Penumbra.GameData.Files.StainMapStructs;
using Penumbra.Interop;
using Penumbra.String.Classes;
using Penumbra.UI.AdvancedWindow.Materials;
using FrameworkManager = OtterGui.Classes.FrameworkManager;
namespace Penumbra.UI.Tabs.Debug;
@ -72,7 +70,6 @@ public class Diagnostics(ServiceManager provider) : IUiService
public class DebugTab : Window, ITab, IUiService
{
private readonly PerformanceTracker _performance;
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager;
@ -111,7 +108,7 @@ public class DebugTab : Window, ITab, IUiService
private readonly ModMigratorDebug _modMigratorDebug;
private readonly ShapeInspector _shapeInspector;
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
public DebugTab(Configuration config, CollectionManager collectionManager, ObjectManager objects,
IClientState clientState, IDataManager dataManager,
ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorManager actors, StainService stains,
ResourceManagerService resourceManager, ResourceLoader resourceLoader, CollectionResolver collectionResolver,
@ -122,7 +119,7 @@ public class DebugTab : Window, ITab, IUiService
HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer,
SchedulerResourceManagementService schedulerService, ObjectIdentification objectIdentification, RenderTargetDrawer renderTargetDrawer,
ModMigratorDebug modMigratorDebug, ShapeInspector shapeInspector)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
: base("Penumbra Debug Window", WindowFlags.NoCollapse)
{
IsOpen = true;
SizeConstraints = new WindowSizeConstraints
@ -130,7 +127,6 @@ public class DebugTab : Window, ITab, IUiService
MinimumSize = new Vector2(200, 200),
MaximumSize = new Vector2(2000, 2000),
};
_performance = performance;
_config = config;
_collectionManager = collectionManager;
_validityChecker = validityChecker;
@ -277,7 +273,7 @@ public class DebugTab : Window, ITab, IUiService
foreach (var (mod, paths, manips) in collection._cache!.ModData.Data.OrderBy(t => t.Item1.Name))
{
using var id = mod is TemporaryMod t ? PushId(t.Priority.Value) : PushId(((Mod)mod).ModPath.Name);
using var node2 = TreeNode(mod.Name.Text);
using var node2 = TreeNode(mod.Name);
if (!node2)
continue;
@ -503,8 +499,6 @@ public class DebugTab : Window, ITab, IUiService
if (start)
ImGui.NewLine();
}
_performance.Draw("##performance", "Enable Runtime Performance Tracking", TimingExtensions.ToName);
}
private unsafe void DrawActorsDebug()
@ -774,9 +768,35 @@ public class DebugTab : Window, ITab, IUiService
DrawActionTmbs();
DrawStainTemplates();
DrawAtch();
DrawFileTest();
DrawChangedItemTest();
}
private string _filePath = string.Empty;
private byte[]? _fileData;
private void DrawFileTest()
{
using var node = TreeNode("Game File Test");
if (!node)
return;
if (Im.Input.Text("##Path"u8, ref _filePath, "File Path..."u8))
_fileData = _dataManager.GetFile(_filePath)?.Data;
using (Im.Group())
{
Im.Text("Exists"u8);
Im.Text("File Size"u8);
}
Im.Line.SameInner();
using (Im.Group())
{
Im.Text($"{_fileData is not null}");
Im.Text($"{_fileData?.Length ?? 0}");
}
}
private string _changedItemPath = string.Empty;
private readonly Dictionary<string, IIdentifiedObjectData> _changedItems = [];
@ -1204,7 +1224,7 @@ public class DebugTab : Window, ITab, IUiService
private string _cloudTesterPath = string.Empty;
private bool? _cloudTesterReturn;
private Exception? _cloudTesterError;
private void DrawCloudApi()
{
if (!ImUtf8.CollapsingHeader("Cloud API"u8))
@ -1213,7 +1233,6 @@ public class DebugTab : Window, ITab, IUiService
using var id = ImRaii.PushId("CloudApiTester"u8);
if (ImUtf8.InputText("Path"u8, ref _cloudTesterPath, flags: ImGuiInputTextFlags.EnterReturnsTrue))
{
try
{
_cloudTesterReturn = CloudApi.IsCloudSynced(_cloudTesterPath);
@ -1224,7 +1243,6 @@ public class DebugTab : Window, ITab, IUiService
_cloudTesterReturn = null;
_cloudTesterError = e;
}
}
if (_cloudTesterReturn.HasValue)
ImUtf8.Text($"Is Cloud Synced? {_cloudTesterReturn}");

View file

@ -1,7 +1,6 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
@ -51,8 +50,8 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
private float _effectiveArrowLength;
// Filters
private LowerString _effectiveGamePathFilter = LowerString.Empty;
private LowerString _effectiveFilePathFilter = LowerString.Empty;
private string _effectiveGamePathFilter = string.Empty;
private string _effectiveFilePathFilter = string.Empty;
/// <summary> Setup table sizes. </summary>
private void SetupEffectiveSizes()
@ -72,14 +71,14 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
/// <summary> Draw the header line for filters. </summary>
private void DrawFilters()
{
var tmp = _effectiveGamePathFilter.Text;
var tmp = _effectiveGamePathFilter;
ImGui.SetNextItemWidth(_effectiveLeftTextLength);
if (ImGui.InputTextWithHint("##gamePathFilter", "Filter game path...", ref tmp, 256))
_effectiveGamePathFilter = tmp;
ImGui.SameLine(_effectiveArrowLength + _effectiveLeftTextLength + 3 * ImGui.GetStyle().ItemSpacing.X);
ImGui.SetNextItemWidth(-1);
tmp = _effectiveFilePathFilter.Text;
tmp = _effectiveFilePathFilter;
if (ImGui.InputTextWithHint("##fileFilter", "Filter file path...", ref tmp, 256))
_effectiveFilePathFilter = tmp;
}
@ -144,7 +143,7 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
}
/// <summary> Draw a line for a path and its name. </summary>
private static void DrawLine((string, LowerString) pair)
private static void DrawLine((string, string) pair)
{
var (path, name) = pair;
ImGui.TableNextColumn();
@ -153,7 +152,7 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
ImGui.TableNextColumn();
ImGuiUtil.PrintIcon(FontAwesomeIcon.LongArrowAltLeft);
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(name.Text);
ImGuiUtil.CopyOnClickSelectable(name);
}
/// <summary> Draw a line for a unfiltered/unconverted manipulation and mod-index pair. </summary>
@ -166,26 +165,26 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
ImGui.TableNextColumn();
ImGuiUtil.PrintIcon(FontAwesomeIcon.LongArrowAltLeft);
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(mod.Name.Text);
ImGuiUtil.CopyOnClickSelectable(mod.Name);
}
/// <summary> Check filters for file replacements. </summary>
private bool CheckFilters(KeyValuePair<Utf8GamePath, ModPath> kvp)
{
var (gamePath, fullPath) = kvp;
if (_effectiveGamePathFilter.Length > 0 && !gamePath.ToString().Contains(_effectiveGamePathFilter.Lower))
if (_effectiveGamePathFilter.Length > 0 && !gamePath.ToString().Contains(_effectiveGamePathFilter, StringComparison.OrdinalIgnoreCase))
return false;
return _effectiveFilePathFilter.Length == 0 || fullPath.Path.FullName.ToLowerInvariant().Contains(_effectiveFilePathFilter.Lower);
return _effectiveFilePathFilter.Length == 0 || fullPath.Path.FullName.Contains(_effectiveFilePathFilter, StringComparison.OrdinalIgnoreCase);
}
/// <summary> Check filters for meta manipulations. </summary>
private bool CheckFilters((string, LowerString) kvp)
private bool CheckFilters((string, string) kvp)
{
var (name, path) = kvp;
if (_effectiveGamePathFilter.Length > 0 && !name.ToLowerInvariant().Contains(_effectiveGamePathFilter.Lower))
if (_effectiveGamePathFilter.Length > 0 && !name.Contains(_effectiveGamePathFilter, StringComparison.OrdinalIgnoreCase))
return false;
return _effectiveFilePathFilter.Length == 0 || path.Contains(_effectiveFilePathFilter.Lower);
return _effectiveFilePathFilter.Length == 0 || path.Contains(_effectiveFilePathFilter, StringComparison.OrdinalIgnoreCase);
}
}

View file

@ -5,10 +5,9 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using ImSharp;
using Luna;
using Luna.Widgets;
using OtterGui;
using OtterGui.Custom;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
@ -105,22 +104,24 @@ public class SettingsTab : ITab, IUiService
_tutorial.OpenTutorial(BasicTutorialSteps.Faq2);
}
private readonly TwoPanelLayout _test = new();
public void DrawContent()
{
using var child = ImRaii.Child("##SettingsTab", -Vector2.One, false);
if (!child)
return;
DrawEnabledBox();
EphemeralCheckbox("Lock Main Window", "Prevent the main window from being resized or moved.", _config.Ephemeral.FixMainWindow,
v => _config.Ephemeral.FixMainWindow = v);
ImGui.NewLine();
DrawRootFolder();
DrawDirectoryButtons();
ImGui.NewLine();
ImGui.NewLine();
DrawGeneralSettings();
_migrationDrawer.Draw();
DrawColorSettings();
@ -582,16 +583,16 @@ public class SettingsTab : ITab, IUiService
_selector.SetFilterDirty();
});
Widget.DoubleModifierSelector("Mod Deletion Modifier",
"A modifier you need to hold while clicking the Delete Mod button for it to take effect.", UiHelpers.InputTextWidth.X,
KeySelector.DoubleModifier("Mod Deletion Modifier"u8,
"A modifier you need to hold while clicking the Delete Mod button for it to take effect."u8, UiHelpers.InputTextWidth.X,
_config.DeleteModModifier,
v =>
{
_config.DeleteModModifier = v;
_config.Save();
});
Widget.DoubleModifierSelector("Incognito Modifier",
"A modifier you need to hold while clicking the Incognito or Temporary Settings Mode button for it to take effect.",
KeySelector.DoubleModifier("Incognito Modifier"u8,
"A modifier you need to hold while clicking the Incognito or Temporary Settings Mode button for it to take effect."u8,
UiHelpers.InputTextWidth.X,
_config.IncognitoModifier,
v =>
@ -646,6 +647,7 @@ public class SettingsTab : ITab, IUiService
DrawDefaultModAuthor();
DrawDefaultModImportFolder();
DrawPcpFolder();
DrawPcpExtension();
DrawDefaultModExportPath();
}
@ -770,6 +772,28 @@ public class SettingsTab : ITab, IUiService
"The folder any penumbra character packs are moved to on import.\nLeave blank to import into Root.");
}
private void DrawPcpExtension()
{
var tmp = _config.PcpSettings.PcpExtension;
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X);
if (ImUtf8.InputText("##pcpExtension"u8, ref tmp))
_config.PcpSettings.PcpExtension = tmp;
if (ImGui.IsItemDeactivatedAfterEdit())
_config.Save();
Im.Line.SameInner();
if (ImEx.Button("Reset##pcpExtension"u8, Vector2.Zero, "Reset the extension to its default value of \".pcp\".",
_config.PcpSettings.PcpExtension is ".pcp"))
{
_config.PcpSettings.PcpExtension = ".pcp";
_config.Save();
}
ImGuiUtil.LabeledHelpMarker("PCP Extension",
"The extension used when exporting PCP files. Should generally be either \".pcp\" or \".pmp\".");
}
/// <summary> Draw all settings pertaining to advanced editing of mods. </summary>
private void DrawModEditorSettings()
@ -1113,7 +1137,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()));
SupportButton.KoFiPatreon(Penumbra.Messager, new Vector2(width, 0));
}

View file

@ -1,66 +0,0 @@
using Dalamud.Plugin.Services;
namespace Penumbra.Util;
public sealed class PerformanceTracker(IFramework framework) : OtterGui.Classes.PerformanceTracker<PerformanceType>(framework), Luna.IService;
public enum PerformanceType
{
UiMainWindow,
UiAdvancedWindow,
CharacterResolver,
IdentifyCollection,
GetResourceHandler,
ReadSqPack,
CharacterBaseCreate,
TimelineResources,
LoadCharacterVfx,
LoadAreaVfx,
LoadSound,
ScheduleClipUpdate,
LoadAction,
LoadCharacterBaseAnimation,
LoadPap,
LoadTextures,
LoadShaders,
LoadApricotResources,
UpdateModels,
GetEqp,
SetupVisor,
SetupCharacter,
ChangeCustomize,
DebugTimes,
}
public static class TimingExtensions
{
public static string ToName(this PerformanceType type)
=> type switch
{
PerformanceType.UiMainWindow => "Main Interface Drawing",
PerformanceType.UiAdvancedWindow => "Advanced Window Drawing",
PerformanceType.GetResourceHandler => "GetResource Hook",
PerformanceType.ReadSqPack => "ReadSqPack Hook",
PerformanceType.CharacterResolver => "Resolving Characters",
PerformanceType.IdentifyCollection => "Identifying Collections",
PerformanceType.CharacterBaseCreate => "CharacterBaseCreate Hook",
PerformanceType.TimelineResources => "LoadTimelineResources Hook",
PerformanceType.LoadCharacterVfx => "LoadCharacterVfx Hook",
PerformanceType.LoadAreaVfx => "LoadAreaVfx Hook",
PerformanceType.LoadTextures => "LoadTextures Hook",
PerformanceType.LoadShaders => "LoadShaders Hook",
PerformanceType.LoadApricotResources => "LoadApricotFiles Hook",
PerformanceType.UpdateModels => "UpdateModels Hook",
PerformanceType.GetEqp => "GetEqp Hook",
PerformanceType.SetupVisor => "SetupVisor Hook",
PerformanceType.SetupCharacter => "SetupCharacter Hook",
PerformanceType.ChangeCustomize => "ChangeCustomize Hook",
PerformanceType.LoadSound => "LoadSound Hook",
PerformanceType.ScheduleClipUpdate => "ScheduleClipUpdate Hook",
PerformanceType.LoadCharacterBaseAnimation => "LoadCharacterAnimation Hook",
PerformanceType.LoadPap => "LoadPap Hook",
PerformanceType.LoadAction => "LoadAction Hook",
PerformanceType.DebugTimes => "Debug Tracking",
_ => $"Unknown {(int)type}",
};
}