mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-30 20:33:43 +01:00
No EST files anymore.
This commit is contained in:
parent
943207cae8
commit
ebef4ff650
8 changed files with 68 additions and 183 deletions
|
|
@ -34,17 +34,6 @@ public sealed class EqpCache(MetaFileManager manager, ModCollection collection)
|
|||
protected override void RevertModInternal(EqpIdentifier identifier)
|
||||
{ }
|
||||
|
||||
public static bool Apply(ExpandedEqpFile file, EqpIdentifier identifier, EqpEntry entry)
|
||||
{
|
||||
var origEntry = file[identifier.SetId];
|
||||
var mask = Eqp.Mask(identifier.Slot);
|
||||
if ((origEntry & mask) == entry)
|
||||
return false;
|
||||
|
||||
file[identifier.SetId] = (origEntry & ~mask) | entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool _)
|
||||
=> Clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
|
@ -8,144 +6,26 @@ namespace Penumbra.Collections.Cache;
|
|||
|
||||
public sealed class EstCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<EstIdentifier, EstEntry>(manager, collection)
|
||||
{
|
||||
private EstFile? _estFaceFile;
|
||||
private EstFile? _estHairFile;
|
||||
private EstFile? _estBodyFile;
|
||||
private EstFile? _estHeadFile;
|
||||
|
||||
public override void SetFiles()
|
||||
{
|
||||
Manager.SetFile(_estFaceFile, MetaIndex.FaceEst);
|
||||
Manager.SetFile(_estHairFile, MetaIndex.HairEst);
|
||||
Manager.SetFile(_estBodyFile, MetaIndex.BodyEst);
|
||||
Manager.SetFile(_estHeadFile, MetaIndex.HeadEst);
|
||||
}
|
||||
|
||||
public void SetFile(MetaIndex index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case MetaIndex.FaceEst:
|
||||
Manager.SetFile(_estFaceFile, MetaIndex.FaceEst);
|
||||
break;
|
||||
case MetaIndex.HairEst:
|
||||
Manager.SetFile(_estHairFile, MetaIndex.HairEst);
|
||||
break;
|
||||
case MetaIndex.BodyEst:
|
||||
Manager.SetFile(_estBodyFile, MetaIndex.BodyEst);
|
||||
break;
|
||||
case MetaIndex.HeadEst:
|
||||
Manager.SetFile(_estHeadFile, MetaIndex.HeadEst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public MetaList.MetaReverter TemporarilySetFiles(EstType type)
|
||||
{
|
||||
var (file, idx) = type switch
|
||||
{
|
||||
EstType.Face => (_estFaceFile, MetaIndex.FaceEst),
|
||||
EstType.Hair => (_estHairFile, MetaIndex.HairEst),
|
||||
EstType.Body => (_estBodyFile, MetaIndex.BodyEst),
|
||||
EstType.Head => (_estHeadFile, MetaIndex.HeadEst),
|
||||
_ => (null, 0),
|
||||
};
|
||||
|
||||
return Manager.TemporarilySetFile(file, idx);
|
||||
}
|
||||
|
||||
public void ResetFiles()
|
||||
{
|
||||
Manager.SetFile(null, MetaIndex.FaceEst);
|
||||
Manager.SetFile(null, MetaIndex.HairEst);
|
||||
Manager.SetFile(null, MetaIndex.BodyEst);
|
||||
Manager.SetFile(null, MetaIndex.HeadEst);
|
||||
}
|
||||
{ }
|
||||
|
||||
protected override void IncorporateChangesInternal()
|
||||
{
|
||||
if (!Manager.CharacterUtility.Ready)
|
||||
return;
|
||||
|
||||
foreach (var (identifier, (_, entry)) in this)
|
||||
Apply(GetFile(identifier)!, identifier, entry);
|
||||
Penumbra.Log.Verbose($"{Collection.AnonymizedName}: Loaded {Count} delayed EST manipulations.");
|
||||
}
|
||||
{ }
|
||||
|
||||
public EstEntry GetEstEntry(EstIdentifier identifier)
|
||||
{
|
||||
var file = GetCurrentFile(identifier);
|
||||
return file != null
|
||||
? file[identifier.GenderRace, identifier.SetId]
|
||||
=> TryGetValue(identifier, out var entry)
|
||||
? entry.Entry
|
||||
: EstFile.GetDefault(Manager, identifier);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_estFaceFile?.Reset();
|
||||
_estHairFile?.Reset();
|
||||
_estBodyFile?.Reset();
|
||||
_estHeadFile?.Reset();
|
||||
Clear();
|
||||
}
|
||||
=> Clear();
|
||||
|
||||
protected override void ApplyModInternal(EstIdentifier identifier, EstEntry entry)
|
||||
{
|
||||
if (GetFile(identifier) is { } file)
|
||||
Apply(file, identifier, entry);
|
||||
}
|
||||
{ }
|
||||
|
||||
protected override void RevertModInternal(EstIdentifier identifier)
|
||||
{
|
||||
if (GetFile(identifier) is { } file)
|
||||
Apply(file, identifier, EstFile.GetDefault(Manager, identifier.Slot, identifier.GenderRace, identifier.SetId));
|
||||
}
|
||||
|
||||
public static bool Apply(EstFile file, EstIdentifier identifier, EstEntry entry)
|
||||
=> file.SetEntry(identifier.GenderRace, identifier.SetId, entry) switch
|
||||
{
|
||||
EstFile.EstEntryChange.Unchanged => false,
|
||||
EstFile.EstEntryChange.Changed => true,
|
||||
EstFile.EstEntryChange.Added => true,
|
||||
EstFile.EstEntryChange.Removed => true,
|
||||
_ => false,
|
||||
};
|
||||
{ }
|
||||
|
||||
protected override void Dispose(bool _)
|
||||
{
|
||||
_estFaceFile?.Dispose();
|
||||
_estHairFile?.Dispose();
|
||||
_estBodyFile?.Dispose();
|
||||
_estHeadFile?.Dispose();
|
||||
_estFaceFile = null;
|
||||
_estHairFile = null;
|
||||
_estBodyFile = null;
|
||||
_estHeadFile = null;
|
||||
Clear();
|
||||
}
|
||||
|
||||
private EstFile? GetCurrentFile(EstIdentifier identifier)
|
||||
=> identifier.Slot switch
|
||||
{
|
||||
EstType.Hair => _estHairFile,
|
||||
EstType.Face => _estFaceFile,
|
||||
EstType.Body => _estBodyFile,
|
||||
EstType.Head => _estHeadFile,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private EstFile? GetFile(EstIdentifier identifier)
|
||||
{
|
||||
if (!Manager.CharacterUtility.Ready)
|
||||
return null;
|
||||
|
||||
return identifier.Slot switch
|
||||
{
|
||||
EstType.Hair => _estHairFile ??= new EstFile(Manager, EstType.Hair),
|
||||
EstType.Face => _estFaceFile ??= new EstFile(Manager, EstType.Face),
|
||||
EstType.Body => _estBodyFile ??= new EstFile(Manager, EstType.Body),
|
||||
EstType.Head => _estHeadFile ??= new EstFile(Manager, EstType.Head),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
=> Clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
|
@ -22,16 +21,6 @@ public sealed class GmpCache(MetaFileManager manager, ModCollection collection)
|
|||
protected override void RevertModInternal(GmpIdentifier identifier)
|
||||
{ }
|
||||
|
||||
public static bool Apply(ExpandedGmpFile file, GmpIdentifier identifier, GmpEntry entry)
|
||||
{
|
||||
var origEntry = file[identifier.SetId];
|
||||
if (entry == origEntry)
|
||||
return false;
|
||||
|
||||
file[identifier.SetId] = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool _)
|
||||
=> Clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,10 +121,8 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
|
|||
switch (metaIndex)
|
||||
{
|
||||
case MetaIndex.Eqp:
|
||||
Eqp.SetFiles();
|
||||
break;
|
||||
case MetaIndex.Gmp:
|
||||
Gmp.SetFiles();
|
||||
break;
|
||||
case MetaIndex.HumanCmp:
|
||||
Rsp.SetFiles();
|
||||
|
|
@ -133,7 +131,6 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
|
|||
case MetaIndex.HairEst:
|
||||
case MetaIndex.HeadEst:
|
||||
case MetaIndex.BodyEst:
|
||||
Est.SetFile(metaIndex);
|
||||
break;
|
||||
default:
|
||||
Eqdp.SetFile(metaIndex);
|
||||
|
|
@ -151,9 +148,6 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
|
|||
public MetaList.MetaReverter TemporarilySetCmpFile()
|
||||
=> Rsp.TemporarilySetFile();
|
||||
|
||||
public MetaList.MetaReverter TemporarilySetEstFile(EstType type)
|
||||
=> Est.TemporarilySetFiles(type);
|
||||
|
||||
/// <summary> Try to obtain a manipulated IMC file. </summary>
|
||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file)
|
||||
=> Imc.GetFile(path, out file);
|
||||
|
|
|
|||
|
|
@ -103,8 +103,4 @@ public partial class ModCollection
|
|||
public MetaList.MetaReverter TemporarilySetCmpFile(CharacterUtility utility)
|
||||
=> _cache?.Meta.TemporarilySetCmpFile()
|
||||
?? utility.TemporarilyResetResource(MetaIndex.HumanCmp);
|
||||
|
||||
public MetaList.MetaReverter TemporarilySetEstFile(CharacterUtility utility, EstType type)
|
||||
=> _cache?.Meta.TemporarilySetEstFile(type)
|
||||
?? utility.TemporarilyResetResource((MetaIndex)type);
|
||||
}
|
||||
|
|
|
|||
49
Penumbra/Interop/Hooks/Meta/EstHook.cs
Normal file
49
Penumbra/Interop/Hooks/Meta/EstHook.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.PathResolving;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Interop.Hooks.Meta;
|
||||
|
||||
public class EstHook : FastHook<EstHook.Delegate>
|
||||
{
|
||||
public delegate EstEntry Delegate(uint id, int estType, uint genderRace);
|
||||
|
||||
private readonly MetaState _metaState;
|
||||
|
||||
public EstHook(HookManager hooks, MetaState metaState)
|
||||
{
|
||||
_metaState = metaState;
|
||||
Task = hooks.CreateHook<Delegate>("GetEstEntry", "44 8B C9 83 EA ?? 74", Detour, true);
|
||||
}
|
||||
|
||||
private EstEntry Detour(uint genderRace, int estType, uint id)
|
||||
{
|
||||
EstEntry ret;
|
||||
if (_metaState.EstCollection is { Valid: true, ModCollection.MetaCache: { } cache }
|
||||
&& cache.Est.TryGetValue(Convert(genderRace, estType, id), out var entry))
|
||||
ret = entry.Entry;
|
||||
else
|
||||
ret = Task.Result.Original(genderRace, estType, id);
|
||||
|
||||
Penumbra.Log.Information($"[GetEstEntry] Invoked with {genderRace}, {estType}, {id}, returned {ret.Value}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static EstIdentifier Convert(uint genderRace, int estType, uint id)
|
||||
{
|
||||
var i = new PrimaryId((ushort)id);
|
||||
var gr = (GenderRace)genderRace;
|
||||
var type = estType switch
|
||||
{
|
||||
1 => EstType.Face,
|
||||
2 => EstType.Hair,
|
||||
3 => EstType.Head,
|
||||
4 => EstType.Body,
|
||||
_ => (EstType)0,
|
||||
};
|
||||
return new EstIdentifier(i, type, gr);
|
||||
}
|
||||
}
|
||||
|
|
@ -158,26 +158,26 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
|
|||
|
||||
private nint ResolvePapHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName)
|
||||
{
|
||||
using var est = GetEstChanges(drawObject, out var data);
|
||||
return ResolvePath(data, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
|
||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
return ResolvePath(_parent.MetaState.EstCollection, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName));
|
||||
}
|
||||
|
||||
private nint ResolvePhybHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||
{
|
||||
using var est = GetEstChanges(drawObject, out var data);
|
||||
return ResolvePath(data, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
return ResolvePath(_parent.MetaState.EstCollection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
}
|
||||
|
||||
private nint ResolveSklbHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||
{
|
||||
using var est = GetEstChanges(drawObject, out var data);
|
||||
return ResolvePath(data, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
return ResolvePath(_parent.MetaState.EstCollection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
}
|
||||
|
||||
private nint ResolveSkpHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex)
|
||||
{
|
||||
using var est = GetEstChanges(drawObject, out var data);
|
||||
return ResolvePath(data, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
_parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
return ResolvePath(_parent.MetaState.EstCollection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex));
|
||||
}
|
||||
|
||||
private nint ResolveVfxHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint unkOutParam)
|
||||
|
|
@ -206,19 +206,6 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
|
|||
return ResolvePath(drawObject, pathBuffer);
|
||||
}
|
||||
|
||||
private DisposableContainer GetEstChanges(nint drawObject, out ResolveData data)
|
||||
{
|
||||
data = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||
if (_parent.InInternalResolve)
|
||||
return DisposableContainer.Empty;
|
||||
|
||||
return new DisposableContainer(data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstType.Face),
|
||||
data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstType.Body),
|
||||
data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstType.Hair),
|
||||
data.ModCollection.TemporarilySetEstFile(_parent.CharacterUtility, EstType.Head));
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Hook<T> Create<T>(string name, HookManager hooks, nint address, Type type, T other, T human) where T : Delegate
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Penumbra.Interop.PathResolving;
|
|||
// GetSlotEqpData seems to be the only function using the EQP table.
|
||||
// It is only called by CheckSlotsForUnload (called by UpdateModels),
|
||||
// SetupModelAttributes (called by UpdateModels and OnModelLoadComplete)
|
||||
// and a unnamed function called by UpdateRender.
|
||||
// and an unnamed function called by UpdateRender.
|
||||
// It seems to be enough to change the EQP entries for UpdateModels.
|
||||
|
||||
// GetEqdpDataFor[Adults|Children|Other] seem to be the only functions using the EQDP tables.
|
||||
|
|
@ -35,7 +35,7 @@ namespace Penumbra.Interop.PathResolving;
|
|||
// they all are called by many functions, but the most relevant seem to be Human.SetupFromCharacterData, which is only called by CharacterBase.Create,
|
||||
// ChangeCustomize and RspSetupCharacter, which is hooked here, as well as Character.CalculateHeight.
|
||||
|
||||
// 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.
|
||||
// GMP Entries seem to be only used by "48 8B ?? 53 55 57 48 83 ?? ?? 48 8B", which is SetupVisor.
|
||||
public sealed unsafe class MetaState : IDisposable
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
|
|
@ -48,6 +48,7 @@ public sealed unsafe class MetaState : IDisposable
|
|||
public ResolveData CustomizeChangeCollection = ResolveData.Invalid;
|
||||
public ResolveData EqpCollection = ResolveData.Invalid;
|
||||
public ResolveData GmpCollection = ResolveData.Invalid;
|
||||
public ResolveData EstCollection = ResolveData.Invalid;
|
||||
public PrimaryId UndividedGmpId = 0;
|
||||
|
||||
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue