From 41cd69fc4d954e164f58243f1644676ec6aebc82 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:13:55 +1100 Subject: [PATCH 1/8] Package bump & net 10 init --- Meddle/Meddle.Plugin/Meddle.Plugin.csproj | 8 +- Meddle/Meddle.Plugin/Services/UI/CommonUI.cs | 16 +- Meddle/Meddle.Plugin/UI/SamplerTab.cs | 82 ++++ Meddle/Meddle.Plugin/UI/TestFunctionTab.cs | 166 +++++++++ Meddle/Meddle.Plugin/Utils/ObjectUtil.cs | 4 +- Meddle/Meddle.Plugin/packages.lock.json | 370 ++++++++++--------- Meddle/Meddle.Utils/Meddle.Utils.csproj | 6 +- Meddle/Meddle.Utils/packages.lock.json | 20 +- 8 files changed, 465 insertions(+), 207 deletions(-) create mode 100644 Meddle/Meddle.Plugin/UI/SamplerTab.cs create mode 100644 Meddle/Meddle.Plugin/UI/TestFunctionTab.cs diff --git a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj index 5572e94..32e1d38 100644 --- a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj +++ b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj @@ -5,13 +5,13 @@ enable Meddle.Plugin 13 - net9.0-windows + net10.0-windows - - - + + + ..\Meddle.Utils\Lib\OtterTex.dll diff --git a/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs b/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs index ecb5dac..55cd802 100644 --- a/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs +++ b/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs @@ -29,11 +29,11 @@ public CommonUi(IClientState clientState, IObjectTable objectTable, Configuratio public unsafe ICharacter[] GetCharacters(ObjectUtil.ValidationFlags flags = ObjectUtil.ValidationFlags.None) { - if (clientState.LocalPlayer != null) + if (objectTable.LocalPlayer != null) { return objectTable.OfType() .Where(obj => obj.IsValid() && obj.IsValidCharacterBase(flags)) - .OrderBy(c => clientState.GetDistanceToLocalPlayer(c).LengthSquared()) + .OrderBy(c => objectTable.GetDistanceToLocalPlayer(c).LengthSquared()) .ToArray(); } else @@ -41,7 +41,7 @@ public unsafe ICharacter[] GetCharacters(ObjectUtil.ValidationFlags flags = Obje // login/char creator produces "invalid" characters but are still usable I guess return objectTable.OfType() .Where(obj => obj.IsValidHuman(flags)) - .OrderBy(c => clientState.GetDistanceToLocalPlayer(c).LengthSquared()) + .OrderBy(c => objectTable.GetDistanceToLocalPlayer(c).LengthSquared()) .ToArray(); } } @@ -117,10 +117,10 @@ public unsafe void DrawCharacterSelect(ref ICharacter? selectedCharacter, Object { ICharacter[] objects = GetCharacters(flags); - selectedCharacter ??= objects.FirstOrDefault() ?? clientState.LocalPlayer; + selectedCharacter ??= objects.FirstOrDefault() ?? objectTable.LocalPlayer; ImGui.Text("Select Character"); - if (selectedCharacter?.IsValid() == false && clientState.LocalPlayer != null) + if (selectedCharacter?.IsValid() == false && objectTable.LocalPlayer != null) { selectedCharacter = null; } @@ -145,9 +145,9 @@ public unsafe void DrawCharacterSelect(ref ICharacter? selectedCharacter, Object if (selectTarget) { - if (clientState.LocalPlayer is {TargetObject: not null}) + if (objectTable.LocalPlayer is {TargetObject: not null}) { - var target = clientState.LocalPlayer.TargetObject; + var target = objectTable.LocalPlayer.TargetObject; if (target is ICharacter targetCharacter && targetCharacter.IsValidCharacterBase()) { selectedCharacter = targetCharacter; @@ -220,7 +220,7 @@ public unsafe string GetCharacterDisplayText(IGameObject obj, bool includeDistan ? $"[{obj.Address:X8}:{obj.GameObjectId:X}]" : string.Empty; string distanceText = includeDistance - ? $" - {clientState.GetDistanceToLocalPlayer(obj).Length():0}y" + ? $" - {objectTable.GetDistanceToLocalPlayer(obj).Length():0}y" : string.Empty; return diff --git a/Meddle/Meddle.Plugin/UI/SamplerTab.cs b/Meddle/Meddle.Plugin/UI/SamplerTab.cs new file mode 100644 index 0000000..2af1b05 --- /dev/null +++ b/Meddle/Meddle.Plugin/UI/SamplerTab.cs @@ -0,0 +1,82 @@ +using Dalamud.Bindings.ImGui; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Graphics.Render; +using Meddle.Plugin.Models; +using Meddle.Plugin.Services; +using Meddle.Plugin.Utils; +using Meddle.Utils.Export; +using Meddle.Utils.Files; +using Meddle.Utils.Files.SqPack; + +namespace Meddle.Plugin.UI; + +public class SamplerTab : ITab +{ + private readonly SqPack pack; + private readonly IDataManager dataManager; + private readonly TextureCache textureCache; + private readonly ITextureProvider textureProvider; + public string Name => "Sampler"; + public int Order => 5; + public MenuType MenuType => MenuType.Debug; + + public SamplerTab(SqPack pack, + IDataManager dataManager, + TextureCache textureCache, + ITextureProvider textureProvider) + { + this.pack = pack; + this.dataManager = dataManager; + this.textureCache = textureCache; + this.textureProvider = textureProvider; + } + + private Dictionary loadedShpkFiles = new(); + + public unsafe void Draw() + { + ImGui.Text("Sampler Tab"); + var manager = Manager.Instance(); + UiUtil.Text($"Manager Ptr: 0x{(nint)manager:X}", $"0x{(nint)manager:X}"); + var shaderPackages = manager->ShaderManager.ShaderPackageResourceHandles; + if (ImGui.CollapsingHeader("Loaded Shader Packages")) + { + foreach (var shpkPtr in shaderPackages) + { + if (shpkPtr == null || shpkPtr.Value == null) + continue; + var shpk = shpkPtr.Value; + if (ImGui.CollapsingHeader($"{shpk->FileName.ParseString()}")) + { + UiUtil.Text($"Handle: 0x{(nint)shpk:X}", $"0x{(nint)shpk:X}"); + UiUtil.Text($"Ref Count: {shpk->RefCount}", $"{shpk->RefCount}"); + if (!loadedShpkFiles.TryGetValue(shpk->FileName.ParseString(), out var shaderPackage)) + { + var shpkData = pack.GetFile(shpk->FileName.ParseString()); + if (shpkData != null) + { + var shpkFile = new ShpkFile(shpkData.Value.file.RawData); + loadedShpkFiles[shpk->FileName.ParseString()] = new ShaderPackage(shpkFile, shpk->FileName.ParseString()); + } + } + + if (shaderPackage != null) + { + foreach (var texture in shaderPackage.Textures.Values) + { + UiUtil.Text($"Texture Slot: {texture}", $"{texture}"); + } + } + } + } + } + + var renderTargetManager = RenderTargetManager.Instance(); + UiUtil.Text($"RenderTargetManager Ptr: 0x{(nint)renderTargetManager:X}", $"0x{(nint)renderTargetManager:X}"); + } + + public void Dispose() + { + // TODO release managed resources here + } +} diff --git a/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs b/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs new file mode 100644 index 0000000..cda612d --- /dev/null +++ b/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs @@ -0,0 +1,166 @@ +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Interface.Textures; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using Meddle.Plugin.Models; +using Meddle.Plugin.Services; +using Meddle.Plugin.Services.UI; +using Meddle.Plugin.Utils; +using Microsoft.Extensions.Logging; + +namespace Meddle.Plugin.UI; + +public class TestFunctionTab : ITab +{ + private readonly ILogger log; + private readonly Configuration config; + private readonly CommonUi commonUi; + private readonly TextureCache textureCache; + private readonly ITextureProvider textureProvider; + private ICharacter? selectedCharacter; + private OnRenderMaterialOutput? output; + public string Name => "Test Functions"; + public int Order => 100; + public MenuType MenuType => MenuType.Debug; + + public TestFunctionTab( + ILogger log, + Configuration config, + CommonUi commonUi, TextureCache textureCache, ITextureProvider textureProvider) + { + this.log = log; + this.config = config; + this.commonUi = commonUi; + this.textureCache = textureCache; + this.textureProvider = textureProvider; + } + + public unsafe void Draw() + { + commonUi.DrawCharacterSelect(ref selectedCharacter, ObjectUtil.ValidationFlags.IsVisible); + if (output != null) + { + var serialized = System.Text.Json.JsonSerializer.Serialize(output, new System.Text.Json.JsonSerializerOptions + { + WriteIndented = true + }); + ImGui.TextWrapped(serialized); + if (output.DecalTexture != null) + { + var wrap = textureCache.GetOrAdd($"{output.DecalTexture.GetHashCode()}", () => + { + var textureData = output.DecalTexture.Bitmap.GetPixelSpan(); + var wrap = textureProvider.CreateFromRaw( + RawImageSpecification.Rgba32(output.DecalTexture.Width, output.DecalTexture.Height), textureData, + $"Meddle_Decal_{output.DecalTexture.GetHashCode()}"); + return wrap; + }); + var availableWidth = ImGui.GetContentRegionAvail().X; + float displayWidth = output.DecalTexture.Width; + float displayHeight = output.DecalTexture.Height; + if (displayWidth > availableWidth) + { + var ratio = availableWidth / displayWidth; + displayWidth *= ratio; + displayHeight *= ratio; + } + ImGui.Image(wrap.Handle, new Vector2(displayWidth, displayHeight)); + } + + if (ImGui.Button("Clear Output")) + { + output = null; + } + } + + if (selectedCharacter == null) + { + ImGui.Text("No character selected"); + return; + } + + var character = (Character*)selectedCharacter.Address; + var drawObject = character->GameObject.DrawObject; + if (drawObject == null) + { + ImGui.Text("Draw object is null"); + return; + } + + if (drawObject->GetObjectType() != ObjectType.CharacterBase) + { + ImGui.Text("Draw object is not a character base"); + return; + } + + var cBase = (CharacterBase*)drawObject; + var modelType = cBase->GetModelType(); + if (modelType != CharacterBase.ModelType.Human) + { + ImGui.Text("Model is not human"); + return; + } + + + var human = (Human*)cBase; + for (int i = 0; i < human->ModelsSpan.Length; i++) + { + var model = human->ModelsSpan[i]; + if (model == null || model.Value == null) + { + ImGui.Text($"Model {i} is null"); + continue; + } + + if (model.Value->ModelResourceHandle == null) + { + ImGui.Text($"Model {i} resource handle is null"); + continue; + } + + using var id = ImRaii.PushId($"model_{i}"); + using var modelNode = ImRaii.TreeNode($"Model {i} - {model.Value->ModelResourceHandle->FileName.ToString()}"); + if (modelNode.Success) + { + for (int j = 0; j < model.Value->MaterialsSpan.Length; j++) + { + var material = model.Value->MaterialsSpan[j]; + if (material == null || material.Value == null) + { + ImGui.Text($"Material {j} is null"); + continue; + } + + if (material.Value->MaterialResourceHandle == null) + { + ImGui.Text($"Material {j} resource handle is null"); + continue; + } + + using var matId = ImRaii.PushId($"material_{j}"); + using var materialNode = ImRaii.TreeNode($"Material {j} - {material.Value->MaterialResourceHandle->FileName.ToString()}"); + if (materialNode.Success) + { + var materialShpk = material.Value->MaterialResourceHandle->ShpkName.ToString(); + ImGui.Text($"Material SHPK: {materialShpk}"); + if (ImGui.Button("Resolve")) + { + var resolveOutput = OnRenderMaterialUtil.ResolveHumanOnRenderMaterial(human, model, (uint)j); + output = resolveOutput; + } + } + } + } + } + } + + + public void Dispose() + { + // TODO release managed resources here + } +} diff --git a/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs b/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs index f01d3a0..8091f8a 100644 --- a/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs +++ b/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs @@ -67,9 +67,9 @@ public static unsafe bool IsValidCharacterBase(this ICharacter obj, ValidationFl return true; } - public static Vector3 GetDistanceToLocalPlayer(this IClientState clientState, IGameObject obj) + public static Vector3 GetDistanceToLocalPlayer(this IObjectTable objectTable, IGameObject obj) { - if (clientState.LocalPlayer is {Position: var charPos}) + if (objectTable.LocalPlayer is {Position: var charPos}) return Vector3.Abs(obj.Position - charPos); return new Vector3(obj.YalmDistanceX, 0, obj.YalmDistanceZ); } diff --git a/Meddle/Meddle.Plugin/packages.lock.json b/Meddle/Meddle.Plugin/packages.lock.json index 48a1aac..4b8a97c 100644 --- a/Meddle/Meddle.Plugin/packages.lock.json +++ b/Meddle/Meddle.Plugin/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net9.0-windows7.0": { + "net10.0-windows7.0": { "DalamudPackager": { "type": "Direct", "requested": "[13.1.0, )", @@ -16,305 +16,310 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "zQV2WOSP+3z1EuK91ULxfGgo2Y75bTRnmJHp08+w/YXAyekZutX/qCd88/HOMNh35MDW9mJJJxPpMPS+1Rww8A==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Hosting": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "DmRsWH3g8yZGho/pLQ79hxhM2ctE1eDTZ/HbAnrD/uw8m+P2pRRJOoBVxlrhbhMP3/y3oAJoy0yITasfmilbTg==", - "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.Configuration.CommandLine": "9.0.9", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "9.0.9", - "Microsoft.Extensions.Configuration.FileExtensions": "9.0.9", - "Microsoft.Extensions.Configuration.Json": "9.0.9", - "Microsoft.Extensions.Configuration.UserSecrets": "9.0.9", - "Microsoft.Extensions.DependencyInjection": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Diagnostics": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Physical": "9.0.9", - "Microsoft.Extensions.Hosting.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Configuration": "9.0.9", - "Microsoft.Extensions.Logging.Console": "9.0.9", - "Microsoft.Extensions.Logging.Debug": "9.0.9", - "Microsoft.Extensions.Logging.EventLog": "9.0.9", - "Microsoft.Extensions.Logging.EventSource": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Logging.Console": "10.0.1", + "Microsoft.Extensions.Logging.Debug": "10.0.1", + "Microsoft.Extensions.Logging.EventLog": "10.0.1", + "Microsoft.Extensions.Logging.EventSource": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Vortice.Direct3D11": { "type": "Direct", - "requested": "[3.6.2, )", - "resolved": "3.6.2", - "contentHash": "+tXkwV4YVFgox3NjLGBOPmEHK8Jd/8ElI3X8aX7xq4JhsC3J9BqpUhpI+4LOKeCGN+XCORhyFq0ow+pB44YQTg==", + "requested": "[3.8.1, )", + "resolved": "3.8.1", + "contentHash": "mysDbhXSTIF2YdNUOrzHVYKxTcSU2U9a30iLy3uTU8U3udl1jufaZZ3HRmyoFm7qVmRl2pu3aHjWmNUDo4ToXA==", "dependencies": { - "SharpGen.Runtime": "2.2.0-beta", - "Vortice.DXGI": "3.6.2" + "SharpGen.Runtime": "2.4.2-beta", + "Vortice.DXGI": "3.8.1" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "w87wF/90/VI0ZQBhf4rbMEeyEy0vi2WKjFmACsNAKNaorY+ZlVz7ddyXkbADvaWouMKffNmR0yQOGcrvSSvKGg==", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "p5RKAY9POvs3axwA/AQRuJeM8AHuE8h4qbP1NxQeGm0ep46aXz1oCLAp/oOYxX1GsjStgdhHrN3XXLLXr0+b3w==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "6SIp/6Bngk4jm2W36JekZbiIbFPdE/eMUtrJEqIqHGpd1zar3jvgnwxnpWQfzUiGrkyY8q8s6V82zkkEZozghA==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Configuration.CommandLine": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "9bzGOcHoTi8ijrj0MHh5qUY6n9CuittZUqEOj5iE0ZJoSCfG0BI9nhcpd8MC9bOOgjZW5OeizKO8rgta9lSVyA==", + "resolved": "10.0.1", + "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "AB8suTh4STAMGDkPer5vL0YNp09eplvbkIbOfFJ1z8D1zOiFF8Hipk9FhCLU4Ea6TosWmGrK30ZIUO9KvAeFcg==", + "resolved": "10.0.1", + "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "fvgubCs++wTowHWuQ5TAyZV0S6ldA59U+tBVqFr4/WLd0oEf6ESbdBN2CFaVdn4sZqnarqMnl2O3++RG/Jrf/w==", + "resolved": "10.0.1", + "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Physical": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "PiPYo1GTinR2ECM80zYdZUIFmde6jj5DryXUcOJg3yIjh+KQMQr42e+COD03QUsUiqNkJk511wVTnVpTm2AVZA==", + "resolved": "10.0.1", + "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.FileExtensions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "bFaNxfU8gQJX3K/Dd6XT0YIJ5ZVihdAY6Z02p2nVTUHjUsaWflLIucZOgB/ecSNnN3zbbBEf1oFC7q5NHTZIHw==", + "resolved": "10.0.1", + "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Json": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Physical": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "gtzl9SD6CvFYOb92qEF41Z9rICzYniM342TWbbJwN3eLS6a5fCLFvO1pQGtpMSnP3h1zHXupMEeKSA9musWYCQ==", + "resolved": "10.0.1", + "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9", - "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "YHGmxccrVZ2Ar3eI+/NdbOHkd1/HzrHvmQ5yBsp0Gl7jTyBe6qcXNYjUt9v9JIO+Z14la44+YYEe63JSqs1fYg==", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "M1ZhL9QkBQ/k6l/Wjgcli5zrV86HzytQ+gQiNtk9vs9Ge1fb17KKZil9T6jd15p2x/BGfXpup7Hg55CC0kkfig==", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "sRrPtEwbK23OCFOQ36Xn6ofiB0/nl54/BOdR7lJ/Vwg3XlyvUdmyXvFUS1EU5ltn+sQtbcPuy1l0hsysO8++SQ==", + "resolved": "10.0.1", + "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.FileSystemGlobbing": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "iQAgORaVIlkhcpxFnVEfjqNWfQCwBEEH7x2IanTwGafA6Tb4xiBoDWySTxUo3MV2NUV/PmwS/8OhT/elPnJCnw==" + "resolved": "10.0.1", + "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "ORA4dICNz7cuwupPkjXpSuoiK6GMg0aygInBIQCCFEimwoHntRKdJqB59faxq2HHJuTPW3NsZm5EjN5P5Zh6nQ==", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.9", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "MaCB0Y9hNDs4YLu3HCJbo199WnJT8xSgajG1JYGANz9FkseQ5f3v/llu3HxLI6mjDlu7pa7ps9BLPWjKzsAAzQ==", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "Abuo+S0Sg+Ke6vzSh5Ell+lwJJM+CEIqg1ImtWnnqF6a/ibJkQnmFJi4/ekEw/0uAcdFKJXtGV7w6cFN0nyXeg==", + "resolved": "10.0.1", + "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.9", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.9" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "x3+W7IfW9Tg3sV+sU9N1039M4CqklaAecwhz9qNtjOCBdmg7h96JaL+NAvhYgZgweVJTJaxAvuO8I+ZZehE7Pg==", + "resolved": "10.0.1", + "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging.Configuration": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "q8IbjIzTjfaGfuf9LAuG3X9BytAWj2hWhLU61rEkit847oaSSbcdx/yybY3yL9RgVG1u9ctk7kbCv18M+7Fi6Q==", + "resolved": "10.0.1", + "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "1SX5+mv16SBb5NrtLNxIvUt8PHbdvDloZazQdxz1CNM39jG7yeF6olH3sceQ4ONF0oVD5mVUsTag0iVX4xgyog==", + "resolved": "10.0.1", + "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "System.Diagnostics.EventLog": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.EventLog": "10.0.1" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "rGQi5mImot7tTFxj1tQWknWjOBHX1+gsX1WLmQNl5WHr4Sx1kXUBGDuRUjfx4c8pe/hcYHdalAmgk7RdusW6Jw==", + "resolved": "10.0.1", + "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Logging": "9.0.9", - "Microsoft.Extensions.Logging.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "loxGGHE1FC2AefwPHzrjPq7X92LQm64qnU/whKfo6oWaceewPUVYQJBJs3S3E2qlWwnCpeZ+dGCPTX+5dgVAuQ==", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "n4DCdnn2qs6V5U06Sx62FySEAZsJiJJgOzrPHDh9hPK7c2W8hEabC76F3Re3tGPjpiKa02RvB6FxZyxo8iICzg==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.9", - "Microsoft.Extensions.Configuration.Binder": "9.0.9", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9", - "Microsoft.Extensions.Options": "9.0.9", - "Microsoft.Extensions.Primitives": "9.0.9" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "z4pyMePOrl733ltTowbN565PxBw1oAr8IHmIXNDiDqd22nFpYltX9KhrNC/qBWAG1/Zx5MHX+cOYhWJQYCO/iw==" + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, "SharpGen.Runtime": { "type": "Transitive", - "resolved": "2.2.0-beta", - "contentHash": "pqf/lAf4jy1iWqkm37JmhoQhBMPVudI/F9qp2zVvzjWAPeSggRIuxGMVEZQ4UQiqtJ1Rf/+j3MVAONGYyCEDzQ==" + "resolved": "2.4.2-beta", + "contentHash": "niTtWAUov1F7wZGAlVeEvW+Tnqvg5yTmhI7rxmYu2Utoyu2TbC9tWWpUpMs0PQg5lu5svoYpCAztNjxCqMFP/w==", + "dependencies": { + "System.Text.Json": "9.0.1" + } }, "SharpGen.Runtime.COM": { "type": "Transitive", - "resolved": "2.2.0-beta", - "contentHash": "4vsXC8ohyVslcUDVBoVXLDkjKprqujh3GWy+DqqULjyZ3GCx7nwRAV5DdrXZxX70iEiKyI3TxW3Qhf/oOXeC1Q==", + "resolved": "2.4.2-beta", + "contentHash": "9lxDFMiepqo3lJQzMSjECCIONeCc2f3Drokgj6HtrTLZcJSftNgcLbD6/iXYj2iphkwgrZb1oLsDOZxoFZkawQ==", "dependencies": { - "SharpGen.Runtime": "2.2.0-beta" + "SharpGen.Runtime": "2.4.2-beta", + "System.Text.Json": "9.0.1" } }, "SharpGLTF.Core": { @@ -359,46 +364,51 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "wpsUfnyv8E5K4WQaok6weewvAbQhcLwXFcHBm5U0gdEaBs85N//ssuYvRPFWwz2rO/9/DFP3A1sGMzUFBj8y3w==" + "resolved": "10.0.1", + "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" }, "System.IO.Hashing": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "hcGHdlcKtox37LQZBLYJ3GdTlHx16F5tL96Rt8iaFscCAJW9IZt3asQbyuJMjcM9oyrn3Yh2454VY2fU0d/stw==" + "resolved": "10.0.1", + "contentHash": "Dy6ULPb2S0GmNndjKrEIpfibNsc8+FTOoZnqygtFDuyun8vWboQbfMpQtKUXpgTxokR5E4zFHETpNnGfeWY6NA==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==" }, "Vortice.DirectX": { "type": "Transitive", - "resolved": "3.6.2", - "contentHash": "pa/97U5IHascS/UJzMWFSKIs3wxh36zc8JpI+fzi/JLQk4wfTZur0n/Y9Lcp8i7hsf613Vu/6n6IImWSc9wupw==", + "resolved": "3.8.1", + "contentHash": "EtlZXbbuSYugPOolL7lgSrRkE7Xmo8ttnrsuH3uZC+xEqpSBm7F1SQroyBjhkLZstV9eSwoXpNyKLZUrt/gtXQ==", "dependencies": { - "SharpGen.Runtime": "2.2.0-beta", - "SharpGen.Runtime.COM": "2.2.0-beta", - "Vortice.Mathematics": "1.9.2" + "SharpGen.Runtime": "2.4.2-beta", + "SharpGen.Runtime.COM": "2.4.2-beta", + "Vortice.Mathematics": "2.0.0" } }, "Vortice.DXGI": { "type": "Transitive", - "resolved": "3.6.2", - "contentHash": "SPnBb1x3+CEqq7L5SEejElD2tMjkdE+bqN4c/eSNHj9WEpRPvAO0QFSEN8d9LiRDt9i9geD8m7E4s8sLMgz2qQ==", + "resolved": "3.8.1", + "contentHash": "To06vONM4kP/oBHVhsj2pd4Aj24oObiE2TyfvuPxThjpRBALgwh1qa61Fa2pVgKHyZDH3a3BbPRKekGRT+ae1A==", "dependencies": { - "SharpGen.Runtime": "2.2.0-beta", - "Vortice.DirectX": "3.6.2" + "SharpGen.Runtime": "2.4.2-beta", + "Vortice.DirectX": "3.8.1" } }, "Vortice.Mathematics": { "type": "Transitive", - "resolved": "1.9.2", - "contentHash": "+fS68BN4Kn1g+Roh7qnuBdXnVrfpr9DpwPMsqe27J04nDCFUuhEvo+T5PqXU1lZwn8sLlNayWCx/U6w4gWCvuA==" + "resolved": "2.0.0", + "contentHash": "I+ctNBG8qLWRlaQgLRiS08HZTiuTdvlrYhrxp5B7uOJHD0QQaDxRfrV0cq4xxKyj+9+jqJZMy28l/NwvsgyVlQ==" }, "meddle.utils": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "[9.0.9, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.1, )", "SharpGLTF.Core": "[1.0.5, )", "SharpGLTF.Toolkit": "[1.0.5, )", "SkiaSharp": "[3.119.0, )", - "System.IO.Hashing": "[9.0.9, )" + "System.IO.Hashing": "[10.0.1, )" } } } diff --git a/Meddle/Meddle.Utils/Meddle.Utils.csproj b/Meddle/Meddle.Utils/Meddle.Utils.csproj index d7765bf..0cd8d53 100644 --- a/Meddle/Meddle.Utils/Meddle.Utils.csproj +++ b/Meddle/Meddle.Utils/Meddle.Utils.csproj @@ -4,7 +4,7 @@ enable true 13 - net9.0-windows + net10.0-windows @@ -21,10 +21,10 @@ - + - + diff --git a/Meddle/Meddle.Utils/packages.lock.json b/Meddle/Meddle.Utils/packages.lock.json index bbeead9..a83e44b 100644 --- a/Meddle/Meddle.Utils/packages.lock.json +++ b/Meddle/Meddle.Utils/packages.lock.json @@ -1,14 +1,14 @@ { "version": 1, "dependencies": { - "net9.0-windows7.0": { + "net10.0-windows7.0": { "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "FEgpSF+Z9StMvrsSViaybOBwR0f0ZZxDm8xV5cSOFiXN/t+ys+rwAlTd/6yG7Ld1gfppgvLcMasZry3GsI9lGA==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.9" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, "SharpGLTF.Core": { @@ -38,14 +38,14 @@ }, "System.IO.Hashing": { "type": "Direct", - "requested": "[9.0.9, )", - "resolved": "9.0.9", - "contentHash": "hcGHdlcKtox37LQZBLYJ3GdTlHx16F5tL96Rt8iaFscCAJW9IZt3asQbyuJMjcM9oyrn3Yh2454VY2fU0d/stw==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Dy6ULPb2S0GmNndjKrEIpfibNsc8+FTOoZnqygtFDuyun8vWboQbfMpQtKUXpgTxokR5E4zFHETpNnGfeWY6NA==" }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.9", - "contentHash": "/hymojfWbE9AlDOa0mczR44m00Jj+T3+HZO0ZnVTI032fVycI0ZbNOVFP6kqZMcXiLSYXzR2ilcwaRi6dzeGyA==" + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, "SharpGLTF.Runtime": { "type": "Transitive", From fa44fea56a25d2531d1bc960eb4032d7340576f8 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:15:54 +1100 Subject: [PATCH 2/8] pipeline version bump --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/test_release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bc4462..1b3a2ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Extract version and custom path run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e80f46..c956345 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - dotnet-version: [ 9.0.x ] # Can add multiple .NET versions here to test against (For example, testing a new version of .NET before it's released) + dotnet-version: [ 10.0.x ] # Can add multiple .NET versions here to test against (For example, testing a new version of .NET before it's released) dalamud-version: [ "stg_latest", "latest" ] # Can add multiple Dalamud branches here to test against (For example, testing against Dalamud staging) env: DALAMUD_HOME: /tmp/dalamud diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index 34326fa..edd7282 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -43,7 +43,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Display build information run: | From 601850965e07c0889c937f0777f326a7b07802ec Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:30:00 +1100 Subject: [PATCH 3/8] sdk bump --- Meddle/Meddle.Plugin/Meddle.Plugin.csproj | 2 +- .../Models/Layout/ParsedInstance.cs | 6 ----- Meddle/Meddle.Plugin/packages.lock.json | 25 ++++++------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj index 32e1d38..fceb402 100644 --- a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj +++ b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj @@ -1,4 +1,4 @@ - + 0.0.1 enable diff --git a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs index d1815ab..2b085d2 100644 --- a/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs +++ b/Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs @@ -219,22 +219,16 @@ public ParsedStain(Stain stain) Color = ImGui.ColorConvertU32ToFloat4(UiUtil.SeColorToRgba(SeColor)); RowId = stain.RowId; Name = stain.Name.ExtractText(); - Name2 = stain.Name2.ExtractText(); Shade = stain.Shade; SubOrder = stain.SubOrder; - Unknown1 = stain.Unknown1; - Unknown2 = stain.Unknown2; } public uint SeColor { get; } public Vector4 Color { get; } public uint RowId { get; } public string Name { get; } - public string Name2 { get; } public uint Shade { get; } public uint SubOrder { get; } - public bool Unknown1 { get; } - public bool Unknown2 { get; } public static implicit operator ParsedStain(Stain stain) => new(stain); public static implicit operator ParsedStain?(Stain? stain) => stain == null ? null : new ParsedStain(stain.Value); diff --git a/Meddle/Meddle.Plugin/packages.lock.json b/Meddle/Meddle.Plugin/packages.lock.json index 4b8a97c..f4b69b8 100644 --- a/Meddle/Meddle.Plugin/packages.lock.json +++ b/Meddle/Meddle.Plugin/packages.lock.json @@ -4,15 +4,15 @@ "net10.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[13.1.0, )", - "resolved": "13.1.0", - "contentHash": "XdoNhJGyFby5M/sdcRhnc5xTop9PHy+H50PTWpzLhJugjB19EDBiHD/AsiDF66RETM+0qKUdJBZrNuebn7qswQ==" + "requested": "[14.0.0, )", + "resolved": "14.0.0", + "contentHash": "9c1q/eAeAs82mkQWBOaCvbt3GIQxAIadz5b/7pCXDIy9nHPtnRc+tDXEvKR+M36Wvi7n+qBTevRupkLUQp6DFA==" }, "DotNet.ReproducibleBuilds": { "type": "Direct", - "requested": "[1.2.25, )", - "resolved": "1.2.25", - "contentHash": "xCXiw7BCxHJ8pF6wPepRUddlh2dlQlbr81gXA72hdk4FLHkKXas7EH/n+fk5UCA/YfMqG1Z6XaPiUjDbUNBUzg==" + "requested": "[1.2.39, )", + "resolved": "1.2.39", + "contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg==" }, "Microsoft.Extensions.DependencyInjection": { "type": "Direct", @@ -308,18 +308,14 @@ "SharpGen.Runtime": { "type": "Transitive", "resolved": "2.4.2-beta", - "contentHash": "niTtWAUov1F7wZGAlVeEvW+Tnqvg5yTmhI7rxmYu2Utoyu2TbC9tWWpUpMs0PQg5lu5svoYpCAztNjxCqMFP/w==", - "dependencies": { - "System.Text.Json": "9.0.1" - } + "contentHash": "niTtWAUov1F7wZGAlVeEvW+Tnqvg5yTmhI7rxmYu2Utoyu2TbC9tWWpUpMs0PQg5lu5svoYpCAztNjxCqMFP/w==" }, "SharpGen.Runtime.COM": { "type": "Transitive", "resolved": "2.4.2-beta", "contentHash": "9lxDFMiepqo3lJQzMSjECCIONeCc2f3Drokgj6HtrTLZcJSftNgcLbD6/iXYj2iphkwgrZb1oLsDOZxoFZkawQ==", "dependencies": { - "SharpGen.Runtime": "2.4.2-beta", - "System.Text.Json": "9.0.1" + "SharpGen.Runtime": "2.4.2-beta" } }, "SharpGLTF.Core": { @@ -372,11 +368,6 @@ "resolved": "10.0.1", "contentHash": "Dy6ULPb2S0GmNndjKrEIpfibNsc8+FTOoZnqygtFDuyun8vWboQbfMpQtKUXpgTxokR5E4zFHETpNnGfeWY6NA==" }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "9.0.1", - "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==" - }, "Vortice.DirectX": { "type": "Transitive", "resolved": "3.8.1", From 6d34b098d21edaf619f2454057408823db59f448 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:39:54 +1100 Subject: [PATCH 4/8] Bump langversion and cleanup --- Meddle/Meddle.Plugin/Meddle.Plugin.csproj | 2 +- Meddle/Meddle.Plugin/Services/ParseService.cs | 38 ------ .../Meddle.Plugin/Services/ResourceHooks.cs | 112 ------------------ Meddle/Meddle.Plugin/UI/AnimationTab.cs | 6 +- .../{Services => }/UI/CommonUI.cs | 32 ++--- Meddle/Meddle.Plugin/UI/DebugTab.cs | 63 +--------- .../Meddle.Plugin/UI/Layout/LayoutWindow.cs | 2 +- Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs | 6 +- .../Meddle.Plugin/UI/MaterialParameterTab.cs | 1 - Meddle/Meddle.Plugin/UI/TestFunctionTab.cs | 3 +- .../Utils/ObjectTableExtensions.cs | 93 +++++++++++++++ Meddle/Meddle.Plugin/Utils/ObjectUtil.cs | 51 -------- Meddle/Meddle.Utils/Files/SqPack/Category.cs | 2 +- Meddle/Meddle.Utils/Meddle.Utils.csproj | 2 +- 14 files changed, 113 insertions(+), 300 deletions(-) delete mode 100644 Meddle/Meddle.Plugin/Services/ParseService.cs delete mode 100644 Meddle/Meddle.Plugin/Services/ResourceHooks.cs rename Meddle/Meddle.Plugin/{Services => }/UI/CommonUI.cs (86%) create mode 100644 Meddle/Meddle.Plugin/Utils/ObjectTableExtensions.cs diff --git a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj index fceb402..d84b213 100644 --- a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj +++ b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj @@ -4,7 +4,7 @@ enable enable Meddle.Plugin - 13 + 14 net10.0-windows diff --git a/Meddle/Meddle.Plugin/Services/ParseService.cs b/Meddle/Meddle.Plugin/Services/ParseService.cs deleted file mode 100644 index 2760e40..0000000 --- a/Meddle/Meddle.Plugin/Services/ParseService.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Concurrent; -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using Meddle.Plugin.Utils; -using Meddle.Utils; -using Meddle.Utils.Files; -using Meddle.Utils.Files.Structs.Material; -using Meddle.Utils.Helpers; -using Microsoft.Extensions.Logging; -using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture; - -namespace Meddle.Plugin.Services; - -public class ParseService : IDisposable, IService -{ - private readonly EventLogger logger; - - public readonly ConcurrentDictionary ShpkCache = new(); - public readonly ConcurrentDictionary MdlCache = new(); - public readonly ConcurrentDictionary MtrlCache = new(); - public readonly ConcurrentDictionary TexCache = new(); - public void ClearCaches() - { - ShpkCache.Clear(); - MdlCache.Clear(); - MtrlCache.Clear(); - TexCache.Clear(); - } - - public ParseService(ILogger logger) - { - this.logger = new EventLogger(logger); - } - - public void Dispose() - { - logger.LogDebug("Disposing ParseUtil"); - } -} diff --git a/Meddle/Meddle.Plugin/Services/ResourceHooks.cs b/Meddle/Meddle.Plugin/Services/ResourceHooks.cs deleted file mode 100644 index 42bb002..0000000 --- a/Meddle/Meddle.Plugin/Services/ResourceHooks.cs +++ /dev/null @@ -1,112 +0,0 @@ -/*using System.Runtime.InteropServices; -using System.Text; -using Dalamud.Game; -using Dalamud.Hooking; -using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.System.Resource; -using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using Microsoft.Extensions.Logging; - -namespace Meddle.Plugin.Services; - -public unsafe class ResourceHooks : IDisposable, IService -{ - private readonly ILogger logger; - public const string GetResourceAsyncSig = "E8 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 83 C4 68"; - public const string GetResourceSyncSig = "E8 ?? ?? ?? ?? 48 8B D8 8B C7"; - - private readonly Hook? getResourceAsyncHook; - private readonly Hook? getResourceSyncHook; - private delegate nint GetResourceAsyncDelegate(ResourceManager* resourceManager, ResourceCategory* category, uint* type, uint* hash, byte* path, void* unknown, bool isUnknown); - private delegate nint GetResourceSyncDelegate(ResourceManager* resourceManager, ResourceCategory* category, uint* type, uint* hash, byte* path, void* unknown); - private readonly Dictionary resourceRequests = new(); - - private class ResourceRequestInfo - { - public required string Path { get; set; } - public int RequestCount { get; set; } - public required uint Type { get; set; } - } - - public ResourceHooks(ISigScanner sigScanner, IGameInteropProvider gameInterop, ILogger logger) - { - this.logger = logger; - - if (sigScanner.TryScanText(GetResourceAsyncSig, out var getResourceAsyncPtr)) - { - logger.LogDebug("Found GetResourceAsync at {ptr:X}", getResourceAsyncPtr); - getResourceAsyncHook = gameInterop.HookFromAddress(getResourceAsyncPtr, GetResourceAsyncDetour); - getResourceAsyncHook.Enable(); - } - - if (sigScanner.TryScanText(GetResourceSyncSig, out var getResourceSyncPtr)) - { - logger.LogDebug("Found GetResourceSync at {ptr:X}", getResourceSyncPtr); - getResourceSyncHook = gameInterop.HookFromAddress(getResourceSyncPtr, GetResourceSyncDetour); - getResourceSyncHook.Enable(); - } - } - - - public void Dispose() - { - getResourceAsyncHook?.Disable(); - getResourceSyncHook?.Disable(); - getResourceAsyncHook?.Dispose(); - getResourceSyncHook?.Dispose(); - } - - private nint GetResourceSyncDetour(ResourceManager* resourceManager, ResourceCategory* category, uint* type, uint* hash, byte* path, void* unknown) - { - var ret = getResourceSyncHook!.Original(resourceManager, category, type, hash, path, unknown); - ProcessHook(path, type, ret, Source.Sync); - return ret; - } - - private nint GetResourceAsyncDetour(ResourceManager* resourceManager, ResourceCategory* category, uint* type, uint* hash, byte* path, void* unknown, bool isUnknown) - { - var ret = getResourceAsyncHook!.Original(resourceManager, category, type, hash, path, unknown, isUnknown); - ProcessHook(path, type, ret, Source.Async); - return ret; - } - - private enum Source - { - Sync, - Async - } - - private void ProcessHook(byte* pathPtr, uint* type, nint resourceHandlePtr, Source source) - { - try - { - if ((nint)pathPtr == nint.Zero) return; - var path = Marshal.PtrToStringAnsi((nint)pathPtr); - if (string.IsNullOrEmpty(path)) return; - if (!resourceRequests.TryGetValue(path, out var requestInfo)) - { - requestInfo = new ResourceRequestInfo { Path = path, Type = 0 }; - var typeValueStr = Marshal.PtrToStringAnsi((nint)type); - - - resourceRequests[path] = requestInfo; - if (resourceHandlePtr != nint.Zero) - { - var resourceHandle = (ResourceHandle*)resourceHandlePtr; - - logger.LogDebug("[{typeValueStr}][{source}] Loaded {path}", typeValueStr, source, path); - } - else - { - logger.LogDebug("[{typeValueStr}][{source}] Requested {path}", typeValueStr, source, path); - } - } - - requestInfo.RequestCount++; - } - catch (Exception ex) - { - logger.LogError(ex, "Error processing hook"); - } - } -}*/ diff --git a/Meddle/Meddle.Plugin/UI/AnimationTab.cs b/Meddle/Meddle.Plugin/UI/AnimationTab.cs index 3f9cae0..0babae2 100644 --- a/Meddle/Meddle.Plugin/UI/AnimationTab.cs +++ b/Meddle/Meddle.Plugin/UI/AnimationTab.cs @@ -9,7 +9,6 @@ using Dalamud.Bindings.ImGui; using Meddle.Plugin.Models; using Meddle.Plugin.Services; -using Meddle.Plugin.Services.UI; using Meddle.Plugin.Utils; using Microsoft.Extensions.Logging; using SharpGLTF.Transforms; @@ -25,6 +24,7 @@ public class AnimationTab : ITab { private readonly AnimationExportService animationExportService; private readonly CommonUi commonUi; + private readonly IObjectTable objectTable; private readonly Configuration config; private readonly List<(DateTime Time, AttachSet[])> frames = []; private readonly IFramework framework; @@ -45,12 +45,14 @@ public AnimationTab( IFramework framework, ILogger logger, AnimationExportService animationExportService, CommonUi commonUi, + IObjectTable objectTable, Configuration config) { this.framework = framework; this.logger = logger; this.animationExportService = animationExportService; this.commonUi = commonUi; + this.objectTable = objectTable; this.config = config; this.framework.Update += OnFrameworkUpdate; } @@ -182,7 +184,7 @@ private unsafe void Capture() return; } - var characters = commonUi.GetCharacters() + var characters = objectTable.GetCharacters() .Where(x => selectedCharacters.Any(s => s.Address == x.Address)).ToArray(); if (characters.Length == 0) { diff --git a/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs b/Meddle/Meddle.Plugin/UI/CommonUI.cs similarity index 86% rename from Meddle/Meddle.Plugin/Services/UI/CommonUI.cs rename to Meddle/Meddle.Plugin/UI/CommonUI.cs index 55cd802..921e612 100644 --- a/Meddle/Meddle.Plugin/Services/UI/CommonUI.cs +++ b/Meddle/Meddle.Plugin/UI/CommonUI.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface; using Dalamud.Interface.Colors; @@ -7,11 +8,11 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using Dalamud.Bindings.ImGui; +using Meddle.Plugin.Services; using Meddle.Plugin.Utils; using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind; -namespace Meddle.Plugin.Services.UI; +namespace Meddle.Plugin.UI; public class CommonUi : IDisposable, IService { @@ -26,29 +27,10 @@ public CommonUi(IClientState clientState, IObjectTable objectTable, Configuratio this.objectTable = objectTable; this.config = config; } - - public unsafe ICharacter[] GetCharacters(ObjectUtil.ValidationFlags flags = ObjectUtil.ValidationFlags.None) - { - if (objectTable.LocalPlayer != null) - { - return objectTable.OfType() - .Where(obj => obj.IsValid() && obj.IsValidCharacterBase(flags)) - .OrderBy(c => objectTable.GetDistanceToLocalPlayer(c).LengthSquared()) - .ToArray(); - } - else - { - // login/char creator produces "invalid" characters but are still usable I guess - return objectTable.OfType() - .Where(obj => obj.IsValidHuman(flags)) - .OrderBy(c => objectTable.GetDistanceToLocalPlayer(c).LengthSquared()) - .ToArray(); - } - } - public unsafe void DrawMultiCharacterSelect(ref List selectedCharacters, ObjectUtil.ValidationFlags flags = ObjectUtil.ValidationFlags.None) + public unsafe void DrawMultiCharacterSelect(ref List selectedCharacters, CharacterValidationFlags flags = CharacterValidationFlags.None) { - ICharacter[] objects = GetCharacters(flags); + ICharacter[] objects = objectTable.GetCharacters(flags); ImGui.Text("Select Characters"); var selected = new List(); @@ -113,9 +95,9 @@ public unsafe void DrawMultiCharacterSelect(ref List selectedCharact } } - public unsafe void DrawCharacterSelect(ref ICharacter? selectedCharacter, ObjectUtil.ValidationFlags flags = ObjectUtil.ValidationFlags.None) + public unsafe void DrawCharacterSelect(ref ICharacter? selectedCharacter, CharacterValidationFlags flags = CharacterValidationFlags.None) { - ICharacter[] objects = GetCharacters(flags); + ICharacter[] objects = objectTable.GetCharacters(flags); selectedCharacter ??= objects.FirstOrDefault() ?? objectTable.LocalPlayer; diff --git a/Meddle/Meddle.Plugin/UI/DebugTab.cs b/Meddle/Meddle.Plugin/UI/DebugTab.cs index e173aaa..ba275aa 100644 --- a/Meddle/Meddle.Plugin/UI/DebugTab.cs +++ b/Meddle/Meddle.Plugin/UI/DebugTab.cs @@ -15,7 +15,6 @@ using Meddle.Plugin.Models.Layout; using Meddle.Plugin.Models.Structs; using Meddle.Plugin.Services; -using Meddle.Plugin.Services.UI; using Meddle.Plugin.UI.Layout; using Meddle.Plugin.Utils; using Meddle.Utils.Constants; @@ -36,7 +35,6 @@ public class DebugTab : ITab private readonly CommonUi commonUi; private readonly IGameGui gui; private readonly LayoutService layoutService; - private readonly ParseService parseService; private readonly PbdHooks pbdHooks; private readonly INotificationManager notificationManager; private readonly SqPack sqPack; @@ -66,7 +64,7 @@ private enum BoneMode public DebugTab(Configuration config, SigUtil sigUtil, CommonUi commonUi, IGameGui gui, IClientState clientState, LayoutService layoutService, - ParseService parseService, PbdHooks pbdHooks, + PbdHooks pbdHooks, INotificationManager notificationManager, ITextureProvider textureProvider, SqPack sqPack, @@ -80,7 +78,6 @@ public DebugTab(Configuration config, SigUtil sigUtil, CommonUi commonUi, this.gui = gui; this.clientState = clientState; this.layoutService = layoutService; - this.parseService = parseService; this.pbdHooks = pbdHooks; this.notificationManager = notificationManager; this.textureProvider = textureProvider; @@ -225,12 +222,7 @@ public void Draw() { DrawAddresses(); } - - if (ImGui.CollapsingHeader("Cache Info")) - { - DrawCacheInfo(); - } - + if (ImGui.CollapsingHeader("Stain Info")) { DrawStainInfo(); @@ -491,56 +483,7 @@ private unsafe void DrawAddresses() UiUtil.Text($"LayoutWorld: {(nint)layoutWorld:X8}", $"{(nint)layoutWorld:X8}"); UiUtil.Text($"ActiveLayout: {(nint)activeLayout:X8}", $"{(nint)activeLayout:X8}"); } - - private void DrawCacheInfo() - { - if (ImGui.Button("Clear Caches")) - { - parseService.ClearCaches(); - } - - using var table = ImRaii.Table("##CacheInfo", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); - ImGui.TableSetupColumn("Type"); - ImGui.TableSetupColumn("Path"); - ImGui.TableHeadersRow(); - - foreach (var (path, _) in parseService.ShpkCache) - { - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - ImGui.Text("Shpk"); - ImGui.TableNextColumn(); - ImGui.Text(path); - } - - foreach (var (path, _) in parseService.MtrlCache) - { - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - ImGui.Text("Mtrl"); - ImGui.TableNextColumn(); - ImGui.Text(path); - } - - foreach (var (path, _) in parseService.MdlCache) - { - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - ImGui.Text("Mdl"); - ImGui.TableNextColumn(); - ImGui.Text(path); - } - - foreach (var (path, _) in parseService.TexCache) - { - ImGui.TableNextRow(); - ImGui.TableNextColumn(); - ImGui.Text("Tex"); - ImGui.TableNextColumn(); - ImGui.Text(path); - } - } - + private unsafe void DrawSelectedCharacter() { using var indent = ImRaii.PushIndent(); diff --git a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs index a9c2415..bbb91d2 100644 --- a/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs +++ b/Meddle/Meddle.Plugin/UI/Layout/LayoutWindow.cs @@ -319,7 +319,7 @@ private void DrawLayoutButtons(Stack stack, ParsedInstance insta { DrawExportSingle(instance); - if (config.DisplayDebugInfo && instance is ParsedBgPartsInstance bg) + if (config.DisplayDebugInfo && instance is ParsedBgPartsInstance {ModelPtr: not null} bg) { // open mdl/material window ImGui.SameLine(); diff --git a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs index 831c8ee..56a35ad 100644 --- a/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs +++ b/Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs @@ -15,7 +15,6 @@ using Meddle.Plugin.Models.Composer; using Meddle.Plugin.Models.Layout; using Meddle.Plugin.Services; -using Meddle.Plugin.Services.UI; using Meddle.Plugin.UI.Layout; using Meddle.Plugin.UI.Windows; using Meddle.Plugin.Utils; @@ -51,7 +50,6 @@ public unsafe class LiveCharacterTab : ITab private readonly ILogger log; private readonly SqPack pack; - private readonly ParseService parseService; private readonly PbdHooks pbd; private readonly Dictionary selectedModels = new(); private readonly TextureCache textureCache; @@ -69,7 +67,6 @@ public unsafe class LiveCharacterTab : ITab public LiveCharacterTab( ILogger log, ITextureProvider textureProvider, - ParseService parseService, TextureCache textureCache, ResolverService resolverService, StainProvider stainProvider, @@ -83,7 +80,6 @@ public LiveCharacterTab( { this.log = log; this.textureProvider = textureProvider; - this.parseService = parseService; this.textureCache = textureCache; this.resolverService = resolverService; this.stainProvider = stainProvider; @@ -104,7 +100,7 @@ public LiveCharacterTab( public void Draw() { UiUtil.DrawProgress(exportTask, progress, cancelToken); - commonUi.DrawCharacterSelect(ref selectedCharacter, ObjectUtil.ValidationFlags.IsVisible); + commonUi.DrawCharacterSelect(ref selectedCharacter, CharacterValidationFlags.IsVisible); if (selectedCharacter != null) { var charPtr = (CSCharacter*)selectedCharacter.Address; diff --git a/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs b/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs index 8b9e628..e96c021 100644 --- a/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs +++ b/Meddle/Meddle.Plugin/UI/MaterialParameterTab.cs @@ -7,7 +7,6 @@ using Dalamud.Bindings.ImGui; using Meddle.Plugin.Models; using Meddle.Plugin.Models.Structs; -using Meddle.Plugin.Services.UI; using Meddle.Plugin.Utils; using Meddle.Utils.Constants; using Meddle.Utils.Files; diff --git a/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs b/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs index cda612d..a38c1a4 100644 --- a/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs +++ b/Meddle/Meddle.Plugin/UI/TestFunctionTab.cs @@ -8,7 +8,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Meddle.Plugin.Models; using Meddle.Plugin.Services; -using Meddle.Plugin.Services.UI; using Meddle.Plugin.Utils; using Microsoft.Extensions.Logging; @@ -41,7 +40,7 @@ public TestFunctionTab( public unsafe void Draw() { - commonUi.DrawCharacterSelect(ref selectedCharacter, ObjectUtil.ValidationFlags.IsVisible); + commonUi.DrawCharacterSelect(ref selectedCharacter, CharacterValidationFlags.IsVisible); if (output != null) { var serialized = System.Text.Json.JsonSerializer.Serialize(output, new System.Text.Json.JsonSerializerOptions diff --git a/Meddle/Meddle.Plugin/Utils/ObjectTableExtensions.cs b/Meddle/Meddle.Plugin/Utils/ObjectTableExtensions.cs new file mode 100644 index 0000000..ec16201 --- /dev/null +++ b/Meddle/Meddle.Plugin/Utils/ObjectTableExtensions.cs @@ -0,0 +1,93 @@ +using System.Numerics; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using CSCharacter = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; + +namespace Meddle.Plugin.Utils; + +[Flags] +public enum CharacterValidationFlags +{ + None = 0, + IsVisible = 1 << 0 +} + +public static class ObjectTableExtensions +{ + extension(IObjectTable table) + { + public ICharacter[] GetCharacters(CharacterValidationFlags flags = CharacterValidationFlags.None) + { + if (table.LocalPlayer != null) + { + return table.OfType() + .Where(obj => obj.IsValid() && obj.IsValidCharacterBase(flags)) + .OrderBy(c => table.GetDistanceToLocalPlayer(c).LengthSquared()) + .ToArray(); + } + else + { + // login/char creator produces "invalid" characters but are still usable I guess + return table.OfType() + .Where(obj => obj.IsValidHuman(flags)) + .OrderBy(c => table.GetDistanceToLocalPlayer(c).LengthSquared()) + .ToArray(); + } + } + + public Vector3 GetDistanceToLocalPlayer(IGameObject obj) + { + if (table.LocalPlayer == null) + { + return Vector3.Zero; + } + + var lpPos = table.LocalPlayer.Position; + var objPos = obj.Position; + return new Vector3( + objPos.X - lpPos.X, + objPos.Y - lpPos.Y, + objPos.Z - lpPos.Z); + } + } + + extension(ICharacter character) + { + public unsafe bool IsValidHuman(CharacterValidationFlags flags = CharacterValidationFlags.None) + { + var drawObject = ((CSCharacter*)character.Address)->GameObject.DrawObject; + if (drawObject == null) + return false; + if (drawObject->Object.GetObjectType() != ObjectType.CharacterBase) + return false; + if (((CharacterBase*)drawObject)->GetModelType() != CharacterBase.ModelType.Human) + return false; + + if (flags.HasFlag(CharacterValidationFlags.IsVisible) && !drawObject->IsVisible) + { + return false; + } + + return true; + } + + public unsafe bool IsValidCharacterBase(CharacterValidationFlags flags = CharacterValidationFlags.None) + { + if (!character.IsValid()) + return false; + var drawObject = ((CSCharacter*)character.Address)->GameObject.DrawObject; + if (drawObject == null) + return false; + if (drawObject->Object.GetObjectType() != ObjectType.CharacterBase) + return false; + + if (flags.HasFlag(CharacterValidationFlags.IsVisible) && !drawObject->IsVisible) + { + return false; + } + + return true; + } + } +} diff --git a/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs b/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs index 8091f8a..84e6624 100644 --- a/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs +++ b/Meddle/Meddle.Plugin/Utils/ObjectUtil.cs @@ -3,7 +3,6 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using SharpGLTF.Transforms; -using CSCharacter = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; namespace Meddle.Plugin.Utils; @@ -23,54 +22,4 @@ public static string ToFormatted(this Quaternion vector) { return $""; } - - [Flags] - public enum ValidationFlags - { - None = 0, - IsVisible = 1 << 0 - } - - public static unsafe bool IsValidHuman(this ICharacter obj, ValidationFlags flags = ValidationFlags.None) - { - var drawObject = ((CSCharacter*)obj.Address)->GameObject.DrawObject; - if (drawObject == null) - return false; - if (drawObject->Object.GetObjectType() != ObjectType.CharacterBase) - return false; - if (((CharacterBase*)drawObject)->GetModelType() != CharacterBase.ModelType.Human) - return false; - - if (flags.HasFlag(ValidationFlags.IsVisible) && !drawObject->IsVisible) - { - return false; - } - - return true; - } - - public static unsafe bool IsValidCharacterBase(this ICharacter obj, ValidationFlags flags = ValidationFlags.None) - { - if (!obj.IsValid()) - return false; - var drawObject = ((CSCharacter*)obj.Address)->GameObject.DrawObject; - if (drawObject == null) - return false; - if (drawObject->Object.GetObjectType() != ObjectType.CharacterBase) - return false; - - if (flags.HasFlag(ValidationFlags.IsVisible) && !drawObject->IsVisible) - { - return false; - } - - return true; - } - - public static Vector3 GetDistanceToLocalPlayer(this IObjectTable objectTable, IGameObject obj) - { - if (objectTable.LocalPlayer is {Position: var charPos}) - return Vector3.Abs(obj.Position - charPos); - return new Vector3(obj.YalmDistanceX, 0, obj.YalmDistanceZ); - } } diff --git a/Meddle/Meddle.Utils/Files/SqPack/Category.cs b/Meddle/Meddle.Utils/Files/SqPack/Category.cs index de68c1a..de6a127 100644 --- a/Meddle/Meddle.Utils/Files/SqPack/Category.cs +++ b/Meddle/Meddle.Utils/Files/SqPack/Category.cs @@ -72,7 +72,7 @@ public bool TryGetFile(ulong hash, out SqPackFile data) return false; } - data = SqPackUtil.ReadFile(entry.Offset, datFilePaths[entry.DataFileId]); + data = SqPackUtil.ReadFile(entry.Offset, datFilePaths[entry.DataFileId]) ?? throw new Exception($"Failed to read file with hash {hash:X}"); return true; } diff --git a/Meddle/Meddle.Utils/Meddle.Utils.csproj b/Meddle/Meddle.Utils/Meddle.Utils.csproj index 0cd8d53..56a1eb2 100644 --- a/Meddle/Meddle.Utils/Meddle.Utils.csproj +++ b/Meddle/Meddle.Utils/Meddle.Utils.csproj @@ -3,7 +3,7 @@ enable enable true - 13 + 14 net10.0-windows From 7803c9e483c0ab1fa0ad813e7fe9c865a9d4de23 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:10:49 +1100 Subject: [PATCH 5/8] Pipeline version bump --- .github/workflows/release.yml | 8 ++++---- .github/workflows/test.yml | 6 +++--- .github/workflows/test_release.yml | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b3a2ff..b26b937 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,12 +27,12 @@ jobs: IsCI: true steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true # Grab any submodules that may be required - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x @@ -86,7 +86,7 @@ jobs: sha512sum latest.zip >> checksums.sha512 - name: Create GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: | Meddle/Meddle.Plugin/bin/Release/Meddle.Plugin/latest.zip @@ -98,7 +98,7 @@ jobs: fail_on_unmatched_files: true # If the files arent found, fail the workflow and abort the release. - name: Upload Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Release Artifacts path: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c956345..6369d66 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,13 +32,13 @@ jobs: IsCI: true steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 # We don't need the history for testing builds, so we can save some time by not fetching it submodules: true # Grab any submodules that may be required - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: ${{ matrix.dotnet-version }} @@ -60,7 +60,7 @@ jobs: run: dotnet build -c Release --no-restore --nologo -o ./bin/${{ matrix.dalamud-version }}/Release - name: Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Meddle.Plugin-${{ matrix.dalamud-version }}-${{ github.sha }} path: Meddle/Meddle.Plugin/bin/${{ matrix.dalamud-version }}/Release diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index edd7282..47396c4 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -36,12 +36,12 @@ jobs: DOWNLOAD_PATH: ${{ github.event.inputs.dalamud_branch }} steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true # Grab any submodules that may be required - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x @@ -79,7 +79,7 @@ jobs: sha512sum latest.zip >> checksums.sha512 - name: Create GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: tag_name: testing_v${{ env.VERSION }}${{ env.DOWNLOAD_PATH && format('_{0}', env.DOWNLOAD_PATH) || '' }} name: Test Release v${{ env.VERSION }}${{ env.DOWNLOAD_PATH && format(' ({0})', env.DOWNLOAD_PATH) || '' }} @@ -93,7 +93,7 @@ jobs: fail_on_unmatched_files: true # If the files arent found, fail the workflow and abort the release. - name: Upload Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Release Artifacts path: | From b59f70c7a66de5b3b5c86a701761fb852d671018 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Fri, 19 Dec 2025 07:45:32 +1100 Subject: [PATCH 6/8] Bump dalamudpackager --- Meddle/Meddle.Plugin/Meddle.Plugin.csproj | 2 +- Meddle/Meddle.Plugin/packages.lock.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj index d84b213..1c256ef 100644 --- a/Meddle/Meddle.Plugin/Meddle.Plugin.csproj +++ b/Meddle/Meddle.Plugin/Meddle.Plugin.csproj @@ -1,4 +1,4 @@ - + 0.0.1 enable diff --git a/Meddle/Meddle.Plugin/packages.lock.json b/Meddle/Meddle.Plugin/packages.lock.json index f4b69b8..d504e7f 100644 --- a/Meddle/Meddle.Plugin/packages.lock.json +++ b/Meddle/Meddle.Plugin/packages.lock.json @@ -4,9 +4,9 @@ "net10.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[14.0.0, )", - "resolved": "14.0.0", - "contentHash": "9c1q/eAeAs82mkQWBOaCvbt3GIQxAIadz5b/7pCXDIy9nHPtnRc+tDXEvKR+M36Wvi7n+qBTevRupkLUQp6DFA==" + "requested": "[14.0.1, )", + "resolved": "14.0.1", + "contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA==" }, "DotNet.ReproducibleBuilds": { "type": "Direct", From 5d057fd8c0be99340c86e9c94d590ec9dbf35e4a Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:08:01 +1100 Subject: [PATCH 7/8] Update test_release.yml --- .github/workflows/test_release.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index 47396c4..ecc65be 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -73,10 +73,12 @@ jobs: -p:AssemblyVersion=${{ env.VERSION }} \ -p:FileVersion=${{ env.VERSION }} - - name: Generate Checksums + - name: Generate Checksums & Export ApiLevel working-directory: Meddle/Meddle.Plugin/bin/Release/Meddle.Plugin run: | sha512sum latest.zip >> checksums.sha512 + dalamud_api_level=$(jq -r '.DalamudApiLevel' Meddle.Plugin.json) + echo "DALAMUD_API_LEVEL=$dalamud_api_level" >> $GITHUB_ENV - name: Create GitHub Release uses: softprops/action-gh-release@v2 @@ -124,7 +126,8 @@ jobs: jq --arg version "${{ env.VERSION }}" \ --arg repo_url "$repo_url/releases/download/$tag_name/latest.zip" \ '.[0].TestingAssemblyVersion = $version | - .[0].DownloadLinkTesting = $repo_url' \ + .[0].DownloadLinkTesting = $repo_url | + .[0].TestingDalamudApiLevel = '"${{ env.DALAMUD_API_LEVEL }}"'' \ repo.json > tmp.json && mv tmp.json repo.json cat repo.json From 8ec38f773a6dc9a3ca997986be146e325897065a Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:11:32 +1100 Subject: [PATCH 8/8] Update release.yml --- .github/workflows/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b26b937..0ca1395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,10 +80,12 @@ jobs: -p:AssemblyVersion=${{ env.VERSION }} \ -p:FileVersion=${{ env.VERSION }} - - name: Generate Checksums + - name: Generate Checksums & Export ApiLevel working-directory: Meddle/Meddle.Plugin/bin/Release/Meddle.Plugin run: | sha512sum latest.zip >> checksums.sha512 + dalamud_api_level=$(jq -r '.DalamudApiLevel' Meddle.Plugin.json) + echo "DALAMUD_API_LEVEL=$dalamud_api_level" >> $GITHUB_ENV - name: Create GitHub Release uses: softprops/action-gh-release@v2 @@ -121,7 +123,9 @@ jobs: .[0].TestingAssemblyVersion = $release_version | .[0].DownloadLinkInstall = $repo_url | .[0].DownloadLinkUpdate = $repo_url | - .[0].DownloadLinkTesting = $repo_url' \ + .[0].DownloadLinkTesting = $repo_url | + .[0].DalamudApiLevel = '"${{ env.DALAMUD_API_LEVEL }}"' | + .[0].TestingDalamudApiLevel = '"${{ env.DALAMUD_API_LEVEL }}"'' \ repo.json > tmp.json && mv tmp.json repo.json cat repo.json