This commit is contained in:
Ottermandias 2022-03-13 16:10:54 +01:00
parent de082439a4
commit 707570615c
10 changed files with 426 additions and 102 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Numerics;
using Dalamud.Logging;
using Dalamud.Memory;
using Newtonsoft.Json;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Util;
@ -18,10 +19,16 @@ public readonly struct ImcEntry : IEquatable< ImcEntry >
public readonly byte MaterialAnimationId;
public ushort AttributeMask
=> ( ushort )( _attributeAndSound & 0x3FF );
{
get => ( ushort )( _attributeAndSound & 0x3FF );
init => _attributeAndSound = ( ushort )( ( _attributeAndSound & ~0x3FF ) | ( value & 0x3FF ) );
}
public byte SoundId
=> ( byte )( _attributeAndSound >> 10 );
{
get => ( byte )( _attributeAndSound >> 10 );
init => _attributeAndSound = ( ushort )( AttributeMask | ( value << 10 ) );
}
public bool Equals( ImcEntry other )
=> MaterialId == other.MaterialId
@ -35,6 +42,18 @@ public readonly struct ImcEntry : IEquatable< ImcEntry >
public override int GetHashCode()
=> HashCode.Combine( MaterialId, DecalId, _attributeAndSound, VfxId, MaterialAnimationId );
[JsonConstructor]
public ImcEntry( byte materialId, byte decalId, ushort attributeMask, byte soundId, byte vfxId, byte materialAnimationId )
{
MaterialId = materialId;
DecalId = decalId;
_attributeAndSound = 0;
VfxId = vfxId;
MaterialAnimationId = materialAnimationId;
AttributeMask = attributeMask;
SoundId = soundId;
}
}
public unsafe class ImcFile : MetaBaseFile
@ -118,7 +137,7 @@ public unsafe class ImcFile : MetaBaseFile
}
PluginLog.Verbose( "Expanded imc from {Count} to {NewCount} variants.", Count, numVariants );
*( ushort* )Count = ( ushort )numVariants;
*( ushort* )Data = ( ushort )numVariants;
return true;
}

View file

@ -1,4 +1,7 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
@ -6,12 +9,16 @@ using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
public readonly struct EqdpManipulation : IEquatable< EqdpManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct EqdpManipulation : IMetaManipulation< EqdpManipulation >
{
public readonly EqdpEntry Entry;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly Gender Gender;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly ModelRace Race;
public readonly ushort SetId;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly EquipSlot Slot;
public EqdpManipulation( EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, ushort setId )
@ -38,6 +45,24 @@ public readonly struct EqdpManipulation : IEquatable< EqdpManipulation >
public override int GetHashCode()
=> HashCode.Combine( ( int )Gender, ( int )Race, SetId, ( int )Slot );
public int CompareTo( EqdpManipulation other )
{
var r = Race.CompareTo( other.Race );
if( r != 0 )
{
return r;
}
var g = Gender.CompareTo( other.Gender );
if( g != 0 )
{
return g;
}
var set = SetId.CompareTo( other.SetId );
return set != 0 ? set : Slot.CompareTo( other.Slot );
}
public int FileIndex()
=> CharacterUtility.EqdpIdx( Names.CombinedRace( Gender, Race ), Slot.IsAccessory() );

View file

@ -1,4 +1,7 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
@ -6,10 +9,12 @@ using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
public readonly struct EqpManipulation : IEquatable< EqpManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct EqpManipulation : IMetaManipulation< EqpManipulation >
{
public readonly EqpEntry Entry;
public readonly ushort SetId;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly EquipSlot Slot;
public EqpManipulation( EqpEntry entry, EquipSlot slot, ushort setId )
@ -32,6 +37,12 @@ public readonly struct EqpManipulation : IEquatable< EqpManipulation >
public override int GetHashCode()
=> HashCode.Combine( ( int )Slot, SetId );
public int CompareTo( EqpManipulation other )
{
var set = SetId.CompareTo( other.SetId );
return set != 0 ? set : Slot.CompareTo( other.Slot );
}
public int FileIndex()
=> CharacterUtility.EqpIdx;

View file

@ -1,11 +1,15 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
public readonly struct EstManipulation : IEquatable< EstManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct EstManipulation : IMetaManipulation< EstManipulation >
{
public enum EstType : byte
{
@ -16,10 +20,13 @@ public readonly struct EstManipulation : IEquatable< EstManipulation >
}
public readonly ushort SkeletonIdx;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly Gender Gender;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly ModelRace Race;
public readonly ushort SetId;
public readonly EstType Type;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly EstType Slot;
public EstManipulation( Gender gender, ModelRace race, EstType estType, ushort setId, ushort skeletonIdx )
{
@ -27,27 +34,45 @@ public readonly struct EstManipulation : IEquatable< EstManipulation >
Gender = gender;
Race = race;
SetId = setId;
Type = estType;
Slot = estType;
}
public override string ToString()
=> $"Est - {SetId} - {Type} - {Race.ToName()} {Gender.ToName()}";
=> $"Est - {SetId} - {Slot} - {Race.ToName()} {Gender.ToName()}";
public bool Equals( EstManipulation other )
=> Gender == other.Gender
&& Race == other.Race
&& SetId == other.SetId
&& Type == other.Type;
&& Slot == other.Slot;
public override bool Equals( object? obj )
=> obj is EstManipulation other && Equals( other );
public override int GetHashCode()
=> HashCode.Combine( ( int )Gender, ( int )Race, SetId, ( int )Type );
=> HashCode.Combine( ( int )Gender, ( int )Race, SetId, ( int )Slot );
public int CompareTo( EstManipulation other )
{
var r = Race.CompareTo( other.Race );
if( r != 0 )
{
return r;
}
var g = Gender.CompareTo( other.Gender );
if( g != 0 )
{
return g;
}
var s = Slot.CompareTo( other.Slot );
return s != 0 ? s : SetId.CompareTo( other.SetId );
}
public int FileIndex()
=> ( int )Type;
=> ( int )Slot;
public bool Apply( EstFile file )
{

View file

@ -1,11 +1,13 @@
using System;
using System.Runtime.InteropServices;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
public readonly struct GmpManipulation : IEquatable< GmpManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct GmpManipulation : IMetaManipulation< GmpManipulation >
{
public readonly GmpEntry Entry;
public readonly ushort SetId;
@ -28,6 +30,9 @@ public readonly struct GmpManipulation : IEquatable< GmpManipulation >
public override int GetHashCode()
=> SetId.GetHashCode();
public int CompareTo( GmpManipulation other )
=> SetId.CompareTo( other.SetId );
public int FileIndex()
=> CharacterUtility.GmpIdx;

View file

@ -1,20 +1,28 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
[StructLayout( LayoutKind.Sequential )]
public readonly struct ImcManipulation : IEquatable< ImcManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
{
public readonly ImcEntry Entry;
public readonly ushort PrimaryId;
public readonly ushort Variant;
public readonly ushort SecondaryId;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly ObjectType ObjectType;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly EquipSlot EquipSlot;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly BodySlot BodySlot;
public ImcManipulation( EquipSlot equipSlot, ushort variant, ushort primaryId, ImcEntry entry )
@ -40,6 +48,19 @@ public readonly struct ImcManipulation : IEquatable< ImcManipulation >
EquipSlot = EquipSlot.Unknown;
}
[JsonConstructor]
internal ImcManipulation( ObjectType objectType, BodySlot bodySlot, ushort primaryId, ushort secondaryId, ushort variant,
EquipSlot equipSlot, ImcEntry entry )
{
Entry = entry;
ObjectType = objectType;
BodySlot = bodySlot;
PrimaryId = primaryId;
SecondaryId = secondaryId;
Variant = variant;
EquipSlot = equipSlot;
}
public override string ToString()
=> ObjectType is ObjectType.Equipment or ObjectType.Accessory
? $"Imc - {PrimaryId} - {EquipSlot} - {Variant}"
@ -59,6 +80,39 @@ public readonly struct ImcManipulation : IEquatable< ImcManipulation >
public override int GetHashCode()
=> HashCode.Combine( PrimaryId, Variant, SecondaryId, ( int )ObjectType, ( int )EquipSlot, ( int )BodySlot );
public int CompareTo( ImcManipulation other )
{
var o = ObjectType.CompareTo( other.ObjectType );
if( o != 0 )
{
return o;
}
var i = PrimaryId.CompareTo( other.PrimaryId );
if( i != 0 )
{
return i;
}
if( ObjectType is ObjectType.Equipment or ObjectType.Accessory )
{
var e = EquipSlot.CompareTo( other.EquipSlot );
return e != 0 ? e : Variant.CompareTo( other.Variant );
}
var s = SecondaryId.CompareTo( other.SecondaryId );
if( s != 0 )
{
return s;
}
var b = BodySlot.CompareTo( other.BodySlot );
return b != 0 ? b : Variant.CompareTo( other.Variant );
}
public int FileIndex()
=> -1;
public Utf8GamePath GamePath()
{
return ObjectType switch

View file

@ -1,60 +1,225 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.Util;
namespace Penumbra.Meta.Manipulations;
public interface IMetaManipulation
{
public int FileIndex();
}
public interface IMetaManipulation< T > : IMetaManipulation, IComparable< T >, IEquatable< T > where T : struct
{ }
public struct ManipulationSet< T > where T : struct, IMetaManipulation< T >
{
private List< T >? _data = null;
public IReadOnlyList< T > Data
=> ( IReadOnlyList< T >? )_data ?? Array.Empty< T >();
public int Count
=> _data?.Count ?? 0;
public ManipulationSet( int count = 0 )
{
if( count > 0 )
{
_data = new List< T >( count );
}
}
public bool TryAdd( T manip )
{
if( _data == null )
{
_data = new List< T > { manip };
return true;
}
var idx = _data.BinarySearch( manip );
if( idx >= 0 )
{
return false;
}
_data.Insert( ~idx, manip );
return true;
}
public int Set( T manip )
{
if( _data == null )
{
_data = new List< T > { manip };
return 0;
}
var idx = _data.BinarySearch( manip );
if( idx >= 0 )
{
_data[ idx ] = manip;
return idx;
}
idx = ~idx;
_data.Insert( idx, manip );
return idx;
}
public bool TryGet( T manip, out T value )
{
var idx = _data?.BinarySearch( manip ) ?? -1;
if( idx < 0 )
{
value = default;
return false;
}
value = _data![ idx ];
return true;
}
public bool Remove( T manip )
{
var idx = _data?.BinarySearch( manip ) ?? -1;
if( idx < 0 )
{
return false;
}
_data!.RemoveAt( idx );
return true;
}
}
[StructLayout( LayoutKind.Explicit, Pack = 1, Size = 16 )]
public readonly struct MetaManipulation : IEquatable< MetaManipulation >, IComparable< MetaManipulation >
{
public enum Type : byte
{
Eqp,
Gmp,
Eqdp,
Est,
Rsp,
Imc,
Unknown = 0,
Imc = 1,
Eqdp = 2,
Eqp = 3,
Est = 4,
Gmp = 5,
Rsp = 6,
}
[FieldOffset( 0 )]
[JsonIgnore]
public readonly EqpManipulation Eqp = default;
[FieldOffset( 0 )]
[JsonIgnore]
public readonly GmpManipulation Gmp = default;
[FieldOffset( 0 )]
[JsonIgnore]
public readonly EqdpManipulation Eqdp = default;
[FieldOffset( 0 )]
[JsonIgnore]
public readonly EstManipulation Est = default;
[FieldOffset( 0 )]
[JsonIgnore]
public readonly RspManipulation Rsp = default;
[FieldOffset( 0 )]
[JsonIgnore]
public readonly ImcManipulation Imc = default;
[FieldOffset( 15 )]
[JsonConverter( typeof( StringEnumConverter ) )]
[JsonProperty("Type")]
public readonly Type ManipulationType;
public object? Manipulation
{
get => ManipulationType switch
{
Type.Unknown => null,
Type.Imc => Imc,
Type.Eqdp => Eqdp,
Type.Eqp => Eqp,
Type.Est => Est,
Type.Gmp => Gmp,
Type.Rsp => Rsp,
_ => null,
};
init
{
switch( value )
{
case EqpManipulation m:
Eqp = m;
ManipulationType = Type.Eqp;
return;
case EqdpManipulation m:
Eqdp = m;
ManipulationType = Type.Eqdp;
return;
case GmpManipulation m:
Gmp = m;
ManipulationType = Type.Gmp;
return;
case EstManipulation m:
Est = m;
ManipulationType = Type.Est;
return;
case RspManipulation m:
Rsp = m;
ManipulationType = Type.Rsp;
return;
case ImcManipulation m:
Imc = m;
ManipulationType = Type.Imc;
return;
}
}
}
public MetaManipulation( EqpManipulation eqp )
=> ( ManipulationType, Eqp ) = ( Type.Eqp, eqp );
{
Eqp = eqp;
ManipulationType = Type.Eqp;
}
public MetaManipulation( GmpManipulation gmp )
=> ( ManipulationType, Gmp ) = ( Type.Gmp, gmp );
{
Gmp = gmp;
ManipulationType = Type.Gmp;
}
public MetaManipulation( EqdpManipulation eqdp )
=> ( ManipulationType, Eqdp ) = ( Type.Eqdp, eqdp );
{
Eqdp = eqdp;
ManipulationType = Type.Eqdp;
}
public MetaManipulation( EstManipulation est )
=> ( ManipulationType, Est ) = ( Type.Est, est );
{
Est = est;
ManipulationType = Type.Est;
}
public MetaManipulation( RspManipulation rsp )
=> ( ManipulationType, Rsp ) = ( Type.Rsp, rsp );
{
Rsp = rsp;
ManipulationType = Type.Rsp;
}
public MetaManipulation( ImcManipulation imc )
=> ( ManipulationType, Imc ) = ( Type.Imc, imc );
{
Imc = imc;
ManipulationType = Type.Imc;
}
public static implicit operator MetaManipulation( EqpManipulation eqp )
=> new(eqp);
@ -111,6 +276,8 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa
public unsafe int CompareTo( MetaManipulation other )
{
fixed( MetaManipulation* lhs = &this )
{
return Functions.MemCmpUnchecked( lhs, &other, sizeof( MetaManipulation ) );
}
}
}

View file

@ -1,14 +1,22 @@
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Files;
namespace Penumbra.Meta.Manipulations;
public readonly struct RspManipulation : IEquatable< RspManipulation >
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public readonly struct RspManipulation : IMetaManipulation< RspManipulation >
{
public readonly float Entry;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly SubRace SubRace;
[JsonConverter( typeof( StringEnumConverter ) )]
public readonly RspAttribute Attribute;
public RspManipulation( SubRace subRace, RspAttribute attribute, float entry )
@ -31,6 +39,12 @@ public readonly struct RspManipulation : IEquatable< RspManipulation >
public override int GetHashCode()
=> HashCode.Combine( ( int )SubRace, ( int )Attribute );
public int CompareTo( RspManipulation other )
{
var s = SubRace.CompareTo( other.SubRace );
return s != 0 ? s : Attribute.CompareTo( other.Attribute );
}
public int FileIndex()
=> CharacterUtility.HumanCmpIdx;

View file

@ -243,7 +243,7 @@ public class MetaManager2 : IDisposable
return false;
}
var file = m.Type switch
var file = m.Slot switch
{
EstManipulation.EstType.Hair => HairEstFile ??= new EstFile( EstManipulation.EstType.Hair ),
EstManipulation.EstType.Face => FaceEstFile ??= new EstFile( EstManipulation.EstType.Face ),

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing.Text;
using System.Linq;
using System.Numerics;
using Dalamud.Interface;
using ImGuiNET;
@ -484,39 +486,39 @@ namespace Penumbra.UI
private bool DrawRspRow( int manipIdx, IList< MetaManipulation > list )
{
var ret = false;
//var id = list[ manipIdx ].RspIdentifier;
//var val = list[ manipIdx ].RspValue;
//
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
//{
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
// if( DefaultButton(
// $"{( _editMode ? "Set to " : "" )}Default: {defaults:F3}##scaleManip", ref val, defaults )
// && _editMode )
// {
// list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, defaults );
// ret = true;
// }
//
// ImGui.SetNextItemWidth( 50 * ImGuiHelpers.GlobalScale );
// if( ImGui.InputFloat( "Scale###manip", ref val, 0, 0, "%.3f",
// _editMode ? ImGuiInputTextFlags.EnterReturnsTrue : ImGuiInputTextFlags.ReadOnly )
// && val >= 0
// && val <= 5
// && _editMode )
// {
// list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, val );
// ret = true;
// }
//}
//
//ImGui.Text( id.Attribute.ToUngenderedString() );
//ImGui.TableNextColumn();
//ImGui.TableNextColumn();
//ImGui.TableNextColumn();
//ImGui.Text( id.SubRace.ToString() );
//ImGui.TableNextColumn();
//ImGui.Text( id.Attribute.ToGender().ToString() );
var id = list[ manipIdx ].RspIdentifier;
var val = list[ manipIdx ].RspValue;
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
{
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
if( DefaultButton(
$"{( _editMode ? "Set to " : "" )}Default: {defaults:F3}##scaleManip", ref val, defaults )
&& _editMode )
{
list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, defaults );
ret = true;
}
ImGui.SetNextItemWidth( 50 * ImGuiHelpers.GlobalScale );
if( ImGui.InputFloat( "Scale###manip", ref val, 0, 0, "%.3f",
_editMode ? ImGuiInputTextFlags.EnterReturnsTrue : ImGuiInputTextFlags.ReadOnly )
&& val >= 0
&& val <= 5
&& _editMode )
{
list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, val );
ret = true;
}
}
ImGui.Text( id.Attribute.ToUngenderedString() );
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.Text( id.SubRace.ToString() );
ImGui.TableNextColumn();
ImGui.Text( id.Attribute.ToGender().ToString() );
return ret;
}
@ -543,35 +545,35 @@ namespace Penumbra.UI
ImGui.TableNextColumn();
var changes = false;
//switch( type )
//{
// case MetaType.Eqp:
// changes = DrawEqpRow( manipIdx, list );
// break;
// case MetaType.Gmp:
// changes = DrawGmpRow( manipIdx, list );
// break;
// case MetaType.Eqdp:
// changes = DrawEqdpRow( manipIdx, list );
// break;
// case MetaType.Est:
// changes = DrawEstRow( manipIdx, list );
// break;
// case MetaType.Imc:
// changes = DrawImcRow( manipIdx, list );
// break;
// case MetaType.Rsp:
// changes = DrawRspRow( manipIdx, list );
// break;
//}
//
//ImGui.TableSetColumnIndex( 9 );
//if( ImGui.Selectable( $"{list[ manipIdx ].Value:X}##{manipIdx}" ) )
//{
// ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
//}
//
//ImGui.TableNextRow();
switch( type )
{
case MetaManipulation.Type.Eqp:
changes = DrawEqpRow( manipIdx, list );
break;
case MetaManipulation.Type.Gmp:
changes = DrawGmpRow( manipIdx, list );
break;
case MetaManipulation.Type.Eqdp:
changes = DrawEqdpRow( manipIdx, list );
break;
case MetaManipulation.Type.Est:
changes = DrawEstRow( manipIdx, list );
break;
case MetaManipulation.Type.Imc:
changes = DrawImcRow( manipIdx, list );
break;
case MetaManipulation.Type.Rsp:
changes = DrawRspRow( manipIdx, list );
break;
}
ImGui.TableSetColumnIndex( 9 );
if( ImGui.Selectable( $"{manipIdx}##{manipIdx}" ) )
{
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
}
ImGui.TableNextRow();
return changes;
}
@ -714,6 +716,8 @@ namespace Penumbra.UI
{
var numRows = _editMode ? 11 : 10;
var changes = false;
if( list.Count > 0
&& ImGui.BeginTable( label, numRows,
ImGuiTableFlags.BordersInner | ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit ) )