mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Initial icon replacement commit
This commit is contained in:
parent
5c7c6fc7d6
commit
84c66ddc6e
5 changed files with 549 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ using Dalamud.Game.ClientState.Actors.Types;
|
|||
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Game.Internal.Gui;
|
||||
using Dalamud.Game.Network;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Settings;
|
||||
|
|
@ -39,6 +40,10 @@ namespace Dalamud {
|
|||
|
||||
public readonly DalamudStartInfo StartInfo;
|
||||
|
||||
public readonly IconReplacer IconReplacer;
|
||||
|
||||
public readonly IconReplaceChecker IconReplaceChecker;
|
||||
|
||||
public Dalamud(DalamudStartInfo info) {
|
||||
this.StartInfo = info;
|
||||
|
||||
|
|
@ -66,6 +71,10 @@ namespace Dalamud {
|
|||
|
||||
this.PluginManager = new PluginManager(this, info.PluginDirectory, info.DefaultPluginDirectory);
|
||||
|
||||
this.IconReplaceChecker = new IconReplaceChecker(this.targetModule, this.sigScanner);
|
||||
|
||||
this.IconReplacer = new IconReplacer(this, this.targetModule, this.sigScanner);
|
||||
|
||||
try {
|
||||
this.PluginManager.LoadPlugins();
|
||||
} catch (Exception ex) {
|
||||
|
|
@ -79,6 +88,10 @@ namespace Dalamud {
|
|||
Framework.Enable();
|
||||
|
||||
this.BotManager.Start();
|
||||
|
||||
this.IconReplaceChecker.Enable();
|
||||
|
||||
this.IconReplacer.Enable();
|
||||
}
|
||||
|
||||
public void Unload() {
|
||||
|
|
@ -95,6 +108,10 @@ namespace Dalamud {
|
|||
this.BotManager.Dispose();
|
||||
|
||||
this.unloadSignal.Dispose();
|
||||
|
||||
this.IconReplaceChecker.Dispose();
|
||||
|
||||
this.IconReplacer.Dispose();
|
||||
}
|
||||
|
||||
private void SetupCommands() {
|
||||
|
|
|
|||
38
Dalamud/Game/Internal/Gui/IconReplaceChecker.cs
Normal file
38
Dalamud/Game/Internal/Gui/IconReplaceChecker.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Dalamud.Hooking;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui {
|
||||
public class IconReplaceChecker {
|
||||
private IconReplaceCheckerAddressResolver address;
|
||||
private Hook<OnCheckDetour> checkerHook;
|
||||
|
||||
public IconReplaceChecker(ProcessModule module, SigScanner scanner) {
|
||||
this.address = new IconReplaceCheckerAddressResolver();
|
||||
this.address.Setup(scanner);
|
||||
hookChecker();
|
||||
}
|
||||
|
||||
private void hookChecker() {
|
||||
this.checkerHook = new Hook<OnCheckDetour>(this.address.BaseAddress, (Delegate)new OnCheckDetour(this.HandleChecker), (object)this);
|
||||
}
|
||||
|
||||
public void Enable() {
|
||||
this.checkerHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.checkerHook.Dispose();
|
||||
}
|
||||
|
||||
// I hate this function. This is the dumbest function to exist in the game. Just return 1.
|
||||
// Determines which abilities are allowed to have their icons updated.
|
||||
private ulong HandleChecker(int actionID) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private delegate ulong OnCheckDetour(int actionID);
|
||||
|
||||
public delegate ulong OnCheckDelegate(int actionID);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui {
|
||||
class IconReplaceCheckerAddressResolver : BaseAddressResolver {
|
||||
public IntPtr BaseAddress { get; private set; }
|
||||
protected bool IsResolved { get; set; }
|
||||
|
||||
protected override void Setup64Bit(SigScanner sig)
|
||||
{
|
||||
this.BaseAddress = sig.ScanText("81 f9 d4 08 00 00 7f 33 0f 84 fa 01 00 00 83 c1 eb 81 f9 a3 00 00 00");
|
||||
}
|
||||
}
|
||||
}
|
||||
461
Dalamud/Game/Internal/Gui/IconReplacer.cs
Normal file
461
Dalamud/Game/Internal/Gui/IconReplacer.cs
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Hooking;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui {
|
||||
public class IconReplacer {
|
||||
private IconReplacerAddressResolver address;
|
||||
private Hook<OnIconDetour> iconHook;
|
||||
private IntPtr comboTimer;
|
||||
private IntPtr lastComboMove;
|
||||
private IntPtr activeBuffArray = IntPtr.Zero;
|
||||
private IntPtr jobInfo;
|
||||
private IntPtr byteBase;
|
||||
private Dalamud dalamud;
|
||||
private PlayerCharacter localCharacter = null;
|
||||
|
||||
public unsafe IconReplacer(Dalamud dalamud, ProcessModule module, SigScanner scanner) {
|
||||
this.dalamud = dalamud;
|
||||
this.address = new IconReplacerAddressResolver();
|
||||
this.address.Setup(scanner);
|
||||
|
||||
this.byteBase = scanner.Module.BaseAddress;
|
||||
this.jobInfo = byteBase + 0x1b2d4b4;
|
||||
this.comboTimer = byteBase + 0x1AE1B10;
|
||||
this.lastComboMove = byteBase + 0x1AE1B14;
|
||||
|
||||
this.iconHook = new Hook<OnIconDetour>(this.address.BaseAddress, (Delegate)new OnIconDetour(this.HandleIconUpdate), (object)this);
|
||||
|
||||
}
|
||||
|
||||
public void Enable() {
|
||||
this.iconHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.iconHook.Dispose();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replace an ability with another ability
|
||||
/// actionID is the original ability to be "used"
|
||||
/// Return either actionID (itself) or a new Action table ID as the
|
||||
/// ability to take its place.
|
||||
/// I tend to make the "combo chain" button be the last move in the combo
|
||||
/// For example, Souleater combo on DRK happens by dragging Souleater
|
||||
/// onto your bar and mashing it.
|
||||
/// </summary>
|
||||
private unsafe ulong HandleIconUpdate(byte self, uint actionID) {
|
||||
|
||||
// TODO: BRD, RDM, level checking for everything.
|
||||
|
||||
// Check if player is loaded in by trying to get their buffs.
|
||||
// If not, skip everything until we are (game will crash cause I'm lazy).
|
||||
if (activeBuffArray == IntPtr.Zero) {
|
||||
try {
|
||||
activeBuffArray = FindBuffAddress();
|
||||
localCharacter = dalamud.ClientState.LocalPlayer;
|
||||
}
|
||||
catch (Exception e) {
|
||||
activeBuffArray = IntPtr.Zero;
|
||||
return this.iconHook.Original(self, actionID);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't clutter the spaghetti any worse than it already is.
|
||||
int lastMove = Marshal.ReadInt32(lastComboMove);
|
||||
float comboTime = (float)Marshal.ReadInt32(comboTimer);
|
||||
byte level = localCharacter.Level;
|
||||
|
||||
// DRAGOON
|
||||
// TODO: Jump/High Jump into Mirage Dive
|
||||
|
||||
// Replace Coerthan Torment with Coerthan Torment combo chain
|
||||
if (actionID == 16477) {
|
||||
if (comboTime > 0) {
|
||||
if (Marshal.ReadInt32(lastComboMove) == 86) return 7397;
|
||||
if (Marshal.ReadInt32(lastComboMove) == 7397) return 16477;
|
||||
}
|
||||
return 86;
|
||||
}
|
||||
|
||||
// Replace Chaos Thrust with the Chaos Thrust combo chain
|
||||
if (actionID == 88) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 75 || lastMove == 16479) return 87;
|
||||
if (lastMove == 87) return 88;
|
||||
|
||||
}
|
||||
if (activeBuffArray != IntPtr.Zero) {
|
||||
if (SearchBuffArray(802)) return 3554;
|
||||
if (SearchBuffArray(803)) return 3556;
|
||||
if (SearchBuffArray(1863)) return 16479;
|
||||
}
|
||||
return 75;
|
||||
}
|
||||
|
||||
// Replace Full Thrust with the Full Thrust combo chain
|
||||
if (actionID == 84) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 75 || lastMove == 16479) return 78;
|
||||
if (lastMove == 78) return 84;
|
||||
|
||||
}
|
||||
if (activeBuffArray != IntPtr.Zero) {
|
||||
if (SearchBuffArray(802)) return 3554;
|
||||
if (SearchBuffArray(803)) return 3556;
|
||||
if (SearchBuffArray(1863)) return 16479;
|
||||
}
|
||||
return 75;
|
||||
}
|
||||
|
||||
// DARK KNIGHT
|
||||
|
||||
// Replace Souleater with Souleater combo chain
|
||||
if (actionID == 3632) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 3617) return 3623;
|
||||
if (lastMove == 3623) return 3632;
|
||||
}
|
||||
return 3617;
|
||||
}
|
||||
|
||||
// Replace Stalwart Soul with Stalwart Soul combo chain
|
||||
if (actionID == 16468) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 3621) return 16468;
|
||||
}
|
||||
return 3621;
|
||||
}
|
||||
|
||||
// PALADIN
|
||||
|
||||
// Replace Goring Blade with Goring Blade combo
|
||||
if (actionID == 3538) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 9) return 15;
|
||||
if (lastMove == 15) return 3538;
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
// Replace Royal Authority with Royal Authority combo
|
||||
if (actionID == 3539) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 9) return 15;
|
||||
if (lastMove == 15) return 3539;
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
// Replace Prominence with Prominence combo
|
||||
if (actionID == 16457) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7381) return 16457;
|
||||
}
|
||||
return 7381;
|
||||
}
|
||||
|
||||
// WARRIOR
|
||||
|
||||
// Replace Storm's Path with Storm's Path combo
|
||||
if (actionID == 42) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 31) return 37;
|
||||
if (lastMove == 37) return 42;
|
||||
}
|
||||
return 31;
|
||||
}
|
||||
|
||||
// Replace Storm's Eye with Storm's Eye combo
|
||||
if (actionID == 45) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 31) return 37;
|
||||
if (lastMove == 37) return 45;
|
||||
}
|
||||
return 31;
|
||||
}
|
||||
|
||||
// Replace Mythril Tempest with Mythril Tempest combo
|
||||
if (actionID == 16462) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 41) return 16462;
|
||||
}
|
||||
return 41;
|
||||
}
|
||||
|
||||
// SAMURAI
|
||||
|
||||
// Replace Yukikaze with Yukikaze combo
|
||||
if (actionID == 7480) {
|
||||
if (activeBuffArray != IntPtr.Zero) {
|
||||
if (SearchBuffArray(1233)) return 7480;
|
||||
}
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7477) return 7480;
|
||||
}
|
||||
return 7477;
|
||||
}
|
||||
|
||||
// Replace Gekko with Gekko combo
|
||||
if (actionID == 7481) {
|
||||
if (activeBuffArray != IntPtr.Zero) {
|
||||
if (SearchBuffArray(1233)) return 7481;
|
||||
}
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7477) return 7478;
|
||||
if (lastMove == 7478) return 7481;
|
||||
}
|
||||
return 7477;
|
||||
}
|
||||
|
||||
// Replace Kasha with Kasha combo
|
||||
if (actionID == 7482) {
|
||||
if (activeBuffArray != null) {
|
||||
if (SearchBuffArray(1233)) return 7482;
|
||||
}
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7477) return 7479;
|
||||
if (lastMove == 7479) return 7482;
|
||||
}
|
||||
return 7477;
|
||||
}
|
||||
|
||||
// Replace Mangetsu with Mangetsu combo
|
||||
if (actionID == 7484) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7483) return 7484;
|
||||
}
|
||||
return 7483;
|
||||
}
|
||||
|
||||
// Replace Yukikaze with Yukikaze combo
|
||||
if (actionID == 7485) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 7483) return 7485;
|
||||
}
|
||||
return 7483;
|
||||
}
|
||||
|
||||
// NINJA
|
||||
|
||||
// Replace Shadow Fang with Shadow Fang combo
|
||||
if (actionID == 2257) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 2240) return 2257;
|
||||
}
|
||||
return 2240;
|
||||
}
|
||||
|
||||
// Replace Armor Crush with Armor Crush combo
|
||||
if (actionID == 2257) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 2240) return 2242;
|
||||
if (lastMove == 2242) return 3563;
|
||||
}
|
||||
return 2240;
|
||||
}
|
||||
|
||||
// Replace Aeolian Edge with Aeolian Edge combo
|
||||
if (actionID == 2257) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 2240) return 2242;
|
||||
if (lastMove == 2242) return 2255;
|
||||
}
|
||||
return 2240;
|
||||
}
|
||||
|
||||
// Replace Hakke Mujinsatsu with Hakke Mujinsatsu combo
|
||||
if (actionID == 16488) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 2254) return 16488;
|
||||
}
|
||||
return 2254;
|
||||
}
|
||||
|
||||
// GUNBREAKER
|
||||
|
||||
// Replace Solid Barrel with Solid Barrel combo
|
||||
if (actionID == 16145) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 16137) return 16139;
|
||||
if (lastMove == 16139) return 16145;
|
||||
}
|
||||
return 16137;
|
||||
}
|
||||
|
||||
// Replace Gnashing Fang with Gnashing Fang combo
|
||||
// TODO: Potentially add Contuation moves as well?
|
||||
if (actionID == 16146) {
|
||||
byte ammoComboState = Marshal.ReadByte(jobInfo, 0x10);
|
||||
if (ammoComboState == 1) return 16147;
|
||||
if (ammoComboState == 2) return 16150;
|
||||
return 16146;
|
||||
}
|
||||
|
||||
// Replace Demon Slaughter with Demon Slaughter combo
|
||||
if (actionID == 16149) {
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 16141) return 16149;
|
||||
}
|
||||
return 16141;
|
||||
}
|
||||
|
||||
// MACHINIST
|
||||
|
||||
// Replace Heated Clean Shot with Heated Clean Shot combo
|
||||
// Or with Heat Blast when overheated.
|
||||
// For some reason the shots use their unheated IDs as combo moves
|
||||
if (actionID == 7413) {
|
||||
if (Marshal.ReadInt16(jobInfo, 0xc) > 0) return 7410;
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 2866) return 7412;
|
||||
if (lastMove == 2868) return 7413;
|
||||
}
|
||||
return 7411;
|
||||
}
|
||||
|
||||
// Replace Spread Shot with Auto Crossbow when overheated.
|
||||
if (actionID == 2870) {
|
||||
if (Marshal.ReadInt16(jobInfo, 0xc) > 0) return 16497;
|
||||
return 2870;
|
||||
}
|
||||
|
||||
// BLACK MAGE
|
||||
|
||||
// Enochian changes to B4 or F4 depending on stance.
|
||||
if (actionID == 3575) {
|
||||
if (Marshal.ReadByte(jobInfo, 0x13) == 1) {
|
||||
if (Marshal.ReadByte(jobInfo, 0x10) > 3) return 3576;
|
||||
if (Marshal.ReadByte(jobInfo, 0x10) > 0) return 3577;
|
||||
}
|
||||
return 3575;
|
||||
}
|
||||
|
||||
// Umbral Soul and Transpose
|
||||
if (actionID == 16506) {
|
||||
if (Marshal.ReadByte(jobInfo, 0x10) > 3) return 16506;
|
||||
return 149;
|
||||
}
|
||||
|
||||
// ASTROLOGIAN
|
||||
|
||||
// Make cards on the same button as draw
|
||||
if (actionID == 17055) {
|
||||
byte x = Marshal.ReadByte(jobInfo, 0x10);
|
||||
switch (x) {
|
||||
case 1:
|
||||
return 4401;
|
||||
case 2:
|
||||
return 4404;
|
||||
case 3:
|
||||
return 4402;
|
||||
case 4:
|
||||
return 4403;
|
||||
case 5:
|
||||
return 4405;
|
||||
case 6:
|
||||
return 4406;
|
||||
case 0x70:
|
||||
return 7444;
|
||||
case 0x80:
|
||||
return 7445;
|
||||
default:
|
||||
return 3590;
|
||||
}
|
||||
}
|
||||
|
||||
// SUMMONER
|
||||
|
||||
// DWT changes.
|
||||
// Now contains DWT, Deathflare, Summon Bahamut, Enkindle Bahamut, FBT, and Enkindle Phoenix.
|
||||
// What a monster of a button.
|
||||
if (actionID == 3581) {
|
||||
byte stackState = Marshal.ReadByte(jobInfo, 0x10);
|
||||
if (Marshal.ReadInt16(jobInfo, 0xc) > 0) {
|
||||
if (Marshal.ReadInt16(jobInfo, 0xe) > 0) {
|
||||
if (stackState > 0) return 16516;
|
||||
return 7429;
|
||||
}
|
||||
return 3582;
|
||||
}
|
||||
else {
|
||||
if (stackState == 0) return 3581;
|
||||
if (stackState == 8) return 7427;
|
||||
if (stackState == 0x10) return 16513;
|
||||
return 3581;
|
||||
}
|
||||
}
|
||||
|
||||
// SCHOLAR
|
||||
|
||||
// Change Fey Blessing into Consolation when Seraph is out.
|
||||
if (actionID == 16543) {
|
||||
if (Marshal.ReadInt16(jobInfo, 0x10) > 0) return 16546;
|
||||
return 16543;
|
||||
}
|
||||
|
||||
// DANCER
|
||||
// TODO: Single-target. This needs to be done alongside 1-button dances.
|
||||
|
||||
// Handle AoE GCDs on one button. Procs take priority over combo.
|
||||
|
||||
if (actionID == 15994) {
|
||||
if (activeBuffArray != null) {
|
||||
if (SearchBuffArray(1816)) return 15995;
|
||||
if (SearchBuffArray(1817)) return 15996;
|
||||
}
|
||||
if (comboTime > 0) {
|
||||
if (lastMove == 15993) return 15994;
|
||||
}
|
||||
return 15993;
|
||||
}
|
||||
|
||||
// Fan Dance changes into Fan Dance 3 while flourishing.
|
||||
if (actionID == 16007) {
|
||||
if (activeBuffArray != null) {
|
||||
if (SearchBuffArray(1820)) return 16009;
|
||||
}
|
||||
|
||||
return 16007;
|
||||
}
|
||||
|
||||
// Fan Dance 2 changes into Fan Dance 3 while flourishing.
|
||||
if (actionID == 16008) {
|
||||
if (activeBuffArray != null) {
|
||||
if (SearchBuffArray(1820)) return 16009;
|
||||
}
|
||||
return 16008;
|
||||
}
|
||||
|
||||
|
||||
return this.iconHook.Original(self, actionID);
|
||||
}
|
||||
|
||||
private unsafe bool SearchBuffArray(short needle) {
|
||||
for (int i = 0; i < 60; i++) {
|
||||
if (Marshal.ReadInt16(activeBuffArray + 4 * i) == needle) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private delegate ulong OnIconDetour(byte param1, uint param2);
|
||||
|
||||
public delegate ulong OnIconDelegate(byte param1, uint param2);
|
||||
|
||||
private unsafe delegate int* getArray(long* address);
|
||||
|
||||
private unsafe IntPtr FindBuffAddress() {
|
||||
IntPtr randomAddress = byteBase + 0x1b2c970;
|
||||
IntPtr num = Marshal.ReadIntPtr(randomAddress);
|
||||
IntPtr step2 = (IntPtr)(Marshal.ReadInt64(num) + 0x248);
|
||||
IntPtr step3 = Marshal.ReadIntPtr(step2);
|
||||
var callback = Marshal.GetDelegateForFunctionPointer<getArray>(step3);
|
||||
return (IntPtr)callback((long*)num);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Dalamud/Game/Internal/Gui/IconReplacerAddressResolver.cs
Normal file
16
Dalamud/Game/Internal/Gui/IconReplacerAddressResolver.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Internal.Gui {
|
||||
class IconReplacerAddressResolver : BaseAddressResolver {
|
||||
public IntPtr BaseAddress { get; private set; }
|
||||
protected bool IsResolved { get; set; }
|
||||
|
||||
protected override void Setup64Bit(SigScanner sig) {
|
||||
this.BaseAddress = sig.ScanText("81 fa d4 08 00 00 7f 4b 74 44 8d 42 eb 3d a3 00 00 00");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue