mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 20:24:17 +01:00
Metamanipulations seemingly working.
This commit is contained in:
parent
707570615c
commit
6f527a1dbc
26 changed files with 1637 additions and 1237 deletions
|
|
@ -108,7 +108,7 @@ public sealed unsafe partial class Utf8String
|
||||||
var start = 0;
|
var start = 0;
|
||||||
for( var idx = IndexOf( b, start ); idx >= 0; idx = IndexOf( b, start ) )
|
for( var idx = IndexOf( b, start ); idx >= 0; idx = IndexOf( b, start ) )
|
||||||
{
|
{
|
||||||
if( start + 1 != idx || !removeEmpty )
|
if( start != idx || !removeEmpty )
|
||||||
{
|
{
|
||||||
ret.Add( Substring( start, idx - start ) );
|
ret.Add( Substring( start, idx - start ) );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
|
|
@ -10,6 +11,7 @@ using Penumbra.GameData.Util;
|
||||||
using Penumbra.Meta.Files;
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
using ImcFile = Penumbra.Meta.Files.ImcFile;
|
||||||
|
|
||||||
namespace Penumbra.Importer;
|
namespace Penumbra.Importer;
|
||||||
|
|
||||||
|
|
@ -254,24 +256,40 @@ public class TexToolsMeta
|
||||||
using var reader = new BinaryReader( new MemoryStream( data ) );
|
using var reader = new BinaryReader( new MemoryStream( data ) );
|
||||||
var values = reader.ReadStructures< ImcEntry >( num );
|
var values = reader.ReadStructures< ImcEntry >( num );
|
||||||
ushort i = 0;
|
ushort i = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
if( info.PrimaryType is ObjectType.Equipment or ObjectType.Accessory )
|
if( info.PrimaryType is ObjectType.Equipment or ObjectType.Accessory )
|
||||||
{
|
{
|
||||||
// TODO check against default.
|
var def = new ImcFile( new ImcManipulation( info.EquipSlot, i, info.PrimaryId, new ImcEntry() ).GamePath() );
|
||||||
|
var partIdx = ImcFile.PartIndex( info.EquipSlot );
|
||||||
foreach( var value in values )
|
foreach( var value in values )
|
||||||
|
{
|
||||||
|
if( !value.Equals( def.GetEntry( partIdx, i ) ) )
|
||||||
{
|
{
|
||||||
ImcManipulations.Add( new ImcManipulation( info.EquipSlot, i, info.PrimaryId, value ) );
|
ImcManipulations.Add( new ImcManipulation( info.EquipSlot, i, info.PrimaryId, value ) );
|
||||||
|
}
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach( var value in values )
|
var def = new ImcFile( new ImcManipulation( info.PrimaryType, info.SecondaryType, info.PrimaryId, info.SecondaryId, i,
|
||||||
|
new ImcEntry() ).GamePath() );
|
||||||
|
foreach( var value in values.Where( v => true || !v.Equals( def.GetEntry( 0, i ) ) ) )
|
||||||
{
|
{
|
||||||
ImcManipulations.Add( new ImcManipulation( info.PrimaryType, info.SecondaryType, info.PrimaryId, info.SecondaryId, i, value ) );
|
ImcManipulations.Add( new ImcManipulation( info.PrimaryType, info.SecondaryType, info.PrimaryId, info.SecondaryId, i,
|
||||||
|
value ) );
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
PluginLog.Error( "Could not compute IMC manipulation. This is in all likelihood due to TexTools corrupting your index files.\n"
|
||||||
|
+ $"If the following error looks like Lumina is having trouble to read an IMC file, please do a do-over in TexTools:\n{e}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string ReadNullTerminated( BinaryReader reader )
|
private static string ReadNullTerminated( BinaryReader reader )
|
||||||
{
|
{
|
||||||
|
|
@ -374,8 +392,10 @@ public class TexToolsMeta
|
||||||
{
|
{
|
||||||
var def = CmpFile.GetDefault( subRace, attribute );
|
var def = CmpFile.GetDefault( subRace, attribute );
|
||||||
if( value != def )
|
if( value != def )
|
||||||
|
{
|
||||||
ret!.RspManipulations.Add( new RspManipulation( subRace, attribute, value ) );
|
ret!.RspManipulations.Add( new RspManipulation( subRace, attribute, value ) );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( gender == 1 )
|
if( gender == 1 )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
|
|
@ -20,7 +21,33 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
public Structs.CharacterUtility* Address
|
public Structs.CharacterUtility* Address
|
||||||
=> *_characterUtilityAddress;
|
=> *_characterUtilityAddress;
|
||||||
|
|
||||||
public (IntPtr Address, int Size)[] DefaultResources = new (IntPtr, int)[Structs.CharacterUtility.NumRelevantResources];
|
// The relevant indices depend on which meta manipulations we allow for.
|
||||||
|
// The defines are set in the project configuration.
|
||||||
|
public static readonly int[] RelevantIndices
|
||||||
|
= Array.Empty< int >()
|
||||||
|
#if USE_EQP
|
||||||
|
.Append( Structs.CharacterUtility.EqpIdx )
|
||||||
|
#endif
|
||||||
|
#if USE_GMP
|
||||||
|
.Append( Structs.CharacterUtility.GmpIdx )
|
||||||
|
#endif
|
||||||
|
#if USE_EQDP
|
||||||
|
.Concat( Enumerable.Range( Structs.CharacterUtility.EqdpStartIdx, Structs.CharacterUtility.NumEqdpFiles ) )
|
||||||
|
#endif
|
||||||
|
#if USE_CMP
|
||||||
|
.Append( Structs.CharacterUtility.HumanCmpIdx )
|
||||||
|
#endif
|
||||||
|
#if USE_EST
|
||||||
|
.Concat( Enumerable.Range( Structs.CharacterUtility.FaceEstIdx, 4 ) )
|
||||||
|
#endif
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
private static readonly int[] ReverseIndices
|
||||||
|
= Enumerable.Range( 0, Structs.CharacterUtility.NumResources )
|
||||||
|
.Select( i => Array.IndexOf( RelevantIndices, i ) ).ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
public (IntPtr Address, int Size)[] DefaultResources = new (IntPtr, int)[RelevantIndices.Length];
|
||||||
|
|
||||||
public CharacterUtility()
|
public CharacterUtility()
|
||||||
{
|
{
|
||||||
|
|
@ -48,9 +75,9 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
// We store the default data of the resources so we can always restore them.
|
// We store the default data of the resources so we can always restore them.
|
||||||
private void LoadDefaultResources()
|
private void LoadDefaultResources()
|
||||||
{
|
{
|
||||||
for( var i = 0; i < Structs.CharacterUtility.NumRelevantResources; ++i )
|
for( var i = 0; i < RelevantIndices.Length; ++i )
|
||||||
{
|
{
|
||||||
var resource = ( Structs.ResourceHandle* )Address->Resources[ i ];
|
var resource = ( Structs.ResourceHandle* )Address->Resources[ RelevantIndices[ i ] ];
|
||||||
DefaultResources[ i ] = resource->GetData();
|
DefaultResources[ i ] = resource->GetData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,19 +92,25 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the data of one of the stored resources to its default values.
|
// Reset the data of one of the stored resources to its default values.
|
||||||
public void ResetResource( int idx )
|
public void ResetResource( int fileIdx )
|
||||||
{
|
{
|
||||||
var resource = ( Structs.ResourceHandle* )Address->Resources[ idx ];
|
var (data, size) = DefaultResources[ ReverseIndices[ fileIdx ] ];
|
||||||
resource->SetData( DefaultResources[ idx ].Address, DefaultResources[ idx ].Size );
|
var resource = ( Structs.ResourceHandle* )Address->Resources[ fileIdx ];
|
||||||
|
resource->SetData( data, size );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return all relevant resources to the default resource.
|
||||||
|
public void ResetAll()
|
||||||
|
{
|
||||||
|
foreach( var idx in RelevantIndices )
|
||||||
|
{
|
||||||
|
ResetResource( idx );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
for( var i = 0; i < Structs.CharacterUtility.NumRelevantResources; ++i )
|
ResetAll();
|
||||||
{
|
|
||||||
ResetResource( i );
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadDataFilesHook.Dispose();
|
LoadDataFilesHook.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,13 +116,16 @@ public unsafe partial class ResourceLoader
|
||||||
// The internal buffer size does not allow for more than 260 characters.
|
// The internal buffer size does not allow for more than 260 characters.
|
||||||
// We use the IsRooted check to signify paths replaced by us pointing to the local filesystem instead of an SqPack.
|
// We use the IsRooted check to signify paths replaced by us pointing to the local filesystem instead of an SqPack.
|
||||||
if( !valid || !gamePath.IsRooted() )
|
if( !valid || !gamePath.IsRooted() )
|
||||||
|
{
|
||||||
|
if( valid && ResourceLoadCustomization != null && gamePath.Path[ 0 ] == ( byte )'|' )
|
||||||
|
{
|
||||||
|
ret = ResourceLoadCustomization.Invoke( gamePath, resourceManager, fileDescriptor, priority, isSync );
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ret = ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
ret = ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
||||||
FileLoaded?.Invoke( gamePath.Path, ret != 0, false );
|
FileLoaded?.Invoke( gamePath.Path, ret != 0, false );
|
||||||
}
|
}
|
||||||
else if( ResourceLoadCustomization != null && gamePath.Path[0] == (byte) '|' )
|
|
||||||
{
|
|
||||||
ret = ResourceLoadCustomization.Invoke( gamePath, resourceManager, fileDescriptor, priority, isSync );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
|
|
@ -7,8 +8,10 @@ namespace Penumbra.Interop.Structs;
|
||||||
[StructLayout( LayoutKind.Explicit )]
|
[StructLayout( LayoutKind.Explicit )]
|
||||||
public unsafe struct CharacterUtility
|
public unsafe struct CharacterUtility
|
||||||
{
|
{
|
||||||
|
public static readonly int[] EqdpIndices
|
||||||
|
= Enumerable.Range( EqdpStartIdx, NumEqdpFiles ).ToArray();
|
||||||
|
|
||||||
public const int NumResources = 85;
|
public const int NumResources = 85;
|
||||||
public const int NumRelevantResources = 68;
|
|
||||||
public const int EqpIdx = 0;
|
public const int EqpIdx = 0;
|
||||||
public const int GmpIdx = 1;
|
public const int GmpIdx = 1;
|
||||||
public const int HumanCmpIdx = 63;
|
public const int HumanCmpIdx = 63;
|
||||||
|
|
@ -16,40 +19,41 @@ public unsafe struct CharacterUtility
|
||||||
public const int HairEstIdx = 65;
|
public const int HairEstIdx = 65;
|
||||||
public const int BodyEstIdx = 66;
|
public const int BodyEstIdx = 66;
|
||||||
public const int HeadEstIdx = 67;
|
public const int HeadEstIdx = 67;
|
||||||
|
public const int EqdpStartIdx = 2;
|
||||||
public const int NumEqdpFiles = 2 * 28;
|
public const int NumEqdpFiles = 2 * 28;
|
||||||
|
|
||||||
public static int EqdpIdx( GenderRace raceCode, bool accessory )
|
public static int EqdpIdx( GenderRace raceCode, bool accessory )
|
||||||
=> ( accessory ? 28 : 0 )
|
=> ( accessory ? NumEqdpFiles / 2 : 0 )
|
||||||
+ ( int )raceCode switch
|
+ ( int )raceCode switch
|
||||||
{
|
{
|
||||||
0101 => 2,
|
0101 => EqdpStartIdx,
|
||||||
0201 => 3,
|
0201 => EqdpStartIdx + 1,
|
||||||
0301 => 4,
|
0301 => EqdpStartIdx + 2,
|
||||||
0401 => 5,
|
0401 => EqdpStartIdx + 3,
|
||||||
0501 => 6,
|
0501 => EqdpStartIdx + 4,
|
||||||
0601 => 7,
|
0601 => EqdpStartIdx + 5,
|
||||||
0701 => 8,
|
0701 => EqdpStartIdx + 6,
|
||||||
0801 => 9,
|
0801 => EqdpStartIdx + 7,
|
||||||
0901 => 10,
|
0901 => EqdpStartIdx + 8,
|
||||||
1001 => 11,
|
1001 => EqdpStartIdx + 9,
|
||||||
1101 => 12,
|
1101 => EqdpStartIdx + 10,
|
||||||
1201 => 13,
|
1201 => EqdpStartIdx + 11,
|
||||||
1301 => 14,
|
1301 => EqdpStartIdx + 12,
|
||||||
1401 => 15,
|
1401 => EqdpStartIdx + 13,
|
||||||
1501 => 16,
|
1501 => EqdpStartIdx + 14,
|
||||||
1601 => 17, // Does not exist yet
|
1601 => EqdpStartIdx + 15, // Does not exist yet
|
||||||
1701 => 18,
|
1701 => EqdpStartIdx + 16,
|
||||||
1801 => 19,
|
1801 => EqdpStartIdx + 17,
|
||||||
0104 => 20,
|
0104 => EqdpStartIdx + 18,
|
||||||
0204 => 21,
|
0204 => EqdpStartIdx + 19,
|
||||||
0504 => 22,
|
0504 => EqdpStartIdx + 20,
|
||||||
0604 => 23,
|
0604 => EqdpStartIdx + 21,
|
||||||
0704 => 24,
|
0704 => EqdpStartIdx + 22,
|
||||||
0804 => 25,
|
0804 => EqdpStartIdx + 23,
|
||||||
1304 => 26,
|
1304 => EqdpStartIdx + 24,
|
||||||
1404 => 27,
|
1404 => EqdpStartIdx + 25,
|
||||||
9104 => 28,
|
9104 => EqdpStartIdx + 26,
|
||||||
9204 => 29,
|
9204 => EqdpStartIdx + 27,
|
||||||
_ => throw new ArgumentException(),
|
_ => throw new ArgumentException(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
throw new IndexOutOfRangeException();
|
throw new IndexOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return *( EqdpEntry* )( Data + DataOffset + EqdpEntrySize * idx );
|
var x = new ReadOnlySpan< ushort >( ( ushort* )Data, Length / 2 );
|
||||||
|
return ( EqdpEntry )( *( ushort* )( Data + DataOffset + EqdpEntrySize * idx ) );
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +57,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
throw new IndexOutOfRangeException();
|
throw new IndexOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
*( EqdpEntry* )( Data + DataOffset + EqdpEntrySize * idx ) = value;
|
*( ushort* )( Data + DataOffset + EqdpEntrySize * idx ) = ( ushort )value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +67,11 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
Functions.MemCpyUnchecked( Data, def, IdentifierSize + PreambleSize );
|
Functions.MemCpyUnchecked( Data, def, IdentifierSize + PreambleSize );
|
||||||
|
|
||||||
var controlPtr = ( ushort* )( def + IdentifierSize + PreambleSize );
|
var controlPtr = ( ushort* )( def + IdentifierSize + PreambleSize );
|
||||||
var dataBasePtr = ( byte* )( controlPtr + BlockCount );
|
var dataBasePtr = controlPtr + BlockCount;
|
||||||
var myDataPtr = ( ushort* )( Data + IdentifierSize + PreambleSize + 2 * BlockCount );
|
var myDataPtrStart = ( ushort* )( Data + IdentifierSize + PreambleSize + 2 * BlockCount );
|
||||||
|
var myDataPtr = myDataPtrStart;
|
||||||
|
var myControlPtr = ( ushort* )( Data + IdentifierSize + PreambleSize );
|
||||||
|
var x = new ReadOnlySpan< ushort >( ( ushort* )Data, Length / 2 );
|
||||||
for( var i = 0; i < BlockCount; ++i )
|
for( var i = 0; i < BlockCount; ++i )
|
||||||
{
|
{
|
||||||
if( controlPtr[ i ] == CollapsedBlock )
|
if( controlPtr[ i ] == CollapsedBlock )
|
||||||
|
|
@ -76,11 +80,16 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var y = new ReadOnlySpan< ushort >( dataBasePtr + controlPtr[ i ], BlockSize );
|
||||||
|
var z = new ReadOnlySpan< ushort >( myDataPtr, BlockSize );
|
||||||
Functions.MemCpyUnchecked( myDataPtr, dataBasePtr + controlPtr[ i ], BlockSize * EqdpEntrySize );
|
Functions.MemCpyUnchecked( myDataPtr, dataBasePtr + controlPtr[ i ], BlockSize * EqdpEntrySize );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myControlPtr[ i ] = ( ushort )( i * BlockSize );
|
||||||
myDataPtr += BlockSize;
|
myDataPtr += BlockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Functions.MemSet( myDataPtr, 0, Length - ( int )( ( byte* )myDataPtr - Data ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset( IEnumerable< int > entries )
|
public void Reset( IEnumerable< int > entries )
|
||||||
|
|
@ -102,7 +111,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
DataOffset = IdentifierSize + PreambleSize + totalBlockCount * BlockHeaderSize;
|
DataOffset = IdentifierSize + PreambleSize + totalBlockCount * BlockHeaderSize;
|
||||||
|
|
||||||
var fullLength = DataOffset + totalBlockCount * totalBlockSize;
|
var fullLength = DataOffset + totalBlockCount * totalBlockSize;
|
||||||
fullLength += ( FileAlignment - ( Length & ( FileAlignment - 1 ) ) ) & ( FileAlignment - 1 );
|
fullLength += ( FileAlignment - ( fullLength & ( FileAlignment - 1 ) ) ) & ( FileAlignment - 1 );
|
||||||
AllocateData( fullLength );
|
AllocateData( fullLength );
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
@ -128,8 +137,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var blockData = ( EqdpEntry* )( data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block );
|
var blockData = ( ushort* )( data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2 );
|
||||||
return *( blockData + blockIdx % blockSize );
|
var x = new ReadOnlySpan< ushort >( blockData, blockSize );
|
||||||
|
return (EqdpEntry) (*( blockData + setIdx % blockSize ));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EqdpEntry GetDefault( GenderRace raceCode, bool accessory, int setIdx )
|
public static EqdpEntry GetDefault( GenderRace raceCode, bool accessory, int setIdx )
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.GameData.Util;
|
using Penumbra.GameData.Util;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
@ -24,17 +25,17 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
public ulong ControlBlock
|
public ulong ControlBlock
|
||||||
=> *( ulong* )Data;
|
=> *( ulong* )Data;
|
||||||
|
|
||||||
protected T Get< T >( int idx ) where T : unmanaged
|
protected ulong GetInternal( int idx )
|
||||||
{
|
{
|
||||||
return idx switch
|
return idx switch
|
||||||
{
|
{
|
||||||
>= Count => throw new IndexOutOfRangeException(),
|
>= Count => throw new IndexOutOfRangeException(),
|
||||||
<= 1 => *( ( T* )Data + 1 ),
|
<= 1 => *( ( ulong* )Data + 1 ),
|
||||||
_ => *( ( T* )Data + idx ),
|
_ => *( ( ulong* )Data + idx ),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Set< T >( int idx, T value ) where T : unmanaged
|
protected void SetInternal( int idx, ulong value )
|
||||||
{
|
{
|
||||||
idx = idx switch
|
idx = idx switch
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +44,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
_ => idx,
|
_ => idx,
|
||||||
};
|
};
|
||||||
|
|
||||||
*( ( T* )Data + idx ) = value;
|
*( ( ulong* )Data + idx ) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SetEmptyBlock( int idx )
|
protected virtual void SetEmptyBlock( int idx )
|
||||||
|
|
@ -55,19 +56,22 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
{
|
{
|
||||||
var ptr = ( byte* )DefaultData.Data;
|
var ptr = ( byte* )DefaultData.Data;
|
||||||
var controlBlock = *( ulong* )ptr;
|
var controlBlock = *( ulong* )ptr;
|
||||||
*( ulong* )Data = ulong.MaxValue;
|
var expandedBlocks = 0;
|
||||||
for( var i = 0; i < 64; ++i )
|
for( var i = 0; i < NumBlocks; ++i )
|
||||||
{
|
{
|
||||||
var collapsed = ( ( controlBlock >> i ) & 1 ) == 0;
|
var collapsed = ( ( controlBlock >> i ) & 1 ) == 0;
|
||||||
if( !collapsed )
|
if( !collapsed )
|
||||||
{
|
{
|
||||||
Functions.MemCpyUnchecked( Data + i * BlockSize * EntrySize, ptr + i * BlockSize * EntrySize, BlockSize * EntrySize );
|
Functions.MemCpyUnchecked( Data + i * BlockSize * EntrySize, ptr + expandedBlocks * BlockSize * EntrySize, BlockSize * EntrySize );
|
||||||
|
expandedBlocks++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetEmptyBlock( i );
|
SetEmptyBlock( i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*( ulong* )Data = ulong.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpandedEqpGmpBase( bool gmp )
|
public ExpandedEqpGmpBase( bool gmp )
|
||||||
|
|
@ -77,7 +81,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static T GetDefault< T >( int fileIdx, int setIdx, T def ) where T : unmanaged
|
protected static ulong GetDefaultInternal( int fileIdx, int setIdx, ulong def )
|
||||||
{
|
{
|
||||||
var data = ( byte* )Penumbra.CharacterUtility.DefaultResources[ fileIdx ].Address;
|
var data = ( byte* )Penumbra.CharacterUtility.DefaultResources[ fileIdx ].Address;
|
||||||
if( setIdx == 0 )
|
if( setIdx == 0 )
|
||||||
|
|
@ -100,7 +104,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
||||||
|
|
||||||
var count = BitOperations.PopCount( control & ( blockBit - 1 ) );
|
var count = BitOperations.PopCount( control & ( blockBit - 1 ) );
|
||||||
var idx = setIdx % BlockSize;
|
var idx = setIdx % BlockSize;
|
||||||
var ptr = ( T* )data + BlockSize * count + idx;
|
var ptr = ( ulong* )data + BlockSize * count + idx;
|
||||||
return *ptr;
|
return *ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,12 +117,12 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase
|
||||||
|
|
||||||
public EqpEntry this[ int idx ]
|
public EqpEntry this[ int idx ]
|
||||||
{
|
{
|
||||||
get => Get< EqpEntry >( idx );
|
get => ( EqpEntry )GetInternal( idx );
|
||||||
set => Set( idx, value );
|
set => SetInternal( idx, ( ulong )value );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EqpEntry GetDefault( int setIdx )
|
public static EqpEntry GetDefault( int setIdx )
|
||||||
=> GetDefault( CharacterUtility.EqpIdx, setIdx, Eqp.DefaultEntry );
|
=> ( EqpEntry )GetDefaultInternal( CharacterUtility.EqpIdx, setIdx, ( ulong )Eqp.DefaultEntry );
|
||||||
|
|
||||||
protected override unsafe void SetEmptyBlock( int idx )
|
protected override unsafe void SetEmptyBlock( int idx )
|
||||||
{
|
{
|
||||||
|
|
@ -147,12 +151,12 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase
|
||||||
|
|
||||||
public GmpEntry this[ int idx ]
|
public GmpEntry this[ int idx ]
|
||||||
{
|
{
|
||||||
get => Get< GmpEntry >( idx );
|
get => ( GmpEntry )GetInternal( idx );
|
||||||
set => Set( idx, value );
|
set => SetInternal( idx, ( ulong )value );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GmpEntry GetDefault( int setIdx )
|
public static GmpEntry GetDefault( int setIdx )
|
||||||
=> GetDefault( CharacterUtility.GmpIdx, setIdx, GmpEntry.Default );
|
=> ( GmpEntry )GetDefaultInternal( CharacterUtility.GmpIdx, setIdx, ( ulong )GmpEntry.Default );
|
||||||
|
|
||||||
public void Reset( IEnumerable< int > entries )
|
public void Reset( IEnumerable< int > entries )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
|
@ -7,6 +8,7 @@ using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Util;
|
using Penumbra.GameData.Util;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
namespace Penumbra.Meta.Files;
|
namespace Penumbra.Meta.Files;
|
||||||
|
|
||||||
|
|
@ -64,41 +66,57 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
=> NumParts * sizeof( ImcEntry ) * ( Count + 1 ) + PreambleSize;
|
=> NumParts * sizeof( ImcEntry ) * ( Count + 1 ) + PreambleSize;
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
=> *( ushort* )Data;
|
=> CountInternal( Data );
|
||||||
|
|
||||||
public ushort PartMask
|
|
||||||
=> *( ushort* )( Data + 2 );
|
|
||||||
|
|
||||||
public readonly int NumParts;
|
public readonly int NumParts;
|
||||||
public readonly Utf8GamePath Path;
|
public readonly Utf8GamePath Path;
|
||||||
|
|
||||||
public ImcEntry* DefaultPartPtr( int partIdx )
|
private static int CountInternal( byte* data )
|
||||||
|
=> *( ushort* )data;
|
||||||
|
|
||||||
|
private static ushort PartMask( byte* data )
|
||||||
|
=> *( ushort* )( data + 2 );
|
||||||
|
|
||||||
|
private static ImcEntry* DefaultPartPtr( byte* data, int partIdx )
|
||||||
{
|
{
|
||||||
var flag = 1 << partIdx;
|
var flag = 1 << partIdx;
|
||||||
if( ( PartMask & flag ) == 0 )
|
if( ( PartMask( data ) & flag ) == 0 )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( ImcEntry* )( Data + PreambleSize ) + partIdx;
|
return ( ImcEntry* )( data + PreambleSize ) + partIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImcEntry* VariantPtr( int partIdx, int variantIdx )
|
private static ImcEntry* VariantPtr( byte* data, int partIdx, int variantIdx )
|
||||||
{
|
{
|
||||||
|
if( variantIdx == 0 )
|
||||||
|
{
|
||||||
|
return DefaultPartPtr( data, partIdx );
|
||||||
|
}
|
||||||
|
|
||||||
|
--variantIdx;
|
||||||
var flag = 1 << partIdx;
|
var flag = 1 << partIdx;
|
||||||
if( ( PartMask & flag ) == 0 || variantIdx >= Count )
|
|
||||||
|
if( ( PartMask( data ) & flag ) == 0 || variantIdx >= CountInternal( data ) )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var numParts = NumParts;
|
var numParts = BitOperations.PopCount( PartMask( data ) );
|
||||||
var ptr = ( ImcEntry* )( Data + PreambleSize );
|
var ptr = ( ImcEntry* )( data + PreambleSize );
|
||||||
ptr += numParts;
|
ptr += numParts;
|
||||||
ptr += variantIdx * numParts;
|
ptr += variantIdx * numParts;
|
||||||
ptr += partIdx;
|
ptr += partIdx;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImcEntry GetEntry( int partIdx, int variantIdx )
|
||||||
|
{
|
||||||
|
var ptr = VariantPtr( Data, partIdx, variantIdx );
|
||||||
|
return ptr == null ? new ImcEntry() : *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
public static int PartIndex( EquipSlot slot )
|
public static int PartIndex( EquipSlot slot )
|
||||||
=> slot switch
|
=> slot switch
|
||||||
{
|
{
|
||||||
|
|
@ -122,7 +140,6 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var numParts = NumParts;
|
|
||||||
if( ActualLength > Length )
|
if( ActualLength > Length )
|
||||||
{
|
{
|
||||||
PluginLog.Warning( "Adding too many variants to IMC, size exceeded." );
|
PluginLog.Warning( "Adding too many variants to IMC, size exceeded." );
|
||||||
|
|
@ -130,10 +147,10 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultPtr = ( ImcEntry* )( Data + PreambleSize );
|
var defaultPtr = ( ImcEntry* )( Data + PreambleSize );
|
||||||
var endPtr = defaultPtr + ( numVariants + 1 ) * numParts;
|
var endPtr = defaultPtr + ( numVariants + 1 ) * NumParts;
|
||||||
for( var ptr = defaultPtr + numParts; ptr < endPtr; ptr += numParts )
|
for( var ptr = defaultPtr + NumParts; ptr < endPtr; ptr += NumParts )
|
||||||
{
|
{
|
||||||
Functions.MemCpyUnchecked( ptr, defaultPtr, numParts * sizeof( ImcEntry ) );
|
Functions.MemCpyUnchecked( ptr, defaultPtr, NumParts * sizeof( ImcEntry ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginLog.Verbose( "Expanded imc from {Count} to {NewCount} variants.", Count, numVariants );
|
PluginLog.Verbose( "Expanded imc from {Count} to {NewCount} variants.", Count, numVariants );
|
||||||
|
|
@ -143,15 +160,14 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
|
|
||||||
public bool SetEntry( int partIdx, int variantIdx, ImcEntry entry )
|
public bool SetEntry( int partIdx, int variantIdx, ImcEntry entry )
|
||||||
{
|
{
|
||||||
var numParts = NumParts;
|
if( partIdx >= NumParts )
|
||||||
if( partIdx >= numParts )
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureVariantCount( variantIdx + 1 );
|
EnsureVariantCount( variantIdx );
|
||||||
|
|
||||||
var variantPtr = VariantPtr( partIdx, variantIdx );
|
var variantPtr = VariantPtr( Data, partIdx, variantIdx );
|
||||||
if( variantPtr == null )
|
if( variantPtr == null )
|
||||||
{
|
{
|
||||||
PluginLog.Error( "Error during expansion of imc file." );
|
PluginLog.Error( "Error during expansion of imc file." );
|
||||||
|
|
@ -168,9 +184,20 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
var file = Dalamud.GameData.GetFile( Path.ToString() );
|
||||||
|
fixed( byte* ptr = file!.Data )
|
||||||
|
{
|
||||||
|
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
||||||
|
Functions.MemSet( Data + file.Data.Length, 0, Length - file.Data.Length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ImcFile( Utf8GamePath path )
|
public ImcFile( Utf8GamePath path )
|
||||||
: base( 0 )
|
: base( 0 )
|
||||||
{
|
{
|
||||||
|
Path = path;
|
||||||
var file = Dalamud.GameData.GetFile( path.ToString() );
|
var file = Dalamud.GameData.GetFile( path.ToString() );
|
||||||
if( file == null )
|
if( file == null )
|
||||||
{
|
{
|
||||||
|
|
@ -182,6 +209,22 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
NumParts = BitOperations.PopCount( *( ushort* )( ptr + 2 ) );
|
NumParts = BitOperations.PopCount( *( ushort* )( ptr + 2 ) );
|
||||||
AllocateData( file.Data.Length + sizeof( ImcEntry ) * 100 * NumParts );
|
AllocateData( file.Data.Length + sizeof( ImcEntry ) * 100 * NumParts );
|
||||||
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
||||||
|
Functions.MemSet( Data + file.Data.Length, 0, sizeof( ImcEntry ) * 100 * NumParts );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImcEntry GetDefault( Utf8GamePath path, EquipSlot slot, int variantIdx )
|
||||||
|
{
|
||||||
|
var file = Dalamud.GameData.GetFile( path.ToString() );
|
||||||
|
if( file == null )
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed( byte* ptr = file.Data )
|
||||||
|
{
|
||||||
|
var entry = VariantPtr( ptr, PartIndex( slot ), variantIdx );
|
||||||
|
return entry == null ? new ImcEntry() : *entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
59
Penumbra/Meta/Manager/MetaManager.Cmp.cs
Normal file
59
Penumbra/Meta/Manager/MetaManager.Cmp.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public struct MetaManagerCmp : IDisposable
|
||||||
|
{
|
||||||
|
public CmpFile? File = null;
|
||||||
|
public readonly Dictionary< RspManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
public MetaManagerCmp()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[Conditional( "USE_CMP" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
if( File == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Reset( Manipulations.Keys.Select( m => ( m.SubRace, m.Attribute ) ) );
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_CMP" )]
|
||||||
|
public void SetFiles()
|
||||||
|
=> SetFile( File, CharacterUtility.HumanCmpIdx );
|
||||||
|
|
||||||
|
public bool ApplyMod( RspManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
#if USE_CMP
|
||||||
|
if( !Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File ??= new CmpFile();
|
||||||
|
return m.Apply( File );
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
File?.Dispose();
|
||||||
|
File = null;
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
Penumbra/Meta/Manager/MetaManager.Eqdp.cs
Normal file
72
Penumbra/Meta/Manager/MetaManager.Eqdp.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public struct MetaManagerEqdp : IDisposable
|
||||||
|
{
|
||||||
|
public ExpandedEqdpFile?[] Files = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles];
|
||||||
|
|
||||||
|
public readonly Dictionary< EqdpManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
public MetaManagerEqdp()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[Conditional( "USE_EQDP" )]
|
||||||
|
public void SetFiles()
|
||||||
|
{
|
||||||
|
foreach( var idx in CharacterUtility.EqdpIndices )
|
||||||
|
{
|
||||||
|
SetFile( Files[ idx - CharacterUtility.EqdpStartIdx ], idx );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_EQDP" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
foreach( var file in Files )
|
||||||
|
{
|
||||||
|
file?.Reset( Manipulations.Keys.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyMod( EqdpManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
#if USE_EQDP
|
||||||
|
if( !Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = Files[ m.FileIndex() - 2 ] ??= new ExpandedEqdpFile( Names.CombinedRace( m.Gender, m.Race ), m.Slot.IsAccessory() );
|
||||||
|
return m.Apply( file );
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpandedEqdpFile? File( GenderRace race, bool accessory )
|
||||||
|
=> Files[ CharacterUtility.EqdpIdx( race, accessory ) - CharacterUtility.EqdpStartIdx ];
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
for( var i = 0; i < Files.Length; ++i )
|
||||||
|
{
|
||||||
|
Files[ i ]?.Dispose();
|
||||||
|
Files[ i ] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
Penumbra/Meta/Manager/MetaManager.Eqp.cs
Normal file
59
Penumbra/Meta/Manager/MetaManager.Eqp.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public struct MetaManagerEqp : IDisposable
|
||||||
|
{
|
||||||
|
public ExpandedEqpFile? File = null;
|
||||||
|
public readonly Dictionary< EqpManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
public MetaManagerEqp()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[Conditional( "USE_EQP" )]
|
||||||
|
public void SetFiles()
|
||||||
|
=> SetFile( File, CharacterUtility.EqpIdx );
|
||||||
|
|
||||||
|
[Conditional( "USE_EQP" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
if( File == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) );
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyMod( EqpManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
#if USE_EQP
|
||||||
|
if( !Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File ??= new ExpandedEqpFile();
|
||||||
|
return m.Apply( File );
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
File?.Dispose();
|
||||||
|
File = null;
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
Penumbra/Meta/Manager/MetaManager.Est.cs
Normal file
78
Penumbra/Meta/Manager/MetaManager.Est.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public struct MetaManagerEst : IDisposable
|
||||||
|
{
|
||||||
|
public EstFile? FaceFile = null;
|
||||||
|
public EstFile? HairFile = null;
|
||||||
|
public EstFile? BodyFile = null;
|
||||||
|
public EstFile? HeadFile = null;
|
||||||
|
|
||||||
|
public readonly Dictionary< EstManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
public MetaManagerEst()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[Conditional( "USE_EST" )]
|
||||||
|
public void SetFiles()
|
||||||
|
{
|
||||||
|
SetFile( FaceFile, CharacterUtility.FaceEstIdx );
|
||||||
|
SetFile( HairFile, CharacterUtility.HairEstIdx );
|
||||||
|
SetFile( BodyFile, CharacterUtility.BodyEstIdx );
|
||||||
|
SetFile( HeadFile, CharacterUtility.HeadEstIdx );
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_EST" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
FaceFile?.Reset();
|
||||||
|
HairFile?.Reset();
|
||||||
|
BodyFile?.Reset();
|
||||||
|
HeadFile?.Reset();
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyMod( EstManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
#if USE_EST
|
||||||
|
if( !Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = m.Slot switch
|
||||||
|
{
|
||||||
|
EstManipulation.EstType.Hair => HairFile ??= new EstFile( EstManipulation.EstType.Hair ),
|
||||||
|
EstManipulation.EstType.Face => FaceFile ??= new EstFile( EstManipulation.EstType.Face ),
|
||||||
|
EstManipulation.EstType.Body => BodyFile ??= new EstFile( EstManipulation.EstType.Body ),
|
||||||
|
EstManipulation.EstType.Head => HeadFile ??= new EstFile( EstManipulation.EstType.Head ),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
return m.Apply( file );
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
FaceFile?.Dispose();
|
||||||
|
HairFile?.Dispose();
|
||||||
|
BodyFile?.Dispose();
|
||||||
|
HeadFile?.Dispose();
|
||||||
|
FaceFile = null;
|
||||||
|
HairFile = null;
|
||||||
|
BodyFile = null;
|
||||||
|
HeadFile = null;
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
Penumbra/Meta/Manager/MetaManager.Gmp.cs
Normal file
57
Penumbra/Meta/Manager/MetaManager.Gmp.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public struct MetaManagerGmp : IDisposable
|
||||||
|
{
|
||||||
|
public ExpandedGmpFile? File = null;
|
||||||
|
public readonly Dictionary< GmpManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
public MetaManagerGmp()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[Conditional( "USE_GMP" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
if( File != null )
|
||||||
|
{
|
||||||
|
File.Reset( Manipulations.Keys.Select( m => ( int )m.SetId ) );
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_GMP" )]
|
||||||
|
public void SetFiles()
|
||||||
|
=> SetFile( File, CharacterUtility.GmpIdx );
|
||||||
|
|
||||||
|
public bool ApplyMod( GmpManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
#if USE_GMP
|
||||||
|
if( Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File ??= new ExpandedGmpFile();
|
||||||
|
return m.Apply( File );
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
File?.Dispose();
|
||||||
|
File = null;
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
Penumbra/Meta/Manager/MetaManager.Imc.cs
Normal file
148
Penumbra/Meta/Manager/MetaManager.Imc.cs
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
|
using Penumbra.GameData.ByteString;
|
||||||
|
using Penumbra.Interop;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.Mods;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager
|
||||||
|
{
|
||||||
|
public readonly struct MetaManagerImc : IDisposable
|
||||||
|
{
|
||||||
|
public readonly Dictionary< Utf8GamePath, ImcFile > Files = new();
|
||||||
|
public readonly Dictionary< ImcManipulation, Mod.Mod > Manipulations = new();
|
||||||
|
|
||||||
|
private readonly ModCollection _collection;
|
||||||
|
private readonly ResourceLoader.ResourceLoadCustomizationDelegate? _previousDelegate;
|
||||||
|
|
||||||
|
|
||||||
|
public MetaManagerImc( ModCollection collection )
|
||||||
|
{
|
||||||
|
_collection = collection;
|
||||||
|
_previousDelegate = Penumbra.ResourceLoader.ResourceLoadCustomization;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_IMC" )]
|
||||||
|
public void SetFiles()
|
||||||
|
{
|
||||||
|
if( _collection.Cache == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( var path in Files.Keys )
|
||||||
|
{
|
||||||
|
_collection.Cache.ResolvedFiles[ path ] = CreateImcPath( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_IMC" )]
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
foreach( var (path, file) in Files )
|
||||||
|
{
|
||||||
|
_collection.Cache?.ResolvedFiles.Remove( path );
|
||||||
|
file.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Manipulations.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool ApplyMod( ImcManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
const uint imcExt = 0x00696D63;
|
||||||
|
#if USE_IMC
|
||||||
|
if( !Manipulations.TryAdd( m, mod ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = m.GamePath();
|
||||||
|
if( !Files.TryGetValue( path, out var file ) )
|
||||||
|
{
|
||||||
|
file = new ImcFile( path );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m.Apply( file ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files[ path ] = file;
|
||||||
|
var fullPath = CreateImcPath( path );
|
||||||
|
if( _collection.Cache != null )
|
||||||
|
{
|
||||||
|
_collection.Cache.ResolvedFiles[ path ] = fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = ResourceLoader.FindResource( ResourceCategory.Chara, imcExt, ( uint )path.Path.Crc32 );
|
||||||
|
if( resource != null )
|
||||||
|
{
|
||||||
|
file.Replace( ( ResourceHandle* )resource );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach( var file in Files.Values )
|
||||||
|
{
|
||||||
|
file.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.Clear();
|
||||||
|
Manipulations.Clear();
|
||||||
|
RestoreDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_IMC" )]
|
||||||
|
private unsafe void SetupDelegate()
|
||||||
|
{
|
||||||
|
Penumbra.ResourceLoader.ResourceLoadCustomization = ImcHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional( "USE_IMC" )]
|
||||||
|
private unsafe void RestoreDelegate()
|
||||||
|
{
|
||||||
|
if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcHandler )
|
||||||
|
{
|
||||||
|
Penumbra.ResourceLoader.ResourceLoadCustomization = _previousDelegate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FullPath CreateImcPath( Utf8GamePath path )
|
||||||
|
=> new($"|{_collection.Name}|{path}");
|
||||||
|
|
||||||
|
private static unsafe byte ImcHandler( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
||||||
|
SeFileDescriptor* fileDescriptor, int priority, bool isSync )
|
||||||
|
{
|
||||||
|
var split = gamePath.Path.Split( ( byte )'|', 2, true );
|
||||||
|
fileDescriptor->ResourceHandle->FileNameData = split[ 1 ].Path;
|
||||||
|
fileDescriptor->ResourceHandle->FileNameLength = split[ 1 ].Length;
|
||||||
|
|
||||||
|
var ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
||||||
|
if( Penumbra.ModManager.Collections.Collections.TryGetValue( split[ 0 ].ToString(), out var collection )
|
||||||
|
&& collection.Cache != null
|
||||||
|
&& collection.Cache.MetaManipulations.Imc.Files.TryGetValue(
|
||||||
|
Utf8GamePath.FromSpan( split[ 1 ].Span, out var p, false ) ? p : Utf8GamePath.Empty, out var file ) )
|
||||||
|
{
|
||||||
|
file.Replace( fileDescriptor->ResourceHandle );
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
||||||
|
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
Penumbra/Meta/Manager/MetaManager.cs
Normal file
99
Penumbra/Meta/Manager/MetaManager.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
using System;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
|
using Penumbra.Mods;
|
||||||
|
|
||||||
|
namespace Penumbra.Meta.Manager;
|
||||||
|
|
||||||
|
public partial class MetaManager : IDisposable
|
||||||
|
{
|
||||||
|
public MetaManagerEqp Eqp = new();
|
||||||
|
public MetaManagerEqdp Eqdp = new();
|
||||||
|
public MetaManagerGmp Gmp = new();
|
||||||
|
public MetaManagerEst Est = new();
|
||||||
|
public MetaManagerCmp Cmp = new();
|
||||||
|
public MetaManagerImc Imc;
|
||||||
|
|
||||||
|
private static unsafe void SetFile( MetaBaseFile? file, int index )
|
||||||
|
{
|
||||||
|
if( file == null )
|
||||||
|
{
|
||||||
|
Penumbra.CharacterUtility.ResetResource( index );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Penumbra.CharacterUtility.SetResource( index, ( IntPtr )file.Data, file.Length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue( MetaManipulation manip, out Mod.Mod? mod )
|
||||||
|
{
|
||||||
|
mod = manip.ManipulationType switch
|
||||||
|
{
|
||||||
|
MetaManipulation.Type.Eqp => Eqp.Manipulations.TryGetValue( manip.Eqp, out var m ) ? m : null,
|
||||||
|
MetaManipulation.Type.Gmp => Gmp.Manipulations.TryGetValue( manip.Gmp, out var m ) ? m : null,
|
||||||
|
MetaManipulation.Type.Eqdp => Eqdp.Manipulations.TryGetValue( manip.Eqdp, out var m ) ? m : null,
|
||||||
|
MetaManipulation.Type.Est => Est.Manipulations.TryGetValue( manip.Est, out var m ) ? m : null,
|
||||||
|
MetaManipulation.Type.Rsp => Cmp.Manipulations.TryGetValue( manip.Rsp, out var m ) ? m : null,
|
||||||
|
MetaManipulation.Type.Imc => Imc.Manipulations.TryGetValue( manip.Imc, out var m ) ? m : null,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
return mod != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
=> Imc.Manipulations.Count
|
||||||
|
+ Eqdp.Manipulations.Count
|
||||||
|
+ Cmp.Manipulations.Count
|
||||||
|
+ Gmp.Manipulations.Count
|
||||||
|
+ Est.Manipulations.Count
|
||||||
|
+ Eqp.Manipulations.Count;
|
||||||
|
|
||||||
|
public MetaManager( ModCollection collection )
|
||||||
|
=> Imc = new MetaManagerImc( collection );
|
||||||
|
|
||||||
|
public void SetFiles()
|
||||||
|
{
|
||||||
|
Eqp.SetFiles();
|
||||||
|
Eqdp.SetFiles();
|
||||||
|
Gmp.SetFiles();
|
||||||
|
Est.SetFiles();
|
||||||
|
Cmp.SetFiles();
|
||||||
|
Imc.SetFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Eqp.Reset();
|
||||||
|
Eqdp.Reset();
|
||||||
|
Gmp.Reset();
|
||||||
|
Est.Reset();
|
||||||
|
Cmp.Reset();
|
||||||
|
Imc.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Eqp.Dispose();
|
||||||
|
Eqdp.Dispose();
|
||||||
|
Gmp.Dispose();
|
||||||
|
Est.Dispose();
|
||||||
|
Cmp.Dispose();
|
||||||
|
Imc.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyMod( MetaManipulation m, Mod.Mod mod )
|
||||||
|
{
|
||||||
|
return m.ManipulationType switch
|
||||||
|
{
|
||||||
|
MetaManipulation.Type.Eqp => Eqp.ApplyMod( m.Eqp, mod ),
|
||||||
|
MetaManipulation.Type.Gmp => Gmp.ApplyMod( m.Gmp, mod ),
|
||||||
|
MetaManipulation.Type.Eqdp => Eqdp.ApplyMod( m.Eqdp, mod ),
|
||||||
|
MetaManipulation.Type.Est => Est.ApplyMod( m.Est, mod ),
|
||||||
|
MetaManipulation.Type.Rsp => Cmp.ApplyMod( m.Rsp, mod ),
|
||||||
|
MetaManipulation.Type.Imc => Imc.ApplyMod( m.Imc, mod ),
|
||||||
|
MetaManipulation.Type.Unknown => false,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,6 +61,10 @@ public readonly struct ImcManipulation : IMetaManipulation< ImcManipulation >
|
||||||
EquipSlot = equipSlot;
|
EquipSlot = equipSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImcManipulation( ImcManipulation copy, ImcEntry entry )
|
||||||
|
: this( copy.ObjectType, copy.BodySlot, copy.PrimaryId, copy.SecondaryId, copy.Variant, copy.EquipSlot, entry )
|
||||||
|
{}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> ObjectType is ObjectType.Equipment or ObjectType.Accessory
|
=> ObjectType is ObjectType.Equipment or ObjectType.Accessory
|
||||||
? $"Imc - {PrimaryId} - {EquipSlot} - {Variant}"
|
? $"Imc - {PrimaryId} - {EquipSlot} - {Variant}"
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,12 @@ public class MetaCollection
|
||||||
|
|
||||||
if( collection != null )
|
if( collection != null )
|
||||||
{
|
{
|
||||||
|
if( collection.DefaultData.Concat( collection.GroupData.Values.SelectMany( kvp => kvp.Values.SelectMany( l => l ) ) )
|
||||||
|
.Any( m => m.ManipulationType == MetaManipulation.Type.Unknown || !Enum.IsDefined( m.ManipulationType ) ) )
|
||||||
|
{
|
||||||
|
throw new Exception( "Invalid collection" );
|
||||||
|
}
|
||||||
|
|
||||||
collection.Count = collection.DefaultData.Count
|
collection.Count = collection.DefaultData.Count
|
||||||
+ collection.GroupData.Values.SelectMany( kvp => kvp.Values ).Sum( l => l.Count );
|
+ collection.GroupData.Values.SelectMany( kvp => kvp.Values ).Sum( l => l.Count );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
|
||||||
using Penumbra.GameData.ByteString;
|
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.Interop;
|
|
||||||
using Penumbra.Interop.Structs;
|
|
||||||
using Penumbra.Meta.Files;
|
|
||||||
using Penumbra.Meta.Manipulations;
|
|
||||||
using Penumbra.Mods;
|
|
||||||
using CharacterUtility = Penumbra.Interop.Structs.CharacterUtility;
|
|
||||||
using ImcFile = Penumbra.Meta.Files.ImcFile;
|
|
||||||
|
|
||||||
namespace Penumbra.Meta;
|
|
||||||
|
|
||||||
public class MetaManager2 : IDisposable
|
|
||||||
{
|
|
||||||
public readonly List< MetaBaseFile > ChangedData = new(7 + CharacterUtility.NumEqdpFiles);
|
|
||||||
|
|
||||||
public ExpandedEqpFile? EqpFile;
|
|
||||||
public ExpandedGmpFile? GmpFile;
|
|
||||||
public ExpandedEqdpFile?[] EqdpFile = new ExpandedEqdpFile?[CharacterUtility.NumEqdpFiles];
|
|
||||||
public EstFile? FaceEstFile;
|
|
||||||
public EstFile? HairEstFile;
|
|
||||||
public EstFile? BodyEstFile;
|
|
||||||
public EstFile? HeadEstFile;
|
|
||||||
public CmpFile? CmpFile;
|
|
||||||
|
|
||||||
public readonly Dictionary< EqpManipulation, Mod.Mod > EqpManipulations = new();
|
|
||||||
public readonly Dictionary< EstManipulation, Mod.Mod > EstManipulations = new();
|
|
||||||
public readonly Dictionary< GmpManipulation, Mod.Mod > GmpManipulations = new();
|
|
||||||
public readonly Dictionary< RspManipulation, Mod.Mod > RspManipulations = new();
|
|
||||||
public readonly Dictionary< EqdpManipulation, Mod.Mod > EqdpManipulations = new();
|
|
||||||
|
|
||||||
public readonly Dictionary< ImcManipulation, Mod.Mod > ImcManipulations = new();
|
|
||||||
public readonly Dictionary< Utf8GamePath, ImcFile > ImcFiles = new();
|
|
||||||
|
|
||||||
private readonly ModCollection _collection;
|
|
||||||
|
|
||||||
public unsafe void SetFiles()
|
|
||||||
{
|
|
||||||
foreach( var file in ChangedData )
|
|
||||||
{
|
|
||||||
Penumbra.CharacterUtility.SetResource( file.Index, ( IntPtr )file.Data, file.Length );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetValue( MetaManipulation manip, out Mod.Mod? mod )
|
|
||||||
{
|
|
||||||
mod = manip.ManipulationType switch
|
|
||||||
{
|
|
||||||
MetaManipulation.Type.Eqp => EqpManipulations.TryGetValue( manip.Eqp, out var m ) ? m : null,
|
|
||||||
MetaManipulation.Type.Gmp => GmpManipulations.TryGetValue( manip.Gmp, out var m ) ? m : null,
|
|
||||||
MetaManipulation.Type.Eqdp => EqdpManipulations.TryGetValue( manip.Eqdp, out var m ) ? m : null,
|
|
||||||
MetaManipulation.Type.Est => EstManipulations.TryGetValue( manip.Est, out var m ) ? m : null,
|
|
||||||
MetaManipulation.Type.Rsp => RspManipulations.TryGetValue( manip.Rsp, out var m ) ? m : null,
|
|
||||||
MetaManipulation.Type.Imc => ImcManipulations.TryGetValue( manip.Imc, out var m ) ? m : null,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
|
||||||
};
|
|
||||||
return mod != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
=> ImcManipulations.Count
|
|
||||||
+ EqdpManipulations.Count
|
|
||||||
+ RspManipulations.Count
|
|
||||||
+ GmpManipulations.Count
|
|
||||||
+ EstManipulations.Count
|
|
||||||
+ EqpManipulations.Count;
|
|
||||||
|
|
||||||
public MetaManager2( ModCollection collection )
|
|
||||||
=> _collection = collection;
|
|
||||||
|
|
||||||
public void ApplyImcFiles( Dictionary< Utf8GamePath, FullPath > resolvedFiles )
|
|
||||||
{
|
|
||||||
foreach( var path in ImcFiles.Keys )
|
|
||||||
{
|
|
||||||
resolvedFiles[ path ] = CreateImcPath( path );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetEqp()
|
|
||||||
{
|
|
||||||
if( EqpFile != null )
|
|
||||||
{
|
|
||||||
EqpFile.Reset( EqpManipulations.Keys.Select( m => ( int )m.SetId ) );
|
|
||||||
EqpManipulations.Clear();
|
|
||||||
ChangedData.Remove( EqpFile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetGmp()
|
|
||||||
{
|
|
||||||
if( GmpFile != null )
|
|
||||||
{
|
|
||||||
GmpFile.Reset( GmpManipulations.Keys.Select( m => ( int )m.SetId ) );
|
|
||||||
GmpManipulations.Clear();
|
|
||||||
ChangedData.Remove( GmpFile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetCmp()
|
|
||||||
{
|
|
||||||
if( CmpFile != null )
|
|
||||||
{
|
|
||||||
CmpFile.Reset( RspManipulations.Keys.Select( m => ( m.SubRace, m.Attribute ) ) );
|
|
||||||
RspManipulations.Clear();
|
|
||||||
ChangedData.Remove( CmpFile );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetEst()
|
|
||||||
{
|
|
||||||
FaceEstFile?.Reset();
|
|
||||||
HairEstFile?.Reset();
|
|
||||||
BodyEstFile?.Reset();
|
|
||||||
HeadEstFile?.Reset();
|
|
||||||
RspManipulations.Clear();
|
|
||||||
ChangedData.RemoveAll( f => f is EstFile );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetEqdp()
|
|
||||||
{
|
|
||||||
foreach( var file in EqdpFile )
|
|
||||||
{
|
|
||||||
file?.Reset( EqdpManipulations.Keys.Where( m => m.FileIndex() == file.Index ).Select( m => ( int )m.SetId ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangedData.RemoveAll( f => f is ExpandedEqdpFile );
|
|
||||||
EqdpManipulations.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private FullPath CreateImcPath( Utf8GamePath path )
|
|
||||||
{
|
|
||||||
var d = new DirectoryInfo( $":{_collection.Name}/" );
|
|
||||||
return new FullPath( d, new Utf8RelPath( path ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetImc()
|
|
||||||
{
|
|
||||||
foreach( var (path, file) in ImcFiles )
|
|
||||||
{
|
|
||||||
_collection.Cache?.ResolvedFiles.Remove( path );
|
|
||||||
path.Dispose();
|
|
||||||
file.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImcFiles.Clear();
|
|
||||||
ImcManipulations.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
ChangedData.Clear();
|
|
||||||
ResetEqp();
|
|
||||||
ResetGmp();
|
|
||||||
ResetCmp();
|
|
||||||
ResetEst();
|
|
||||||
ResetEqdp();
|
|
||||||
ResetImc();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Dispose< T >( ref T? file ) where T : class, IDisposable
|
|
||||||
{
|
|
||||||
if( file != null )
|
|
||||||
{
|
|
||||||
file.Dispose();
|
|
||||||
file = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
ChangedData.Clear();
|
|
||||||
EqpManipulations.Clear();
|
|
||||||
EstManipulations.Clear();
|
|
||||||
GmpManipulations.Clear();
|
|
||||||
RspManipulations.Clear();
|
|
||||||
EqdpManipulations.Clear();
|
|
||||||
Dispose( ref EqpFile );
|
|
||||||
Dispose( ref GmpFile );
|
|
||||||
Dispose( ref FaceEstFile );
|
|
||||||
Dispose( ref HairEstFile );
|
|
||||||
Dispose( ref BodyEstFile );
|
|
||||||
Dispose( ref HeadEstFile );
|
|
||||||
Dispose( ref CmpFile );
|
|
||||||
for( var i = 0; i < CharacterUtility.NumEqdpFiles; ++i )
|
|
||||||
{
|
|
||||||
Dispose( ref EqdpFile[ i ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetImc();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddFile( MetaBaseFile file )
|
|
||||||
{
|
|
||||||
if( !ChangedData.Contains( file ) )
|
|
||||||
{
|
|
||||||
ChangedData.Add( file );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( EqpManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
if( !EqpManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EqpFile ??= new ExpandedEqpFile();
|
|
||||||
if( !m.Apply( EqpFile ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFile( EqpFile );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( GmpManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
if( !GmpManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GmpFile ??= new ExpandedGmpFile();
|
|
||||||
if( !m.Apply( GmpFile ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFile( GmpFile );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( EstManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
if( !EstManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var file = m.Slot switch
|
|
||||||
{
|
|
||||||
EstManipulation.EstType.Hair => HairEstFile ??= new EstFile( EstManipulation.EstType.Hair ),
|
|
||||||
EstManipulation.EstType.Face => FaceEstFile ??= new EstFile( EstManipulation.EstType.Face ),
|
|
||||||
EstManipulation.EstType.Body => BodyEstFile ??= new EstFile( EstManipulation.EstType.Body ),
|
|
||||||
EstManipulation.EstType.Head => HeadEstFile ??= new EstFile( EstManipulation.EstType.Head ),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
|
||||||
};
|
|
||||||
if( !m.Apply( file ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFile( file );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( RspManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
if( !RspManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CmpFile ??= new CmpFile();
|
|
||||||
if( !m.Apply( CmpFile ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFile( CmpFile );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( EqdpManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
if( !EqdpManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var file = EqdpFile[ m.FileIndex() - 2 ] ??= new ExpandedEqdpFile( Names.CombinedRace( m.Gender, m.Race ), m.Slot.IsAccessory() );
|
|
||||||
if( !m.Apply( file ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFile( file );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe bool ApplyMod( ImcManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
const uint imcExt = 0x00696D63;
|
|
||||||
|
|
||||||
if( !ImcManipulations.TryAdd( m, mod ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = m.GamePath();
|
|
||||||
if( !ImcFiles.TryGetValue( path, out var file ) )
|
|
||||||
{
|
|
||||||
file = new ImcFile( path );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !m.Apply( file ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImcFiles[ path ] = file;
|
|
||||||
var fullPath = CreateImcPath( path );
|
|
||||||
if( _collection.Cache != null )
|
|
||||||
{
|
|
||||||
_collection.Cache.ResolvedFiles[ path ] = fullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resource = ResourceLoader.FindResource( ResourceCategory.Chara, imcExt, ( uint )path.Path.Crc32 );
|
|
||||||
if( resource != null )
|
|
||||||
{
|
|
||||||
file.Replace( ( ResourceHandle* )resource );
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe byte ImcHandler( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
|
||||||
SeFileDescriptor* fileDescriptor, int priority, bool isSync )
|
|
||||||
{
|
|
||||||
var split = gamePath.Path.Split( ( byte )'|', 2, true );
|
|
||||||
fileDescriptor->ResourceHandle->FileNameData = split[ 1 ].Path;
|
|
||||||
fileDescriptor->ResourceHandle->FileNameLength = split[ 1 ].Length;
|
|
||||||
|
|
||||||
var ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
|
||||||
if( Penumbra.ModManager.Collections.Collections.TryGetValue( split[ 0 ].ToString(), out var collection )
|
|
||||||
&& collection.Cache != null
|
|
||||||
&& collection.Cache.MetaManipulations.ImcFiles.TryGetValue(
|
|
||||||
Utf8GamePath.FromSpan( split[ 1 ].Span, out var p, false ) ? p : Utf8GamePath.Empty, out var file ) )
|
|
||||||
{
|
|
||||||
file.Replace( fileDescriptor->ResourceHandle );
|
|
||||||
}
|
|
||||||
|
|
||||||
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
|
||||||
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyMod( MetaManipulation m, Mod.Mod mod )
|
|
||||||
{
|
|
||||||
return m.ManipulationType switch
|
|
||||||
{
|
|
||||||
MetaManipulation.Type.Eqp => ApplyMod( m.Eqp, mod ),
|
|
||||||
MetaManipulation.Type.Gmp => ApplyMod( m.Gmp, mod ),
|
|
||||||
MetaManipulation.Type.Eqdp => ApplyMod( m.Eqdp, mod ),
|
|
||||||
MetaManipulation.Type.Est => ApplyMod( m.Est, mod ),
|
|
||||||
MetaManipulation.Type.Rsp => ApplyMod( m.Rsp, mod ),
|
|
||||||
MetaManipulation.Type.Imc => ApplyMod( m.Imc, mod ),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
using Penumbra.Mod;
|
using Penumbra.Mod;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
|
|
@ -43,6 +44,14 @@ public class CollectionManager
|
||||||
{
|
{
|
||||||
ActiveCollection = newActive;
|
ActiveCollection = newActive;
|
||||||
Penumbra.ResidentResources.Reload();
|
Penumbra.ResidentResources.Reload();
|
||||||
|
if( ActiveCollection.Cache == null )
|
||||||
|
{
|
||||||
|
Penumbra.CharacterUtility.ResetAll();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ActiveCollection.Cache.MetaManipulations.SetFiles();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -206,10 +215,9 @@ public class CollectionManager
|
||||||
public void SetDefaultCollection( ModCollection newCollection )
|
public void SetDefaultCollection( ModCollection newCollection )
|
||||||
=> SetCollection( newCollection, DefaultCollection, c =>
|
=> SetCollection( newCollection, DefaultCollection, c =>
|
||||||
{
|
{
|
||||||
if( !CollectionChangedTo.Any() )
|
if( CollectionChangedTo.Length == 0 )
|
||||||
{
|
{
|
||||||
ActiveCollection = c;
|
SetActiveCollection( c, string.Empty );
|
||||||
Penumbra.ResidentResources.Reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultCollection = c;
|
DefaultCollection = c;
|
||||||
|
|
@ -228,8 +236,7 @@ public class CollectionManager
|
||||||
{
|
{
|
||||||
if( CollectionChangedTo == characterName && CharacterCollection.TryGetValue( characterName, out var collection ) )
|
if( CollectionChangedTo == characterName && CharacterCollection.TryGetValue( characterName, out var collection ) )
|
||||||
{
|
{
|
||||||
ActiveCollection = c;
|
SetActiveCollection( c, CollectionChangedTo );
|
||||||
Penumbra.ResidentResources.Reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterCollection[ characterName ] = c;
|
CharacterCollection[ characterName ] = c;
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.GameData.Util;
|
using Penumbra.Meta.Manager;
|
||||||
using Penumbra.Meta;
|
|
||||||
using Penumbra.Mod;
|
using Penumbra.Mod;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
|
|
||||||
|
|
@ -27,7 +26,7 @@ public class ModCollectionCache
|
||||||
private readonly SortedList< string, object? > _changedItems = new();
|
private readonly SortedList< string, object? > _changedItems = new();
|
||||||
public readonly Dictionary< Utf8GamePath, FullPath > ResolvedFiles = new();
|
public readonly Dictionary< Utf8GamePath, FullPath > ResolvedFiles = new();
|
||||||
public readonly HashSet< FullPath > MissingFiles = new();
|
public readonly HashSet< FullPath > MissingFiles = new();
|
||||||
public readonly MetaManager2 MetaManipulations;
|
public readonly MetaManager MetaManipulations;
|
||||||
|
|
||||||
public IReadOnlyDictionary< string, object? > ChangedItems
|
public IReadOnlyDictionary< string, object? > ChangedItems
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +38,7 @@ public class ModCollectionCache
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModCollectionCache( ModCollection collection )
|
public ModCollectionCache( ModCollection collection )
|
||||||
=> MetaManipulations = new MetaManager2( collection );
|
=> MetaManipulations = new MetaManager( collection );
|
||||||
|
|
||||||
private static void ResetFileSeen( int size )
|
private static void ResetFileSeen( int size )
|
||||||
{
|
{
|
||||||
|
|
@ -258,7 +257,7 @@ public class ModCollectionCache
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddMetaFiles()
|
private void AddMetaFiles()
|
||||||
=> MetaManipulations.ApplyImcFiles( ResolvedFiles );
|
=> MetaManipulations.Imc.SetFiles();
|
||||||
|
|
||||||
private void AddSwaps( Mod.Mod mod )
|
private void AddSwaps( Mod.Mod mod )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.Mod;
|
using Penumbra.Mod;
|
||||||
|
|
||||||
namespace Penumbra.Mods;
|
namespace Penumbra.Mods;
|
||||||
|
|
@ -9,6 +10,7 @@ namespace Penumbra.Mods;
|
||||||
public partial class ModManagerNew
|
public partial class ModManagerNew
|
||||||
{
|
{
|
||||||
private readonly List< ModData > _mods = new();
|
private readonly List< ModData > _mods = new();
|
||||||
|
|
||||||
public IReadOnlyList< ModData > Mods
|
public IReadOnlyList< ModData > Mods
|
||||||
=> _mods;
|
=> _mods;
|
||||||
|
|
||||||
|
|
@ -35,7 +37,6 @@ public partial class ModManagerNew
|
||||||
//Collections.RecreateCaches();
|
//Collections.RecreateCaches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ModManagerNew
|
public partial class ModManagerNew
|
||||||
{
|
{
|
||||||
public DirectoryInfo BasePath { get; private set; } = null!;
|
public DirectoryInfo BasePath { get; private set; } = null!;
|
||||||
|
|
@ -87,7 +88,6 @@ public partial class ModManagerNew
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
( BasePath, Valid ) = CreateDirectory( path );
|
( BasePath, Valid ) = CreateDirectory( path );
|
||||||
|
|
||||||
if( Penumbra.Config.ModDirectory != BasePath.FullName )
|
if( Penumbra.Config.ModDirectory != BasePath.FullName )
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
//PathResolver.Dispose();
|
//PathResolver.Dispose();
|
||||||
ResourceLogger.Dispose();
|
ResourceLogger.Dispose();
|
||||||
ResourceLoader.Dispose();
|
ResourceLoader.Dispose();
|
||||||
|
CharacterUtility.Dispose();
|
||||||
|
|
||||||
ShutdownWebServer();
|
ShutdownWebServer();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE;USE_EQP;USE_EQDP;USE_GMP;USE_EST;USE_CMP;USE_IMC</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<DefineConstants>$(DefineConstants)TRACE;USE_EQP;USE_EQDP;USE_GMP;USE_EST;USE_CMP;USE_IMC</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing.Text;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Penumbra.Api;
|
using Penumbra.Api;
|
||||||
using Penumbra.GameData.ByteString;
|
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.GameData.Util;
|
|
||||||
using Penumbra.Interop;
|
using Penumbra.Interop;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Mods;
|
|
||||||
using Penumbra.UI.Custom;
|
using Penumbra.UI.Custom;
|
||||||
using Penumbra.Util;
|
|
||||||
using CharacterUtility = Penumbra.Interop.Structs.CharacterUtility;
|
|
||||||
using ResourceHandle = Penumbra.Interop.Structs.ResourceHandle;
|
using ResourceHandle = Penumbra.Interop.Structs.ResourceHandle;
|
||||||
using Utf8String = Penumbra.GameData.ByteString.Utf8String;
|
|
||||||
|
|
||||||
namespace Penumbra.UI;
|
namespace Penumbra.UI;
|
||||||
|
|
||||||
|
|
@ -391,6 +382,31 @@ public partial class SettingsInterface
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var eqp = 0;
|
||||||
|
ImGui.InputInt( "##EqpInput", ref eqp );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var def = ExpandedEqpFile.GetDefault( eqp );
|
||||||
|
var val = Penumbra.ModManager.Collections.ActiveCollection.Cache?.MetaManipulations.Eqp.File?[ eqp ] ?? def;
|
||||||
|
ImGui.Text( Convert.ToString( ( long )def, 2 ).PadLeft( 64, '0' ) );
|
||||||
|
ImGui.Text( Convert.ToString( ( long )val, 2 ).PadLeft( 64, '0' ) );
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ }
|
||||||
|
|
||||||
|
var eqdp = 0;
|
||||||
|
ImGui.InputInt( "##EqdpInput", ref eqdp );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var def = ExpandedEqdpFile.GetDefault(GenderRace.MidlanderMale, false, eqdp );
|
||||||
|
var val = Penumbra.ModManager.Collections.ActiveCollection.Cache?.MetaManipulations.Eqdp.File(GenderRace.MidlanderMale, false)?[eqdp] ?? def;
|
||||||
|
ImGui.Text( Convert.ToString( ( ushort )def, 2 ).PadLeft( 16, '0' ) );
|
||||||
|
ImGui.Text( Convert.ToString( ( ushort )val, 2 ).PadLeft( 16, '0' ) );
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
if( !ImGui.BeginTable( "##CharacterUtilityDebugList", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX ) )
|
if( !ImGui.BeginTable( "##CharacterUtilityDebugList", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX ) )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -398,9 +414,10 @@ public partial class SettingsInterface
|
||||||
|
|
||||||
using var end = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
using var end = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
||||||
|
|
||||||
for( var i = 0; i < CharacterUtility.NumRelevantResources; ++i )
|
for( var i = 0; i < CharacterUtility.RelevantIndices.Length; ++i )
|
||||||
{
|
{
|
||||||
var resource = ( ResourceHandle* )Penumbra.CharacterUtility.Address->Resources[ i ];
|
var idx = CharacterUtility.RelevantIndices[ i ];
|
||||||
|
var resource = ( ResourceHandle* )Penumbra.CharacterUtility.Address->Resources[ idx ];
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.Text( $"0x{( ulong )resource:X}" );
|
ImGui.Text( $"0x{( ulong )resource:X}" );
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@ using System.Collections.Generic;
|
||||||
using System.Drawing.Text;
|
using System.Drawing.Text;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.Meta.Files;
|
||||||
using Penumbra.Meta.Manipulations;
|
using Penumbra.Meta.Manipulations;
|
||||||
using Penumbra.UI.Custom;
|
using Penumbra.UI.Custom;
|
||||||
using ObjectType = Penumbra.GameData.Enums.ObjectType;
|
using ObjectType = Penumbra.GameData.Enums.ObjectType;
|
||||||
|
|
||||||
namespace Penumbra.UI
|
namespace Penumbra.UI;
|
||||||
{
|
|
||||||
public partial class SettingsInterface
|
public partial class SettingsInterface
|
||||||
{
|
{
|
||||||
private partial class PluginDetails
|
private partial class PluginDetails
|
||||||
|
|
@ -220,74 +223,75 @@ namespace Penumbra.UI
|
||||||
private bool DrawEqpRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawEqpRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
//var id = list[ manipIdx ].EqpIdentifier;
|
var id = list[ manipIdx ].Eqp;
|
||||||
//var val = list[ manipIdx ].EqpValue;
|
var val = id.Entry;
|
||||||
//
|
|
||||||
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
|
||||||
//{
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
{
|
||||||
// var defaults = ( EqpEntry )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
// var attributes = Eqp.EqpAttributes[ id.Slot ];
|
var defaults = ExpandedEqpFile.GetDefault( id.SetId );
|
||||||
//
|
var attributes = Eqp.EqpAttributes[ id.Slot ];
|
||||||
// foreach( var flag in attributes )
|
|
||||||
// {
|
foreach( var flag in attributes )
|
||||||
// var name = flag.ToLocalName();
|
{
|
||||||
// var tmp = val.HasFlag( flag );
|
var name = flag.ToLocalName();
|
||||||
// if( PrintCheckBox( $"{name}##manip", ref tmp, defaults.HasFlag( flag ) ) && _editMode && tmp != val.HasFlag( flag ) )
|
var tmp = val.HasFlag( flag );
|
||||||
// {
|
if( PrintCheckBox( $"{name}##manip", ref tmp, defaults.HasFlag( flag ) ) && _editMode && tmp != val.HasFlag( flag ) )
|
||||||
// list[ manipIdx ] = MetaManipulation.Eqp( id.Slot, id.SetId, tmp ? val | flag : val & ~flag );
|
{
|
||||||
// ret = true;
|
list[ manipIdx ] = new MetaManipulation( new EqpManipulation( tmp ? val | flag : val & ~flag, id.Slot, id.SetId ) );
|
||||||
// }
|
ret = true;
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
//
|
}
|
||||||
//ImGui.Text( ObjectType.Equipment.ToString() );
|
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( ObjectType.Equipment.ToString() );
|
||||||
//ImGui.Text( id.SetId.ToString() );
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.SetId.ToString() );
|
||||||
//ImGui.Text( id.Slot.ToString() );
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text( id.Slot.ToString() );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawGmpRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawGmpRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
//var id = list[ manipIdx ].GmpIdentifier;
|
var id = list[ manipIdx ].Gmp;
|
||||||
//var val = list[ manipIdx ].GmpValue;
|
var val = id.Entry;
|
||||||
//
|
|
||||||
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
//{
|
{
|
||||||
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
// var enabled = val.Enabled;
|
var defaults = ExpandedGmpFile.GetDefault( id.SetId );
|
||||||
// var animated = val.Animated;
|
var enabled = val.Enabled;
|
||||||
// var rotationA = val.RotationA;
|
var animated = val.Animated;
|
||||||
// var rotationB = val.RotationB;
|
var rotationA = val.RotationA;
|
||||||
// var rotationC = val.RotationC;
|
var rotationB = val.RotationB;
|
||||||
// ushort unk = val.UnknownTotal;
|
var rotationC = val.RotationC;
|
||||||
//
|
ushort unk = val.UnknownTotal;
|
||||||
// ret |= PrintCheckBox( "Visor Enabled##manip", ref enabled, defaults.Enabled ) && enabled != val.Enabled;
|
|
||||||
// ret |= PrintCheckBox( "Visor Animated##manip", ref animated, defaults.Animated );
|
ret |= PrintCheckBox( "Visor Enabled##manip", ref enabled, defaults.Enabled ) && enabled != val.Enabled;
|
||||||
// ret |= DrawInputWithDefault( "Rotation A##manip", ref rotationA, defaults.RotationA, 0x3FF );
|
ret |= PrintCheckBox( "Visor Animated##manip", ref animated, defaults.Animated );
|
||||||
// ret |= DrawInputWithDefault( "Rotation B##manip", ref rotationB, defaults.RotationB, 0x3FF );
|
ret |= DrawInputWithDefault( "Rotation A##manip", ref rotationA, defaults.RotationA, 0x3FF );
|
||||||
// ret |= DrawInputWithDefault( "Rotation C##manip", ref rotationC, defaults.RotationC, 0x3FF );
|
ret |= DrawInputWithDefault( "Rotation B##manip", ref rotationB, defaults.RotationB, 0x3FF );
|
||||||
// ret |= DrawInputWithDefault( "Unknown Byte##manip", ref unk, defaults.UnknownTotal, 0xFF );
|
ret |= DrawInputWithDefault( "Rotation C##manip", ref rotationC, defaults.RotationC, 0x3FF );
|
||||||
//
|
ret |= DrawInputWithDefault( "Unknown Byte##manip", ref unk, defaults.UnknownTotal, 0xFF );
|
||||||
// if( ret && _editMode )
|
|
||||||
// {
|
if( ret && _editMode )
|
||||||
// list[ manipIdx ] = MetaManipulation.Gmp( id.SetId,
|
{
|
||||||
// new GmpEntry
|
list[ manipIdx ] = new MetaManipulation( new GmpManipulation( new GmpEntry
|
||||||
// {
|
{
|
||||||
// Animated = animated, Enabled = enabled, UnknownTotal = ( byte )unk,
|
Animated = animated, Enabled = enabled, UnknownTotal = ( byte )unk,
|
||||||
// RotationA = rotationA, RotationB = rotationB, RotationC = rotationC,
|
RotationA = rotationA, RotationB = rotationB, RotationC = rotationC,
|
||||||
// } );
|
}, id.SetId ) );
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//ImGui.Text( ObjectType.Equipment.ToString() );
|
ImGui.Text( ObjectType.Equipment.ToString() );
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.Text( id.SetId.ToString() );
|
ImGui.Text( id.SetId.ToString() );
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.Text( EquipSlot.Head.ToString() );
|
ImGui.Text( EquipSlot.Head.ToString() );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,66 +365,65 @@ namespace Penumbra.UI
|
||||||
private bool DrawEqdpRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawEqdpRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
//var id = list[ manipIdx ].EqdpIdentifier;
|
var id = list[ manipIdx ].Eqdp;
|
||||||
//var val = list[ manipIdx ].EqdpValue;
|
var val = id.Entry;
|
||||||
//
|
|
||||||
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
//{
|
{
|
||||||
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
// var (bit1, bit2) = GetEqdpBits( id.Slot, val );
|
var defaults = ExpandedEqdpFile.GetDefault( id.FileIndex(), id.SetId );
|
||||||
// var (defBit1, defBit2) = GetEqdpBits( id.Slot, defaults );
|
var (bit1, bit2) = GetEqdpBits( id.Slot, val );
|
||||||
//
|
var (defBit1, defBit2) = GetEqdpBits( id.Slot, defaults );
|
||||||
// ret |= PrintCheckBox( "Bit 1##manip", ref bit1, defBit1 );
|
|
||||||
// ret |= PrintCheckBox( "Bit 2##manip", ref bit2, defBit2 );
|
ret |= PrintCheckBox( "Bit 1##manip", ref bit1, defBit1 );
|
||||||
//
|
ret |= PrintCheckBox( "Bit 2##manip", ref bit2, defBit2 );
|
||||||
// if( ret && _editMode )
|
|
||||||
// {
|
if( ret && _editMode )
|
||||||
// list[ manipIdx ] = MetaManipulation.Eqdp( id.Slot, id.GenderRace, id.SetId, SetEqdpBits( id.Slot, val, bit1, bit2 ) );
|
{
|
||||||
// }
|
list[ manipIdx ] = new MetaManipulation( new EqdpManipulation( SetEqdpBits( id.Slot, val, bit1, bit2 ), id.Slot, id.Gender,
|
||||||
//}
|
id.Race, id.SetId ) );
|
||||||
//
|
}
|
||||||
//ImGui.Text( id.Slot.IsAccessory()
|
}
|
||||||
// ? ObjectType.Accessory.ToString()
|
|
||||||
// : ObjectType.Equipment.ToString() );
|
ImGui.Text( id.Slot.IsAccessory()
|
||||||
//ImGui.TableNextColumn();
|
? ObjectType.Accessory.ToString()
|
||||||
//ImGui.Text( id.SetId.ToString() );
|
: ObjectType.Equipment.ToString() );
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.Text( id.Slot.ToString() );
|
ImGui.Text( id.SetId.ToString() );
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//var (gender, race) = id.GenderRace.Split();
|
ImGui.Text( id.Slot.ToString() );
|
||||||
//ImGui.Text( race.ToString() );
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.Race.ToString() );
|
||||||
//ImGui.Text( gender.ToString() );
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text( id.Gender.ToString() );
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawEstRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawEstRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
//var id = list[ manipIdx ].EstIdentifier;
|
var id = list[ manipIdx ].Est;
|
||||||
//var val = list[ manipIdx ].EstValue;
|
var val = id.SkeletonIdx;
|
||||||
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
|
||||||
//{
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
{
|
||||||
// if( DrawInputWithDefault( "No Idea what this does!##manip", ref val, defaults, ushort.MaxValue ) && _editMode )
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
// {
|
var defaults = EstFile.GetDefault( id.Slot, Names.CombinedRace( id.Gender, id.Race ), id.SetId );
|
||||||
// list[ manipIdx ] = new MetaManipulation( id.Value, val );
|
if( DrawInputWithDefault( "No Idea what this does!##manip", ref val, defaults, ushort.MaxValue ) && _editMode )
|
||||||
// ret = true;
|
{
|
||||||
// }
|
list[ manipIdx ] = new MetaManipulation( new EstManipulation( id.Gender, id.Race, id.Slot, id.SetId, val ) );
|
||||||
//}
|
ret = true;
|
||||||
//
|
}
|
||||||
//ImGui.Text( id.ObjectType.ToString() );
|
}
|
||||||
//ImGui.TableNextColumn();
|
|
||||||
//ImGui.Text( id.PrimaryId.ToString() );
|
ImGui.Text( id.Slot.ToString() );
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.Text( id.ObjectType == ObjectType.Equipment
|
ImGui.Text( id.SetId.ToString() );
|
||||||
// ? id.EquipSlot.ToString()
|
ImGui.TableNextColumn();
|
||||||
// : id.BodySlot.ToString() );
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.Race.ToName() );
|
||||||
//var (gender, race) = id.GenderRace.Split();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.Text( race.ToString() );
|
ImGui.Text( id.Gender.ToName() );
|
||||||
//ImGui.TableNextColumn();
|
|
||||||
//ImGui.Text( gender.ToString() );
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -428,67 +431,66 @@ namespace Penumbra.UI
|
||||||
private bool DrawImcRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawImcRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
//var id = list[ manipIdx ].ImcIdentifier;
|
var id = list[ manipIdx ].Imc;
|
||||||
//var val = list[ manipIdx ].ImcValue;
|
var val = id.Entry;
|
||||||
//
|
|
||||||
//if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
//{
|
{
|
||||||
// using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
// ushort materialId = val.MaterialId;
|
var defaults = new ImcFile( id.GamePath() ).GetEntry( ImcFile.PartIndex( id.EquipSlot ), id.Variant );
|
||||||
// ushort vfxId = val.VfxId;
|
ushort materialId = val.MaterialId;
|
||||||
// ushort decalId = val.DecalId;
|
ushort vfxId = val.VfxId;
|
||||||
// var soundId = ( ushort )( val.SoundId >> 10 );
|
ushort decalId = val.DecalId;
|
||||||
// var attributeMask = val.AttributeMask;
|
var soundId = ( ushort )val.SoundId;
|
||||||
// var materialAnimationId = ( ushort )( val.MaterialAnimationId >> 12 );
|
var attributeMask = val.AttributeMask;
|
||||||
// ret |= DrawInputWithDefault( "Material Id", ref materialId, defaults.MaterialId, byte.MaxValue );
|
var materialAnimationId = ( ushort )val.MaterialAnimationId;
|
||||||
// ret |= DrawInputWithDefault( "Vfx Id", ref vfxId, defaults.VfxId, byte.MaxValue );
|
ret |= DrawInputWithDefault( "Material Id", ref materialId, defaults.MaterialId, byte.MaxValue );
|
||||||
// ret |= DrawInputWithDefault( "Decal Id", ref decalId, defaults.DecalId, byte.MaxValue );
|
ret |= DrawInputWithDefault( "Vfx Id", ref vfxId, defaults.VfxId, byte.MaxValue );
|
||||||
// ret |= DrawInputWithDefault( "Sound Id", ref soundId, defaults.SoundId, 0x3F );
|
ret |= DrawInputWithDefault( "Decal Id", ref decalId, defaults.DecalId, byte.MaxValue );
|
||||||
// ret |= DrawInputWithDefault( "Attribute Mask", ref attributeMask, defaults.AttributeMask, 0x3FF );
|
ret |= DrawInputWithDefault( "Sound Id", ref soundId, defaults.SoundId, 0x3F );
|
||||||
// ret |= DrawInputWithDefault( "Material Animation Id", ref materialAnimationId, defaults.MaterialAnimationId,
|
ret |= DrawInputWithDefault( "Attribute Mask", ref attributeMask, defaults.AttributeMask, 0x3FF );
|
||||||
// byte.MaxValue );
|
ret |= DrawInputWithDefault( "Material Animation Id", ref materialAnimationId, defaults.MaterialAnimationId,
|
||||||
//
|
byte.MaxValue );
|
||||||
// if( ret && _editMode )
|
|
||||||
// {
|
if( ret && _editMode )
|
||||||
// var value = ImcExtensions.FromValues( ( byte )materialId, ( byte )decalId, attributeMask, ( byte )soundId,
|
{
|
||||||
// ( byte )vfxId, ( byte )materialAnimationId );
|
var value = new ImcEntry( ( byte )materialId, ( byte )decalId, attributeMask, ( byte )soundId, ( byte )vfxId,
|
||||||
// list[ manipIdx ] = new MetaManipulation( id.Value, value.ToInteger() );
|
( byte )materialAnimationId );
|
||||||
// }
|
list[ manipIdx ] = new MetaManipulation( new ImcManipulation( id, value ) );
|
||||||
//}
|
}
|
||||||
//
|
}
|
||||||
//ImGui.Text( id.ObjectType.ToString() );
|
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.ObjectType.ToString() );
|
||||||
//ImGui.Text( id.PrimaryId.ToString() );
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.PrimaryId.ToString() );
|
||||||
//if( id.ObjectType == ObjectType.Accessory
|
ImGui.TableNextColumn();
|
||||||
// || id.ObjectType == ObjectType.Equipment )
|
if( id.ObjectType is ObjectType.Accessory or ObjectType.Equipment )
|
||||||
//{
|
{
|
||||||
// ImGui.Text( id.ObjectType == ObjectType.Equipment
|
ImGui.Text( id.ObjectType is ObjectType.Equipment or ObjectType.Accessory
|
||||||
// || id.ObjectType == ObjectType.Accessory
|
? id.EquipSlot.ToString()
|
||||||
// ? id.EquipSlot.ToString()
|
: id.BodySlot.ToString() );
|
||||||
// : id.BodySlot.ToString() );
|
}
|
||||||
//}
|
|
||||||
//
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
if( id.ObjectType != ObjectType.Equipment
|
||||||
//if( id.ObjectType != ObjectType.Equipment
|
&& id.ObjectType != ObjectType.Accessory )
|
||||||
// && id.ObjectType != ObjectType.Accessory )
|
{
|
||||||
//{
|
ImGui.Text( id.SecondaryId.ToString() );
|
||||||
// ImGui.Text( id.SecondaryId.ToString() );
|
}
|
||||||
//}
|
|
||||||
//
|
ImGui.TableNextColumn();
|
||||||
//ImGui.TableNextColumn();
|
ImGui.Text( id.Variant.ToString() );
|
||||||
//ImGui.Text( id.Variant.ToString() );
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DrawRspRow( int manipIdx, IList< MetaManipulation > list )
|
private bool DrawRspRow( int manipIdx, IList< MetaManipulation > list )
|
||||||
{
|
{
|
||||||
var ret = false;
|
var ret = false;
|
||||||
var id = list[ manipIdx ].RspIdentifier;
|
var id = list[ manipIdx ].Rsp;
|
||||||
var val = list[ manipIdx ].RspValue;
|
var defaults = CmpFile.GetDefault( id.SubRace, id.Attribute );
|
||||||
|
var val = id.Entry;
|
||||||
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
if( ImGui.BeginPopup( $"##MetaPopup{manipIdx}" ) )
|
||||||
{
|
{
|
||||||
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndPopup );
|
||||||
|
|
@ -496,7 +498,7 @@ namespace Penumbra.UI
|
||||||
$"{( _editMode ? "Set to " : "" )}Default: {defaults:F3}##scaleManip", ref val, defaults )
|
$"{( _editMode ? "Set to " : "" )}Default: {defaults:F3}##scaleManip", ref val, defaults )
|
||||||
&& _editMode )
|
&& _editMode )
|
||||||
{
|
{
|
||||||
list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, defaults );
|
list[ manipIdx ] = new MetaManipulation( new RspManipulation( id.SubRace, id.Attribute, val ) );
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -507,7 +509,7 @@ namespace Penumbra.UI
|
||||||
&& val <= 5
|
&& val <= 5
|
||||||
&& _editMode )
|
&& _editMode )
|
||||||
{
|
{
|
||||||
list[ manipIdx ] = MetaManipulation.Rsp( id.SubRace, id.Attribute, val );
|
list[ manipIdx ] = new MetaManipulation( new RspManipulation( id.SubRace, id.Attribute, val ) );
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -549,30 +551,62 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
case MetaManipulation.Type.Eqp:
|
case MetaManipulation.Type.Eqp:
|
||||||
changes = DrawEqpRow( manipIdx, list );
|
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 );
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
if( ImGui.Selectable( $"{manipIdx}##{manipIdx}" ) )
|
if( ImGui.Selectable( $"{list[ manipIdx ].Eqp.Entry}##{manipIdx}" ) )
|
||||||
{
|
{
|
||||||
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MetaManipulation.Type.Gmp:
|
||||||
|
changes = DrawGmpRow( manipIdx, list );
|
||||||
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
|
if( ImGui.Selectable( $"{list[ manipIdx ].Gmp.Entry.Value}##{manipIdx}" ) )
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MetaManipulation.Type.Eqdp:
|
||||||
|
changes = DrawEqdpRow( manipIdx, list );
|
||||||
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
|
var (bit1, bit2) = GetEqdpBits( list[ manipIdx ].Eqdp.Slot, list[ manipIdx ].Eqdp.Entry );
|
||||||
|
if( ImGui.Selectable( $"{bit1} {bit2}##{manipIdx}" ) )
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MetaManipulation.Type.Est:
|
||||||
|
changes = DrawEstRow( manipIdx, list );
|
||||||
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
|
if( ImGui.Selectable( $"{list[ manipIdx ].Est.SkeletonIdx}##{manipIdx}" ) )
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MetaManipulation.Type.Imc:
|
||||||
|
changes = DrawImcRow( manipIdx, list );
|
||||||
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
|
if( ImGui.Selectable( $"{list[ manipIdx ].Imc.Entry.MaterialId}##{manipIdx}" ) )
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MetaManipulation.Type.Rsp:
|
||||||
|
changes = DrawRspRow( manipIdx, list );
|
||||||
|
ImGui.TableSetColumnIndex( 9 );
|
||||||
|
if( ImGui.Selectable( $"{list[ manipIdx ].Rsp.Entry}##{manipIdx}" ) )
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup( $"##MetaPopup{manipIdx}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
@ -771,4 +805,3 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Reflection.Metadata.Ecma335;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||||
using FFXIVClientStructs.STD;
|
using FFXIVClientStructs.STD;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Penumbra.GameData.ByteString;
|
||||||
using Penumbra.Interop;
|
using Penumbra.Interop;
|
||||||
using Penumbra.UI.Custom;
|
using Penumbra.UI.Custom;
|
||||||
|
|
||||||
|
|
@ -55,6 +57,11 @@ public partial class SettingsInterface
|
||||||
|
|
||||||
ResourceLoader.IterateResourceMap( map, ( hash, r ) =>
|
ResourceLoader.IterateResourceMap( map, ( hash, r ) =>
|
||||||
{
|
{
|
||||||
|
if( _filter.Length != 0 && !r->FileName.ToString().Contains( _filter, StringComparison.InvariantCultureIgnoreCase ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.Text( $"0x{hash:X8}" );
|
ImGui.Text( $"0x{hash:X8}" );
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
@ -148,6 +155,8 @@ public partial class SettingsInterface
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _filter = string.Empty;
|
||||||
|
|
||||||
private unsafe void DrawResourceManagerTab()
|
private unsafe void DrawResourceManagerTab()
|
||||||
{
|
{
|
||||||
if( !ImGui.BeginTabItem( "Resource Manager Tab" ) )
|
if( !ImGui.BeginTabItem( "Resource Manager Tab" ) )
|
||||||
|
|
@ -164,6 +173,8 @@ public partial class SettingsInterface
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.InputTextWithHint( "##resourceFilter", "Filter...", ref _filter, Utf8GamePath.MaxGamePathLength );
|
||||||
|
|
||||||
raii.Push( ImGui.EndChild );
|
raii.Push( ImGui.EndChild );
|
||||||
if( !ImGui.BeginChild( "##ResourceManagerChild", -Vector2.One, true ) )
|
if( !ImGui.BeginChild( "##ResourceManagerChild", -Vector2.One, true ) )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue