HUDManager/HudSwap/HUD.cs
Anna d1b122576d fix: back up the hotswap slot
The game checks if the slot being switched to is already selected and
does nothing if so. (For what it's worth, I did try changing the code
in memory to ignore these checks, but it didn't seem to work.) In
order to get the game to change slot, we write a different slot number
to the "current" slot in memory and then ask the game to change slot.

This doesn't swap the slot on screen and still works, which is
ideal. However, the game seems to overwrite the slot that is marked as
current when we do this. To remedy this issue, back up the slot before
telling the game to swap, then restore the slot afterwards.
2020-07-30 13:21:08 -04:00

95 lines
3.7 KiB
C#

using Dalamud.Plugin;
using System;
using System.Runtime.InteropServices;
namespace HudSwap {
public class HUD {
private const int LAYOUT_SIZE = 0xb40;
private delegate IntPtr GetFilePointerDelegate(byte index);
private delegate uint SetHudLayoutDelegate(IntPtr filePtr, uint hudLayout, byte unk0, byte unk1);
private GetFilePointerDelegate _getFilePointer;
private SetHudLayoutDelegate _setHudLayout;
private readonly DalamudPluginInterface pi;
public HUD(DalamudPluginInterface pi) {
this.pi = pi;
IntPtr getFilePointerPtr = this.pi.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 85 C0 74 14 83 7B 44 00");
IntPtr setHudLayoutPtr = this.pi.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 33 C0 EB 15");
if (getFilePointerPtr != IntPtr.Zero) {
this._getFilePointer = Marshal.GetDelegateForFunctionPointer<GetFilePointerDelegate>(getFilePointerPtr);
}
if (setHudLayoutPtr != IntPtr.Zero) {
this._setHudLayout = Marshal.GetDelegateForFunctionPointer<SetHudLayoutDelegate>(setHudLayoutPtr);
}
}
private IntPtr GetFilePointer(byte index) {
return this._getFilePointer.Invoke(index);
}
public uint SelectSlot(HudSlot slot, bool force = false) {
IntPtr file = this.GetFilePointer(0);
// change the current slot so the game lets us pick one that's currently in use
if (force) {
IntPtr currentSlotPtr = this.GetDataPointer() + 0x5958;
// read the current slot
uint currentSlot = (uint)Marshal.ReadInt32(currentSlotPtr);
// change it to a different slot
if (currentSlot == (uint)slot) {
if (currentSlot < 3) {
currentSlot += 1;
} else {
currentSlot = 0;
}
// back up this different slot
byte[] backup = this.ReadLayout((HudSlot)currentSlot);
// change the current slot in memory
Marshal.WriteInt32(currentSlotPtr, (int)currentSlot);
// ask the game to change slot to our desired slot
// for some reason, this overwrites the current slot, so this is why we back up
uint res = this._setHudLayout.Invoke(file, (uint)slot, 0, 1);
// restore the backup
this.WriteLayout((HudSlot)currentSlot, backup);
return res;
}
}
return this._setHudLayout.Invoke(file, (uint)slot, 0, 1);
}
private IntPtr GetDataPointer() {
IntPtr dataPtr = this.GetFilePointer(0) + 0x50;
return Marshal.ReadIntPtr(dataPtr);
}
private IntPtr GetLayoutPointer(HudSlot slot) {
int slotNum = (int)slot;
return this.GetDataPointer() + 0x2c58 + (slotNum * LAYOUT_SIZE);
}
public byte[] ReadLayout(HudSlot slot) {
IntPtr slotPtr = this.GetLayoutPointer(slot);
byte[] bytes = new byte[LAYOUT_SIZE];
Marshal.Copy(slotPtr, bytes, 0, LAYOUT_SIZE);
return bytes;
}
public void WriteLayout(HudSlot slot, byte[] layout) {
if (layout.Length != LAYOUT_SIZE) {
throw new ArgumentException($"layout must be {LAYOUT_SIZE} bytes", nameof(layout));
}
IntPtr slotPtr = this.GetLayoutPointer(slot);
Marshal.Copy(layout, 0, slotPtr, LAYOUT_SIZE);
}
}
public enum HudSlot {
One = 0,
Two = 1,
Three = 2,
Four = 3,
}
}