mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
This commit is contained in:
parent
c5d09d7cd1
commit
924c9b9f7e
48 changed files with 1323 additions and 1271 deletions
|
|
@ -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
2
Luna
|
|
@ -1 +1 @@
|
|||
Subproject commit f39b514304d8f30030310f75d83f99385105c271
|
||||
Subproject commit 15636cda90725e6af7071512cf9873dd273570fc
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 2183dc78c46dd9b72fd2d95370e1bf223eff93a3
|
||||
Subproject commit 3104cfeefbdf0dd616a5886e07d1b615db15afd4
|
||||
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
namespace Penumbra;
|
||||
|
||||
namespace Penumbra;
|
||||
|
||||
public class DebugConfiguration
|
||||
{
|
||||
public static bool WriteImcBytesToLog = false;
|
||||
public static bool UseSkinMaterialProcessing = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Enums;
|
||||
using Penumbra.Interop;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}",
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue