mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-18 06:34:19 +01:00
236 lines
No EOL
7.9 KiB
C#
236 lines
No EOL
7.9 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using Newtonsoft.Json;
|
|
using Penumbra.Game;
|
|
using Penumbra.Game.Enums;
|
|
using Penumbra.Meta.Files;
|
|
using Penumbra.Util;
|
|
using Swan;
|
|
using ImcFile = Lumina.Data.Files.ImcFile;
|
|
|
|
namespace Penumbra.Meta
|
|
{
|
|
// Write a single meta manipulation as a Base64string of the 16 bytes defining it.
|
|
public class MetaManipulationConverter : JsonConverter< MetaManipulation >
|
|
{
|
|
public override void WriteJson( JsonWriter writer, MetaManipulation manip, JsonSerializer serializer )
|
|
{
|
|
var s = Convert.ToBase64String( manip.ToBytes() );
|
|
writer.WriteValue( s );
|
|
}
|
|
|
|
public override MetaManipulation ReadJson( JsonReader reader, Type objectType, MetaManipulation existingValue, bool hasExistingValue,
|
|
JsonSerializer serializer )
|
|
|
|
{
|
|
if( reader.TokenType != JsonToken.String )
|
|
{
|
|
throw new JsonReaderException();
|
|
}
|
|
|
|
var bytes = Convert.FromBase64String( ( string )reader.Value! );
|
|
using MemoryStream m = new( bytes );
|
|
using BinaryReader br = new( m );
|
|
var i = br.ReadUInt64();
|
|
var v = br.ReadUInt64();
|
|
return new MetaManipulation( i, v );
|
|
}
|
|
}
|
|
|
|
// A MetaManipulation is a union of a type of Identifier (first 8 bytes, cf. Identifier.cs)
|
|
// and the appropriate Value to change the meta entry to (the other 8 bytes).
|
|
// Its comparison for sorting and hashes depends only on the identifier.
|
|
// The first byte is guaranteed to be a MetaType enum value in any case, so Type can always be read.
|
|
[StructLayout( LayoutKind.Explicit )]
|
|
[JsonConverter( typeof( MetaManipulationConverter ) )]
|
|
public struct MetaManipulation : IComparable
|
|
{
|
|
public static MetaManipulation Eqp( EquipSlot equipSlot, ushort setId, EqpEntry value )
|
|
=> new()
|
|
{
|
|
EqpIdentifier = new EqpIdentifier()
|
|
{
|
|
Type = MetaType.Eqp,
|
|
Slot = equipSlot,
|
|
SetId = setId,
|
|
},
|
|
EqpValue = value,
|
|
};
|
|
|
|
public static MetaManipulation Eqdp( EquipSlot equipSlot, GenderRace gr, ushort setId, EqdpEntry value )
|
|
=> new()
|
|
{
|
|
EqdpIdentifier = new EqdpIdentifier()
|
|
{
|
|
Type = MetaType.Eqdp,
|
|
Slot = equipSlot,
|
|
GenderRace = gr,
|
|
SetId = setId,
|
|
},
|
|
EqdpValue = value,
|
|
};
|
|
|
|
public static MetaManipulation Gmp( ushort setId, GmpEntry value )
|
|
=> new()
|
|
{
|
|
GmpIdentifier = new GmpIdentifier()
|
|
{
|
|
Type = MetaType.Gmp,
|
|
SetId = setId,
|
|
},
|
|
GmpValue = value,
|
|
};
|
|
|
|
public static MetaManipulation Est( ObjectType type, EquipSlot equipSlot, GenderRace gr, BodySlot bodySlot, ushort setId,
|
|
ushort value )
|
|
=> new()
|
|
{
|
|
EstIdentifier = new EstIdentifier()
|
|
{
|
|
Type = MetaType.Est,
|
|
ObjectType = type,
|
|
GenderRace = gr,
|
|
EquipSlot = equipSlot,
|
|
BodySlot = bodySlot,
|
|
PrimaryId = setId,
|
|
},
|
|
EstValue = value,
|
|
};
|
|
|
|
public static MetaManipulation Imc( ObjectType type, BodySlot secondaryType, ushort primaryId, ushort secondaryId
|
|
, ushort idx, ImcFile.ImageChangeData value )
|
|
=> new()
|
|
{
|
|
ImcIdentifier = new ImcIdentifier()
|
|
{
|
|
Type = MetaType.Imc,
|
|
ObjectType = type,
|
|
BodySlot = secondaryType,
|
|
PrimaryId = primaryId,
|
|
SecondaryId = secondaryId,
|
|
Variant = idx,
|
|
},
|
|
ImcValue = value,
|
|
};
|
|
|
|
public static MetaManipulation Imc( EquipSlot slot, ushort primaryId, ushort idx, ImcFile.ImageChangeData value )
|
|
=> new()
|
|
{
|
|
ImcIdentifier = new ImcIdentifier()
|
|
{
|
|
Type = MetaType.Imc,
|
|
ObjectType = slot.IsAccessory() ? ObjectType.Accessory : ObjectType.Equipment,
|
|
EquipSlot = slot,
|
|
PrimaryId = primaryId,
|
|
Variant = idx,
|
|
},
|
|
ImcValue = value,
|
|
};
|
|
|
|
internal MetaManipulation( ulong identifier, ulong value )
|
|
: this()
|
|
{
|
|
Identifier = identifier;
|
|
Value = value;
|
|
}
|
|
|
|
[FieldOffset( 0 )]
|
|
public readonly ulong Identifier;
|
|
|
|
[FieldOffset( 8 )]
|
|
public readonly ulong Value;
|
|
|
|
[FieldOffset( 0 )]
|
|
public MetaType Type;
|
|
|
|
[FieldOffset( 0 )]
|
|
public EqpIdentifier EqpIdentifier;
|
|
|
|
[FieldOffset( 0 )]
|
|
public GmpIdentifier GmpIdentifier;
|
|
|
|
[FieldOffset( 0 )]
|
|
public EqdpIdentifier EqdpIdentifier;
|
|
|
|
[FieldOffset( 0 )]
|
|
public EstIdentifier EstIdentifier;
|
|
|
|
[FieldOffset( 0 )]
|
|
public ImcIdentifier ImcIdentifier;
|
|
|
|
|
|
[FieldOffset( 8 )]
|
|
public EqpEntry EqpValue;
|
|
|
|
[FieldOffset( 8 )]
|
|
public GmpEntry GmpValue;
|
|
|
|
[FieldOffset( 8 )]
|
|
public EqdpEntry EqdpValue;
|
|
|
|
[FieldOffset( 8 )]
|
|
public ushort EstValue;
|
|
|
|
[FieldOffset( 8 )]
|
|
public ImcFile.ImageChangeData ImcValue; // 6 bytes.
|
|
|
|
public override int GetHashCode()
|
|
=> Identifier.GetHashCode();
|
|
|
|
public int CompareTo( object? rhs )
|
|
=> Identifier.CompareTo( rhs is MetaManipulation m ? m.Identifier : null );
|
|
|
|
public GamePath CorrespondingFilename()
|
|
{
|
|
return Type switch
|
|
{
|
|
MetaType.Eqp => MetaFileNames.Eqp(),
|
|
MetaType.Eqdp => MetaFileNames.Eqdp( EqdpIdentifier.Slot, EqdpIdentifier.GenderRace ),
|
|
MetaType.Est => MetaFileNames.Est( EstIdentifier.ObjectType, EstIdentifier.EquipSlot, EstIdentifier.BodySlot ),
|
|
MetaType.Gmp => MetaFileNames.Gmp(),
|
|
MetaType.Imc => MetaFileNames.Imc( ImcIdentifier.ObjectType, ImcIdentifier.PrimaryId, ImcIdentifier.SecondaryId ),
|
|
_ => throw new InvalidEnumArgumentException(),
|
|
};
|
|
}
|
|
|
|
// No error checking.
|
|
public bool Apply( EqpFile file )
|
|
=> file[ EqpIdentifier.SetId ].Apply( this );
|
|
|
|
public bool Apply( EqdpFile file )
|
|
=> file[ EqdpIdentifier.SetId ].Apply( this );
|
|
|
|
public bool Apply( GmpFile file )
|
|
=> file.SetEntry( GmpIdentifier.SetId, GmpValue );
|
|
|
|
public bool Apply( EstFile file )
|
|
=> file.SetEntry( EstIdentifier.GenderRace, EstIdentifier.PrimaryId, EstValue );
|
|
|
|
public bool Apply( ImcFile file )
|
|
{
|
|
ref var value = ref file.GetValue( this );
|
|
if( ImcValue.Equal( value ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
value = ImcValue;
|
|
return true;
|
|
}
|
|
|
|
public string IdentifierString()
|
|
{
|
|
return Type switch
|
|
{
|
|
MetaType.Eqp => EqpIdentifier.ToString(),
|
|
MetaType.Eqdp => EqdpIdentifier.ToString(),
|
|
MetaType.Est => EstIdentifier.ToString(),
|
|
MetaType.Gmp => GmpIdentifier.ToString(),
|
|
MetaType.Imc => ImcIdentifier.ToString(),
|
|
_ => throw new InvalidEnumArgumentException(),
|
|
};
|
|
}
|
|
}
|
|
} |