mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Use MetaReverter for all cases, improve Eqdp handling through this.
This commit is contained in:
parent
af3a07c227
commit
b34999a1a5
13 changed files with 199 additions and 195 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 2b4b78b03d679144440d35b30830608b6d2bcb79
|
||||
Subproject commit b92dbe60887503a77a89aeae80729236fb2bfa10
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta.Manager;
|
||||
using Penumbra.Mods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Penumbra.Interop;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
|
|
@ -203,4 +206,21 @@ public partial class ModCollection
|
|||
Penumbra.Log.Debug( $"Set CharacterUtility resources for collection {Name}." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for short periods of changed files.
|
||||
public CharacterUtility.List.MetaReverter? TemporarilySetEqdpFile( GenderRace genderRace, bool accessory )
|
||||
=> _cache?.MetaManipulations.TemporarilySetEqdpFile( genderRace, accessory );
|
||||
|
||||
public CharacterUtility.List.MetaReverter? TemporarilySetEqpFile()
|
||||
=> _cache?.MetaManipulations.TemporarilySetEqpFile();
|
||||
|
||||
public CharacterUtility.List.MetaReverter? TemporarilySetGmpFile()
|
||||
=> _cache?.MetaManipulations.TemporarilySetGmpFile();
|
||||
|
||||
public CharacterUtility.List.MetaReverter? TemporarilySetCmpFile()
|
||||
=> _cache?.MetaManipulations.TemporarilySetCmpFile();
|
||||
|
||||
public CharacterUtility.List.MetaReverter? TemporarilySetEstFile( EstManipulation.EstType type )
|
||||
=> _cache?.MetaManipulations.TemporarilySetEstFile( type );
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,18 +84,32 @@ public unsafe partial class CharacterUtility : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
public List.MetaReverter SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
||||
public void SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
||||
{
|
||||
var idx = ReverseIndices[ ( int )resourceIdx ];
|
||||
var list = _lists[ idx.Value ];
|
||||
return list.SetResource( data, length );
|
||||
var idx = ReverseIndices[( int )resourceIdx];
|
||||
var list = _lists[idx.Value];
|
||||
list.SetResource( data, length );
|
||||
}
|
||||
|
||||
public List.MetaReverter ResetResource( Structs.CharacterUtility.Index resourceIdx )
|
||||
public void ResetResource( Structs.CharacterUtility.Index resourceIdx )
|
||||
{
|
||||
var idx = ReverseIndices[( int )resourceIdx];
|
||||
var list = _lists[idx.Value];
|
||||
list.ResetResource();
|
||||
}
|
||||
|
||||
public List.MetaReverter TemporarilySetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
||||
{
|
||||
var idx = ReverseIndices[ ( int )resourceIdx ];
|
||||
var list = _lists[ idx.Value ];
|
||||
return list.ResetResource();
|
||||
return list.TemporarilySetResource( data, length );
|
||||
}
|
||||
|
||||
public List.MetaReverter TemporarilyResetResource( Structs.CharacterUtility.Index resourceIdx )
|
||||
{
|
||||
var idx = ReverseIndices[ ( int )resourceIdx ];
|
||||
var list = _lists[ idx.Value ];
|
||||
return list.TemporarilyResetResource();
|
||||
}
|
||||
|
||||
// Return all relevant resources to the default resource.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, out ResolveData resolveData )
|
||||
{
|
||||
if( type == ResourceType.Tex
|
||||
if( type == ResourceType.Tex
|
||||
&& LastCreatedCollection.Valid
|
||||
&& gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l', '_', 'f', 'a', 'c', 'e' ) )
|
||||
{
|
||||
|
|
@ -123,7 +123,7 @@ public unsafe partial class PathResolver
|
|||
|
||||
// This map links DrawObjects directly to Actors (by ObjectTable index) and their collections.
|
||||
// It contains any DrawObjects that correspond to a human actor, even those without specific collections.
|
||||
private readonly Dictionary< IntPtr, (ResolveData, int) > _drawObjectToObject = new();
|
||||
private readonly Dictionary< IntPtr, (ResolveData, int) > _drawObjectToObject = new();
|
||||
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
|
||||
|
||||
// Keep track of created DrawObjects that are CharacterBase,
|
||||
|
|
@ -135,22 +135,37 @@ public unsafe partial class PathResolver
|
|||
|
||||
private IntPtr CharacterBaseCreateDetour( uint a, IntPtr b, IntPtr c, byte d )
|
||||
{
|
||||
using var cmp = MetaChanger.ChangeCmp( LastGameObject, out _lastCreatedCollection );
|
||||
|
||||
CharacterUtility.List.MetaReverter? cmp = null;
|
||||
if( LastGameObject != null )
|
||||
{
|
||||
_lastCreatedCollection = IdentifyCollection( LastGameObject );
|
||||
var modelPtr = &a;
|
||||
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ( IntPtr )modelPtr, b, c );
|
||||
if( _lastCreatedCollection.ModCollection != Penumbra.CollectionManager.Default )
|
||||
{
|
||||
cmp = _lastCreatedCollection.ModCollection.TemporarilySetCmpFile();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ( IntPtr )modelPtr, b, c );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Penumbra.Log.Error( $"Unknown Error during CreatingCharacterBase:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
var ret = _characterBaseCreateHook.Original( a, b, c, d );
|
||||
if( LastGameObject != null )
|
||||
using( cmp )
|
||||
{
|
||||
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
|
||||
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
|
||||
}
|
||||
if( LastGameObject != null )
|
||||
{
|
||||
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
|
||||
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.GameData.Enums;
|
||||
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
||||
|
|
@ -29,17 +29,13 @@ namespace Penumbra.Interop.Resolver;
|
|||
// ChangeCustomize and RspSetupCharacter, which is hooked here.
|
||||
|
||||
// GMP Entries seem to be only used by "48 8B ?? 53 55 57 48 83 ?? ?? 48 8B", which has a DrawObject as its first parameter.
|
||||
|
||||
public unsafe partial class PathResolver
|
||||
{
|
||||
public unsafe class MetaState : IDisposable
|
||||
public class MetaState : IDisposable
|
||||
{
|
||||
private readonly PathResolver _parent;
|
||||
|
||||
public MetaState( PathResolver parent, IntPtr* humanVTable )
|
||||
public MetaState( IntPtr* humanVTable )
|
||||
{
|
||||
SignatureHelper.Initialise( this );
|
||||
_parent = parent;
|
||||
_onModelLoadCompleteHook = Hook< OnModelLoadCompleteDelegate >.FromAddress( humanVTable[ 58 ], OnModelLoadCompleteDetour );
|
||||
}
|
||||
|
||||
|
|
@ -81,8 +77,10 @@ public unsafe partial class PathResolver
|
|||
var collection = GetResolveData( drawObject );
|
||||
if( collection.Valid )
|
||||
{
|
||||
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||
var race = GetDrawObjectGenderRace( drawObject );
|
||||
using var eqp = collection.ModCollection.TemporarilySetEqpFile();
|
||||
using var eqdp1 = collection.ModCollection.TemporarilySetEqdpFile( race, false );
|
||||
using var eqdp2 = collection.ModCollection.TemporarilySetEqdpFile( race, true );
|
||||
_onModelLoadCompleteHook.Original.Invoke( drawObject );
|
||||
}
|
||||
else
|
||||
|
|
@ -108,8 +106,10 @@ public unsafe partial class PathResolver
|
|||
var collection = GetResolveData( drawObject );
|
||||
if( collection.Valid )
|
||||
{
|
||||
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
|
||||
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
|
||||
var race = GetDrawObjectGenderRace( drawObject );
|
||||
using var eqp = collection.ModCollection.TemporarilySetEqpFile();
|
||||
using var eqdp1 = collection.ModCollection.TemporarilySetEqdpFile( race, false );
|
||||
using var eqdp2 = collection.ModCollection.TemporarilySetEqdpFile( race, true );
|
||||
_updateModelsHook.Original.Invoke( drawObject );
|
||||
}
|
||||
else
|
||||
|
|
@ -118,6 +118,24 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
}
|
||||
|
||||
private static GenderRace GetDrawObjectGenderRace( IntPtr drawObject )
|
||||
{
|
||||
var draw = ( DrawObject* )drawObject;
|
||||
if( draw->Object.GetObjectType() == ObjectType.CharacterBase )
|
||||
{
|
||||
var c = ( CharacterBase* )drawObject;
|
||||
if( c->GetModelType() == CharacterBase.ModelType.Human )
|
||||
{
|
||||
return GetHumanGenderRace( drawObject );
|
||||
}
|
||||
}
|
||||
|
||||
return GenderRace.Unknown;
|
||||
}
|
||||
|
||||
public static GenderRace GetHumanGenderRace( IntPtr human )
|
||||
=> ( GenderRace )( ( Human* )human )->RaceSexId;
|
||||
|
||||
[Signature( "40 ?? 48 83 ?? ?? ?? 81 ?? ?? ?? ?? ?? 48 8B ?? 74 ?? ?? 83 ?? ?? ?? ?? ?? ?? 74 ?? 4C",
|
||||
DetourName = nameof( GetEqpIndirectDetour ) )]
|
||||
private readonly Hook< OnModelLoadCompleteDelegate > _getEqpIndirectHook = null!;
|
||||
|
|
@ -131,7 +149,8 @@ public unsafe partial class PathResolver
|
|||
return;
|
||||
}
|
||||
|
||||
using var eqp = MetaChanger.ChangeEqp( _parent, drawObject );
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
using var eqp = resolveData.Valid ? resolveData.ModCollection.TemporarilySetEqpFile() : null;
|
||||
_getEqpIndirectHook.Original( drawObject );
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +164,8 @@ public unsafe partial class PathResolver
|
|||
|
||||
private byte SetupVisorDetour( IntPtr drawObject, ushort modelId, byte visorState )
|
||||
{
|
||||
using var gmp = MetaChanger.ChangeGmp( _parent, drawObject );
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
using var eqp = resolveData.Valid ? resolveData.ModCollection.TemporarilySetGmpFile() : null;
|
||||
return _setupVisorHook.Original( drawObject, modelId, visorState );
|
||||
}
|
||||
|
||||
|
|
@ -163,13 +183,14 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
else
|
||||
{
|
||||
using var rsp = MetaChanger.ChangeCmp( _parent, drawObject );
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
using var eqp = resolveData.Valid ? resolveData.ModCollection.TemporarilySetCmpFile() : null;
|
||||
_rspSetupCharacterHook.Original( drawObject, unk2, unk3, unk4, unk5 );
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeCustomize calls RspSetupCharacter, so skip the additional cmp change.
|
||||
private bool _inChangeCustomize = false;
|
||||
private bool _inChangeCustomize;
|
||||
private delegate bool ChangeCustomizeDelegate( IntPtr human, IntPtr data, byte skipEquipment );
|
||||
|
||||
[Signature( "E8 ?? ?? ?? ?? 41 0F B6 C5 66 41 89 86", DetourName = nameof( ChangeCustomizeDetour ) )]
|
||||
|
|
@ -178,154 +199,9 @@ public unsafe partial class PathResolver
|
|||
private bool ChangeCustomizeDetour( IntPtr human, IntPtr data, byte skipEquipment )
|
||||
{
|
||||
_inChangeCustomize = true;
|
||||
using var rsp = MetaChanger.ChangeCmp( _parent, human );
|
||||
var resolveData = GetResolveData( human );
|
||||
using var eqp = resolveData.Valid ? resolveData.ModCollection.TemporarilySetEqpFile() : null;
|
||||
return _changeCustomize.Original( human, data, skipEquipment );
|
||||
}
|
||||
}
|
||||
|
||||
// Small helper to handle setting metadata and reverting it at the end of the function.
|
||||
// Since eqp and eqdp may be called multiple times in a row, we need to count them,
|
||||
// so that we do not reset the files too early.
|
||||
private readonly struct MetaChanger : IDisposable
|
||||
{
|
||||
private static int _eqpCounter;
|
||||
private static int _eqdpCounter;
|
||||
private readonly MetaManipulation.Type _type;
|
||||
|
||||
private MetaChanger( MetaManipulation.Type type )
|
||||
{
|
||||
_type = type;
|
||||
if( type == MetaManipulation.Type.Eqp )
|
||||
{
|
||||
++_eqpCounter;
|
||||
}
|
||||
else if( type == MetaManipulation.Type.Eqdp )
|
||||
{
|
||||
++_eqdpCounter;
|
||||
}
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeEqp( ModCollection collection )
|
||||
{
|
||||
collection.SetEqpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Eqp );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeEqp( PathResolver _, IntPtr drawObject )
|
||||
{
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
if( resolveData.Valid )
|
||||
{
|
||||
return ChangeEqp( resolveData.ModCollection );
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
// We only need to change anything if it is actually equipment here.
|
||||
public static MetaChanger ChangeEqdp( PathResolver _, IntPtr drawObject, uint modelType )
|
||||
{
|
||||
if( modelType < 10 )
|
||||
{
|
||||
var collection = GetResolveData( drawObject );
|
||||
if( collection.Valid )
|
||||
{
|
||||
return ChangeEqdp( collection.ModCollection );
|
||||
}
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeEqdp( ModCollection collection )
|
||||
{
|
||||
collection.SetEqdpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Eqdp );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeGmp( PathResolver resolver, IntPtr drawObject )
|
||||
{
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
if( resolveData.Valid )
|
||||
{
|
||||
resolveData.ModCollection.SetGmpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Gmp );
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeEst( PathResolver resolver, IntPtr drawObject )
|
||||
{
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
if( resolveData.Valid )
|
||||
{
|
||||
resolveData.ModCollection.SetEstFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Est );
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeCmp( GameObject* gameObject, out ResolveData resolveData )
|
||||
{
|
||||
if( gameObject != null )
|
||||
{
|
||||
resolveData = IdentifyCollection( gameObject );
|
||||
if( resolveData.ModCollection != Penumbra.CollectionManager.Default && resolveData.ModCollection.HasCache )
|
||||
{
|
||||
resolveData.ModCollection.SetCmpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Rsp );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resolveData = ResolveData.Invalid;
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
public static MetaChanger ChangeCmp( PathResolver resolver, IntPtr drawObject )
|
||||
{
|
||||
var resolveData = GetResolveData( drawObject );
|
||||
if( resolveData.Valid )
|
||||
{
|
||||
resolveData.ModCollection.SetCmpFiles();
|
||||
return new MetaChanger( MetaManipulation.Type.Rsp );
|
||||
}
|
||||
|
||||
return new MetaChanger( MetaManipulation.Type.Unknown );
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
switch( _type )
|
||||
{
|
||||
case MetaManipulation.Type.Eqdp:
|
||||
if( --_eqdpCounter == 0 )
|
||||
{
|
||||
Penumbra.CollectionManager.Default.SetEqdpFiles();
|
||||
}
|
||||
|
||||
break;
|
||||
case MetaManipulation.Type.Eqp:
|
||||
if( --_eqpCounter == 0 )
|
||||
{
|
||||
Penumbra.CollectionManager.Default.SetEqpFiles();
|
||||
}
|
||||
|
||||
break;
|
||||
case MetaManipulation.Type.Est:
|
||||
Penumbra.CollectionManager.Default.SetEstFiles();
|
||||
break;
|
||||
case MetaManipulation.Type.Gmp:
|
||||
Penumbra.CollectionManager.Default.SetGmpFiles();
|
||||
break;
|
||||
case MetaManipulation.Type.Rsp:
|
||||
Penumbra.CollectionManager.Default.SetCmpFiles();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ using System;
|
|||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
||||
|
|
@ -140,34 +143,64 @@ public partial class PathResolver
|
|||
|
||||
private IntPtr ResolveMdlHuman( IntPtr drawObject, IntPtr path, IntPtr unk3, uint modelType )
|
||||
{
|
||||
using var eqdp = MetaChanger.ChangeEqdp( _parent, drawObject, modelType );
|
||||
CharacterUtility.List.MetaReverter? Get()
|
||||
{
|
||||
if( modelType > 9 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var race = MetaState.GetHumanGenderRace( drawObject );
|
||||
if( race == GenderRace.Unknown )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = GetResolveData( drawObject );
|
||||
return !data.Valid ? null : data.ModCollection.TemporarilySetEqdpFile( race, modelType > 4 );
|
||||
}
|
||||
|
||||
using var eqdp = Get();
|
||||
return ResolvePath( drawObject, _resolveMdlPathHook.Original( drawObject, path, unk3, modelType ) );
|
||||
}
|
||||
|
||||
private IntPtr ResolvePapHuman( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4, ulong unk5 )
|
||||
{
|
||||
using var est = MetaChanger.ChangeEst( _parent, drawObject );
|
||||
using var est = GetEstChanges( drawObject );
|
||||
return ResolvePath( drawObject, _resolvePapPathHook.Original( drawObject, path, unk3, unk4, unk5 ) );
|
||||
}
|
||||
|
||||
private IntPtr ResolvePhybHuman( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
{
|
||||
using var est = MetaChanger.ChangeEst( _parent, drawObject );
|
||||
using var est = GetEstChanges( drawObject );
|
||||
return ResolvePath( drawObject, _resolvePhybPathHook.Original( drawObject, path, unk3, unk4 ) );
|
||||
}
|
||||
|
||||
private IntPtr ResolveSklbHuman( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
{
|
||||
using var est = MetaChanger.ChangeEst( _parent, drawObject );
|
||||
using var est = GetEstChanges( drawObject );
|
||||
return ResolvePath( drawObject, _resolveSklbPathHook.Original( drawObject, path, unk3, unk4 ) );
|
||||
}
|
||||
|
||||
private IntPtr ResolveSkpHuman( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
{
|
||||
using var est = MetaChanger.ChangeEst( _parent, drawObject );
|
||||
using var est = GetEstChanges( drawObject );
|
||||
return ResolvePath( drawObject, _resolveSkpPathHook.Original( drawObject, path, unk3, unk4 ) );
|
||||
}
|
||||
|
||||
private DisposableContainer GetEstChanges( IntPtr drawObject )
|
||||
{
|
||||
var data = GetResolveData( drawObject );
|
||||
if( !data.Valid )
|
||||
{
|
||||
return DisposableContainer.Empty;
|
||||
}
|
||||
|
||||
return new DisposableContainer( data.ModCollection.TemporarilySetEstFile( EstManipulation.EstType.Face ),
|
||||
data.ModCollection.TemporarilySetEstFile( EstManipulation.EstType.Body ),
|
||||
data.ModCollection.TemporarilySetEstFile( EstManipulation.EstType.Hair ),
|
||||
data.ModCollection.TemporarilySetEstFile( EstManipulation.EstType.Head ) );
|
||||
}
|
||||
|
||||
private IntPtr ResolveDecalWeapon( IntPtr drawObject, IntPtr path, IntPtr unk3, uint unk4 )
|
||||
=> ResolveWeaponPath( drawObject, _resolveDecalPathHook.Original( drawObject, path, unk3, unk4 ) );
|
||||
|
|
@ -226,9 +259,10 @@ public partial class PathResolver
|
|||
// Implementation
|
||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||
private IntPtr ResolvePath( IntPtr drawObject, IntPtr path )
|
||||
=> _parent._paths.ResolvePath( (IntPtr?)FindParent( drawObject, out _) ?? IntPtr.Zero, FindParent( drawObject, out var collection ) == null
|
||||
? Penumbra.CollectionManager.Default
|
||||
: collection.ModCollection, path );
|
||||
=> _parent._paths.ResolvePath( ( IntPtr? )FindParent( drawObject, out _ ) ?? IntPtr.Zero,
|
||||
FindParent( drawObject, out var collection ) == null
|
||||
? Penumbra.CollectionManager.Default
|
||||
: collection.ModCollection, path );
|
||||
|
||||
// Weapons have the characters DrawObject as a parent,
|
||||
// but that may not be set yet when creating a new object, so we have to do the same detour
|
||||
|
|
@ -239,18 +273,18 @@ public partial class PathResolver
|
|||
var parent = FindParent( drawObject, out var collection );
|
||||
if( parent != null )
|
||||
{
|
||||
return _parent._paths.ResolvePath( (IntPtr)parent, collection.ModCollection, path );
|
||||
return _parent._paths.ResolvePath( ( IntPtr )parent, collection.ModCollection, path );
|
||||
}
|
||||
|
||||
var parentObject = ( IntPtr )( ( DrawObject* )drawObject )->Object.ParentObject;
|
||||
var parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject );
|
||||
if( parentCollection.Valid )
|
||||
{
|
||||
return _parent._paths.ResolvePath( (IntPtr)FindParent(parentObject, out _), parentCollection.ModCollection, path );
|
||||
return _parent._paths.ResolvePath( ( IntPtr )FindParent( parentObject, out _ ), parentCollection.ModCollection, path );
|
||||
}
|
||||
|
||||
parent = FindParent( parentObject, out collection );
|
||||
return _parent._paths.ResolvePath( (IntPtr?)parent ?? IntPtr.Zero, parent == null
|
||||
return _parent._paths.ResolvePath( ( IntPtr? )parent ?? IntPtr.Zero, parent == null
|
||||
? Penumbra.CollectionManager.Default
|
||||
: collection.ModCollection, path );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public partial class PathResolver : IDisposable
|
|||
_loader = loader;
|
||||
_animations = new AnimationState( DrawObjects );
|
||||
_paths = new PathState( this );
|
||||
_meta = new MetaState( this, _paths.HumanVTable );
|
||||
_meta = new MetaState( _paths.HumanVTable );
|
||||
_materials = new MaterialState( _paths );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public partial class MetaManager
|
|||
public static void ResetCmpFiles()
|
||||
=> SetFile( null, CharacterUtility.Index.HumanCmp );
|
||||
|
||||
public Interop.CharacterUtility.List.MetaReverter TemporarilySetCmpFile()
|
||||
=> TemporarilySetFile( _cmpFile, CharacterUtility.Index.HumanCmp );
|
||||
|
||||
public void ResetCmp()
|
||||
{
|
||||
if( _cmpFile == null )
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OtterGui;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
|
@ -23,6 +24,21 @@ public partial class MetaManager
|
|||
}
|
||||
}
|
||||
|
||||
public Interop.CharacterUtility.List.MetaReverter? TemporarilySetEqdpFile( GenderRace genderRace, bool accessory )
|
||||
{
|
||||
var idx = CharacterUtility.EqdpIdx( genderRace, accessory );
|
||||
if( ( int )idx != -1 )
|
||||
{
|
||||
var i = CharacterUtility.EqdpIndices.IndexOf( idx );
|
||||
if( i != -1 )
|
||||
{
|
||||
return TemporarilySetFile( _eqdpFiles[ i ], idx );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void ResetEqdpFiles()
|
||||
{
|
||||
foreach( var idx in CharacterUtility.EqdpIndices )
|
||||
|
|
@ -33,7 +49,7 @@ public partial class MetaManager
|
|||
|
||||
public void ResetEqdp()
|
||||
{
|
||||
foreach( var file in _eqdpFiles.OfType<ExpandedEqdpFile>() )
|
||||
foreach( var file in _eqdpFiles.OfType< ExpandedEqdpFile >() )
|
||||
{
|
||||
var relevant = Interop.CharacterUtility.RelevantIndices[ file.Index.Value ];
|
||||
file.Reset( _eqdpManipulations.Where( m => m.FileIndex() == relevant ).Select( m => ( int )m.SetId ) );
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public partial class MetaManager
|
|||
public static void ResetEqpFiles()
|
||||
=> SetFile( null, CharacterUtility.Index.Eqp );
|
||||
|
||||
public Interop.CharacterUtility.List.MetaReverter TemporarilySetEqpFile()
|
||||
=> TemporarilySetFile( _eqpFile, CharacterUtility.Index.Eqp );
|
||||
|
||||
public void ResetEqp()
|
||||
{
|
||||
if( _eqpFile == null )
|
||||
|
|
|
|||
|
|
@ -33,6 +33,20 @@ public partial class MetaManager
|
|||
SetFile( null, CharacterUtility.Index.HeadEst );
|
||||
}
|
||||
|
||||
public Interop.CharacterUtility.List.MetaReverter? TemporarilySetEstFile(EstManipulation.EstType type)
|
||||
{
|
||||
var (file, idx) = type switch
|
||||
{
|
||||
EstManipulation.EstType.Face => ( _estFaceFile, CharacterUtility.Index.FaceEst ),
|
||||
EstManipulation.EstType.Hair => ( _estHairFile, CharacterUtility.Index.HairEst ),
|
||||
EstManipulation.EstType.Body => ( _estBodyFile, CharacterUtility.Index.BodyEst ),
|
||||
EstManipulation.EstType.Head => ( _estHeadFile, CharacterUtility.Index.HeadEst ),
|
||||
_ => ( null, 0 ),
|
||||
};
|
||||
|
||||
return idx != 0 ? TemporarilySetFile( file, idx ) : null;
|
||||
}
|
||||
|
||||
public void ResetEst()
|
||||
{
|
||||
_estFaceFile?.Reset();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ public partial class MetaManager
|
|||
public static void ResetGmpFiles()
|
||||
=> SetFile( null, CharacterUtility.Index.Gmp );
|
||||
|
||||
public Interop.CharacterUtility.List.MetaReverter TemporarilySetGmpFile()
|
||||
=> TemporarilySetFile( _gmpFile, CharacterUtility.Index.Gmp );
|
||||
|
||||
public void ResetGmp()
|
||||
{
|
||||
if( _gmpFile == null )
|
||||
|
|
|
|||
|
|
@ -179,4 +179,10 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM
|
|||
Penumbra.CharacterUtility.SetResource( index, ( IntPtr )file.Data, file.Length );
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
|
||||
private static unsafe Interop.CharacterUtility.List.MetaReverter TemporarilySetFile( MetaBaseFile? file, CharacterUtility.Index index )
|
||||
=> file == null
|
||||
? Penumbra.CharacterUtility.TemporarilyResetResource( index )
|
||||
: Penumbra.CharacterUtility.TemporarilySetResource( index, ( IntPtr )file.Data, file.Length );
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue