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)
{
// 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)
return Task.Result.Original(a1, unused, timeOffset);
var someIntermediate = *(nint*)(a1 + 0x1F8);
var flags = someIntermediate == nint.Zero ? (ushort)0 : *(ushort*)(someIntermediate + 0x49C);
if (((flags >> 13) & 1) == 0)
var someIntermediate = *(nint*)(a1 + VolatileOffsets.ApricotListenerSoundPlayCaller.SomeIntermediate);
var flags = someIntermediate == nint.Zero
? (ushort)0
: *(ushort*)(someIntermediate + VolatileOffsets.ApricotListenerSoundPlayCaller.Flags);
if (((flags >> VolatileOffsets.ApricotListenerSoundPlayCaller.BitShift) & 1) == 0)
return Task.Result.Original(a1, unused, timeOffset);
Penumbra.Log.Excessive(
$"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}.");
// 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)
return Task.Result.Original(a1, unused, timeOffset);
// In some cases we can obtain the associated caster via vfunc 1.
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)
{
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)
{
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)
{
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 OtterGui.Services;
using Penumbra.Interop.PathResolving;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
namespace Penumbra.Interop.Hooks.Meta;
@ -14,18 +14,19 @@ public sealed unsafe class CalculateHeight : FastHook<CalculateHeight.Delegate>
{
_collectionResolver = collectionResolver;
_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)]
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);
var ret = Task.Result.Original.Invoke(character);
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}.");
var ret = Task.Result.Original.Invoke(container);
Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)container:X} -> {ret}.");
_metaState.RspCollection.Pop();
return ret;
}

View file

@ -25,7 +25,7 @@ public sealed unsafe class UpdateModel : FastHook<UpdateModel.Delegate>
{
// Shortcut because this is called all the time.
// 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;
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,
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)
{
_modelRendererUnkFuncHook.Original(modelRenderer, unkPayload, unk2, unk3, unk4, unk5);

View file

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

View file

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

View file

@ -43,7 +43,7 @@ public unsafe class FontReloader : IService
return;
_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.
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)
=> ((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.
// 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)
{
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false;
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);

View file

@ -90,7 +90,7 @@ public class ModGroupEditor(
{
var mod = group.Mod;
var idxFrom = group.GetIndex();
if (!mod.Groups.Move(idxFrom, groupIdxTo))
if (!mod.Groups.Move(ref idxFrom, ref groupIdxTo))
return;
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)
{
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false;
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)
{
if (!group.OptionData.Move(optionIdxFrom, optionIdxTo))
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
return false;
group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo);

View file

@ -1,6 +1,5 @@
using Dalamud.Plugin;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui;
using OtterGui.Log;
using OtterGui.Services;
@ -20,6 +19,7 @@ using OtterGui.Tasks;
using Penumbra.UI;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using Penumbra.GameData.Data;
using Penumbra.Interop.Hooks;
using Penumbra.Interop.Hooks.ResourceLoading;
@ -111,7 +111,7 @@ public class Penumbra : IDalamudPlugin
private void SetupApi()
{
_services.GetService<IpcProviders>();
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>()!;
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>();
_communicatorService.ChangedItemHover.Subscribe(it =>
{
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.ImGuiNotification;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
using OtterGui.Log;
using OtterGui.Services;
using Penumbra.Mods.Manager;
@ -16,7 +16,7 @@ namespace Penumbra.Services;
public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager)
: OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService
{
public void LinkItem(Item item)
public void LinkItem(in Item item)
{
// @formatter:off
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 UIForegroundPayload(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, 0x13, 0x02, 0xEC, 0x03]),
};

View file

@ -54,6 +54,9 @@ public class Diagnostics(ServiceManager provider) : IUiService
return;
using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg);
if (!table)
return;
foreach (var type in typeof(ActorManager).Assembly.GetTypes()
.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}");
}
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;
ImGui.SameLine();
if (ImUtf8.Button("Disable All Hooks"u8))
if (ImUtf8.Button("Disable All Hooks"))
all = true;
ImGui.SameLine();
if (ImUtf8.Button("Enable All Hooks"u8))
if (ImUtf8.Button("Enable All Hooks"))
all = false;
foreach (var propertyField in typeof(HookOverrides).GetFields().Where(f => f is { IsStatic: false, FieldType.IsValueType: true }))
{
using var tree = ImUtf8.TreeNode(propertyField.Name);
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);
foreach (var valueField in propertyField.FieldType.GetFields())
{
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);
}
}
}
}
}
}