mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Fix EQP Entries not deserializing correctly due to C# json bug.
This commit is contained in:
parent
2b0844a21e
commit
2cece9c422
2 changed files with 98 additions and 2 deletions
|
|
@ -6,14 +6,18 @@ using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Meta.Manipulations;
|
namespace Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
||||||
public readonly struct EqpManipulation : IMetaManipulation< EqpManipulation >
|
public readonly struct EqpManipulation : IMetaManipulation< EqpManipulation >
|
||||||
{
|
{
|
||||||
public readonly EqpEntry Entry;
|
[JsonConverter( typeof( ForceNumericFlagEnumConverter ) )]
|
||||||
public readonly ushort SetId;
|
public readonly EqpEntry Entry;
|
||||||
|
|
||||||
|
public readonly ushort SetId;
|
||||||
|
|
||||||
[JsonConverter( typeof( StringEnumConverter ) )]
|
[JsonConverter( typeof( StringEnumConverter ) )]
|
||||||
public readonly EquipSlot Slot;
|
public readonly EquipSlot Slot;
|
||||||
|
|
||||||
|
|
|
||||||
92
Penumbra/Util/FixedUlongStringEnumConverter.cs
Normal file
92
Penumbra/Util/FixedUlongStringEnumConverter.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace Penumbra.Util;
|
||||||
|
|
||||||
|
// Json.Net has a bug
|
||||||
|
// ulong enums can not be correctly deserialized if they exceed long.MaxValue.
|
||||||
|
// These converters fix this, taken from https://stackoverflow.com/questions/61740964/json-net-unable-to-deserialize-ulong-flag-type-enum/
|
||||||
|
public class ForceNumericFlagEnumConverter : FixedUlongStringEnumConverter
|
||||||
|
{
|
||||||
|
private static bool HasFlagsAttribute( Type? objectType )
|
||||||
|
=> objectType != null && Attribute.IsDefined( Nullable.GetUnderlyingType( objectType ) ?? objectType, typeof( System.FlagsAttribute ) );
|
||||||
|
|
||||||
|
public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer )
|
||||||
|
{
|
||||||
|
var enumType = value?.GetType();
|
||||||
|
if( HasFlagsAttribute( enumType ) )
|
||||||
|
{
|
||||||
|
var underlyingType = Enum.GetUnderlyingType( enumType! );
|
||||||
|
var underlyingValue = Convert.ChangeType( value, underlyingType );
|
||||||
|
writer.WriteValue( underlyingValue );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.WriteJson( writer, value, serializer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FixedUlongStringEnumConverter : StringEnumConverter
|
||||||
|
{
|
||||||
|
public override object? ReadJson( JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer )
|
||||||
|
{
|
||||||
|
if( reader.MoveToContentAndAssert().TokenType != JsonToken.Integer || reader.ValueType != typeof( System.Numerics.BigInteger ) )
|
||||||
|
{
|
||||||
|
return base.ReadJson( reader, objectType, existingValue, serializer );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: throw an exception if !this.AllowIntegerValues
|
||||||
|
// https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Converters_StringEnumConverter_AllowIntegerValues.htm
|
||||||
|
var enumType = Nullable.GetUnderlyingType( objectType ) ?? objectType;
|
||||||
|
if( Enum.GetUnderlyingType( enumType ) == typeof( ulong ) )
|
||||||
|
{
|
||||||
|
var bigInteger = ( System.Numerics.BigInteger )reader.Value!;
|
||||||
|
if( bigInteger >= ulong.MinValue && bigInteger <= ulong.MaxValue )
|
||||||
|
{
|
||||||
|
return Enum.ToObject( enumType, checked( ( ulong )bigInteger ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.ReadJson( reader, objectType, existingValue, serializer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static partial class JsonExtensions
|
||||||
|
{
|
||||||
|
public static JsonReader MoveToContentAndAssert( this JsonReader reader )
|
||||||
|
{
|
||||||
|
if( reader == null )
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( reader.TokenType == JsonToken.None ) // Skip past beginning of stream.
|
||||||
|
{
|
||||||
|
reader.ReadAndAssert();
|
||||||
|
}
|
||||||
|
|
||||||
|
while( reader.TokenType == JsonToken.Comment ) // Skip past comments.
|
||||||
|
{
|
||||||
|
reader.ReadAndAssert();
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonReader ReadAndAssert( this JsonReader reader )
|
||||||
|
{
|
||||||
|
if( reader == null )
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !reader.Read() )
|
||||||
|
{
|
||||||
|
throw new JsonReaderException( "Unexpected end of JSON stream." );
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue