Update packages.

This commit is contained in:
Ottermandias 2024-08-24 20:43:11 +02:00
parent 3549283769
commit a2237773e3
8 changed files with 139 additions and 94 deletions

View file

@ -1,4 +1,6 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text.Json;
using System.Text.Json.Nodes;
using Lumina.Extensions; using Lumina.Extensions;
using OtterGui; using OtterGui;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
@ -23,11 +25,11 @@ public class MeshExporter
? scene.AddSkinnedMesh(data.Mesh, Matrix4x4.Identity, [.. skeleton.Value.Joints]) ? scene.AddSkinnedMesh(data.Mesh, Matrix4x4.Identity, [.. skeleton.Value.Joints])
: scene.AddRigidMesh(data.Mesh, Matrix4x4.Identity); : scene.AddRigidMesh(data.Mesh, Matrix4x4.Identity);
var extras = new Dictionary<string, object>(data.Attributes.Length); var node = new JsonObject();
foreach (var attribute in data.Attributes) foreach (var attribute in data.Attributes)
extras.Add(attribute, true); node[attribute] = true;
instance.WithExtras(JsonContent.CreateFrom(extras)); instance.WithExtras(node);
} }
} }
} }
@ -233,10 +235,7 @@ public class MeshExporter
// Named morph targets aren't part of the specification, however `MESH.extras.targetNames` // Named morph targets aren't part of the specification, however `MESH.extras.targetNames`
// is a commonly-accepted means of providing the data. // is a commonly-accepted means of providing the data.
meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary<string, object>() meshBuilder.Extras = new JsonObject { ["targetNames"] = JsonSerializer.SerializeToNode(shapeNames) };
{
{ "targetNames", shapeNames },
});
string[] attributes = []; string[] attributes = [];
var maxAttribute = 31 - BitOperations.LeadingZeroCount(attributeMask); var maxAttribute = 31 - BitOperations.LeadingZeroCount(attributeMask);

View file

@ -1,4 +1,6 @@
using System;
using SharpGLTF.Geometry.VertexTypes; using SharpGLTF.Geometry.VertexTypes;
using SharpGLTF.Memory;
using SharpGLTF.Schema2; using SharpGLTF.Schema2;
namespace Penumbra.Import.Models.Export; namespace Penumbra.Import.Models.Export;
@ -11,35 +13,40 @@ and there's reason to overhaul the export pipeline.
public struct VertexColorFfxiv : IVertexCustom public struct VertexColorFfxiv : IVertexCustom
{ {
public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
{
// NOTE: We only realistically require UNSIGNED_BYTE for this, however Blender 3.6 errors on that (fixed in 4.0). // NOTE: We only realistically require UNSIGNED_BYTE for this, however Blender 3.6 errors on that (fixed in 4.0).
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, true)] yield return new KeyValuePair<string, AttributeFormat>("_FFXIV_COLOR",
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
}
public Vector4 FfxivColor; public Vector4 FfxivColor;
public int MaxColors => 0; public int MaxColors
=> 0;
public int MaxTextCoords => 0; public int MaxTextCoords
=> 0;
private static readonly string[] CustomNames = ["_FFXIV_COLOR"]; private static readonly string[] CustomNames = ["_FFXIV_COLOR"];
public IEnumerable<string> CustomAttributes => CustomNames;
public IEnumerable<string> CustomAttributes
=> CustomNames;
public VertexColorFfxiv(Vector4 ffxivColor) public VertexColorFfxiv(Vector4 ffxivColor)
{ => FfxivColor = ffxivColor;
FfxivColor = ffxivColor;
}
public void Add(in VertexMaterialDelta delta) public void Add(in VertexMaterialDelta delta)
{ { }
}
public VertexMaterialDelta Subtract(IVertexMaterial baseValue) public VertexMaterialDelta Subtract(IVertexMaterial baseValue)
=> new VertexMaterialDelta(Vector4.Zero, Vector4.Zero, Vector2.Zero, Vector2.Zero); => new(Vector4.Zero, Vector4.Zero, Vector2.Zero, Vector2.Zero);
public Vector2 GetTexCoord(int index) public Vector2 GetTexCoord(int index)
=> throw new ArgumentOutOfRangeException(nameof(index)); => throw new ArgumentOutOfRangeException(nameof(index));
public void SetTexCoord(int setIndex, Vector2 coord) public void SetTexCoord(int setIndex, Vector2 coord)
{ { }
}
public bool TryGetCustomAttribute(string attributeName, out object? value) public bool TryGetCustomAttribute(string attributeName, out object? value)
{ {
@ -65,12 +72,17 @@ public struct VertexColorFfxiv : IVertexCustom
=> throw new ArgumentOutOfRangeException(nameof(index)); => throw new ArgumentOutOfRangeException(nameof(index));
public void SetColor(int setIndex, Vector4 color) public void SetColor(int setIndex, Vector4 color)
{ { }
}
public void Validate() public void Validate()
{ {
var components = new[] { FfxivColor.X, FfxivColor.Y, FfxivColor.Z, FfxivColor.W }; var components = new[]
{
FfxivColor.X,
FfxivColor.Y,
FfxivColor.Z,
FfxivColor.W,
};
if (components.Any(component => component < 0 || component > 1)) if (components.Any(component => component < 0 || component > 1))
throw new ArgumentOutOfRangeException(nameof(FfxivColor)); throw new ArgumentOutOfRangeException(nameof(FfxivColor));
} }
@ -78,18 +90,28 @@ public struct VertexColorFfxiv : IVertexCustom
public struct VertexTexture1ColorFfxiv : IVertexCustom public struct VertexTexture1ColorFfxiv : IVertexCustom
{ {
[VertexAttribute("TEXCOORD_0")] public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
{
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_0",
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
yield return new KeyValuePair<string, AttributeFormat>("_FFXIV_COLOR",
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
}
public Vector2 TexCoord0; public Vector2 TexCoord0;
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, true)]
public Vector4 FfxivColor; public Vector4 FfxivColor;
public int MaxColors => 0; public int MaxColors
=> 0;
public int MaxTextCoords => 1; public int MaxTextCoords
=> 1;
private static readonly string[] CustomNames = ["_FFXIV_COLOR"]; private static readonly string[] CustomNames = ["_FFXIV_COLOR"];
public IEnumerable<string> CustomAttributes => CustomNames;
public IEnumerable<string> CustomAttributes
=> CustomNames;
public VertexTexture1ColorFfxiv(Vector2 texCoord0, Vector4 ffxivColor) public VertexTexture1ColorFfxiv(Vector2 texCoord0, Vector4 ffxivColor)
{ {
@ -103,9 +125,7 @@ public struct VertexTexture1ColorFfxiv : IVertexCustom
} }
public VertexMaterialDelta Subtract(IVertexMaterial baseValue) public VertexMaterialDelta Subtract(IVertexMaterial baseValue)
{ => new(Vector4.Zero, Vector4.Zero, TexCoord0 - baseValue.GetTexCoord(0), Vector2.Zero);
return new VertexMaterialDelta(Vector4.Zero, Vector4.Zero, TexCoord0 - baseValue.GetTexCoord(0), Vector2.Zero);
}
public Vector2 GetTexCoord(int index) public Vector2 GetTexCoord(int index)
=> index switch => index switch
@ -116,8 +136,10 @@ public struct VertexTexture1ColorFfxiv : IVertexCustom
public void SetTexCoord(int setIndex, Vector2 coord) public void SetTexCoord(int setIndex, Vector2 coord)
{ {
if (setIndex == 0) TexCoord0 = coord; if (setIndex == 0)
if (setIndex >= 1) throw new ArgumentOutOfRangeException(nameof(setIndex)); TexCoord0 = coord;
if (setIndex >= 1)
throw new ArgumentOutOfRangeException(nameof(setIndex));
} }
public bool TryGetCustomAttribute(string attributeName, out object? value) public bool TryGetCustomAttribute(string attributeName, out object? value)
@ -144,12 +166,17 @@ public struct VertexTexture1ColorFfxiv : IVertexCustom
=> throw new ArgumentOutOfRangeException(nameof(index)); => throw new ArgumentOutOfRangeException(nameof(index));
public void SetColor(int setIndex, Vector4 color) public void SetColor(int setIndex, Vector4 color)
{ { }
}
public void Validate() public void Validate()
{ {
var components = new[] { FfxivColor.X, FfxivColor.Y, FfxivColor.Z, FfxivColor.W }; var components = new[]
{
FfxivColor.X,
FfxivColor.Y,
FfxivColor.Z,
FfxivColor.W,
};
if (components.Any(component => component < 0 || component > 1)) if (components.Any(component => component < 0 || component > 1))
throw new ArgumentOutOfRangeException(nameof(FfxivColor)); throw new ArgumentOutOfRangeException(nameof(FfxivColor));
} }
@ -157,21 +184,30 @@ public struct VertexTexture1ColorFfxiv : IVertexCustom
public struct VertexTexture2ColorFfxiv : IVertexCustom public struct VertexTexture2ColorFfxiv : IVertexCustom
{ {
[VertexAttribute("TEXCOORD_0")] public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
{
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_0",
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_1",
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
yield return new KeyValuePair<string, AttributeFormat>("_FFXIV_COLOR",
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
}
public Vector2 TexCoord0; public Vector2 TexCoord0;
[VertexAttribute("TEXCOORD_1")]
public Vector2 TexCoord1; public Vector2 TexCoord1;
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, true)]
public Vector4 FfxivColor; public Vector4 FfxivColor;
public int MaxColors => 0; public int MaxColors
=> 0;
public int MaxTextCoords => 2; public int MaxTextCoords
=> 2;
private static readonly string[] CustomNames = ["_FFXIV_COLOR"]; private static readonly string[] CustomNames = ["_FFXIV_COLOR"];
public IEnumerable<string> CustomAttributes => CustomNames;
public IEnumerable<string> CustomAttributes
=> CustomNames;
public VertexTexture2ColorFfxiv(Vector2 texCoord0, Vector2 texCoord1, Vector4 ffxivColor) public VertexTexture2ColorFfxiv(Vector2 texCoord0, Vector2 texCoord1, Vector4 ffxivColor)
{ {
@ -187,9 +223,7 @@ public struct VertexTexture2ColorFfxiv : IVertexCustom
} }
public VertexMaterialDelta Subtract(IVertexMaterial baseValue) public VertexMaterialDelta Subtract(IVertexMaterial baseValue)
{ => new(Vector4.Zero, Vector4.Zero, TexCoord0 - baseValue.GetTexCoord(0), TexCoord1 - baseValue.GetTexCoord(1));
return new VertexMaterialDelta(Vector4.Zero, Vector4.Zero, TexCoord0 - baseValue.GetTexCoord(0), TexCoord1 - baseValue.GetTexCoord(1));
}
public Vector2 GetTexCoord(int index) public Vector2 GetTexCoord(int index)
=> index switch => index switch
@ -201,9 +235,12 @@ public struct VertexTexture2ColorFfxiv : IVertexCustom
public void SetTexCoord(int setIndex, Vector2 coord) public void SetTexCoord(int setIndex, Vector2 coord)
{ {
if (setIndex == 0) TexCoord0 = coord; if (setIndex == 0)
if (setIndex == 1) TexCoord1 = coord; TexCoord0 = coord;
if (setIndex >= 2) throw new ArgumentOutOfRangeException(nameof(setIndex)); if (setIndex == 1)
TexCoord1 = coord;
if (setIndex >= 2)
throw new ArgumentOutOfRangeException(nameof(setIndex));
} }
public bool TryGetCustomAttribute(string attributeName, out object? value) public bool TryGetCustomAttribute(string attributeName, out object? value)
@ -230,12 +267,17 @@ public struct VertexTexture2ColorFfxiv : IVertexCustom
=> throw new ArgumentOutOfRangeException(nameof(index)); => throw new ArgumentOutOfRangeException(nameof(index));
public void SetColor(int setIndex, Vector4 color) public void SetColor(int setIndex, Vector4 color)
{ { }
}
public void Validate() public void Validate()
{ {
var components = new[] { FfxivColor.X, FfxivColor.Y, FfxivColor.Z, FfxivColor.W }; var components = new[]
{
FfxivColor.X,
FfxivColor.Y,
FfxivColor.Z,
FfxivColor.W,
};
if (components.Any(component => component < 0 || component > 1)) if (components.Any(component => component < 0 || component > 1))
throw new ArgumentOutOfRangeException(nameof(FfxivColor)); throw new ArgumentOutOfRangeException(nameof(FfxivColor));
} }

View file

@ -61,7 +61,7 @@ public class SubMeshImporter
try try
{ {
_morphNames = node.Mesh.Extras.GetNode("targetNames").Deserialize<List<string>>(); _morphNames = node.Mesh.Extras["targetNames"].Deserialize<List<string>>();
} }
catch catch
{ {

View file

@ -148,7 +148,7 @@ public partial class TexToolsImporter : IDisposable
// You can in no way rely on any file paths in TTMPs so we need to just do this, sorry // You can in no way rely on any file paths in TTMPs so we need to just do this, sorry
private static ZipArchiveEntry? FindZipEntry(ZipArchive file, string fileName) private static ZipArchiveEntry? FindZipEntry(ZipArchive file, string fileName)
=> file.Entries.FirstOrDefault(e => !e.IsDirectory && e.Key.Contains(fileName)); => file.Entries.FirstOrDefault(e => e is { IsDirectory: false, Key: not null } && e.Key.Contains(fileName));
private static string GetStringFromZipEntry(ZipArchiveEntry entry, Encoding encoding) private static string GetStringFromZipEntry(ZipArchiveEntry entry, Encoding encoding)
{ {

View file

@ -82,7 +82,7 @@ public partial class TexToolsImporter
if (name.Length == 0) if (name.Length == 0)
throw new Exception("Invalid mod archive: mod meta has no name."); throw new Exception("Invalid mod archive: mod meta has no name.");
using var f = File.OpenWrite(Path.Combine(_currentModDirectory.FullName, reader.Entry.Key)); using var f = File.OpenWrite(Path.Combine(_currentModDirectory.FullName, reader.Entry.Key!));
s.Seek(0, SeekOrigin.Begin); s.Seek(0, SeekOrigin.Begin);
s.WriteTo(f); s.WriteTo(f);
} }
@ -155,13 +155,9 @@ public partial class TexToolsImporter
ret = directory; ret = directory;
// Check that all other files are also contained in the top-level directory. // Check that all other files are also contained in the top-level directory.
if (ret.IndexOfAny(new[] if (ret.IndexOfAny(['/', '\\']) >= 0
{ || !archive.Entries.All(e
'/', => e.Key != null && e.Key.StartsWith(ret) && (e.Key.Length == ret.Length || e.Key[ret.Length] is '/' or '\\')))
'\\',
})
>= 0
|| !archive.Entries.All(e => e.Key.StartsWith(ret) && (e.Key.Length == ret.Length || e.Key[ret.Length] is '/' or '\\')))
throw new Exception( throw new Exception(
"Invalid mod archive: meta.json in wrong location. It needs to be either at root or one directory deep, in which all other files must be nested too."); "Invalid mod archive: meta.json in wrong location. It needs to be either at root or one directory deep, in which all other files must be nested too.");
} }

View file

@ -86,11 +86,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="EmbedIO" Version="3.4.3" /> <PackageReference Include="EmbedIO" Version="3.5.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="SharpCompress" Version="0.33.0" /> <PackageReference Include="SharpCompress" Version="0.37.2" />
<PackageReference Include="SharpGLTF.Core" Version="1.0.0-alpha0030" /> <PackageReference Include="SharpGLTF.Core" Version="1.0.1" />
<PackageReference Include="SharpGLTF.Toolkit" Version="1.0.0-alpha0030" /> <PackageReference Include="SharpGLTF.Toolkit" Version="1.0.1" />
<PackageReference Include="PeNet" Version="4.0.5" /> <PackageReference Include="PeNet" Version="4.0.5" />
</ItemGroup> </ItemGroup>

View file

@ -242,7 +242,7 @@ public class MigrationManager(Configuration config) : IService
return; return;
} }
var path = Path.Combine(directory, reader.Entry.Key); var path = Path.Combine(directory, reader.Entry.Key!);
using var s = new MemoryStream(); using var s = new MemoryStream();
using var e = reader.OpenEntryStream(); using var e = reader.OpenEntryStream();
e.CopyTo(s); e.CopyTo(s);

View file

@ -4,11 +4,11 @@
"net8.0-windows7.0": { "net8.0-windows7.0": {
"EmbedIO": { "EmbedIO": {
"type": "Direct", "type": "Direct",
"requested": "[3.4.3, )", "requested": "[3.5.2, )",
"resolved": "3.4.3", "resolved": "3.5.2",
"contentHash": "YM6hpZNAfvbbixfG9T4lWDGfF0D/TqutbTROL4ogVcHKwPF1hp+xS3ABwd3cxxTxvDFkj/zZl57QgWuFA8Igxw==", "contentHash": "YU4j+3XvuO8/VPkNf7KWOF1TpMhnyVhXnPsG1mvnDhTJ9D5BZOFXVDvCpE/SkQ1AJ0Aa+dXOVSW3ntgmLL7aJg==",
"dependencies": { "dependencies": {
"Unosquare.Swan.Lite": "3.0.0" "Unosquare.Swan.Lite": "3.1.0"
} }
}, },
"PeNet": { "PeNet": {
@ -23,23 +23,26 @@
}, },
"SharpCompress": { "SharpCompress": {
"type": "Direct", "type": "Direct",
"requested": "[0.33.0, )", "requested": "[0.37.2, )",
"resolved": "0.33.0", "resolved": "0.37.2",
"contentHash": "FlHfpTAADzaSlVCBF33iKJk9UhOr3Xj+r5LXbW2GzqYr0SrhiOf6shLX2LC2fqs7g7d+YlwKbBXqWFtb+e7icw==" "contentHash": "cFBpTct57aubLQXkdqMmgP8GGTFRh7fnRWP53lgE/EYUpDZJ27SSvTkdjB4OYQRZ20SJFpzczUquKLbt/9xkhw==",
"dependencies": {
"ZstdSharp.Port": "0.8.0"
}
}, },
"SharpGLTF.Core": { "SharpGLTF.Core": {
"type": "Direct", "type": "Direct",
"requested": "[1.0.0-alpha0030, )", "requested": "[1.0.1, )",
"resolved": "1.0.0-alpha0030", "resolved": "1.0.1",
"contentHash": "HVL6PcrM0H/uEk96nRZfhtPeYvSFGHnni3g1aIckot2IWVp0jLMH5KWgaWfsatEz4Yds3XcdSLUWmJZivDBUPA==" "contentHash": "ykeV1oNHcJrEJE7s0pGAsf/nYGYY7wqF9nxCMxJUjp/WdW+UUgR1cGdbAa2lVZPkiXEwLzWenZ5wPz7yS0Gj9w=="
}, },
"SharpGLTF.Toolkit": { "SharpGLTF.Toolkit": {
"type": "Direct", "type": "Direct",
"requested": "[1.0.0-alpha0030, )", "requested": "[1.0.1, )",
"resolved": "1.0.0-alpha0030", "resolved": "1.0.1",
"contentHash": "nsoJWAFhXgEky9bVCY0zLeZVDx+S88u7VjvuebvMb6dJiNyFOGF6FrrMHiJe+x5pcVBxxlc3VoXliBF7r/EqYA==", "contentHash": "LYBjHdHW5Z8R1oT1iI04si3559tWdZ3jTdHfDEu0jqhuyU8w3oJRLFUoDfVeCOI5zWXlVQPtlpjhH9XTfFFAcA==",
"dependencies": { "dependencies": {
"SharpGLTF.Runtime": "1.0.0-alpha0030" "SharpGLTF.Runtime": "1.0.1"
} }
}, },
"SixLabors.ImageSharp": { "SixLabors.ImageSharp": {
@ -50,8 +53,8 @@
}, },
"JetBrains.Annotations": { "JetBrains.Annotations": {
"type": "Transitive", "type": "Transitive",
"resolved": "2023.3.0", "resolved": "2024.2.0",
"contentHash": "PHfnvdBUdGaTVG9bR/GEfxgTwWM0Z97Y6X3710wiljELBISipSfF5okn/vz+C2gfO+ihoEyVPjaJwn8ZalVukA==" "contentHash": "GNnqCFW/163p1fOehKx0CnAqjmpPrUSqrgfHM6qca+P+RN39C9rhlfZHQpJhxmQG/dkOYe/b3Z0P8b6Kv5m1qw=="
}, },
"Microsoft.Extensions.DependencyInjection": { "Microsoft.Extensions.DependencyInjection": {
"type": "Transitive", "type": "Transitive",
@ -73,10 +76,10 @@
}, },
"SharpGLTF.Runtime": { "SharpGLTF.Runtime": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.0.0-alpha0030", "resolved": "1.0.1",
"contentHash": "Ysn+fyj9EVXj6mfG0BmzSTBGNi/QvcnTrMd54dBMOlI/TsMRvnOY3JjTn0MpeH2CgHXX4qogzlDt4m+rb3n4Og==", "contentHash": "KsgEBKLfsEnu2IPeKaWp4Ih97+kby17IohrAB6Ev8gET18iS80nKMW/APytQWpenMmcWU06utInpANqyrwRlDg==",
"dependencies": { "dependencies": {
"SharpGLTF.Core": "1.0.0-alpha0030" "SharpGLTF.Core": "1.0.1"
} }
}, },
"System.Formats.Asn1": { "System.Formats.Asn1": {
@ -99,16 +102,21 @@
}, },
"Unosquare.Swan.Lite": { "Unosquare.Swan.Lite": {
"type": "Transitive", "type": "Transitive",
"resolved": "3.0.0", "resolved": "3.1.0",
"contentHash": "noPwJJl1Q9uparXy1ogtkmyAPGNfSGb0BLT1292nFH1jdMKje6o2kvvrQUvF9Xklj+IoiAI0UzF6Aqxlvo10lw==", "contentHash": "X3s5QE/KMj3WAPFqFve7St+Ds10BB50u8kW8PmKIn7FVkn7yEXe9Yxr2htt1WV85DRqfFR0MN/BUNHkGHtL4OQ==",
"dependencies": { "dependencies": {
"System.ValueTuple": "4.5.0" "System.ValueTuple": "4.5.0"
} }
}, },
"ZstdSharp.Port": {
"type": "Transitive",
"resolved": "0.8.0",
"contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA=="
},
"ottergui": { "ottergui": {
"type": "Project", "type": "Project",
"dependencies": { "dependencies": {
"JetBrains.Annotations": "[2023.3.0, )", "JetBrains.Annotations": "[2024.2.0, )",
"Microsoft.Extensions.DependencyInjection": "[8.0.0, )" "Microsoft.Extensions.DependencyInjection": "[8.0.0, )"
} }
}, },
@ -122,7 +130,7 @@
"type": "Project", "type": "Project",
"dependencies": { "dependencies": {
"OtterGui": "[1.0.0, )", "OtterGui": "[1.0.0, )",
"Penumbra.Api": "[5.2.0, )", "Penumbra.Api": "[5.3.0, )",
"Penumbra.String": "[1.0.4, )" "Penumbra.String": "[1.0.4, )"
} }
}, },