From 5561305af87d1f6f8169be5372d163ac1d29b454 Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sun, 21 Dec 2025 15:05:00 +0100 Subject: [PATCH] Allow for SortaKinda imports --- AetherBags/Addons/AddonInventoryWindow.cs | 6 + .../Helpers/Import/SortaKindaImportExport.cs | 176 ++++++++++++++++++ AetherBags/Helpers/ImportExportResetHelper.cs | 32 ++++ AetherBags/Helpers/Util.cs | 16 +- AetherBags/Plugin.cs | 6 + 5 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 AetherBags/Helpers/Import/SortaKindaImportExport.cs diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index 2d0be8f..ee63b56 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -106,6 +106,12 @@ public class AddonInventoryWindow : NativeAddon base.OnUpdate(addon); } + public void ManualRefresh() + { + InventoryState.RefreshFromGame(); + RefreshCategoriesCore(true); + } + private void OnInventoryUpdate(AddonEvent type, AddonArgs args) { InventoryState.RefreshFromGame(); diff --git a/AetherBags/Helpers/Import/SortaKindaImportExport.cs b/AetherBags/Helpers/Import/SortaKindaImportExport.cs new file mode 100644 index 0000000..7826397 --- /dev/null +++ b/AetherBags/Helpers/Import/SortaKindaImportExport.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using AetherBags.Configuration; +using AetherBags.Configuration.Import; + +namespace AetherBags.Helpers.Import; + +public static class SortaKindaImportExport +{ + private static readonly JsonSerializerOptions ExternalJsonOptions = new() + { + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true, + WriteIndented = true, + IncludeFields = true + }; + + public static bool TryImportFromClipboard( + SystemConfiguration targetConfig, + bool replaceExisting, + out string error) + { + error = string.Empty; + string clipboard; + try + { + clipboard = Dalamud.Bindings.ImGui.ImGui.GetClipboardText(); + } + catch (Exception ex) + { + error = $"Failed to read clipboard: {ex.Message}"; + return false; + } + + return TryImportFromJson(clipboard, targetConfig, replaceExisting, out error); + } + + public static bool TryImportFromJson( + string input, + SystemConfiguration targetConfig, + bool replaceExisting, + out string error) + { + error = string.Empty; + + if (string.IsNullOrWhiteSpace(input)) + { + error = "Input was empty."; + return false; + } + + var external = Util.DeserializeCompressed(input.Trim(), ExternalJsonOptions); + + if (external is null) + { + error = "Failed to parse SortaKinda input."; + return false; + } + + var mapped = external + .Select(MapToUserCategory) + .OrderBy(c => c.Order) + .ToList(); + + var dest = targetConfig.Categories.UserCategories; + + if (replaceExisting) + { + dest.Clear(); + dest.AddRange(mapped); + } + else + { + var byId = dest + .Where(c => !string.IsNullOrWhiteSpace(c.Id)) + .ToDictionary(c => c.Id, StringComparer.OrdinalIgnoreCase); + + foreach (var incoming in mapped) + { + if (!string.IsNullOrWhiteSpace(incoming.Id) && byId.TryGetValue(incoming.Id, out var existing)) + { + existing.Name = incoming.Name; + existing.Description = incoming.Description; + existing.Order = incoming.Order; + existing.Priority = incoming.Priority; + existing.Color = incoming.Color; + existing.Rules = incoming.Rules; + } + else + { + dest.Add(incoming); + if (!string.IsNullOrWhiteSpace(incoming.Id)) + byId[incoming.Id] = incoming; + } + } + } + + targetConfig.Categories.UserCategoriesEnabled = true; + return true; + } + + public static string ExportToJson(SystemConfiguration sourceConfig) + { + var exported = sourceConfig.Categories.UserCategories + .OrderBy(c => c.Order) + .Select(MapToExternal) + .ToArray(); + + return Util.SerializeCompressed(exported, ExternalJsonOptions); + } + + public static void ExportToClipboard(SystemConfiguration sourceConfig) + => Dalamud.Bindings.ImGui.ImGui.SetClipboardText(ExportToJson(sourceConfig)); + + private static UserCategoryDefinition MapToUserCategory(SortaKindaCategory external) + => new() + { + Id = string.IsNullOrWhiteSpace(external.Id) ? Guid.NewGuid().ToString("N") : external.Id, + Name = external.Name, + Description = string.Empty, + Order = external.Index, + Priority = 100, + Color = external.Color, + Rules = new CategoryRuleSet + { + AllowedItemIds = new List(), + AllowedItemNamePatterns = external.AllowedItemNames?.ToList() ?? new List(), + AllowedUiCategoryIds = external.AllowedItemTypes?.ToList() ?? new List(), + AllowedRarities = external.AllowedItemRarities?.ToList() ?? new List(), + ItemLevel = new RangeFilter + { + Enabled = external.ItemLevelFilter?.Enable ?? false, + Min = external.ItemLevelFilter?.MinValue ?? 0, + Max = external.ItemLevelFilter?.MaxValue ?? 2000, + }, + VendorPrice = new RangeFilter + { + Enabled = external.VendorPriceFilter?.Enable ?? false, + Min = external.VendorPriceFilter?.MinValue ?? 0u, + Max = external.VendorPriceFilter?.MaxValue ?? 9_999_999u, + } + } + }; + + private static SortaKindaCategory MapToExternal(UserCategoryDefinition internalCat) + => new() + { + Color = internalCat.Color, + Id = internalCat.Id, + Name = internalCat.Name, + Index = internalCat.Order, + AllowedItemNames = internalCat.Rules.AllowedItemNamePatterns?.ToList() ?? new List(), + AllowedItemTypes = internalCat.Rules.AllowedUiCategoryIds?.ToList() ?? new List(), + AllowedItemRarities = internalCat.Rules.AllowedRarities?.ToList() ?? new List(), + ItemLevelFilter = new ExternalRangeFilterDto + { + Enable = internalCat.Rules.ItemLevel.Enabled, + Label = "Item Level Filter", + MinValue = internalCat.Rules.ItemLevel.Min, + MaxValue = internalCat.Rules.ItemLevel.Max + }, + VendorPriceFilter = new ExternalRangeFilterDto + { + Enable = internalCat.Rules.VendorPrice.Enabled, + Label = "Vendor Price Filter", + MinValue = internalCat.Rules.VendorPrice.Min, + MaxValue = internalCat.Rules.VendorPrice.Max + }, + Direction = 0, + FillMode = 0, + SortMode = 0 + }; +} \ No newline at end of file diff --git a/AetherBags/Helpers/ImportExportResetHelper.cs b/AetherBags/Helpers/ImportExportResetHelper.cs index 85d2e24..b667f58 100644 --- a/AetherBags/Helpers/ImportExportResetHelper.cs +++ b/AetherBags/Helpers/ImportExportResetHelper.cs @@ -1,5 +1,6 @@ using System.Linq; using AetherBags.Configuration; +using AetherBags.Helpers.Import; using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Keys; using Dalamud.Interface.ImGuiNotification; @@ -62,4 +63,35 @@ public abstract class ImportExportResetHelper { ); Services.Logger.Info("Configuration reset to default."); } + + public static void TryImportSortaKindaFromClipboard(bool replaceExisting) + { + //if (!Services.KeyState[VirtualKey.SHIFT]) + // return; + + var notification = new Notification { Content = "SortaKinda categories imported.", Type = NotificationType.Success }; + + if (!SortaKindaImportExport.TryImportFromClipboard(System.Config, replaceExisting, out var error)) + { + notification.Content = error; + notification.Type = NotificationType.Error; + Services.Logger.Warning(error); + } + else + { + Util.SaveConfig(System.Config); + Services.Logger.Info("SortaKinda categories imported from clipboard."); + } + + Services.NotificationManager.AddNotification(notification); + } + + public static void TryExportSortaKindaToClipboard() + { + SortaKindaImportExport.ExportToClipboard(System.Config); + Services.NotificationManager.AddNotification( + new Notification { Content = "SortaKinda JSON exported to clipboard.", Type = NotificationType.Success } + ); + Services.Logger.Info("SortaKinda JSON exported to clipboard."); + } } diff --git a/AetherBags/Helpers/Util.cs b/AetherBags/Helpers/Util.cs index 3fac973..4d865fe 100644 --- a/AetherBags/Helpers/Util.cs +++ b/AetherBags/Helpers/Util.cs @@ -53,25 +53,31 @@ public static class Util } } - public static string SerializeConfig(SystemConfiguration config) + public static string SerializeCompressed(T value, JsonSerializerOptions? options = null) { - var json = JsonSerializer.Serialize(config, ConfigJsonOptions); + var json = JsonSerializer.Serialize(value, options ?? ConfigJsonOptions); return CompressToBase64(json); } - public static SystemConfiguration? DeserializeConfig(string input) + public static T? DeserializeCompressed(string input, JsonSerializerOptions? options = null) { try { var json = DecompressFromBase64(input); - return JsonSerializer.Deserialize(json, ConfigJsonOptions); + return JsonSerializer.Deserialize(json, options ?? ConfigJsonOptions); } catch { - return null; + return default; } } + public static string SerializeConfig(SystemConfiguration config) + => SerializeCompressed(config, ConfigJsonOptions); + + public static SystemConfiguration? DeserializeConfig(string input) + => DeserializeCompressed(input, ConfigJsonOptions); + public static void SaveConfig(SystemConfiguration config) { FileInfo file = FileHelpers.GetFileInfo(SystemConfiguration.FileName); diff --git a/AetherBags/Plugin.cs b/AetherBags/Plugin.cs index d48b1eb..43018e9 100644 --- a/AetherBags/Plugin.cs +++ b/AetherBags/Plugin.cs @@ -73,6 +73,12 @@ public class Plugin : IDalamudPlugin System.AddonInventoryWindow.Toggle(); if(args == "config") System.AddonInventoryWindow.Toggle(); + if (args == "import-sk") + { + // Manually import from SortaKinda for testing until we have a proper config window + ImportExportResetHelper.TryImportSortaKindaFromClipboard(true); + System.AddonInventoryWindow.ManualRefresh(); + } break; } }