Current state.

This commit is contained in:
Ottermandias 2024-11-17 00:50:14 +01:00
parent c54141be54
commit e3a1ae6938
20 changed files with 104 additions and 43 deletions

@ -1 +1 @@
Subproject commit 3e6b085749741f35dd6732c33d0720c6a51ebb97 Subproject commit 8ba88eff15326bb28ed5e6157f5252c114d40b5f

@ -1 +1 @@
Subproject commit e39a04c83b67246580492677414888357b5ebed8 Subproject commit fb81a0b55d3c68f2b26357fac3049c79fb0c22fb

View file

@ -33,25 +33,27 @@ public sealed unsafe class ApricotListenerSoundPlayCaller : FastHook<ApricotList
private nint Detour(nint a1, nint unused, float timeOffset) private nint Detour(nint a1, nint unused, float timeOffset)
{ {
// Short-circuiting and sanity checks done by game. // Short-circuiting and sanity checks done by game.
var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + 0x250); var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.PlayTimeOffset);
if (playTime < 0) if (playTime < 0)
return Task.Result.Original(a1, unused, timeOffset); return Task.Result.Original(a1, unused, timeOffset);
var someIntermediate = *(nint*)(a1 + 0x1F8); var someIntermediate = *(nint*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.SomeIntermediate);
var flags = someIntermediate == nint.Zero ? (ushort)0 : *(ushort*)(someIntermediate + 0x49C); var flags = someIntermediate == nint.Zero
if (((flags >> 13) & 1) == 0) ? (ushort)0
: *(ushort*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.Flags);
if (((flags >> VolatileOffsets.ApricotListenerSoundPlayCaller.BitShift) & 1) == 0)
return Task.Result.Original(a1, unused, timeOffset); return Task.Result.Original(a1, unused, timeOffset);
Penumbra.Log.Excessive( Penumbra.Log.Excessive(
$"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}."); $"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}.");
// Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay) // Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay)
var apricotIInstanceListenner = *(nint*)(someIntermediate + 0x270); var apricotIInstanceListenner = *(nint*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.IInstanceListenner);
if (apricotIInstanceListenner == nint.Zero) if (apricotIInstanceListenner == nint.Zero)
return Task.Result.Original(a1, unused, timeOffset); return Task.Result.Original(a1, unused, timeOffset);
// In some cases we can obtain the associated caster via vfunc 1. // In some cases we can obtain the associated caster via vfunc 1.
var newData = ResolveData.Invalid; var newData = ResolveData.Invalid;
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[1](apricotIInstanceListenner); var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[VolatileOffsets.ApricotListenerSoundPlayCaller.CasterVFunc](apricotIInstanceListenner);
if (gameObject != null) if (gameObject != null)
{ {
newData = _collectionResolver.IdentifyCollection(gameObject, true); newData = _collectionResolver.IdentifyCollection(gameObject, true);

View file

@ -31,7 +31,7 @@ public sealed unsafe class SomePapLoad : FastHook<SomePapLoad.Delegate>
private void Detour(nint a1, int a2, nint a3, int a4) private void Detour(nint a1, int a2, nint a3, int a4)
{ {
Penumbra.Log.Excessive($"[Some PAP Load] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}."); Penumbra.Log.Excessive($"[Some PAP Load] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}.");
var timelinePtr = a1 + Offsets.TimeLinePtr; var timelinePtr = a1 + VolatileOffsets.AnimationState.TimeLinePtr;
if (timelinePtr != nint.Zero) if (timelinePtr != nint.Zero)
{ {
var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3); var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3);

View file

@ -1,7 +1,7 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
namespace Penumbra.Interop.Hooks.Meta; namespace Penumbra.Interop.Hooks.Meta;
@ -14,18 +14,19 @@ public sealed unsafe class CalculateHeight : FastHook<CalculateHeight.Delegate>
{ {
_collectionResolver = collectionResolver; _collectionResolver = collectionResolver;
_metaState = metaState; _metaState = metaState;
Task = hooks.CreateHook<Delegate>("Calculate Height", (nint)Character.MemberFunctionPointers.CalculateHeight, Detour, !HookOverrides.Instance.Meta.CalculateHeight); Task = hooks.CreateHook<Delegate>("Calculate Height", (nint)HeightContainer.MemberFunctionPointers.CalculateHeight, Detour,
!HookOverrides.Instance.Meta.CalculateHeight);
} }
public delegate ulong Delegate(Character* character); public delegate ulong Delegate(HeightContainer* character);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private ulong Detour(Character* character) private ulong Detour(HeightContainer* container)
{ {
var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true); var collection = _collectionResolver.IdentifyCollection((GameObject*)container->OwnerObject, true);
_metaState.RspCollection.Push(collection); _metaState.RspCollection.Push(collection);
var ret = Task.Result.Original.Invoke(character); var ret = Task.Result.Original.Invoke(container);
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}."); Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)container:X} -> {ret}.");
_metaState.RspCollection.Pop(); _metaState.RspCollection.Pop();
return ret; return ret;
} }

View file

@ -25,7 +25,7 @@ public sealed unsafe class UpdateModel : FastHook<UpdateModel.Delegate>
{ {
// Shortcut because this is called all the time. // Shortcut because this is called all the time.
// Same thing is checked at the beginning of the original function. // Same thing is checked at the beginning of the original function.
if (*(int*)((nint)drawObject + Offsets.UpdateModelSkip) == 0) if (*(int*)((nint)drawObject + VolatileOffsets.UpdateModel.ShortCircuit) == 0)
return; return;
Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}."); Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}.");

View file

@ -401,7 +401,6 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3, private void ModelRendererUnkFuncDetour(CSModelRenderer* modelRenderer, ModelRendererStructs.UnkPayload* unkPayload, uint unk2, uint unk3,
uint unk4, uint unk5) uint unk4, uint unk5)
{ {
// If we don't have any on-screen instances of modded iris.shpk or others, we don't need the slow path at all.
if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0) if (!Enabled || GetTotalMaterialCountForModelRendererUnk() == 0)
{ {
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5); _modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);

View file

@ -240,7 +240,7 @@ public sealed unsafe class CollectionResolver(
} }
// Only handle human models. // Only handle human models.
if (!IsModelHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) if (!IsModelHuman((uint)actor.AsCharacter->ModelCharaId))
return null; return null;
if (actor.Customize->Data[0] == 0) if (actor.Customize->Data[0] == 0)

View file

@ -68,7 +68,7 @@ public class ResourceTree
Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10), Unsafe.AsPointer(ref character->DrawData.EquipmentModelIds[0]), 10),
_ => [], _ => [],
}; };
ModelId = character->CharacterData.ModelCharaId; ModelId = character->ModelCharaId;
CustomizeData = character->DrawData.CustomizeData; CustomizeData = character->DrawData.CustomizeData;
RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown; RaceCode = human != null ? (GenderRace)human->RaceSexId : GenderRace.Unknown;

View file

@ -43,7 +43,7 @@ public unsafe class FontReloader : IService
return; return;
_atkModule = &atkModule->AtkModule; _atkModule = &atkModule->AtkModule;
_reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->VirtualTable)[Offsets.ReloadFontsVfunc]; _reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->VirtualTable)[VolatileOffsets.FontReloader.ReloadFontsVFunc];
}); });
} }
} }

View file

@ -38,10 +38,10 @@ public unsafe partial class RedrawService : IService
// VFuncs that disable and enable draw, used only for GPose actors. // VFuncs that disable and enable draw, used only for GPose actors.
private static void DisableDraw(IGameObject actor) private static void DisableDraw(IGameObject actor)
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][Offsets.DisableDrawVfunc](actor.Address); => ((delegate* unmanaged<nint, void >**)actor.Address)[0][VolatileOffsets.RedrawService.DisableDrawVFunc](actor.Address);
private static void EnableDraw(IGameObject actor) private static void EnableDraw(IGameObject actor)
=> ((delegate* unmanaged<nint, void >**)actor.Address)[0][Offsets.EnableDrawVfunc](actor.Address); => ((delegate* unmanaged<nint, void >**)actor.Address)[0][VolatileOffsets.RedrawService.EnableDrawVFunc](actor.Address);
// Check whether we currently are in GPose. // Check whether we currently are in GPose.
// Also clear the name list. // Also clear the name list.

View file

@ -0,0 +1,35 @@
namespace Penumbra.Interop;
public static class VolatileOffsets
{
public static class ApricotListenerSoundPlayCaller
{
public const int PlayTimeOffset = 0x254;
public const int SomeIntermediate = 0x1F8;
public const int Flags = 0x4A4;
public const int IInstanceListenner = 0x270;
public const int BitShift = 13;
public const int CasterVFunc = 1;
}
public static class AnimationState
{
public const int TimeLinePtr = 0x50;
}
public static class UpdateModel
{
public const int ShortCircuit = 0xA2C;
}
public static class FontReloader
{
public const int ReloadFontsVFunc = 43;
}
public static class RedrawService
{
public const int EnableDrawVFunc = 12;
public const int DisableDrawVFunc = 13;
}
}

View file

@ -138,7 +138,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo) protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo)
{ {
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false; return false;
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);

View file

@ -90,7 +90,7 @@ public class ModGroupEditor(
{ {
var mod = group.Mod; var mod = group.Mod;
var idxFrom = group.GetIndex(); var idxFrom = group.GetIndex();
if (!mod.Groups.Move(idxFrom, groupIdxTo)) if (!mod.Groups.Move(ref idxFrom, ref groupIdxTo))
return; return;
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport); saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);

View file

@ -75,7 +75,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo) protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo)
{ {
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false; return false;
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo); group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);

View file

@ -48,7 +48,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS
protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo) protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo)
{ {
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo)) if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false; return false;
group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo); group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo);

View file

@ -1,6 +1,5 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui; using OtterGui;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
@ -20,6 +19,7 @@ using OtterGui.Tasks;
using Penumbra.UI; using Penumbra.UI;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.Interop.Hooks; using Penumbra.Interop.Hooks;
using Penumbra.Interop.Hooks.ResourceLoading; using Penumbra.Interop.Hooks.ResourceLoading;
@ -111,7 +111,7 @@ public class Penumbra : IDalamudPlugin
private void SetupApi() private void SetupApi()
{ {
_services.GetService<IpcProviders>(); _services.GetService<IpcProviders>();
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>()!; var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>();
_communicatorService.ChangedItemHover.Subscribe(it => _communicatorService.ChangedItemHover.Subscribe(it =>
{ {
if (it is IdentifiedItem { Item.Id.IsItem: true }) if (it is IdentifiedItem { Item.Id.IsItem: true })

View file

@ -4,7 +4,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.Sheets;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
@ -16,7 +16,7 @@ namespace Penumbra.Services;
public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager) public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager)
: OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService : OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService
{ {
public void LinkItem(Item item) public void LinkItem(in Item item)
{ {
// @formatter:off // @formatter:off
var payloadList = new List<Payload> var payloadList = new List<Payload>
@ -29,7 +29,7 @@ public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INoti
new TextPayload($"{(char)SeIconChar.LinkMarker}"), new TextPayload($"{(char)SeIconChar.LinkMarker}"),
new UIForegroundPayload(0), new UIForegroundPayload(0),
new UIGlowPayload(0), new UIGlowPayload(0),
new TextPayload(item.Name), new TextPayload(item.Name.ExtractText()),
new RawPayload([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]), new RawPayload([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]),
new RawPayload([0x02, 0x13, 0x02, 0xEC, 0x03]), new RawPayload([0x02, 0x13, 0x02, 0xEC, 0x03]),
}; };

View file

@ -54,6 +54,9 @@ public class Diagnostics(ServiceManager provider) : IUiService
return; return;
using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg); using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg);
if (!table)
return;
foreach (var type in typeof(ActorManager).Assembly.GetTypes() foreach (var type in typeof(ActorManager).Assembly.GetTypes()
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer)))) .Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer))))
{ {

View file

@ -34,30 +34,51 @@ public class HookOverrideDrawer(IDalamudPluginInterface pluginInterface) : IUiSe
Penumbra.Log.Error($"Could not delete hook override file at {path}:\n{ex}"); Penumbra.Log.Error($"Could not delete hook override file at {path}:\n{ex}");
} }
bool? allVisible = null;
ImGui.SameLine();
if (ImUtf8.Button("Disable All Visible Hooks"u8))
allVisible = true;
ImGui.SameLine();
if (ImUtf8.Button("Enable All VisibleHooks"u8))
allVisible = false;
bool? all = null; bool? all = null;
ImGui.SameLine(); ImGui.SameLine();
if (ImUtf8.Button("Disable All Hooks"u8)) if (ImUtf8.Button("Disable All Hooks"))
all = true; all = true;
ImGui.SameLine(); ImGui.SameLine();
if (ImUtf8.Button("Enable All Hooks"u8)) if (ImUtf8.Button("Enable All Hooks"))
all = false; all = false;
foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true })) foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true }))
{ {
using var tree = ImUtf8.TreeNode(propertyField.Name); using var tree = ImUtf8.TreeNode(propertyField.Name);
if (!tree) if (!tree)
continue; {
if (all.HasValue)
{
var property = propertyField.GetValue(_overrides);
foreach (var valueField in propertyField.FieldType.GetFields())
{
valueField.SetValue(property, all.Value);
propertyField.SetValue(_overrides, property);
}
}
}
else
{
allVisible ??= all;
var property = propertyField.GetValue(_overrides); var property = propertyField.GetValue(_overrides);
foreach (var valueField in propertyField.FieldType.GetFields()) foreach (var valueField in propertyField.FieldType.GetFields())
{ {
var value = valueField.GetValue(property) as bool? ?? false; var value = valueField.GetValue(property) as bool? ?? false;
if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || all.HasValue) if (ImUtf8.Checkbox($"Disable {valueField.Name}", ref value) || allVisible.HasValue)
{ {
valueField.SetValue(property, all ?? value); valueField.SetValue(property, allVisible ?? value);
propertyField.SetValue(_overrides, property); propertyField.SetValue(_overrides, property);
} }
} }
} }
} }
}
} }