diff --git a/.gitignore b/.gitignore index c2766a8..9c2a435 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,18 @@ !/EEex/copy/*.* !/EEex/loader !/EEex/loader/*.* +!/EEex/loader/Lua52 +!/EEex/loader/Lua52/*.* +!/EEex/loader/LuaJIT +!/EEex/loader/LuaJIT/*.* !/EEex/patch !/EEex/patch/*.* !/pdb !/pdb/*.* +!/pdb/Lua52 +!/pdb/Lua52/*.* +!/pdb/LuaJIT +!/pdb/LuaJIT/*.* !/style !/style/*.* !/.gitattributes diff --git a/EEex/EEex.tp2 b/EEex/EEex.tp2 index 4852cc0..579e571 100644 --- a/EEex/EEex.tp2 +++ b/EEex/EEex.tp2 @@ -3,11 +3,11 @@ AUTHOR ~Bubb~ VERSION ~master~ README ~EEex/readme-EEex.html~ -BEGIN ~EEex~ +BEGIN ~EEex~ LABEL ~B3-EEex-Main~ REQUIRE_PREDICATE (GAME_IS ~bgee bg2ee eet iwdee~ AND FILE_EXISTS ~data/PATCH26.BIF~) ~Game not supported.~ ACTION_FOR_EACH file IN ACTION OBJECT TRIGGER BEGIN - COPY ~EEex\patch\%file%.IDS~ ~EEex\patch~ + COPY ~EEex/patch/%file%.IDS~ ~EEex/patch~ COUNT_2DA_ROWS 2 rows FOR (i = 0; i <= rows; ++i) BEGIN READ_2DA_ENTRY i 0 2 "col1" @@ -44,11 +44,23 @@ REQUIRE_PREDICATE (GAME_IS ~bgee bg2ee eet iwdee~ AND FILE_EXISTS ~data/PATCH26. APPEND ~%data%.IDS~ ~%data_1% %data_2%~ UNLESS ~^[ %TAB%]*%data_1_escaped%[ %TAB%]+%data_2_escaped%[ %TAB%]*%MNL%*$~ END - COPY ~EEex/loader~ ~.~ IF_EXISTS - COPY ~EEex/copy~ ~override~ IF_EXISTS + COPY ~EEex/loader~ ~.~ + COPY ~EEex/loader/Lua52~ ~.~ + COPY ~EEex/copy~ ~override~ -BEGIN ~Enable effect menu module - LShift-on-hover to view spells affecting creature~ +BEGIN ~Experimental - Use LuaJIT (can help stuttering)~ LABEL ~B3-EEex-LuaJIT~ +REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to be installed.~ + + COPY ~InfinityLoader.ini~ ~.~ + REPLACE_TEXTUALLY ~^\(LuaLibrary=\).*~ ~\1lua51.dll~ + REPLACE_TEXTUALLY ~^\(LuaPatchMode=\).*~ ~\1REPLACE_INTERNAL_WITH_EXTERNAL~ + BUT_ONLY + + COPY ~EEex/loader/LuaJIT~ ~.~ + + +BEGIN ~Enable effect menu module - LShift-on-hover to view spells affecting creature~ LABEL ~B3-EEex-Module-Effect-Menu~ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to be installed.~ COPY ~override/EEex_Modules.lua~ ~override~ @@ -56,7 +68,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to BUT_ONLY -BEGIN ~Enable timer module - Visual indicators for modal actions, contingencies, and spell/item cooldowns~ +BEGIN ~Enable timer module - Visual indicators for modal actions, contingencies, and spell/item cooldowns~ LABEL ~B3-EEex-Module-Timer-Main~ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to be installed.~ COPY ~override/EEex_Modules.lua~ ~override~ @@ -64,7 +76,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to BUT_ONLY -BEGIN ~Timer module - Show modal actions (red bar)~ +BEGIN ~Timer module - Show modal actions (red bar)~ LABEL ~B3-EEex-Module-Timer-Modal~ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer module" component to be installed.~ COPY ~override/B3Timer.lua~ ~override~ @@ -72,7 +84,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer modu BUT_ONLY -BEGIN ~Timer module - Show contingencies (green bar)~ +BEGIN ~Timer module - Show contingencies (green bar)~ LABEL ~B3-EEex-Module-Timer-Contingency~ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer module" component to be installed.~ COPY ~override/B3Timer.lua~ ~override~ @@ -80,7 +92,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer modu BUT_ONLY -BEGIN ~Timer module - Show spell/item cooldowns (cyan bar)~ +BEGIN ~Timer module - Show spell/item cooldowns (cyan bar)~ LABEL ~B3-EEex-Module-Timer-Aura~ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer module" component to be installed.~ COPY ~override/B3Timer.lua~ ~override~ @@ -88,7 +100,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~2~ ~This component requires the "Enable timer modu BUT_ONLY -BEGIN ~Time step module - Advance 1 game tick on keypress~ +BEGIN ~Time step module - Advance 1 game tick on keypress~ LABEL ~B3-EEex-Module-Time-Step~ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to be installed.~ COPY ~override/EEex_Modules.lua~ ~override~ @@ -96,7 +108,7 @@ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to BUT_ONLY -BEGIN ~Hotkey module - Edit override/B3Hotkey.lua to create advanced spell hotkeys~ +BEGIN ~Hotkey module - Edit override/B3Hotkey.lua to create advanced spell hotkeys~ LABEL ~B3-EEex-Module-Hotkey~ REQUIRE_COMPONENT ~EEex.tp2~ ~0~ ~This component requires the main component to be installed.~ COPY ~override/EEex_Modules.lua~ ~override~ diff --git a/EEex/copy/B3Invis.lua b/EEex/copy/B3Invis.lua index b90b8f1..4358d0f 100644 --- a/EEex/copy/B3Invis.lua +++ b/EEex/copy/B3Invis.lua @@ -24,92 +24,178 @@ end EEex_DisableCodeProtection() - for _, address in ipairs({ - EEex_Label("Hook-CGameSprite::IsOver()-B3Invis"), - EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis1") - }) do - - EEex_HookJumpOnFail(address, 7, EEex_FlattenTable({[[ - - #MAKE_SHADOW_SPACE(32) - - ]], EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { + --[[ + +------------------------------------------------------------------------------------------------------+ + | Allow cursor to interact with invisible creatures if a selected creature has op193 | + +------------------------------------------------------------------------------------------------------+ + | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Allow the cursor to interact with the creature regardless of it being invisible | + +------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::IsOver()-B3Invis"), 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + ]]}, + EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + test rax, rax + + #DESTROY_SHADOW_SPACE + jnz #L(jmp_success) + ]]}, + }) + ) + + --[[ + +---------------------------------------------------------------------------------------+ + | Allow invisible creature markers to render if a selected creature has op193 | + +---------------------------------------------------------------------------------------+ + | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the creature's marker to not be hidden due to invisibility | + +---------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis1"), 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r9 + ]]}, + EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error - jmp no_error + call_error: + xor rax, rax - call_error: - xor rax, rax + no_error: + test rax, rax - no_error: - test rax, rax - - #DESTROY_SHADOW_SPACE - jnz #L(jmp_success) - jmp jmp_fail - ]]})) - end + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jnz #L(jmp_success) + ]]}, + }) + ) if B3Invis_RenderAsInvisible then - EEex_HookJumpOnSuccess(EEex_Label("Hook-CGameSprite::Render()-B3Invis"), 6, EEex_FlattenTable({[[ - - #MAKE_SHADOW_SPACE(48) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - - ]], EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ - - jmp no_error - - call_error: - xor rax, rax - - no_error: - test rax, rax - - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jnz jmp_fail - jmp #L(jmp_success) - ]]})) + --[[ + +---------------------------------------------------------------------------------------------------------------------+ + | (Option #1) Render invisible creatures as transparent (like improved invisibility) if a selected creature has op193 | + +---------------------------------------------------------------------------------------------------------------------+ + | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the creature to be rendered as transparent | + +---------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameSprite::Render()-B3Invis"), 6, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + ]]}, + EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + test rax, rax + + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jnz #L(jmp_fail) + ]]}, + }) + ) else - -- Force circle - EEex_HookAfterRestore(EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis2"), 0, 6, 6, EEex_FlattenTable({[[ - - jnz #L(return) - - #MAKE_SHADOW_SPACE(64) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx - - ]], EEex_GenLuaCall("B3Invis_Hook_ForceCircle", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rsi - ]], {rspOffset}}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ - - jmp no_error - - call_error: - xor rax, rax - - no_error: - test rax, rax - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]})) + --[[ + +-------------------------------------------------------------------------------------------------------+ + | (Option #2) Render invisible creatures with an always-visible marker if a selected creature has op193 | + +-------------------------------------------------------------------------------------------------------+ + | [Lua] B3Invis_Hook_ForceCircle(sprite: CGameSprite) -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the creature's marker to render | + +-------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis2"), 0, 6, 6, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + jnz #L(return) + + #MAKE_SHADOW_SPACE(72) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 + ]]}, + EEex_GenLuaCall("B3Invis_Hook_ForceCircle", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + test rax, rax + + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) end EEex_EnableCodeProtection() diff --git a/EEex/copy/B3Scale.lua b/EEex/copy/B3Scale.lua index f56114c..c35e69c 100644 --- a/EEex/copy/B3Scale.lua +++ b/EEex/copy/B3Scale.lua @@ -48,12 +48,23 @@ end EEex_DisableCodeProtection() - EEex_HookAfterCall(EEex_Label("Hook-CChitin::OnResizeWindow()-B3Scale"), EEex_FlattenTable({[[ - #MAKE_SHADOW_SPACE(32) - ]], EEex_GenLuaCall("B3Scale_Hook_DoSizeChange"), [[ - call_error: - #DESTROY_SHADOW_SPACE - ]]})) + --[[ + +---------------------------------------------------+ + | Tweak the UI scale whenever the window is resized | + +---------------------------------------------------+ + | [Lua] B3Scale_Hook_DoSizeChange() | + +---------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CChitin::OnResizeWindow()-B3Scale"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({[[ + #MAKE_SHADOW_SPACE(32) + ]], EEex_GenLuaCall("B3Scale_Hook_DoSizeChange"), [[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Action.lua b/EEex/copy/EEex_Action.lua index eb7ee94..200f355 100644 --- a/EEex/copy/EEex_Action.lua +++ b/EEex/copy/EEex_Action.lua @@ -259,6 +259,7 @@ EEex_Action_Private_Switch = { -- EEex_MatchObject / EEex_MatchObjectEx [473] = function(aiBase, curAction) + -- [EEex.dll] EEex_GetUDAux(aiBase)["EEex_MatchObject"] = EEex.MatchObject(aiBase, curAction.m_string1.m_pchData:get(), curAction.m_specificID, curAction.m_specificID2, curAction.m_specificID3) @@ -311,7 +312,7 @@ function EEex_Action_Hook_OnEvaluatingUnknown(aiBase) return EEex_Action_ReturnType.ACTION_ERROR end -function EEex_Action_Hook_OnAfterSpriteStartedAction(sprite) +function EEex_Action_LuaHook_OnAfterSpriteStartedAction(sprite) local action = sprite.m_curAction @@ -325,12 +326,4 @@ function EEex_Action_Hook_OnAfterSpriteStartedAction(sprite) for _, listener in ipairs(EEex_Action_Private_SpriteStartedActionListeners) do listener(sprite, action) end - - local statsAux = EEex_GetUDAux(sprite:getActiveStats()) - for funcName, effect in pairs(statsAux["EEex_EnabledActionListeners"]) do - if effect.m_effectAmount ~= 0 then - local func = EEex_Action_Private_EnabledSpriteStartedActionListeners[funcName] - EEex_Utility_CallIfExists(func, sprite, action, effect) - end - end end diff --git a/EEex/copy/EEex_Action_Patch.lua b/EEex/copy/EEex_Action_Patch.lua index 2d99802..e081b65 100644 --- a/EEex/copy/EEex_Action_Patch.lua +++ b/EEex/copy/EEex_Action_Patch.lua @@ -3,50 +3,85 @@ EEex_DisableCodeProtection() - ------------------------------------------ - -- EEex_Action_Hook_OnEvaluatingUnknown -- - ------------------------------------------ - - EEex_HookJumpAutoSucceed(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultJmp"), 0, EEex_FlattenTable({[[ - jbe jmp_fail - #MAKE_SHADOW_SPACE(48) - ]], EEex_GenLuaCall("EEex_Action_Hook_OnEvaluatingUnknown", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rbx - ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), [[ - - mov esi, eax - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(Hook-CGameAIBase::ExecuteAction()-NormalBranch) - - call_error: - #RESUME_SHADOW_ENTRY - #DESTROY_SHADOW_SPACE - mov esi, -2 ; ACTION_ERROR - ]]})) - - ------------------------------------------------- - -- EEex_Action_Hook_OnAfterSpriteStartedAction -- - ------------------------------------------------- - - EEex_HookAfterCall(EEex_Label("CGameSprite::SetCurrAction()-LastCall"), EEex_FlattenTable({[[ - - cmp word ptr ds:[r14], 0 ; Don't call the hook for NoAction() since the engine spams it - jz #L(return) - - #MAKE_SHADOW_SPACE(40) - ]], EEex_GenLuaCall("EEex_Action_Hook_OnAfterSpriteStartedAction", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), [[ - call_error: - #DESTROY_SHADOW_SPACE - ]]})) + --[[ + +--------------------------------------------------------------------------------+ + | Implement new actions | + +--------------------------------------------------------------------------------+ + | 472 EEex_LuaAction(S:Chunk*) | + | 473 EEex_MatchObject(S:Chunk*) | + | 473 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) | + | 474 EEex_SetTarget(S:Name*,O:Target*) | + | 476 EEex_SpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | + | 476 EEex_SpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | + | 477 EEex_SpellObjectOffsetNoDec(O:Target*,I:Spell*Spell,P:Offset*) | + | 477 EEex_SpellObjectOffsetNoDecRES(S:RES*,O:Target*,P:Offset*) | + | 478 EEex_ForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | + | 478 EEex_ForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | + | 479 EEex_ReallyForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | + | 479 EEex_ReallyForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | + +--------------------------------------------------------------------------------+ + | [Lua] EEex_Action_Hook_OnEvaluatingUnknown(evaluator: CGameAIBase) -> number | + | return -> Set as internal action return value: | + | -> EEex_Action_ReturnType.ACTION_STOPPED | + | -> EEex_Action_ReturnType.ACTION_ERROR | + | -> EEex_Action_ReturnType.ACTION_DONE | + | -> EEex_Action_ReturnType.ACTION_NORMAL | + | -> EEex_Action_ReturnType.ACTION_INTERRUPTABLE | + | -> EEex_Action_ReturnType.ACTION_NO_ACTION | + +--------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Action_Hook_OnEvaluatingUnknown", { + ["args"] = { + function(rspOffset) return {[[ + mov qword ptr ss:[rsp+#$(1)], rbx + ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Number, + }), + {[[ + mov esi, eax + #DESTROY_SHADOW_SPACE(KEEP_ENTRY) + #MANUAL_HOOK_EXIT(0) + jmp #L(Hook-CGameAIBase::ExecuteAction()-NormalBranch) + + call_error: + #RESUME_SHADOW_ENTRY + #DESTROY_SHADOW_SPACE + ]]} + }) + ) + + --[[ + +----------------------------------------------------------------------------------+ + | Implement "sprite started action" listeners | + +----------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Action_Hook_OnAfterSpriteStartedAction(pSprite: CGameSprite*) | + +----------------------------------------------------------------------------------+ + | [Lua] EEex_Action_LuaHook_OnAfterSpriteStartedAction(sprite: CGameSprite) | + +----------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("CGameSprite::SetCurrAction()-LastCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + {[[ + cmp word ptr ds:[r14], 0 ; Don't call the hook for NoAction() since the engine spams it + jz #L(return) + + mov rcx, rdi ; pSprite + call #L(EEex::Action_Hook_OnAfterSpriteStartedAction) + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Actionbar_Patch.lua b/EEex/copy/EEex_Actionbar_Patch.lua index 6db6a95..2a9f2d0 100644 --- a/EEex/copy/EEex_Actionbar_Patch.lua +++ b/EEex/copy/EEex_Actionbar_Patch.lua @@ -3,133 +3,185 @@ EEex_DisableCodeProtection() - EEex_HookRelativeBranch(EEex_Label("Hook-CInfButtonArray::SetState()-SaveArg"), {[[ - mov dword ptr ds:[rsp+70h], r15d - call #L(original) - jmp #L(return) + --[[ + +--------------------------------------------------------------------------+ + | Implement actionbar listeners | + +--------------------------------------------------------------------------+ + | [Lua] EEex_Actionbar_Hook_StateUpdating(config: number, state: number) | + +--------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeCall(EEex_Label("Hook-CInfButtonArray::SetState()-SaveArg"), {[[ + mov dword ptr ds:[rsp+70h], r15d ; store it in unused spill space ]]}) - - EEex_HookRelativeBranch(EEex_Label("Hook-CInfButtonArray::SetState()-CInfButtonArray::UpdateButtons()"), EEex_FlattenTable({ - {[[ - mov eax, dword ptr ds:[rsp+70h] - dec eax - cmp eax, 71h - ja NoConfig - - mov rdx, #L(Data-CInfButtonArray::SetState()-IndirectJumpTable) - movzx eax, byte ptr ds:[rdx+rax] - jmp CallHook - - NoConfig: - mov rax, -1 - - CallHook: - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Actionbar_Hook_StateUpdating", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax", {rspOffset}, "#ENDL"} end, - function(rspOffset) return {[[ - mov edx, dword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(70h)] - mov qword ptr ss:[rsp+#$(1)], rdx ]], {rspOffset}, [[ #ENDL - ]]} end, - }, - }), + EEex_HookIntegrityWatchdog_IgnoreStackSizes(EEex_Label("Hook-CInfButtonArray::SetState()-SaveArg"), {{0x70, 4}}) + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CInfButtonArray::SetState()-CInfButtonArray::UpdateButtons()"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + mov eax, dword ptr ds:[rsp+70h] + dec eax + cmp eax, 71h + ja NoConfig + + mov rdx, #L(Data-CInfButtonArray::SetState()-IndirectJumpTable) + movzx eax, byte ptr ds:[rdx+rax] + jmp CallHook + + NoConfig: + mov rax, -1 + + CallHook: + #MAKE_SHADOW_SPACE(48) + ]]}, + EEex_GenLuaCall("EEex_Actionbar_Hook_StateUpdating", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax", {rspOffset}, "#ENDL"} end, + function(rspOffset) return {[[ + mov edx, dword ptr ds:[rsp+#LAST_FRAME_TOP(70h)] + mov qword ptr ss:[rsp+#$(1)], rdx ]], {rspOffset}, [[ #ENDL + ]]} end, + }, + }), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + mov rcx, r14 + ]]}, + }) + ) + + --[[ + +--------------------------------------------------------------------------------------------------+ + | Make it possible to grant non-thieves full thieving capabilities | + +--------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Actionbar_Hook_HasFullThieving(sprite: CGameSprite) -> boolean | + | return: | + | -> false - The creature is limited to pickpocketing (cannot pick locks / disarm traps) | + | -> true - The creature can take all thieving actions | + +--------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CInfButtonArray::OnLButtonPressed()-HasFullThieving"), 0, 7, 11, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + ]]}, + EEex_GenLuaCall("EEex_Actionbar_Hook_HasFullThieving", { + ["args"] = { + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(48h)] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + mov rax, 1 + + no_error: + mov dl, 24h + test rax, rax + jnz full_thieving + + mov dl, 28h + + full_thieving: + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +---------------------------------------------------------------------------------------------------------+ + | Allow non-party-members with EEex_Actionbar_Hook_HasFullThieving() == true to pick locks / disarm traps | + +---------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Actionbar_Hook_IsPartyLeader(sprite: CGameSprite) -> boolean | + | return: | + | false -> The creature is treated as a non-party-member for certain cursor mechanics | + | true -> The creature is treated as a party member for certain cursor mechanics | + +---------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIGroup::IsPartyLeader()-Override"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + test rax, rax + jnz #L(return) + + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Actionbar_Hook_IsPartyLeader", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +------------------------------------------------------------------------------------------------------------------+ + | Set a Lua global that flags whether the engine has opened the special abilities menu to find the thieving button | + +------------------------------------------------------------------------------------------------------------------+ + | [Lua Global] EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities: boolean | + +------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeAndAfterCallWithLabels(EEex_Label("Hook-CScreenWorld::OnKeyDown()-ThievingHotkeyPressSpecialAbilitiesCall"), { + {"hook_integrity_watchdog_ignore_registers_0", { + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}, + {"hook_integrity_watchdog_ignore_registers_1", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - mov rcx, r14 - call #L(original) - jmp short #L(return) - ]]}, - })) - - EEex_HookAfterRestore(EEex_Label("Hook-CInfButtonArray::OnLButtonPressed()-HasFullThieving"), 0, 7, 11, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(48) + #MAKE_SHADOW_SPACE(16) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - ]]}, - EEex_GenLuaCall("EEex_Actionbar_Hook_HasFullThieving", { - ["args"] = { - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(48h)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - call_error: - mov rax, 1 + mov rdx, 1 + mov rcx, #L(Hardcoded_InternalLuaState) + call #L(Hardcoded_lua_pushboolean) + mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL + mov rcx, #L(Hardcoded_InternalLuaState) + call #L(Hardcoded_lua_setglobal) - no_error: - mov dl, 24h - test rax, rax - jnz full_thieving - - mov dl, 28h - - full_thieving: + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE ]]}, - })) - - EEex_HookAfterCall(EEex_Label("Hook-CAIGroup::IsPartyLeader()-Override"), EEex_FlattenTable({ - {[[ - test rax, rax - jnz #L(return) - - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Actionbar_Hook_IsPartyLeader", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), {[[ - jmp no_error - - call_error: - xor rax, rax - - no_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - EEex_HookRelativeBranch(EEex_Label("Hook-CScreenWorld::OnKeyDown()-ThievingHotkeyPressSpecialAbilitiesCall"), {[[ - - #MAKE_SHADOW_SPACE(16) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - - mov rdx, 1 - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(Hardcoded_lua_pushboolean) - mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(Hardcoded_lua_setglobal) - - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - call #L(original) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - - mov rdx, 0 - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(Hardcoded_lua_pushboolean) - mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(Hardcoded_lua_setglobal) - - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}) + mov rdx, 0 + mov rcx, #L(Hardcoded_InternalLuaState) + call #L(Hardcoded_lua_pushboolean) + mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL + mov rcx, #L(Hardcoded_InternalLuaState) + call #L(Hardcoded_lua_setglobal) + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Assembly.lua b/EEex/copy/EEex_Assembly.lua index 74253fe..3853ce0 100644 --- a/EEex/copy/EEex_Assembly.lua +++ b/EEex/copy/EEex_Assembly.lua @@ -1,7 +1,21 @@ -EEex_OnceTable = {} -EEex_GlobalAssemblyLabels = {} -EEex_CodePageAllocations = {} +-- This file needs to be called by both EEex_EarlyMain.lua and +-- EEex_Main.lua. This guard prevents the file from being +-- needlessly processed twice. +if EEex_Assembly_AlreadyLoaded then + return +end +EEex_Assembly_AlreadyLoaded = true + +-- LuaJIT compatibility +if not table.pack then + table.pack = function(...) + local t = {...} + t.n = #t + return t + end + table.unpack = unpack +end ------------------- -- Debug Utility -- @@ -38,6 +52,8 @@ end -- Random Utility -- -------------------- +EEex_OnceTable = {} + function EEex_Once(key, func) if not EEex_OnceTable[key] then EEex_OnceTable[key] = true @@ -356,10 +372,29 @@ end -- General Assembly Writing -- ------------------------------ +-- This table is stored in the Lua registry. InfinityLoader automatically +-- updates it when new patterns are added by Lua bindings DLLs. +EEex_GlobalAssemblyLabels = EEex_GetPatternMap() + function EEex_DefineAssemblyLabel(label, value) EEex_GlobalAssemblyLabels[label] = value end +function EEex_ClearAssemblyLabel(label) + EEex_GlobalAssemblyLabels[label] = nil +end + +function EEex_RunWithAssemblyLabels(labels, func) + for _, labelPair in ipairs(labels) do + EEex_DefineAssemblyLabel(labelPair[1], labelPair[2]) + end + local retVal = func() + for _, labelPair in ipairs(labels) do + EEex_ClearAssemblyLabel(labelPair[1]) + end + return retVal +end + function EEex_LabelDefault(label, default) return EEex_GlobalAssemblyLabels[label] or default end @@ -367,11 +402,15 @@ end function EEex_Label(label) local value = EEex_GlobalAssemblyLabels[label] if not value then - EEex_Error("Label \""..label.."\" is not defined in the global scope!") + EEex_Error(string.format("Label \"#L(%s)\" not defined", label)) end return EEex_GlobalAssemblyLabels[label] end +function EEex_TryLabel(label) + return EEex_GlobalAssemblyLabels[label] +end + EEex_StringCache = {} function EEex_WriteStringCache(str) @@ -403,45 +442,234 @@ function EEex_StoreBytesAssembly(startAddress, size) return bytes end -function EEex_HookRelativeBranch(address, assemblyT) +function EEex_ReplaceCall(address, newTarget) + local opcode = EEex_ReadU8(address) + if opcode ~= 0xE8 then EEex_Error("Not disp32 call: "..EEex_ToHex(opcode)) end + EEex_JITAt(address, {"call short ", EEex_JITNear({"jmp ", newTarget, "#ENDL"}), "#ENDL"}) +end + +function EEex_HookRemoveCallInternal(address, labelPairs, assemblyT) + + local opcode = EEex_ReadU8(address) + if opcode ~= 0xE8 then EEex_Error("Not disp32 call: "..EEex_ToHex(opcode)) end + + local afterCall = address + 5 + local target = afterCall + EEex_Read32(address + 1) + + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + + if EEex_HookIntegrityWatchdog_Load then + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + end + + local manualReturn = EEex_TryLabel("manual_return") + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"original", target}, + {"return", manualReturn and afterCall or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + #IF ]], not manualReturn, [[ { + return: + #MANUAL_HOOK_EXIT(0) + jmp ]], afterCall, [[ + } + ]]})) + end + ) + end) + + EEex_JITAt(address, {"jmp short "..hookAddress}) +end + +function EEex_HookRemoveCall(address, assemblyT) + EEex_HookRemoveCallInternal(address, {}, assemblyT) +end + +function EEex_HookRemoveCallWithLabels(address, labelPairs, assemblyT) + EEex_HookRemoveCallInternal(address, labelPairs, assemblyT) +end + +function EEex_HookRelativeJumpInternal(address, labelPairs, assemblyT) + local opcode = EEex_ReadU8(address) - if opcode ~= 0xE8 and opcode ~= 0xE9 then EEex_Error("Not disp32 relative: "..EEex_ToHex(opcode)) end + if opcode ~= 0xE9 then EEex_Error("Not disp32 jmp: "..EEex_ToHex(opcode)) end + local afterCall = address + 5 - EEex_DefineAssemblyLabel("return", afterCall) local target = afterCall + EEex_Read32(address + 1) - EEex_DefineAssemblyLabel("original", target) - local hookAddress = EEex_JITNear(assemblyT) + + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + local manualContinue = EEex_TryLabel("manual_continue") + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"original", manualContinue and target or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + #IF ]], not manualContinue, [[ { + original: + #MANUAL_HOOK_EXIT(0) + jmp ]], target, [[ + } + ]]})) + end + ) + end) + EEex_JITAt(address, {"jmp short "..hookAddress}) end -function EEex_HookBeforeCall(address, assemblyT) +function EEex_HookRelativeJump(address, assemblyT) + EEex_HookRelativeJumpInternal(address, {}, assemblyT) +end + +function EEex_HookRelativeJumpWithLabels(address, labelPairs, assemblyT) + EEex_HookRelativeJumpInternal(address, labelPairs, assemblyT) +end + +function EEex_HookCallInternal(address, labelPairs, assemblyT, after) + local opcode = EEex_ReadU8(address) if opcode ~= 0xE8 then EEex_Error("Not disp32 call: "..EEex_ToHex(opcode)) end + local afterCall = address + 5 - EEex_DefineAssemblyLabel("return", afterCall) local target = afterCall + EEex_Read32(address + 1) - local hookAddress = EEex_JITNear(EEex_FlattenTable({ - assemblyT, - {"call #$(1) #ENDL", {target}}, - {"jmp #$(1) #ENDL", {afterCall}}, - })) + + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + + if EEex_HookIntegrityWatchdog_Load then + if not after then + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {EEex_HookIntegrityWatchdogRegister.RAX}) + else + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }) + end + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + end + + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"return_skip", (not EEex_HookIntegrityWatchdog_Load and not after) and afterCall or nil}, + {"return", (not EEex_HookIntegrityWatchdog_Load and after) and afterCall or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable( + not after + and { + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + return: + #MANUAL_HOOK_EXIT(0) + call ]], target, [[ #ENDL + jmp ]], afterCall, [[ #ENDL + + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + return_skip: + #MANUAL_HOOK_EXIT(0) + jmp ]], afterCall, [[ #ENDL + } + ]]} + or {[[ + call ]], target, "#ENDL", + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + return: + #MANUAL_HOOK_EXIT(0) + } + jmp ]], afterCall, [[ #ENDL + ]]} + )) + end + ) + end) + EEex_JITAt(address, {"jmp short "..hookAddress}) end +function EEex_HookBeforeCall(address, assemblyT) + EEex_HookCallInternal(address, {}, assemblyT, false) +end + +function EEex_HookBeforeCallWithLabels(address, labelPairs, assemblyT) + EEex_HookCallInternal(address, labelPairs, assemblyT, false) +end + function EEex_HookAfterCall(address, assemblyT) + EEex_HookCallInternal(address, {}, assemblyT, true) +end + +function EEex_HookAfterCallWithLabels(address, labelPairs, assemblyT) + EEex_HookCallInternal(address, labelPairs, assemblyT, true) +end + +function EEex_HookBeforeAndAfterCallInternal(address, labelPairs, beforeAssemblyT, afterAssemblyT) + local opcode = EEex_ReadU8(address) if opcode ~= 0xE8 then EEex_Error("Not disp32 call: "..EEex_ToHex(opcode)) end + local afterCall = address + 5 - EEex_DefineAssemblyLabel("return", afterCall) local target = afterCall + EEex_Read32(address + 1) - local hookAddress = EEex_JITNear(EEex_FlattenTable({ - {"call #$(1) #ENDL", {target}}, - assemblyT, - {"jmp #$(1) #ENDL", {afterCall}}, - })) + + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + + if EEex_HookIntegrityWatchdog_Load then + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(address, 0, {EEex_HookIntegrityWatchdogRegister.RAX}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance(address, 0) + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(address, 1, { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }) + EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance(address, 1) + end + + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"return", not EEex_HookIntegrityWatchdog_Load and afterCall or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + + EEex_HookIntegrityWatchdog_HookEnter, + beforeAssemblyT, [[ + call: + #MANUAL_HOOK_EXIT(0) + + call ]], target, "#ENDL", + + EEex_HookIntegrityWatchdog_HookEnter, + afterAssemblyT, [[ + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + return: + } + #MANUAL_HOOK_EXIT(1) + jmp ]], afterCall, "#ENDL", + })) + end + ) + end) + EEex_JITAt(address, {"jmp short "..hookAddress}) end +function EEex_HookBeforeAndAfterCall(address, beforeAssemblyT, afterAssemblyT) + EEex_HookBeforeAndAfterCallInternal(address, {}, beforeAssemblyT, afterAssemblyT) +end + +function EEex_HookBeforeAndAfterCallWithLabels(address, labelPairs, beforeAssemblyT, afterAssemblyT) + EEex_HookBeforeAndAfterCallInternal(address, labelPairs, beforeAssemblyT, afterAssemblyT) +end + function EEex_GetJmpInfo(address) local opcode = EEex_ReadU8(address) @@ -517,203 +745,374 @@ function EEex_GetJmpInfo(address) return entry[1], afterInst + readFunc(curAddress), afterInst - address, afterInst end -function EEex_HookJump(address, restoreSize, assemblyT) +function EEex_ForceJump(address) + local _, jmpDest, instructionLength, _ = EEex_GetJmpInfo(address) + EEex_JITAt(address, {[[ + jmp short ]], jmpDest, [[ #ENDL + #REPEAT(#$(1),nop #ENDL) ]], {instructionLength - 5} + }) +end + +function EEex_HookBeforeConditionalJumpInternal(address, restoreSize, labelPairs, assemblyT) local jmpMnemonic, jmpDest, instructionLength, afterInstruction = EEex_GetJmpInfo(address) local jmpFailDest = afterInstruction + restoreSize local restoreBytes = EEex_StoreBytesAssembly(afterInstruction, restoreSize) - EEex_DefineAssemblyLabel("jmp_success", jmpDest) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"jmp_success", not EEex_HookIntegrityWatchdog_Load and jmpDest or nil}, + {"jmp_fail", (not EEex_HookIntegrityWatchdog_Load and restoreSize <= 0) and jmpFailDest or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ - local hookCode = EEex_JITNear(EEex_FlattenTable({ - assemblyT, - {[[ - jmp: - ]]}, - { - jmpMnemonic, " ", jmpDest, [[ #ENDL - jmp_fail: - ]]}, - restoreBytes, - {[[ - jmp ]], jmpFailDest, [[ #ENDL - ]]}, - })) + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ - EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL - #REPEAT(#$(1),nop #ENDL) ]], {restoreSize - 5 + instructionLength} - }) -end + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { -function EEex_HookJumpOnFail(address, restoreSize, assemblyT) + return: #ENDL ]], + jmpMnemonic, [[ jmp_success - local jmpMnemonic, jmpDest, instructionLength, afterInstruction = EEex_GetJmpInfo(address) + jmp_fail: + #MANUAL_HOOK_EXIT(0) ]], + restoreBytes, [[ + jmp ]], jmpFailDest, [[ #ENDL - local jmpFailDest = afterInstruction + restoreSize - local restoreBytes = EEex_StoreBytesAssembly(afterInstruction, restoreSize) + jmp_success: + #MANUAL_HOOK_EXIT(0) + jmp ]], jmpDest, [[ #ENDL + } - EEex_DefineAssemblyLabel("jmp_success", jmpDest) + #IF ]], not EEex_HookIntegrityWatchdog_Load, [[ { - local hookCode = EEex_JITNear(EEex_FlattenTable({ - {jmpMnemonic, " ", jmpDest, "#ENDL"}, - assemblyT, - {[[ - jmp_fail: - ]]}, - restoreBytes, - {[[ - jmp ]], jmpFailDest, [[ #ENDL - ]]}, - })) + return: #ENDL ]], + jmpMnemonic, " ", jmpDest, [[ #ENDL + + #IF ]], restoreSize > 0, [[ { + jmp_fail: #ENDL ]], + restoreBytes, [[ + } + + jmp ]], jmpFailDest, [[ #ENDL + } ]], + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {restoreSize - 5 + instructionLength} }) end -function EEex_HookJumpOnSuccess(address, restoreSize, assemblyT) +function EEex_HookBeforeConditionalJump(address, restoreSize, assemblyT) + EEex_HookBeforeConditionalJumpInternal(address, restoreSize, {}, assemblyT) +end + +function EEex_HookBeforeConditionalJumpWithLabels(address, restoreSize, labelPairs, assemblyT) + EEex_HookBeforeConditionalJumpInternal(address, restoreSize, labelPairs, assemblyT) +end + +function EEex_HookConditionalJumpOnFailInternal(address, restoreSize, labelPairs, assemblyT) local jmpMnemonic, jmpDest, instructionLength, afterInstruction = EEex_GetJmpInfo(address) local jmpFailDest = afterInstruction + restoreSize local restoreBytes = EEex_StoreBytesAssembly(afterInstruction, restoreSize) - EEex_DefineAssemblyLabel("jmp_success", jmpDest) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"jmp_success", not EEex_HookIntegrityWatchdog_Load and jmpDest or nil}, + {"jmp_fail", (not EEex_HookIntegrityWatchdog_Load and restoreSize <= 0) and jmpFailDest or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ - local hookCode = EEex_JITNear(EEex_FlattenTable({ - {jmpMnemonic, [[ jmp_succeeded - jmp_fail: - ]]}, - restoreBytes, - {[[ - jmp ]], jmpFailDest, [[ #ENDL - ]]}, - {[[ - jmp_succeeded: - ]]}, - assemblyT, - {"jmp ", jmpDest, "#ENDL"}, - })) + jmpMnemonic, " ", jmpDest, "#ENDL", + + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + + jmp_fail: + #MANUAL_HOOK_EXIT(0) ]], + restoreBytes, [[ + jmp ]], jmpFailDest, [[ #ENDL + + jmp_success: + #MANUAL_HOOK_EXIT(0) + jmp ]], jmpDest, [[ #ENDL + } + + #IF ]], not EEex_HookIntegrityWatchdog_Load, [[ { + + #IF ]], restoreSize > 0, [[ { + jmp_fail: #ENDL ]], + restoreBytes, [[ + } + + jmp ]], jmpFailDest, [[ #ENDL + } ]], + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {restoreSize - 5 + instructionLength} }) end -function EEex_HookJumpAuto(address, restoreSize, assemblyT, bAutoSuccess) +function EEex_HookConditionalJumpOnFail(address, restoreSize, assemblyT) + EEex_HookConditionalJumpOnFailInternal(address, restoreSize, {}, assemblyT) +end + +function EEex_HookConditionalJumpOnFailWithLabels(address, restoreSize, labelPairs, assemblyT) + EEex_HookConditionalJumpOnFailInternal(address, restoreSize, labelPairs, assemblyT) +end + +function EEex_HookConditionalJumpOnSuccessInternal(address, restoreSize, labelPairs, assemblyT) local jmpMnemonic, jmpDest, instructionLength, afterInstruction = EEex_GetJmpInfo(address) local jmpFailDest = afterInstruction + restoreSize local restoreBytes = EEex_StoreBytesAssembly(afterInstruction, restoreSize) - EEex_DefineAssemblyLabel("jmp_success", jmpDest) - - local hookCode = EEex_JITNear(EEex_FlattenTable({ - assemblyT, - {[[ - #IF ]], bAutoSuccess, [[ { - jmp #L(jmp_success) - } - jmp_fail: - ]]}, - restoreBytes, - {[[ - jmp ]], jmpFailDest, [[ #ENDL - ]]}, - })) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"jmp_success", not EEex_HookIntegrityWatchdog_Load and jmpDest or nil}, + {"jmp_fail", (not EEex_HookIntegrityWatchdog_Load and restoreSize <= 0) and jmpFailDest or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + + jmpMnemonic, " jmp_success_internal #ENDL", + restoreBytes, [[ + jmp ]], jmpFailDest, [[ #ENDL + + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + + jmp_success_internal: #ENDL ]], + EEex_HookIntegrityWatchdog_HookEnter, + + assemblyT, [[ + jmp_success: + #MANUAL_HOOK_EXIT(0) + jmp ]], jmpDest, [[ #ENDL + + jmp_fail: + #MANUAL_HOOK_EXIT(0) ]], + restoreBytes, [[ + jmp ]], jmpFailDest, [[ #ENDL + } + + #IF ]], not EEex_HookIntegrityWatchdog_Load, [[ { + + jmp_success_internal: #ENDL ]], + assemblyT, [[ + jmp ]], jmpDest, [[ #ENDL + + #IF ]], restoreSize > 0, [[ { + jmp_fail: #ENDL ]], + restoreBytes, [[ + jmp ]], jmpFailDest, [[ #ENDL + } + } ]], + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {restoreSize - 5 + instructionLength} }) end -function EEex_HookJumpAutoFail(address, restoreSize, assemblyT) - EEex_HookJumpAuto(address, restoreSize, assemblyT, false) +function EEex_HookConditionalJumpOnSuccess(address, restoreSize, assemblyT) + EEex_HookConditionalJumpOnSuccessInternal(address, restoreSize, {}, assemblyT) end -function EEex_HookJumpAutoSucceed(address, restoreSize, assemblyT) - EEex_HookJumpAuto(address, restoreSize, assemblyT, true) +function EEex_HookConditionalJumpOnSuccessWithLabels(address, restoreSize, labelPairs, assemblyT) + EEex_HookConditionalJumpOnSuccessInternal(address, restoreSize, labelPairs, assemblyT) end -function EEex_HookBeforeRestore(address, restoreDelay, restoreSize, returnDelay, assemblyT) +function EEex_HookBeforeRestoreInternal(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) local restoreBytes = EEex_StoreBytesAssembly(address + restoreDelay, restoreSize) local returnAddress = address + returnDelay - local hookCode = EEex_JITNear(EEex_FlattenTable({ - assemblyT, - {[[ - return: - ]]}, - restoreBytes, - {[[ - jmp ]], returnAddress, [[ #ENDL - ]]}, - })) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + + local manualHookIntegrityExit = EEex_TryLabel("manual_hook_integrity_exit") + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + return: + #IF ]], not manualHookIntegrityExit, [[ { + #MANUAL_HOOK_EXIT(0) + } ]], + restoreBytes, [[ + jmp ]], returnAddress, "#ENDL" + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {returnDelay - 5} }) end -function EEex_HookAfterRestore(address, restoreDelay, restoreSize, returnDelay, assemblyT) +function EEex_HookBeforeRestore(address, restoreDelay, restoreSize, returnDelay, assemblyT) + EEex_HookBeforeRestoreInternal(address, restoreDelay, restoreSize, returnDelay, {}, assemblyT) +end + +function EEex_HookBeforeRestoreWithLabels(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) + EEex_HookBeforeRestoreInternal(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) +end + +function EEex_HookAfterRestoreInternal(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) local restoreBytes = EEex_StoreBytesAssembly(address + restoreDelay, restoreSize) local returnAddress = address + returnDelay - EEex_DefineAssemblyLabel("return", returnAddress) - local hookCode = EEex_JITNear(EEex_FlattenTable({ - restoreBytes, - assemblyT, - {[[ - jmp ]], returnAddress, [[ #ENDL - ]]}, - })) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + + local manualReturn = EEex_TryLabel("manual_return") + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"return", (not EEex_HookIntegrityWatchdog_Load or manualReturn) and returnAddress or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + restoreBytes, + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + #IF ]], not manualReturn, [[ { + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + return: + #MANUAL_HOOK_EXIT(0) + } + jmp ]], returnAddress, [[ #ENDL + } ]] + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {returnDelay - 5} }) end -function EEex_HookBetweenRestore(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, assemblyT) +function EEex_HookAfterRestore(address, restoreDelay, restoreSize, returnDelay, assemblyT) + EEex_HookAfterRestoreInternal(address, restoreDelay, restoreSize, returnDelay, {}, assemblyT) +end + +function EEex_HookAfterRestoreWithLabels(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) + EEex_HookAfterRestoreInternal(address, restoreDelay, restoreSize, returnDelay, labelPairs, assemblyT) +end + +function EEex_HookBetweenRestoreInternal(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, labelPairs, assemblyT) local restoreBytes1 = EEex_StoreBytesAssembly(address + restoreDelay1, restoreSize1) local restoreBytes2 = EEex_StoreBytesAssembly(address + restoreDelay2, restoreSize2) local returnAddress = address + returnDelay - local hookCode = EEex_JITNear(EEex_FlattenTable({ - restoreBytes1, - assemblyT, - restoreBytes2, - {[[ - return: - jmp ]], returnAddress, [[ #ENDL - ]]}, - })) + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + restoreBytes1, + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + return: + #MANUAL_HOOK_EXIT(0) ]], + restoreBytes2, [[ + jmp ]], returnAddress, "#ENDL" + })) + end + ) + end) EEex_JITAt(address, {[[ - jmp short ]], hookCode, [[ #ENDL + jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {returnDelay - 5} }) end -function EEex_HookNOPs(address, nopCount, assemblyStr) - EEex_DefineAssemblyLabel("return", address + 5 + nopCount) - local hookAddress = EEex_JITNear(assemblyStr) +function EEex_HookBetweenRestore(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, assemblyT) + EEex_HookBetweenRestoreInternal(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, {}, assemblyT) +end + +function EEex_HookBetweenRestoreWithLabels(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, labelPairs, assemblyT) + EEex_HookBetweenRestoreInternal(address, restoreDelay1, restoreSize1, restoreDelay2, restoreSize2, returnDelay, labelPairs, assemblyT) +end + +function EEex_HookNOPsInternal(address, nopCount, labelPairs, assemblyT) + + local returnAddress = address + 5 + nopCount + + local hookAddress = EEex_RunWithAssemblyLabels(labelPairs or {}, function() + EEex_HookIntegrityWatchdog_IgnoreRegisters(address, {}) + EEex_HookIntegrityWatchdog_DefaultIgnoreStack(address) + return EEex_RunWithAssemblyLabels({ + {"hook_address", address}, + {"return", not EEex_HookIntegrityWatchdog_Load and returnAddress or nil}}, + function() + return EEex_JITNear(EEex_FlattenTable({ + EEex_HookIntegrityWatchdog_HookEnter, + assemblyT, [[ + #IF ]], EEex_HookIntegrityWatchdog_Load == true, [[ { + return: + #MANUAL_HOOK_EXIT(0) + } + jmp ]], returnAddress, "#ENDL" + })) + end + ) + end) + EEex_JITAt(address, {[[ jmp short ]], hookAddress, [[ #ENDL #REPEAT(#$(1),nop #ENDL) ]], {nopCount} }) end +function EEex_HookNOPs(address, nopCount, assemblyT) + EEex_HookNOPsInternal(address, nopCount, {}, assemblyT) +end + +function EEex_HookNOPsWithLabels(address, nopCount, labelPairs, assemblyT) + EEex_HookNOPsInternal(address, nopCount, labelPairs, assemblyT) +end + EEex_LuaCallReturnType = { ["Boolean"] = 0, ["Number"] = 1, @@ -843,7 +1242,7 @@ function EEex_GenLuaCall(funcName, meta) mov rdx, ]], -(2 + errorFuncLuaStackPopAmount + argI), [[ ; index mov rcx, rbx ; L #ALIGN - call short #L(Hardcoded_lua_settop) + call #L(Hardcoded_lua_settop) #ALIGN_END jmp EEex_GenLuaCall_call_error#$(1) ]], {labelSuffix}, [[ #ENDL @@ -926,7 +1325,7 @@ function EEex_GenLuaCall(funcName, meta) {[[ mov rcx, rbx ; L #ALIGN - call short #L(Hardcoded_luaL_loadstring) + call #L(Hardcoded_luaL_loadstring) #ALIGN_END test rax, rax @@ -1040,7 +1439,7 @@ function EEex_GenLuaCall(funcName, meta) mov r8, ]], numRet, [[ ; nresults mov rdx, ]], numArgs, [[ ; nargs mov rcx, rbx ; L - call short #L(Hardcoded_lua_pcallk) + call #L(Hardcoded_lua_pcallk) #ALIGN_END #ALIGN @@ -1051,12 +1450,12 @@ function EEex_GenLuaCall(funcName, meta) test rax, rax #IF ]], errorFunc ~= nil, [[ { - jz short EEex_GenLuaCall_no_error#$(1) ]], {labelSuffix}, [[ #ENDL + jz EEex_GenLuaCall_no_error#$(1) ]], {labelSuffix}, [[ #ENDL ; Clear error function and its precursors off of Lua stack mov rdx, ]], -(1 + errorFuncLuaStackPopAmount), [[ ; index mov rcx, rbx ; L #ALIGN - call short #L(Hardcoded_lua_settop) + call #L(Hardcoded_lua_settop) #ALIGN_END jmp EEex_GenLuaCall_call_error#$(1) ]], {labelSuffix}, [[ #ENDL } @@ -1164,22 +1563,10 @@ function EEex_ReverseTable(t, tMaxI) return newT end -function EEex_AssemblyToHex(number) - - local hexT = {} - local insertI = 1 - - repeat - hexT[insertI] = string.format("%x", EEex_Extract(number, 0, 4)):upper() - insertI = insertI + 1 - number = EEex_RShift(number, 4) - until number == 0x0 - - hexT = EEex_ReverseTable(hexT, insertI - 1) - hexT[insertI] = "h" - return table.concat(hexT) -end - +-- Warning: Don't use this with negative numbers in anything critical! +-- Lua's precision breaks down when RShifting near-max 64bit values. +-- If you need to convert a 64bit integer to a string, use +-- EEex_ToDecStr(), which is written in C++. function EEex_ToHex(number, minLength, suppressPrefix) if type(number) ~= "number" then @@ -1322,52 +1709,75 @@ function EEex_FindClosing(str, openStr, endStr, curLevel) return false, curLevel end +EEex_DebugPreprocessAssembly = false + function EEex_PreprocessAssemblyStr(assemblyT, curI, assemblyStr) local advanceCount = 1 -- #IF assemblyStr = EEex_ReplacePattern(assemblyStr, "#IF(.*)", function(match) - if EEex_FindPattern(match.groups[1], "[^%s]") then EEex_Error("Text between #IF and immediate condition") end + + if EEex_FindPattern(match.groups[1], "[^%s]") then + EEex_Error("Text between #IF and immediate condition") + end + advanceCount = 2 local conditionV = assemblyT[curI + 1] + if type(conditionV) == "boolean" then + local hadBody = false local bodyI = curI + 2 local bodyV = assemblyT[bodyI] + if type(bodyV) == "string" then + + -- Find and remove the opening "{" local hadOpen = false - bodyV = EEex_ReplacePattern(bodyV, "^%s*{(.*)", function(bodyMatch) + bodyV = EEex_ReplacePattern(bodyV, "^%s-{(.*)", function(bodyMatch) hadOpen = true return bodyMatch.groups[1], true end) - assemblyT[bodyI] = bodyV + if hadOpen then + + assemblyT[bodyI] = bodyV + local curLevel = 1 - while true do + repeat + -- Look for closing "}" if type(bodyV) == "string" then + + local findV -- curLevel if not hadBody, else found closingI hadBody, findV = EEex_FindClosing(bodyV, "{", "}", curLevel) + if hadBody then - if conditionV and findV > 1 then - assemblyT[bodyI] = bodyV:sub(1, findV - 1)..bodyV:sub(findV + 1) - else - assemblyT[bodyI] = bodyV:sub(findV + 1) - end + -- Save contents before and after the "}" if condition was true, + -- else only save contents after the "}" + assemblyT[bodyI] = conditionV and findV > 1 + and bodyV:sub(1, findV - 1)..bodyV:sub(findV + 1) + or bodyV:sub(findV + 1) break end curLevel = findV end + + -- Skip every assemblyT value until "}" is found if not conditionV then advanceCount = advanceCount + 1 end + bodyI = bodyI + 1 bodyV = assemblyT[bodyI] - if bodyV == nil then break end - curLevel = findV - end + + until bodyV == nil end end - if not hadBody then EEex_Error("#IF has no immediate body") end + + if not hadBody then + EEex_Error("#IF has no immediate body") + end else EEex_Error("#IF has no immediate condition") end @@ -1383,15 +1793,20 @@ function EEex_PreprocessAssemblyStr(assemblyT, curI, assemblyStr) local argsTableSize = #argsTable if argIndex > argsTableSize then EEex_Error(string.format("#$%d out of bounds for arg table of size %d", argIndex, argsTableSize)) end advanceCount = 2 - return tostring(argsTable[argIndex]) + local argVal = argsTable[argIndex] + return type(argVal) == "number" + and EEex_ToDecStr(argVal) + or tostring(argVal) end) -- #L assemblyStr = EEex_ReplacePattern(assemblyStr, "#L(%b())", function(match) - return EEex_AssemblyToHex(EEex_Label(match.groups[1]:sub(2, -2))) + local labelName = match.groups[1]:sub(2, -2) + local labelAddress = EEex_TryLabel(labelName) + return labelAddress and EEex_ToDecStr(labelAddress) or labelName end) - --#REPEAT + -- #REPEAT assemblyStr = EEex_ReplacePattern(assemblyStr, "#REPEAT(%b())", function(match) local toBuild = {} @@ -1410,7 +1825,7 @@ function EEex_PreprocessAssemblyStr(assemblyT, curI, assemblyStr) return assemblyStr, advanceCount end -function EEex_PreprocessAssembly(assemblyT) +function EEex_PreprocessAssembly(assemblyT, state) local builtStr = {} local insertI = 1 @@ -1425,7 +1840,7 @@ function EEex_PreprocessAssembly(assemblyT) builtStr[insertI], advanceCount = EEex_PreprocessAssemblyStr(assemblyT, i, v) insertI = insertI + 1 elseif vtype == "number" then - builtStr[insertI] = tostring(v) + builtStr[insertI] = EEex_ToDecStr(v) insertI = insertI + 1 else EEex_Error("Unexpected type encountered during JIT: "..vtype) @@ -1445,96 +1860,151 @@ function EEex_PreprocessAssembly(assemblyT) (#ALIGN_END)[6] (#ALIGN((\d+)[8?]))[7] (#SHADOW_SPACE_BOTTOM((\d+)[10?]))[9] - (#RESUME_SHADOW_ENTRY)[11] + (#LAST_FRAME_TOP((\d+)[12?]))[11] + (#RESUME_SHADOW_ENTRY)[13] + (#MANUAL_HOOK_EXIT(\d+)[15])[14] --]] - local shadowSpaceStack = {} - local shadowSpaceStackTop = 0 - local alignModStack = {} - local alignModStackTop = 0 - local hintAccumulator = 0 + if not state then + state = { + ["shadowSpaceStack"] = {}, + ["shadowSpaceStackTop"] = 0, + ["alignModStack"] = {}, + ["alignModStackTop"] = 0, + ["hintAccumulator"] = 0, + ["debug"] = false, + } + end - toReturn = EEex_ReplaceRegex(toReturn, "(?:#STACK_MOD\\s*\\((-{0,1}\\d+)\\))|(#MAKE_SHADOW_SPACE(?:\\s*\\((\\d+)\\)){0,1})|(#DESTROY_SHADOW_SPACE(?:(?!\\(.*?\\))|(?:\\((KEEP_ENTRY)\\))))|(#ALIGN_END)|(#ALIGN(?:\\s*\\((\\d+)\\)){0,1})|(#SHADOW_SPACE_BOTTOM\\s*\\((-{0,1}.+)\\))|(#RESUME_SHADOW_ENTRY)", function(pos, endPos, str, groups) + toReturn = EEex_ReplaceRegex(toReturn, "(?:#STACK_MOD\\s*\\((-{0,1}\\d+)\\))|(#MAKE_SHADOW_SPACE(?:\\s*\\((\\d+)\\)){0,1})|(#DESTROY_SHADOW_SPACE(?:(?!\\(.*?\\))|(?:\\((KEEP_ENTRY)\\))))|(#ALIGN_END)|(#ALIGN(?:\\s*\\((\\d+)\\)){0,1})|(#SHADOW_SPACE_BOTTOM\\s*\\((-{0,1}.+?)\\))|(#LAST_FRAME_TOP\\s*\\((-{0,1}.+?)\\))|(#RESUME_SHADOW_ENTRY)|(#MANUAL_HOOK_EXIT\\s*\\((\\d+)\\))|(#DEBUG_ON)|(#DEBUG_OFF)", function(pos, endPos, str, groups) if groups[1] then --print("#STACK_MOD("..tonumber(groups[1])..")") - hintAccumulator = hintAccumulator + tonumber(groups[1]) + state.hintAccumulator = state.hintAccumulator + tonumber(groups[1]) elseif groups[2] then --print("#MAKE_SHADOW_SPACE") local neededShadow = 32 + (groups[3] and tonumber(groups[3]) or 0) - if shadowSpaceStackTop > 0 and shadowSpaceStack[shadowSpaceStackTop].top == hintAccumulator then - local shadowEntry = shadowSpaceStack[shadowSpaceStackTop] + if state.shadowSpaceStackTop > 0 and state.shadowSpaceStack[state.shadowSpaceStackTop].top == state.hintAccumulator then + local shadowEntry = state.shadowSpaceStack[state.shadowSpaceStackTop] if shadowEntry.sizeNoRounding < neededShadow then print(debug.traceback("[!] #MAKE_SHADOW_SPACE redefined where original failed to provide enough space! Correct this by expanding "..(shadowEntry.sizeNoRounding - 32).." to "..(neededShadow - 32).."; continuing with suboptimal configuration.")) local sizeDiff = EEex_RoundUp(neededShadow, 16) - shadowEntry.size - hintAccumulator = hintAccumulator + sizeDiff + state.hintAccumulator = state.hintAccumulator + sizeDiff shadowEntry.top = shadowEntry.top + sizeDiff shadowEntry.size = shadowEntry.size + sizeDiff + shadowEntry.active = true -- Ideally this would be merged with the previous shadow space instruction, but abusing -- regex like this doesn't help make that happen, (would require an additional pass) - return string.format("sub rsp, %d #ENDL", sizeDiff) + return string.format("lea rsp, qword ptr ss:[rsp-%d] #ENDL", sizeDiff) end else - local neededStack = EEex_DistanceToMultiple(hintAccumulator + neededShadow, 16) + neededShadow - hintAccumulator = hintAccumulator + neededStack - shadowSpaceStackTop = shadowSpaceStackTop + 1 - shadowSpaceStack[shadowSpaceStackTop] = { - ["top"] = hintAccumulator, + local neededStack = EEex_DistanceToMultiple(state.hintAccumulator + neededShadow, 16) + neededShadow + + if state.debug then + print(string.format( + "[?] #MAKE_SHADOW_SPACE() with hintAccumulator = %d, need %d bytes for shadow space, allocating %d to maintain alignment", + state.hintAccumulator, neededShadow, neededStack + )) + end + + state.hintAccumulator = state.hintAccumulator + neededStack + state.shadowSpaceStackTop = state.shadowSpaceStackTop + 1 + state.shadowSpaceStack[state.shadowSpaceStackTop] = { + ["top"] = state.hintAccumulator, ["size"] = neededStack, ["sizeNoRounding"] = neededShadow, + ["active"] = true, } - return string.format("sub rsp, %d #ENDL", neededStack) + return string.format("lea rsp, qword ptr ss:[rsp-%d] #ENDL", neededStack) end elseif groups[4] then --print("#DESTROY_SHADOW_SPACE") - local shadowEntry = shadowSpaceStack[shadowSpaceStackTop] + local shadowEntry = state.shadowSpaceStack[state.shadowSpaceStackTop] + if state.hintAccumulator ~= shadowEntry.top then EEex_Error("#DESTROY_SHADOW_SPACE() failed - stack top not where it should be") end if not groups[5] then - shadowSpaceStackTop = shadowSpaceStackTop - 1 + state.shadowSpaceStackTop = state.shadowSpaceStackTop - 1 + else + shadowEntry.active = false -- KEEP_ENTRY end - hintAccumulator = hintAccumulator - shadowEntry.size - -- LEA maintains flags (as opposed to SUB), which allows us to test a register + state.hintAccumulator = state.hintAccumulator - shadowEntry.size + -- LEA maintains flags (as opposed to ADD), which allows us to test a register -- and restore it before calling #DESTROY_SHADOW_SPACE and still use the result -- for a branch. return string.format("lea rsp, qword ptr ss:[rsp+%d]", shadowEntry.size) elseif groups[6] then --print("#ALIGN_END") - local alignEntry = alignModStack[alignModStackTop] - if alignEntry.madeShadow then shadowSpaceStackTop = shadowSpaceStackTop - 1 end - alignModStackTop = alignModStackTop - 1 + local alignEntry = state.alignModStack[state.alignModStackTop] + if alignEntry.madeShadow then state.shadowSpaceStackTop = state.shadowSpaceStackTop - 1 end + state.alignModStackTop = state.alignModStackTop - 1 if alignEntry.popAmount > 0 then - return string.format("add rsp, %d #ENDL", tonumber(alignEntry.popAmount)) + return string.format("lea rsp, qword ptr ss:[rsp+%d] #ENDL", tonumber(alignEntry.popAmount)) end elseif groups[7] then local pushedArgBytes = groups[8] and tonumber(groups[8]) or 0 --print("#ALIGN("..pushedArgBytes..")") local neededShadow = 0 - if shadowSpaceStackTop == 0 or shadowSpaceStack[shadowSpaceStackTop].top ~= hintAccumulator then + if state.shadowSpaceStackTop == 0 or state.shadowSpaceStack[state.shadowSpaceStackTop].top ~= state.hintAccumulator then neededShadow = 32 - shadowSpaceStackTop = shadowSpaceStackTop + 1 - shadowSpaceStack[shadowSpaceStackTop] = { - ["top"] = hintAccumulator, + state.shadowSpaceStackTop = state.shadowSpaceStackTop + 1 + state.shadowSpaceStack[state.shadowSpaceStackTop] = { + ["top"] = state.hintAccumulator, ["size"] = neededShadow, ["sizeNoRounding"] = neededShadow, } end - local neededStack = EEex_DistanceToMultiple(hintAccumulator + neededShadow + pushedArgBytes, 16) + neededShadow - pushedArgBytes - alignModStackTop = alignModStackTop + 1 - alignModStack[alignModStackTop] = { + local neededStack = EEex_DistanceToMultiple(state.hintAccumulator + neededShadow + pushedArgBytes, 16) + neededShadow - pushedArgBytes + state.alignModStackTop = state.alignModStackTop + 1 + state.alignModStack[state.alignModStackTop] = { ["popAmount"] = neededStack + pushedArgBytes, ["madeShadow"] = neededShadow > 0, } if neededStack > 0 then - return string.format("sub rsp, %d #ENDL", neededStack) + return string.format("lea rsp, qword ptr ss:[rsp-%d] #ENDL", neededStack) end elseif groups[9] then --print("#SHADOW_SPACE_BOTTOM") - local adjust = 0 local adjustStr = groups[10] - if adjustStr then - adjust = adjustStr:sub(-1) == "h" and tonumber(adjustStr:sub(1,-2), 16) or tonumber(adjustStr) - end - return tostring(shadowSpaceStack[shadowSpaceStackTop].size + adjust) + local adjust = adjustStr + and (adjustStr:sub(-1) == "h" and tonumber(adjustStr:sub(1,-2), 16) or tonumber(adjustStr)) + or 0 + if adjust >= 0 then EEex_Error("#SHADOW_SPACE_BOTTOM must have a negative offset") end + local shadowEntry = state.shadowSpaceStack[state.shadowSpaceStackTop] + local stackModAdj = state.hintAccumulator - shadowEntry.top -- For when #STACK_MOD() adjusts the stack after #MAKE_SHADOW_SPACE() + return tostring(shadowEntry.sizeNoRounding + stackModAdj + adjust) elseif groups[11] then - hintAccumulator = hintAccumulator + shadowSpaceStack[shadowSpaceStackTop].size + --print("#LAST_FRAME_TOP") + local adjustStr = groups[12] + local adjust = adjustStr + and (adjustStr:sub(-1) == "h" and tonumber(adjustStr:sub(1,-2), 16) or tonumber(adjustStr)) + or 0 + if adjust < 0 then EEex_Error("#LAST_FRAME_TOP must have a positive offset") end + if state.shadowSpaceStackTop == 0 then return adjust end + local shadowEntry = state.shadowSpaceStack[state.shadowSpaceStackTop] + local stackModAdj = state.hintAccumulator - shadowEntry.top -- For when #STACK_MOD() adjusts the stack after #MAKE_SHADOW_SPACE() + return tostring(shadowEntry.size + stackModAdj + adjust) + elseif groups[13] then + --print("#RESUME_SHADOW_ENTRY") + local shadowEntry = state.shadowSpaceStack[state.shadowSpaceStackTop] + state.hintAccumulator = state.hintAccumulator + shadowEntry.size + shadowEntry.active = true + elseif groups[14] then + --print("#MANUAL_HOOK_EXIT") + local hadActiveShadowSpace = false + for i = state.shadowSpaceStackTop, 1, -1 do + if state.shadowSpaceStack[state.shadowSpaceStackTop].active then + hadActiveShadowSpace = true + break + end + end + if hadActiveShadowSpace or state.alignModStackTop ~= 0 then EEex_Error("#MANUAL_HOOK_EXIT cannot exit inside a stack frame") end + local instance = tonumber(groups[15]) + if instance == nil or instance < 0 then EEex_Error("#MANUAL_HOOK_EXIT has invalid instance") end + return EEex_PreprocessAssembly(EEex_HookIntegrityWatchdog_HookExit(instance), state) + elseif groups[16] then + --print("#DEBUG_ON") + state.debug = true + elseif groups[17] then + --print("#DEBUG_OFF") + state.debug = false end return "" end) @@ -1545,11 +2015,31 @@ function EEex_PreprocessAssembly(assemblyT) toReturn = EEex_ReplacePattern(toReturn, "\n+", "\n") -- Merge newlines toReturn = EEex_ReplacePattern(toReturn, "\n[ \t]+", "\n") -- Remove whitespace after newlines, (indentation) toReturn = EEex_ReplacePattern(toReturn, "^[ \t]+", "") -- Remove initial indent + toReturn = EEex_ReplacePattern(toReturn, "[ \t]+;", " ;") -- Remove indentation before comments + + if EEex_DebugPreprocessAssembly then + print("EEex_PreprocessAssembly returning:\n\n"..toReturn.."\n") + end + + -- Validate labels to prevent subtle bug where zero-offset branch instructions are written for non-existing labels + local seenLabels = {} + EEex_IterateRegex(toReturn, "^\\s*(\\S+):", function(pos, endPos, matchedStr, groups) + seenLabels[groups[1]] = true + end) + + local branchUsingLabel = "^\\s*(?:call|ja|jae|jb|jbe|jc|je|jg|jge|jl|jle|jmp|jna|jnae|jnb|jnbe|jnc|jne|jng|jnge|jnl|jnle|jno|jnp|jns|jnz|jo|jp|jpe|jpo|js|jz|loope|loopne|loopnz|loopz)\\s+([^0-9]\\S*)\\s*$" + EEex_IterateRegex(toReturn, branchUsingLabel, function(pos, endPos, matchedStr, groups) + local expectedLabel = groups[1] + if not seenLabels[expectedLabel] then + EEex_Error(string.format("Label \"%s\" not defined. Did you mean to use \"#L(%s)\"?", expectedLabel, expectedLabel)) + end + end) - --print("EEex_PreprocessAssembly returning:\n\n"..toReturn.."\n") return toReturn end +EEex_CodePageAllocations = {} + function EEex_AllocCodePage() local address, size = EEex_AllocCodePageInternal() local initialEntry = {} @@ -1563,6 +2053,14 @@ end function EEex_JITNear(assemblyT) + local stackMod = EEex_TryLabel("stack_mod") + if stackMod then + assemblyT = EEex_FlattenTable({ + {"#STACK_MOD(#$(1)) #ENDL", {stackMod}}, + assemblyT, + }) + end + local assemblyStr = EEex_PreprocessAssembly(assemblyT) local finalWriteSize @@ -1663,3 +2161,17 @@ function EEex_JITAt(dst, assemblyT) local checkJIT = function(writeSize) return 0 end EEex_JITAtInternal(dst, checkJIT, assemblyStr) end + +(function() + local dummyTable = {} + EEex_HookIntegrityWatchdog_HookEnter = dummyTable + EEex_HookIntegrityWatchdog_HookExit = function() return dummyTable end + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance = function() end + EEex_HookIntegrityWatchdog_IgnoreRegisters = function() end + EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance = function() end + EEex_HookIntegrityWatchdog_DefaultIgnoreStack = function() end + EEex_HookIntegrityWatchdog_IgnoreStackSizesForInstance = function() end + EEex_HookIntegrityWatchdog_IgnoreStackSizes = function() end + -- Contains some assembly functions for EEex_Assembly.lua + EEex_DoFile("EEex_Assembly_Patch") +end)() diff --git a/EEex/copy/EEex_Assembly_Patch.lua b/EEex/copy/EEex_Assembly_Patch.lua index 2e9c464..435d735 100644 --- a/EEex/copy/EEex_Assembly_Patch.lua +++ b/EEex/copy/EEex_Assembly_Patch.lua @@ -37,7 +37,7 @@ EEex_JITNearAsLabel("EEex_PrintPopLuaString", {[[ mov r8, 0 ; nresults mov rdx, 1 ; nargs mov rcx, rbx ; L - call short #L(Hardcoded_lua_pcallk) + call #L(Hardcoded_lua_pcallk) #ALIGN_END ; Clear error string off of stack @@ -64,7 +64,7 @@ EEex_JITNearAsLabel("EEex_CheckCallError", {[[ #MAKE_SHADOW_SPACE #ALIGN - call short #L(EEex_PrintPopLuaString) + call #L(EEex_PrintPopLuaString) #ALIGN_END mov rax, 1 diff --git a/EEex/copy/EEex_Debug.lua b/EEex/copy/EEex_Debug.lua index e3ac727..f8910e3 100644 --- a/EEex/copy/EEex_Debug.lua +++ b/EEex/copy/EEex_Debug.lua @@ -53,51 +53,72 @@ EEex_Debug_LogActions = false EEex_DisableCodeProtection() - EEex_HookRelativeBranch(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultJmp"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(56) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r8 - ]]}, - EEex_GenLuaCall("EEex_Debug_LogAction", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rbx - ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 1", {rspOffset}, "#ENDL"} end, - }, - }), - {[[ - call_error: - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jmp #L(original) - ]]}, - })) - - EEex_HookJump(EEex_Label("Hook-CGameSprite::ExecuteAction()-DefaultJmp"), 0, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(72) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r9 - ]]}, - EEex_GenLuaCall("EEex_Debug_LogAction", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rdi - ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 0", {rspOffset}, "#ENDL"} end, - }, - }), - {[[ - call_error: - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - cmp ecx, 0x1D7 - ]]}, - })) + --[[ + +---------------------------------------------------------------------------------------------------------+ + | Debug-log details about a CGameAIBase's action before it is executed | + +---------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Debug_LogAction(executingObject: CGameAIBase|EEex_GameObject_CastUT, bFromAIBase: boolean) | + +---------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeConditionalJumpWithLabels(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(56) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r8 + ]]}, + EEex_GenLuaCall("EEex_Debug_LogAction", { + ["args"] = { + function(rspOffset) return {[[ + mov qword ptr ss:[rsp+#$(1)], rbx + ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 1", {rspOffset}, "#ENDL"} end, + }, + }), + {[[ + call_error: + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + cmp r8d, 0x1D5 + ]]}, + }) + ) + + EEex_HookBeforeConditionalJumpWithLabels(EEex_Label("Hook-CGameSprite::ExecuteAction()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(80) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r9 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r10 + ]]}, + EEex_GenLuaCall("EEex_Debug_LogAction", { + ["args"] = { + function(rspOffset) return {[[ + mov qword ptr ss:[rsp+#$(1)], rdi + ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 0", {rspOffset}, "#ENDL"} end, + }, + }), + {[[ + call_error: + mov r10, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + cmp ecx, 0x1D7 + ]]}, + }) + ) EEex_EnableCodeProtection() end diff --git a/EEex/copy/EEex_EarlyMain.lua b/EEex/copy/EEex_EarlyMain.lua new file mode 100644 index 0000000..f5f5e84 --- /dev/null +++ b/EEex/copy/EEex_EarlyMain.lua @@ -0,0 +1,11 @@ + +-- This is the early startup file for EEex. InfinityLoader calls this file before the game is +-- resumed when [General].LuaPatchMode = REPLACE_INTERNAL_WITH_EXTERNAL. This file replaces +-- in-engine Lua functions before they are used. + +(function() + -- Contains most of the code editing functions. This file is the core of EEex. + EEex_DoFile("EEex_Assembly") + -- Replaces the statically compiled, in-exe Lua version with LuaLibrary. + EEex_DoFile("EEex_ReplaceLua") +end)() diff --git a/EEex/copy/EEex_Fix.lua b/EEex/copy/EEex_Fix.lua index 7cc72d8..1c7679e 100644 --- a/EEex/copy/EEex_Fix.lua +++ b/EEex/copy/EEex_Fix.lua @@ -1,11 +1,4 @@ --- BUG: v2.6.6.0 - op206/318/324 incorrectly indexes source object's items --- list if the incoming effect's source spell has a name strref of -1 --- without first checking if the source was a sprite. -function EEex_Fix_Hook_SpellImmunityShouldSkipItemIndexing(object) - return object.m_objectType ~= CGameObjectType.SPRITE -end - -- The engine doesn't update quick lists when a special ability is added, -- such as from op171 or act279. function EEex_Fix_Hook_OnAddSpecialAbility(sprite, spell) @@ -44,15 +37,3 @@ end function EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow(sprite) EEex_GetUDAux(sprite)["EEex_Fix_HasSpellOrSpellPointStartedCasting"] = 1 end - -------------------------------------------------------------------------------------------- --- Fix several regressions in v2.6 where: -- --- 1) op206's param1 only works for values 0xF00074 and 0xF00080. -- --- 2) op232 and op256's "you cannot cast multiple instances" message fails to display. -- -------------------------------------------------------------------------------------------- - -function EEex_Fix_Hook_ShouldTransformSpellImmunityStrref(effect, immunitySpell) - local sourceResRef = effect.m_sourceRes:get() - local errorStrref = immunitySpell.m_error - return sourceResRef == "" and (errorStrref == 0xF00074 or errorStrref == 0xF00080) -end diff --git a/EEex/copy/EEex_Fix_Patch.lua b/EEex/copy/EEex_Fix_Patch.lua index 2d5f9f8..ebb020e 100644 --- a/EEex/copy/EEex_Fix_Patch.lua +++ b/EEex/copy/EEex_Fix_Patch.lua @@ -3,60 +3,80 @@ EEex_DisableCodeProtection() - EEex_HookJumpOnFail(EEex_Label("Hook-CGameEffect::CheckAdd()-FixSpellImmunityShouldSkipItemIndexing"), 4, EEex_FlattenTable({ + --[[ + +----------------------------------------------------------------------------------------------------+ + | BUG: v2.6.6.0 - op206/318/324 incorrectly indexes the source object's item list if the incoming | + | effect's source spell has a name strref of -1 without first checking if the source was a sprite | + +----------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Fix_Hook_SpellImmunityShouldSkipItemIndexing(pGameObject: CGameObject*) -> bool | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the engine to skip its item list check | + +----------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameEffect::CheckAdd()-FixSpellImmunityShouldSkipItemIndexing"), 4, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Fix_Hook_SpellImmunityShouldSkipItemIndexing", { - ["args"] = { - function(rspOffset) return {[[ - mov rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(50h)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}, "#ENDL"}, "CGameObject" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - xor rax, rax - - no_error: - test rax, rax - - #DESTROY_SHADOW_SPACE + mov rcx, qword ptr ds:[rsp+#LAST_FRAME_TOP(50h)] ; pGameObject + call #L(EEex::Fix_Hook_SpellImmunityShouldSkipItemIndexing) + test al, al jnz #L(jmp_success) - ]]}, - })) - - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::AddSpecialAbility()-LastCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Fix_Hook_OnAddSpecialAbility", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, - function(rspOffset) return {[[ - lea rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(48h)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CSpell" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------------------------------------------------- - -- Fix Spell() and SpellPoint() not being disruptable if the creature is facing -- - -- SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), NEE(11), SEE(13), or SSE(15) -- - ---------------------------------------------------------------------------------- - - -------------------------------------------------- - -- EEex_Fix_Hook_ShouldForceMainSpellActionCode -- - -------------------------------------------------- + ]]} + ) + + --[[ + +------------------------------------------------------------------------------------------------------+ + | Fix quick spell slots not updating when a special ability is added (for example, by op171 or act279) | + +------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Fix_Hook_OnAddSpecialAbility(sprite: CGameSprite, spell: CSpell) | + +------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::AddSpecialAbility()-LastCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + ]]}, + EEex_GenLuaCall("EEex_Fix_Hook_OnAddSpecialAbility", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, + function(rspOffset) return {[[ + lea rax, qword ptr ds:[rsp+#LAST_FRAME_TOP(48h)] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CSpell" end, + }, + }), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +----------------------------------------------------------------------------------------------------------------------+ + | Fix Spell() and SpellPoint() not being disruptable if the creature is facing SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), | + | NEE(11), SEE(13), or SSE(15) | + +----------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Fix_Hook_ShouldForceMainSpellActionCode(sprite: CGameSprite, point: CPoint) -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the engine to run the main spell action code regardless of the sprite's orientation | + | (which includes spell disruption handling) | + +----------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow(sprite: CGameSprite) | + +----------------------------------------------------------------------------------------------------------------------+ + --]] + + ---------------------------------------------------------- + -- [Lua] EEex_Fix_Hook_ShouldForceMainSpellActionCode() -- + ---------------------------------------------------------- local callShouldForceMainSpellActionCode = EEex_JITNear(EEex_FlattenTable({ {[[ @@ -82,25 +102,39 @@ ]]}, })) - EEex_HookJumpOnFail(EEex_Label("Hook-CGameSprite::Spell()-CheckDirectionJmp"), 3, {[[ - mov rdx, r14 - mov rcx, rbx - call short #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL - test rax, rax - jnz #L(jmp_success) - ]]}) - - EEex_HookJumpOnFail(EEex_Label("Hook-CGameSprite::SpellPoint()-CheckDirectionJmp"), 5, {[[ - lea rdx, qword ptr ss:[rsp+0x60] - mov rcx, rbx - call short #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL - test rax, rax - jnz #L(jmp_success) - ]]}) + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::Spell()-CheckDirectionJmp"), 3, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + {[[ + mov rdx, r14 ; point + mov rcx, rbx ; sprite + call #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL + test rax, rax + jnz #L(jmp_success) + ]]} + ) + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::SpellPoint()-CheckDirectionJmp"), 5, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + {[[ + lea rdx, qword ptr ss:[rsp+0x60] ; point + mov rcx, rbx ; sprite + call #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL + test rax, rax + jnz #L(jmp_success) + ]]} + ) - --------------------------------------------------------- - -- EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow -- - --------------------------------------------------------- + ----------------------------------------------------------------- + -- [Lua] EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow() -- + ----------------------------------------------------------------- local callOnSpellOrSpellPointStartedCastingGlow = EEex_JITNear(EEex_FlattenTable({ {[[ @@ -123,50 +157,57 @@ EEex_Label("Hook-CGameSprite::Spell()-ApplyCastingEffect()"), EEex_Label("Hook-CGameSprite::SpellPoint()-ApplyCastingEffect()") }) do - EEex_HookAfterCall(address, {[[ - mov rcx, rbx - call short #$(1) ]], {callOnSpellOrSpellPointStartedCastingGlow}, [[ #ENDL - ]]}) + EEex_HookAfterCallWithLabels(address, { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + {[[ + mov rcx, rbx ; sprite + call #$(1) ]], {callOnSpellOrSpellPointStartedCastingGlow}, [[ #ENDL + ]]} + ) end - -------------------------------------------------------------------------------------------------------------- - -- Opcode #182 should consider -1 (instead of 0) the fail return value from CGameSprite::FindItemPersonal() -- - -------------------------------------------------------------------------------------------------------------- + --[[ + +----------------------------------------------------------------------------------------------------------------+ + | [JIT] Opcode #182 should consider -1 (instead of 0) the fail return value from CGameSprite::FindItemPersonal() | + +----------------------------------------------------------------------------------------------------------------+ + --]] - EEex_HookJump(EEex_Label("Hook-CGameEffectApplyEffectEquipItem::ApplyEffect()-CheckRetVal"), 0, {[[ + EEex_HookBeforeConditionalJump(EEex_Label("Hook-CGameEffectApplyEffectEquipItem::ApplyEffect()-CheckRetVal"), 0, {[[ cmp ax, -1 ]]}) - ------------------------------------------------------------------------------------------- - -- Fix several regressions in v2.6 where: -- - -- 1) op206's param1 only works for values 0xF00074 and 0xF00080. -- - -- 2) op232 and op256's "you cannot cast multiple instances" message fails to display. -- - ------------------------------------------------------------------------------------------- - - EEex_HookAfterRestore(EEex_Label("Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrref"), 0, 5, 5, EEex_FlattenTable({ + --[[ + +--------------------------------------------------------------------------------------------------------------------------------+ + | Fix a couple of regressions in v2.6 regarding op206/op232/op256 | + +--------------------------------------------------------------------------------------------------------------------------------+ + | 1) op206's param1 only works for values 0xF00074 and 0xF00080 | + | 2) op232 and op256's "you cannot cast multiple instances" message fails to display | + +--------------------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Fix_Hook_ShouldTransformSpellImmunityStrref(pEffect: CGameEffect*, pImmunitySpell: CImmunitySpell*) -> bool | + | return: | + | -> false - Don't transform immunity strref | + | -> true - Transform immunity strref | + +--------------------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrref"), 0, 5, 5, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}, + {"manual_return", true}}, {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Fix_Hook_ShouldTransformSpellImmunityStrref", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r12 #ENDL", {rspOffset}}, "CImmunitySpell" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - xor rax, rax + mov rdx, r12 ; pImmunitySpell + mov rcx, rdi ; pEffect + call #L(EEex::Fix_Hook_ShouldTransformSpellImmunityStrref) + test al, al - no_error: - test rax, rax - #DESTROY_SHADOW_SPACE + #MANUAL_HOOK_EXIT(0) jnz #L(Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrrefBody) jmp #L(Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrrefElse) - ]]}, - })) + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_GameObject_Patch.lua b/EEex/copy/EEex_GameObject_Patch.lua index 73ccad8..eed432e 100644 --- a/EEex/copy/EEex_GameObject_Patch.lua +++ b/EEex/copy/EEex_GameObject_Patch.lua @@ -3,24 +3,38 @@ EEex_DisableCodeProtection() - --------------------------------------- - -- EEex_GameObject_Hook_OnDeleting() -- - --------------------------------------- + --[[ + +---------------------------------------------------------------------+ + | Clean up any EEex data linked to a game object before it is deleted | + +---------------------------------------------------------------------+ + | [Lua] EEex_GameObject_Hook_OnDeleting(objectID: number) | + +---------------------------------------------------------------------+ + --]] - EEex_HookJumpOnSuccess(EEex_Label("Hook-CGameObjectArray::Delete()-DeleteJmp"), 5, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_GameObject_Hook_OnDeleting", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbp", {rspOffset}, "#ENDL"} end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameObjectArray::Delete()-DeleteJmp"), 5, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(56) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + ]]}, + EEex_GenLuaCall("EEex_GameObject_Hook_OnDeleting", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbp", {rspOffset}, "#ENDL"} end, + }, + }), + {[[ + call_error: + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_GameState.lua b/EEex/copy/EEex_GameState.lua index 336f70a..2ac05fa 100644 --- a/EEex/copy/EEex_GameState.lua +++ b/EEex/copy/EEex_GameState.lua @@ -94,7 +94,7 @@ end -- Hooks -- ----------- -function EEex_GameState_Hook_OnInitialized() +function EEex_GameState_LuaHook_OnInitialized() for _, listener in ipairs(EEex_GameState_InitializedListeners) do listener() end diff --git a/EEex/copy/EEex_GameState_Patch.lua b/EEex/copy/EEex_GameState_Patch.lua index d709754..29c4a85 100644 --- a/EEex/copy/EEex_GameState_Patch.lua +++ b/EEex/copy/EEex_GameState_Patch.lua @@ -3,39 +3,49 @@ EEex_DisableCodeProtection() - ----------------------------------------- - -- EEex_GameState_Hook_OnInitialized() -- - ----------------------------------------- - - EEex_HookRelativeBranch(EEex_Label("Hook-SDL_main()-CLUAConsole::LuaInit()"), EEex_FlattenTable({ - {[[ - call #L(original) - #MAKE_SHADOW_SPACE(32) - ]]}, - EEex_GenLuaCall("EEex_GameState_Hook_OnInitialized"), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}, - })) - - --------------------------------------- - -- EEex_GameState_Hook_OnDestroyed() -- - --------------------------------------- - - EEex_HookRelativeBranch(EEex_Label("Hook-CInfGame::DestroyGame()-LastCall"), EEex_FlattenTable({ - {[[ - call #L(original) - #MAKE_SHADOW_SPACE(32) - ]]}, - EEex_GenLuaCall("EEex_GameState_Hook_OnDestroyed"), + --[[ + +---------------------------------------------------------------------------+ + | Call a hook after the engine has completed most of its initialization | + +---------------------------------------------------------------------------+ + | Used to implement listeners that need the game state to be initialized, | + | yet also require early execution during engine startup | + +---------------------------------------------------------------------------+ + | [EEex.dll] EEex::GameState_Hook_OnInitialized() | + +---------------------------------------------------------------------------+ + | [Lua] EEex_GameState_LuaHook_OnInitialized() | + +---------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-SDL_main()-CLUAConsole::LuaInit()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}, - })) + call #L(EEex::GameState_Hook_OnInitialized) + ]]} + ) + + --[[ + +---------------------------------------------------------------------------+ + | Call a hook after the engine has "destroyed" a game instance | + +---------------------------------------------------------------------------+ + | Used to implement listeners that need to react to a game session ending | + +---------------------------------------------------------------------------+ + | [Lua] EEex_GameState_Hook_OnDestroyed() | + +---------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CInfGame::DestroyGame()-LastCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + ]]}, + EEex_GenLuaCall("EEex_GameState_Hook_OnDestroyed"), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_HookIntegrityWatchdog.lua b/EEex/copy/EEex_HookIntegrityWatchdog.lua new file mode 100644 index 0000000..2ed6adc --- /dev/null +++ b/EEex/copy/EEex_HookIntegrityWatchdog.lua @@ -0,0 +1,209 @@ + +EEex_HookIntegrityWatchdog_Load = true + +(function() + + if not EEex_HookIntegrityWatchdog_Load then + return + end + + local hookIntegrityWatchdogEnter = EEex_JITNear({[[ + + #STACK_MOD(8) + #MAKE_SHADOW_SPACE(184) + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r8 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)], r9 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)], r10 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)], r11 + + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)], rax + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-176)], rbx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-168)], rcx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-160)], rdx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-152)], rbp + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(48)] ; 8 + 32 + 8 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-144)], rax ; rsp + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-136)], rsi + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-128)], rdi + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-120)], r8 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-112)], r9 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-104)], r10 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-96)], r11 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-88)], r12 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-80)], r13 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-72)], r14 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-64)], r15 + + lea rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)] + mov rcx, qword ptr ss:[rsp+#LAST_FRAME_TOP(40)] ; 8 + 32 + 0 + call #L(EEex::HookIntegrityWatchdogEnter) + + mov r11, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)] + mov r10, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)] + mov r9, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)] + mov r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rcx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ret + ]]}) + + local hookIntegrityWatchdogExit = EEex_JITNear({[[ + + #STACK_MOD(8) + #MAKE_SHADOW_SPACE(184) + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r8 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)], r9 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)], r10 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)], r11 + + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)], rax + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-176)], rbx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-168)], rcx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-160)], rdx + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-152)], rbp + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(56)] ; 8 + 32 + 16 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-144)], rax ; rsp + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-136)], rsi + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-128)], rdi + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-120)], r8 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-112)], r9 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-104)], r10 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-96)], r11 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-88)], r12 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-80)], r13 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-72)], r14 + mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-64)], r15 + + lea r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)] + mov rdx, qword ptr ds:[rsp+#LAST_FRAME_TOP(48)] ; 8 + 32 + 8 + mov rcx, qword ptr ss:[rsp+#LAST_FRAME_TOP(40)] ; 8 + 32 + 0 + call #L(EEex::HookIntegrityWatchdogExit) + + mov r11, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)] + mov r10, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)] + mov r9, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)] + mov r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rcx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ret + ]]}) + + EEex_HookIntegrityWatchdog_HookEnter = {[[ + + #MAKE_SHADOW_SPACE(32) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax ; Save RAX + pushfq #STACK_MOD(8) ; Save status flags + pop rax #STACK_MOD(-8) + and rax, 0x8D5 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rax + + lea rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; Save previous frame's rsp as second stack arg + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rax + + mov rax, #L(hook_address) ; Save hook address as first stack arg + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], rax + + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX + call ]], hookIntegrityWatchdogEnter, [[ #ENDL + + pushfq #STACK_MOD(8) ; Restore status flags + and qword ptr ss:[rsp], 0xFFFFFFFFFFFFF72A + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + or qword ptr ss:[rsp], rax + popfq #STACK_MOD(-8) + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX (again) + #DESTROY_SHADOW_SPACE + ]]} + + local cachedHookExit = {} + EEex_HookIntegrityWatchdog_HookExit = function(instance) + local cached = cachedHookExit[instance] + if cached then return cached end + local t = {[[ + + #MAKE_SHADOW_SPACE(40) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax ; Save RAX + pushfq #STACK_MOD(8) ; Save status flags + pop rax #STACK_MOD(-8) + and rax, 0x8D5 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rax + + lea rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; Save previous frame's rsp as third stack arg + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rax + + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], ]], instance, [[ ; Save instance as second stack arg + + mov rax, #L(hook_address) ; Save hook address as first stack arg + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-40)], rax + + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX + call ]], hookIntegrityWatchdogExit, [[ #ENDL + + pushfq #STACK_MOD(8) ; Restore status flags + and qword ptr ss:[rsp], 0xFFFFFFFFFFFFF72A + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + or qword ptr ss:[rsp], rax + popfq #STACK_MOD(-8) + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX (again) + #DESTROY_SHADOW_SPACE + ]]} + cachedHookExit[instance] = t + return t + end + + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance = function(address, instance, defaultRegisters) + local ignoreFlags = EEex_Flags(defaultRegisters) + local ignoredRegisters = EEex_TryLabel("hook_integrity_watchdog_ignore_registers_"..instance) + if ignoredRegisters then + ignoreFlags = EEex_BOr(ignoreFlags, EEex_Flags(ignoredRegisters)) + end + EEex.HookIntegrityWatchdogIgnoreRegisters(address, instance, ignoreFlags) + end + + EEex_HookIntegrityWatchdog_IgnoreRegisters = function(address, defaultRegisters) + local ignoreFlags = EEex_Flags(defaultRegisters) + local ignoredRegisters = EEex_TryLabel("hook_integrity_watchdog_ignore_registers") + if ignoredRegisters then + ignoreFlags = EEex_BOr(ignoreFlags, EEex_Flags(ignoredRegisters)) + end + EEex.HookIntegrityWatchdogIgnoreRegisters(address, 0, ignoreFlags) + end + + local ignoreStackSize = function(address, instance, offset, size) + EEex.HookIntegrityWatchdogIgnoreStackRange(address, instance, offset, offset + size - 1) + end + + EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance = function(address, instance) + -- Stack mod indicates that the hook isn't operating in the usual stack frame. None of + -- the stack should be ignored if stack_mod is defined, as the shadow space at the top + -- of the stack actually belongs to the called function that's being hooked. + if not EEex_TryLabel("stack_mod") then + ignoreStackSize(address, instance, 0, 32) + end + end + + EEex_HookIntegrityWatchdog_DefaultIgnoreStack = function(address) + EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance(address, 0) + end + + EEex_HookIntegrityWatchdog_IgnoreStackSizesForInstance = function(address, instance, stackSizes) + for _, stackSize in ipairs(stackSizes) do + ignoreStackSize(address, instance, stackSize[1], stackSize[2]) + end + end + + EEex_HookIntegrityWatchdog_IgnoreStackSizes = function(address, stackSizes) + EEex_HookIntegrityWatchdog_IgnoreStackSizesForInstance(address, 0, stackSizes) + end + +end)() diff --git a/EEex/copy/EEex_Key_Patch.lua b/EEex/copy/EEex_Key_Patch.lua index 0593fe4..c8e5a00 100644 --- a/EEex/copy/EEex_Key_Patch.lua +++ b/EEex/copy/EEex_Key_Patch.lua @@ -3,9 +3,16 @@ EEex_DisableCodeProtection() - ------------------------------------- - -- EEex_Key_Hook_AfterEventsPoll() -- - ------------------------------------- + --[[ + +--------------------------------------------------------------------+ + | React to input by intercepting SDL events | + +--------------------------------------------------------------------+ + | [Lua] EEex_Key_Hook_AfterEventsPoll(event: SDL_Event) -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Suppress event | + +--------------------------------------------------------------------+ + --]] local afterEventsPollHook = EEex_JITNear(EEex_FlattenTable({ {[[ @@ -40,17 +47,19 @@ ]]}, })) - EEex_HookRelativeBranch(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-1"), {[[ - call #L(original) - call ]], afterEventsPollHook, [[ #ENDL - jmp #L(return) - ]]}) - - EEex_HookRelativeBranch(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-2"), {[[ - call #L(original) - call ]], afterEventsPollHook, [[ #ENDL - jmp #L(return) - ]]}) + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-1"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + {[[ + call ]], afterEventsPollHook, [[ #ENDL + ]]} + ) + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-2"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + {[[ + call ]], afterEventsPollHook, [[ #ENDL + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_LuaBindings_Patch.lua b/EEex/copy/EEex_LuaBindings_Patch.lua index 85105a1..aa0c6af 100644 --- a/EEex/copy/EEex_LuaBindings_Patch.lua +++ b/EEex/copy/EEex_LuaBindings_Patch.lua @@ -9,14 +9,15 @@ }) end - override("tolua_open") - override("tolua_cclass") - - override("module_newindex_event") + override("class_index_event") override("class_newindex_event") - override("module_index_event") - override("class_index_event") + override("module_newindex_event") + override("tolua_beginmodule") + override("tolua_cclass") + override("tolua_module") + override("tolua_open") + override("tolua_usertype") EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Main.lua b/EEex/copy/EEex_Main.lua index 0f9f04e..57402cd 100644 --- a/EEex/copy/EEex_Main.lua +++ b/EEex/copy/EEex_Main.lua @@ -1,4 +1,8 @@ +-- This is the main startup file for EEex. InfinityLoader redirects control flow after the CALL instruction +-- located at [Hardcoded_InternalPatchLocation] in order to (potentially) initialize Lua, initialize +-- hardcoded EEex state, and call this file. + ------------- -- Options -- ------------- @@ -61,15 +65,24 @@ EEex_Main_Private_MinimalStutterStartupFiles = { (function() + -- Contains most of the code editing functions. This file is the core of EEex. EEex_DoFile("EEex_Assembly") - EEex_LoadLuaBindings("LuaBindings-v2.6.6.0", function() - EEex_GlobalAssemblyLabels = EEex_GetPatternMap() + + -- Contains Lua bindings that map engine structures to Lua + EEex_OpenLuaBindings("LuaBindings-v2.6.6.0", function() + -- Patches in-engine tolua functions with EEex versions. + -- These are required for proper bindings operation. EEex_DoFile("EEex_LuaBindings_Patch") end) - EEex_DoFile("EEex_Assembly_Patch") + -- Contains EEex's C++ functionality + EEex_OpenLuaBindings("EEex") + EEex_DoFile("EEex_HookIntegrityWatchdog") + + -- Defines information about usertypes for the EEex_MemoryManager helper EEex_DoFile("EEex_MemoryManagerDefinitions") + -- Run EEex's other files (which each pertain to a specific category) for _, fileName in ipairs(not EEex_Main_MinimalStutterStartup and EEex_Main_Private_NormalStartupFiles or EEex_Main_Private_MinimalStutterStartupFiles) @@ -78,21 +91,25 @@ EEex_Main_Private_MinimalStutterStartupFiles = { end if not EEex_Main_MinimalStutterStartup then + + -- This file may run before the game is initialized. + -- The following listener runs files that need to + -- wait for the game to be somewhat initialized. EEex_GameState_AddInitializedListener(function() EEex_DoFile("EEex_UserDataGlobals") - EEex_DoFile("EEex_Opcode_Init") + EEex_DoFile("EEex_StutterDetector") end) + -- Run EEex_Modules.lua, which determines the enabled EEex modules EEex_DoFile("EEex_Modules") for moduleName, enabled in pairs(EEex_Modules) do if enabled then + -- Load the enabled modules EEex_DoFile(moduleName) end end end - EEex_DoFile("EEex_StutterDetector") - -- Stops a call to SDL_LogOutput() higher in this file -- preventing the console from attaching later on EEex_Write32(EEex_Label("Data-EngineConsoleAttachedPtr"), 0) diff --git a/EEex/copy/EEex_Menu_Patch.lua b/EEex/copy/EEex_Menu_Patch.lua index 7b19475..1cb1de1 100644 --- a/EEex/copy/EEex_Menu_Patch.lua +++ b/EEex/copy/EEex_Menu_Patch.lua @@ -3,259 +3,345 @@ EEex_DisableCodeProtection() - ---------------------------------------- - -- EEex_Menu_Hook_BeforeMenuStackSave -- - ---------------------------------------- - - EEex_HookRelativeBranch(EEex_Label("Hook-uiRefreshMenu()-saveMenuStack()"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(32) - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_BeforeMenuStackSave"), - {[[ - call_error: - call #L(original) - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}, - })) - - ------------------------------------------ - -- EEex_Menu_Hook_AfterMenuStackRestore -- - ------------------------------------------ - - EEex_HookRelativeBranch(EEex_Label("Hook-uiRefreshMenu()-restoreMenuStack()"), EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; The stack wasn't aligned to begin with - #MAKE_SHADOW_SPACE(32) - call #L(original) - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_AfterMenuStackRestore"), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ret - ]]}, - })) - - ------------------------------------------ - -- EEex_Menu_Hook_AfterMainFileLoaded() -- - ------------------------------------------ - - EEex_HookRelativeBranch(EEex_Label("Hook-dimmInit()-uiLoadMenu()"), EEex_FlattenTable({ - {[[ - call #L(original) - #MAKE_SHADOW_SPACE(32) - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_AfterMainFileLoaded"), + --[[ + +--------------------------------------------------------------------------------------------+ + | Call a hook before the engine saves the menu stack (UI edit mode - F5) | + +--------------------------------------------------------------------------------------------+ + | Used to implement listeners that dynamically load additional menus / edit existing menus | + +--------------------------------------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_BeforeMenuStackSave() | + +--------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-uiRefreshMenu()-saveMenuStack()"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_BeforeMenuStackSave"), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +--------------------------------------------------------------------------------------------+ + | Call a hook after the engine restores the menu stack (UI edit mode - F5) | + +--------------------------------------------------------------------------------------------+ + | Used to implement listeners that dynamically load additional menus / edit existing menus | + +--------------------------------------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_AfterMenuStackRestore() | + +--------------------------------------------------------------------------------------------+ + --]] + + EEex_HookRelativeJumpWithLabels(EEex_Label("Hook-uiRefreshMenu()-restoreMenuStack()"), { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}, + {"manual_continue", true}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + call #L(original) + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_AfterMenuStackRestore"), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + #MANUAL_HOOK_EXIT(0) + ret + ]]}, + }) + ) + + --[[ + +------------------------------------------------------------------------------------------------------------+ + | Call a hook after the engine loads UI.MENU | + +------------------------------------------------------------------------------------------------------------+ + | * Allows EEex to distinguish "native" menus (those in UI.MENU) from those that were dynamically injected | + | * Used to implement listeners that dynamically load additional menus / edit existing menus | + +------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_AfterMainFileLoaded() | + +------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-dimmInit()-uiLoadMenu()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_AfterMainFileLoaded"), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +-------------------------------------------------------------------------------------------------------------------+ + | Infinity_InstanceAnimation() can be forced to create the template in an arbitrary menu by setting a hook variable | + +-------------------------------------------------------------------------------------------------------------------+ + | [Lua Global] EEex_Menu_HookGlobal_TemplateMenuOverride: uiMenu | + +-------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookNOPsWithLabels(EEex_Label("Hook-Infinity_InstanceAnimation()-TemplateMenuOverride"), 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}, - })) - - -------------------------------------------------------------------------------- - -- EEex_Menu_HookGlobal_TemplateMenuOverride for Infinity_InstanceAnimation() -- - -------------------------------------------------------------------------------- - - EEex_HookNOPs(EEex_Label("Hook-Infinity_InstanceAnimation()-TemplateMenuOverride"), 7, {[[ - - #MAKE_SHADOW_SPACE(16) - - mov qword ptr ss:[rsp+32], rdx - - mov rdx, ]], EEex_WriteStringCache("EEex_Menu_HookGlobal_TemplateMenuOverride"), [[ ; name - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_lua_getglobal) - #ALIGN_END - - mov r8, 0 ; def - mov rdx, -1 ; narg - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_tolua_tousertype) - #ALIGN_END - - mov qword ptr ss:[rsp+40], rax - - mov rdx, -2 ; index - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_lua_settop) - #ALIGN_END - - mov rax, qword ptr ss:[rsp+40] - test rax, rax - jnz keep_override - - mov rax, qword ptr ds:[rbp+8] + #MAKE_SHADOW_SPACE(16) - keep_override: - mov rdx, qword ptr ss:[rsp+32] - mov qword ptr ds:[rdx+8], rax + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx - #DESTROY_SHADOW_SPACE - jmp #L(return) - ]]}) + mov rdx, ]], EEex_WriteStringCache("EEex_Menu_HookGlobal_TemplateMenuOverride"), [[ ; name + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_lua_getglobal) - ------------------------------------------------------------------------------- - -- EEex_Menu_HookGlobal_TemplateMenuOverride for Infinity_DestroyAnimation() -- - ------------------------------------------------------------------------------- + mov r8, 0 ; def + mov rdx, -1 ; narg + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_tolua_tousertype) - EEex_HookBetweenRestore(EEex_Label("Hook-Infinity_DestroyAnimation()-TemplateMenuOverride"), 0, 4, 4, 3, 7, {[[ + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rax - #MAKE_SHADOW_SPACE(8) + mov rdx, -2 ; index + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_lua_settop) - mov rdx, ]], EEex_WriteStringCache("EEex_Menu_HookGlobal_TemplateMenuOverride"), [[ ; name - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_lua_getglobal) - #ALIGN_END - - mov r8, 0 ; def - mov rdx, -1 ; narg - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_tolua_tousertype) - #ALIGN_END - - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + test rax, rax + jnz keep_override - mov rdx, -2 ; index - mov rcx, #L(Hardcoded_InternalLuaState) ; L - #ALIGN - call #L(Hardcoded_lua_settop) - #ALIGN_END + mov rax, qword ptr ds:[rbp+8] - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - test rax, rax - jz no_override + keep_override: + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + mov qword ptr ds:[rdx+8], rax ; (Potentially) override menu - mov rbx, rax + #DESTROY_SHADOW_SPACE + ]]} + ) + + --[[ + +-------------------------------------------------------------------------------------------------------------------+ + | Infinity_DestroyAnimation() can be forced to destroy the template in an arbitrary menu by setting a hook variable | + +-------------------------------------------------------------------------------------------------------------------+ + | [Lua Global] EEex_Menu_HookGlobal_TemplateMenuOverride: uiMenu | + +-------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBetweenRestoreWithLabels(EEex_Label("Hook-Infinity_DestroyAnimation()-TemplateMenuOverride"), 0, 4, 4, 3, 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RBX, EEex_HookIntegrityWatchdogRegister.RCX, + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + {[[ + #MAKE_SHADOW_SPACE(8) - no_override: - #DESTROY_SHADOW_SPACE - ]]}) + mov rdx, ]], EEex_WriteStringCache("EEex_Menu_HookGlobal_TemplateMenuOverride"), [[ ; name + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_lua_getglobal) - ------------------------------------------------------------------- - -- EEex_Menu_Hook_CheckSaveMenuItem() -- - -- Prevent EEex_LoadMenuFile() from causing crash when using F11 -- - ------------------------------------------------------------------- + mov r8, 0 ; def + mov rdx, -1 ; narg + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_tolua_tousertype) - EEex_HookJumpOnFail(EEex_Label("Hook-saveMenus()-CheckItemSave"), 5, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(56) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_CheckSaveMenuItem", { - ["args"] = { - function(rspOffset) return {[[ - lea rax, qword ptr ds:[r14-0x28] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "uiMenu" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+", rspOffset, "], rbx #ENDL"}, "uiItem" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - mov rax, 1 - no_error: - test rax, rax + mov rdx, -2 ; index + mov rcx, #L(Hardcoded_InternalLuaState) ; L + call #L(Hardcoded_lua_settop) mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jz #L(jmp_success) - ]]}, - })) - - -------------------------------------------- - -- EEex_Menu_Hook_BeforeListRenderingItem -- - -------------------------------------------- - - EEex_HookBeforeCall(EEex_Label("Hook-RenderListCallback()-drawItem()"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(112) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_BeforeListRenderingItem", { - ["args"] = { - -- list - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "uiItem" end, - -- item - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "uiItem" end, - -- window - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "SDL_Rect" end, - -- rClipBase - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r8 #ENDL", {rspOffset}}, "SDL_Rect" end, - -- alpha - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r9 #ENDL", {rspOffset}} end, - -- menu - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(20h)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "uiMenu" end, - }, - }), - {[[ - call_error: - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------------- - -- EEex_Menu_Hook_CheckForceScrollbarRender -- - ---------------------------------------------- - - EEex_HookJumpOnSuccess(EEex_Label("Hook-drawItem()-CheckScrollbarContentHeight"), 0, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Menu_Hook_CheckForceScrollbarRender", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "uiItem" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - xor rax, rax - - no_error: test rax, rax + jz no_override - #DESTROY_SHADOW_SPACE - jnz jmp_fail - ]]}, - })) + mov rbx, rax ; Override menu - --------------------------------------------------------- - -- Fix forced scrollbar crashing with a divide by zero -- - --------------------------------------------------------- + no_override: + #DESTROY_SHADOW_SPACE + ]]} + ) + + --[[ + +---------------------------------------------------------------------------------+ + | Prevent EEex_LoadMenuFile() from causing a crash when using UI edit mode's F11 | + +---------------------------------------------------------------------------------+ + | Menus injected by EEex do not exist in UI.MENU, and yet the engine attempts | + | to write their items back to UI.MENU when F11 is toggled off. This hook | + | forces the engine to skip this writing behavior for dynamically injected | + | menus. | + +---------------------------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_CheckSaveMenuItem(menu: uiMenu, item: uiItem) -> boolean | + | return: | + | -> false - Don't write item back to UI.MENU | + | -> true - Write item back to UI.MENU | + +---------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-saveMenus()-CheckItemSave"), 5, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(56) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_CheckSaveMenuItem", { + ["args"] = { + function(rspOffset) return {[[ + lea rax, qword ptr ds:[r14-0x28] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "uiMenu" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+", rspOffset, "], rbx #ENDL"}, "uiItem" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + mov rax, 1 + + no_error: + test rax, rax + + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jz #L(jmp_success) + ]]}, + }) + ) + + --[[ + +--------------------------------------------------------------------------------------------------+ + | Call a hook before UI lists render one of their items | + +--------------------------------------------------------------------------------------------------+ + | Used to implement listeners that can alter list rendering behavior | + +--------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_BeforeListRenderingItem(list: uiItem, item: uiItem, window: SDL_Rect, | + | rClipBase: SDL_Rect, alpha: number, menu: uiMenu) | + +--------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-RenderListCallback()-drawItem()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(112) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_BeforeListRenderingItem", { + ["args"] = { + -- list + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "uiItem" end, + -- item + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "uiItem" end, + -- window + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "SDL_Rect" end, + -- rClipBase + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r8 #ENDL", {rspOffset}}, "SDL_Rect" end, + -- alpha + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r9 #ENDL", {rspOffset}} end, + -- menu + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(20h)] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "uiMenu" end, + }, + }), + {[[ + call_error: + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +---------------------------------------------------------------+ + | Call a hook that can force a UI list to render its scrollbar | + +---------------------------------------------------------------+ + | [Lua] EEex_Menu_Hook_CheckForceScrollbarRender() -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Force the list's scrollbar to render | + +---------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-drawItem()-CheckScrollbarContentHeight"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx + ]]}, + EEex_GenLuaCall("EEex_Menu_Hook_CheckForceScrollbarRender", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "uiItem" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + test rax, rax + + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jnz #L(jmp_fail) + ]]}, + }) + ) + + --[[ + +----------------------------------------------------------------------+ + | [JIT] Fix forced scrollbars sometimes crashing with a divide by zero | + +----------------------------------------------------------------------+ + --]] for _, address in ipairs({ EEex_Label("Hook-drawItem()-FixForcedScrollbarDivideByZero1"), EEex_Label("Hook-drawItem()-FixForcedScrollbarDivideByZero2") }) do - EEex_HookAfterRestore(address, 0, 11, 11, {[[ - test eax, eax - jnz #L(return) - mov eax, -1 - ]]}) + EEex_HookAfterRestoreWithLabels(address, 0, 11, 11, { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + {[[ + test eax, eax + jnz #L(return) + mov eax, -1 + ]]} + ) end EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Object_Patch.lua b/EEex/copy/EEex_Object_Patch.lua index d004402..f8476c8 100644 --- a/EEex/copy/EEex_Object_Patch.lua +++ b/EEex/copy/EEex_Object_Patch.lua @@ -3,92 +3,116 @@ EEex_DisableCodeProtection() - --------------------- - -- 117 EEex_Target -- - --------------------- - - EEex_HookJumpOnFail(EEex_Label("Hook-CAIObjectType::Decode()-TargetNameOverride"), 0, EEex_FlattenTable({[[ - - #MAKE_SHADOW_SPACE(40) - - ]], EEex_GenLuaCall("EEex_Object_Hook_ForceIgnoreActorScriptName", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ - - jmp no_error - - call_error: - xor rax, rax - - no_error: - test rax, rax - - #DESTROY_SHADOW_SPACE - jnz #L(jmp_success) - ]]})) - - ------------------------------------------ - -- EEex_Object_Hook_OnEvaluatingUnknown -- - ------------------------------------------ - - EEex_HookJumpAutoSucceed(EEex_Label("Hook-CAIObjectType::Decode()-DefaultJmp"), 0, EEex_FlattenTable({[[ - - jbe jmp_fail - - #MAKE_SHADOW_SPACE(88) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 - - ]], EEex_GenLuaCall("EEex_Object_Hook_OnEvaluatingUnknown", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r12 #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}} end, - function(rspOffset) return {[[ - lea rax, qword ptr ss:[rbp-11h] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CAIObjectType" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), [[ - - jmp no_error - - call_error: - mov rax, #$(1) ]], {{EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED}}, [[ #ENDL - - no_error: - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - - cmp rax, #$(1) ]], {{EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE}}, [[ #ENDL - je normal_return - - cmp rax, #$(1) ]], {{EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE}}, [[ #ENDL - je done_return - - jmp unhandled_return - - normal_return: - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(Hook-CAIObjectType::Decode()-NormalBranch) - - done_return: - #RESUME_SHADOW_ENTRY - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(Hook-CAIObjectType::Decode()-ReturnBranch) - - unhandled_return: - #RESUME_SHADOW_ENTRY - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]})) + --[[ + +----------------------------------------------------------------------------------------------+ + | Prevent certain OBJECT.IDS entries from interpreting their string parameter as a script name | + +----------------------------------------------------------------------------------------------+ + | 117 EEex_Target() | + +----------------------------------------------------------------------------------------------+ + | [Lua] EEex_Object_Hook_ForceIgnoreActorScriptName(aiType: CAIObjectType) -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Even though the script object was defined with a string parameter, | + | this string should not be treated as a script name | + +----------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CAIObjectType::Decode()-TargetNameOverride"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Object_Hook_ForceIgnoreActorScriptName", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor rax, rax + + no_error: + test rax, rax + + #DESTROY_SHADOW_SPACE + jnz #L(jmp_success) + ]]}, + }) + ) + + --[[ + +------------------------------------------------------------------------------------------------------------------------------+ + | Implement new OBJECT.IDS entries | + +------------------------------------------------------------------------------------------------------------------------------+ + | 117 EEex_Target() | + +------------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Object_Hook_OnEvaluatingUnknown(decodingAIType: CAIObjectType, caller CGameAIBase|EEex_GameObject_CastUT, | + | nSpecialCaseI: number, curAIType: CAIObjectType) -> number | + | return: | + | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE - Continue evaluating outer objects | + | | + | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE - Halt OBJECT.IDS processing and use curAIType | + | | + | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED - Engine falls back to "Myself" and continues | + | evaluating outer objects | + +------------------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CAIObjectType::Decode()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(72) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx + ]]}, + EEex_GenLuaCall("EEex_Object_Hook_OnEvaluatingUnknown", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r12 #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}} end, + function(rspOffset) return {[[ + lea rax, qword ptr ss:[rbp-11h] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CAIObjectType" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Number, + }), + {[[ + jmp no_error + + call_error: + mov rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED}, [[ #ENDL + + no_error: + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + + cmp rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE}, [[ #ENDL + je normal_return + + cmp rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE}, [[ #ENDL + jne #L(jmp_success) + + #MANUAL_HOOK_EXIT(0) + jmp #L(Hook-CAIObjectType::Decode()-ReturnBranch) + + normal_return: + #MANUAL_HOOK_EXIT(0) + jmp #L(Hook-CAIObjectType::Decode()-NormalBranch) + ]]}, + }) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Opcode.lua b/EEex/copy/EEex_Opcode.lua index db26d37..c7c96a2 100644 --- a/EEex/copy/EEex_Opcode.lua +++ b/EEex/copy/EEex_Opcode.lua @@ -6,6 +6,8 @@ EEex_Opcode_ListsResolvedListeners = {} function EEex_Opcode_AddListsResolvedListener(func) + -- [EEex.dll] + EEex.Opcode_LuaHook_AfterListsResolved_Enabled = true table.insert(EEex_Opcode_ListsResolvedListeners, func) end @@ -17,7 +19,8 @@ function EEex_Opcode_Private_ApplyExtraMeleeEffects(sprite, targetSprite) EEex_Utility_IterateCPtrList(sprite:getActiveStats().m_cExtraMeleeEffects, function(effect) - if not EEex_GetUDAux(effect)["EEex_BypassOp120"] then + -- [EEex.dll] + if not EEex.ShouldEffectBypassOp120(effect) then return -- continue end @@ -46,7 +49,8 @@ function EEex_Opcode_Private_ApplyExtraRangedEffects(sprite, targetSprite) EEex_Utility_IterateCPtrList(sprite:getActiveStats().m_cExtraRangedEffects, function(effect) - if not EEex_GetUDAux(effect)["EEex_BypassOp120"] then + -- [EEex.dll] + if not EEex.ShouldEffectBypassOp120(effect) then return -- continue end @@ -119,21 +123,23 @@ end -- Hooks -- ----------- -function EEex_Opcode_Hook_AfterListsResolved(sprite) +function EEex_Opcode_LuaHook_AfterListsResolved(sprite) for _, func in ipairs(EEex_Opcode_ListsResolvedListeners) do func(sprite) end end ---+--------------------------------------------------------------------------------+ ---| Opcode #214 | ---+--------------------------------------------------------------------------------+ ---| param2 == 3 -> Call Lua function in resource field to get CButtonData iterator | ---+--------------------------------------------------------------------------------+ ---| Hook return: | ---| false -> Effect not handled | ---| true -> Effect handled (skip normal code) | ---+--------------------------------------------------------------------------------+ +--[[ ++--------------------------------------------------------------------------------+ +| Opcode #214 | ++--------------------------------------------------------------------------------+ +| param2 == 3 -> Call Lua function in resource field to get CButtonData iterator | ++--------------------------------------------------------------------------------+ +| Hook return: | +| false -> Effect not handled | +| true -> Effect handled (skip normal code) | ++--------------------------------------------------------------------------------+ +--]] function EEex_Opcode_Hook_OnOp214ApplyEffect(effect, sprite) @@ -157,12 +163,6 @@ end -- Opcode #248 (Special BIT0 allows .EFF to bypass op120) -- ------------------------------------------------------------ -function EEex_Opcode_Hook_OnOp248AddTail(op248, effect) - if EEex_IsBitSet(op248.m_special, 0) then - EEex_GetUDAux(effect)["EEex_BypassOp120"] = true - end -end - function EEex_Opcode_Hook_OnAfterSwingCheckedOp248(sprite, targetSprite, bBlocked) if bBlocked then EEex_Opcode_Private_ApplyExtraMeleeEffects(sprite, targetSprite) @@ -173,12 +173,6 @@ end -- Opcode #249 (Special BIT0 allows .EFF to bypass op120) -- ------------------------------------------------------------ -function EEex_Opcode_Hook_OnOp249AddTail(op249, effect) - if EEex_IsBitSet(op249.m_special, 0) then - EEex_GetUDAux(effect)["EEex_BypassOp120"] = true - end -end - function EEex_Opcode_Hook_OnAfterSwingCheckedOp249(sprite, targetSprite, bBlocked) if bBlocked then @@ -197,167 +191,12 @@ function EEex_Opcode_Hook_OnAfterSwingCheckedOp249(sprite, targetSprite, bBlocke end end ------------------------------------------------------------------------ --- Opcode #280 -- --- param1 != 0 => Force wild surge number -- --- special != 0 => Suppress wild surge feedback string and visuals -- ------------------------------------------------------------------------ - -function EEex_Opcode_Hook_OnOp280ApplyEffect(effect, sprite) - local statsAux = EEex_GetUDAux(sprite.m_derivedStats) - local t = EEex_Utility_GetOrCreateTable(statsAux, "EEex_Op280") - t.param1 = effect.m_effectAmount - t.special = effect.m_special -end - --- Return: --- 0 => Don't override wild surge number --- !0 => Override wild surge number -function EEex_Opcode_Hook_OverrideWildSurgeNumber(sprite) - local statsAux = EEex_GetUDAux(sprite:getActiveStats()) - local t = statsAux["EEex_Op280"] - return t and t.param1 or 0 -end - --- Return: --- false => Don't suppress wild surge feedback string and visuals --- true => Suppress wild surge feedback string and visuals -function EEex_Opcode_Hook_SuppressWildSurgeVisuals(sprite) - local statsAux = EEex_GetUDAux(sprite:getActiveStats()) - local t = statsAux["EEex_Op280"] - return t and t.special ~= 0 or false -end - --------------------------------------------------------------------------- --- Opcode #326 (Special BIT0 flips SPLPROT.2DA's "source" and "target") -- --------------------------------------------------------------------------- - -function EEex_Opcode_Hook_ApplySpell_ShouldFlipSplprotSourceAndTarget(effect) - return EEex_IsBitSet(effect.m_special, 1) -end - -------------------------------------------------------------------------------------------------- --- Opcode #333 (param3 BIT0 allows "SPL" file not to terminate upon a successful saving throw) -- -------------------------------------------------------------------------------------------------- +-------------------------------------------------------------- +-- Opcode #333 (param3 BIT0 - only check saving throw once) -- +-------------------------------------------------------------- function EEex_Opcode_Hook_OnOp333CopiedSelf(effect) if EEex_IsBitSet(effect.m_effectAmount2, 0) then effect.m_savingThrow = 0 end end - ----------------------------------------------------- --- Allow saving throw BIT23 to bypass opcode #101 -- ----------------------------------------------------- - -function EEex_Opcode_Hook_CImmunitiesEffect_BypassOp101(effect) - return EEex_IsBitSet(effect.m_savingThrow, 23) -end - --------------------------------------------- --- New Opcode #400 (SetTemporaryAIScript) -- --------------------------------------------- - -function EEex_Opcode_Hook_SetTemporaryAIScript_ApplyEffect(effect, sprite) - if effect.m_firstCall == 0 then - return - end - local param2 = effect.m_dWFlags - if param2 < 0 or param2 > 7 or param2 == 3 then - -- Engine fails to call OnRemove() if an effect was applied with immediateResolve=1 - -- and it immediately returns from ApplyEffect() with m_done=1 - effect.m_done = 1 - return - end - effect.m_firstCall = 0 - local existingScript = sprite:getScriptLevel(param2) - effect.m_effectAmount2 = EEex_Utility_Ternary(existingScript, - function() return existingScript:isPlayerScript() end, - function() return false end) - effect.m_res2:set(existingScript and existingScript:getResRef() or "") - sprite:setScriptLevelResRef(param2, effect.m_res:get()) -end - -function EEex_Opcode_Hook_SetTemporaryAIScript_OnRemove(effect, sprite) - if effect.m_firstCall == 0 then - sprite:setScriptLevelResRef(effect.m_dWFlags, effect.m_res2:get(), effect.m_effectAmount2) - end -end - ---------------------------------------- --- New Opcode #401 (SetExtendedStat) -- ---------------------------------------- - -function EEex_Opcode_Hook_ApplySetExtendedStat(effect, sprite) - - local exStats = EEex_GetUDAux(sprite.m_derivedStats)["EEex_ExtendedStats"] - - local param1 = effect.m_effectAmount - local modType = effect.m_dWFlags - local exStatID = effect.m_special - - if not EEex_Stats_ExtendedInfo[exStatID] then - print("[EEex_SetExtendedStat - Opcode #401] Invalid extended stat id: "..exStatID) - return - end - - local newVal - - if modType == 0 then -- cumulative - newVal = exStats[exStatID] + param1 - elseif modType == 1 then -- flat - newVal = param1 - elseif modType == 2 then -- percentage - newVal = math.floor(exStats[exStatID] * math.floor(param1 / 100)) - else - return - end - - EEex_Stats_Private_SetExtended(exStats, exStatID, newVal) -end - -------------------------------------- --- New Opcode #403 (ScreenEffects) -- -------------------------------------- - -function EEex_Opcode_Hook_ApplyScreenEffects(effect, sprite) - local statsAux = EEex_GetUDAux(sprite.m_derivedStats) - table.insert(statsAux["EEex_ScreenEffects"], effect) -end - --- Return: --- false => Allow effect (other immunities can still block it) --- true => Block effect -function EEex_Opcode_Hook_OnCheckAdd(effect, sprite) - - local foundImmunity = false - local statsAux = EEex_GetUDAux(sprite:getActiveStats()) - - for _, screenEffect in ipairs(statsAux["EEex_ScreenEffects"]) do - local immunityFunc = _G[screenEffect.m_res:get()] - if immunityFunc and immunityFunc(screenEffect, effect, sprite) then - foundImmunity = true - break - end - end - - return foundImmunity -end - ------------------------------------------ --- New Opcode #408 (ProjectileMutator) -- ------------------------------------------ - -function EEex_Opcode_Hook_ProjectileMutator_ApplyEffect(effect, sprite) - local statsAux = EEex_GetUDAux(sprite.m_derivedStats) - table.insert(statsAux["EEex_ProjectileMutatorEffects"], effect) -end - --------------------------------------------- --- New Opcode #409 (EnableActionListener) -- --------------------------------------------- - -function EEex_Opcode_Hook_EnableActionListener_ApplyEffect(effect, sprite) - local statsAux = EEex_GetUDAux(sprite.m_derivedStats) - statsAux["EEex_EnabledActionListeners"][effect.m_res:get()] = effect -end diff --git a/EEex/copy/EEex_Opcode_Init.lua b/EEex/copy/EEex_Opcode_Init.lua deleted file mode 100644 index 7e3c44e..0000000 --- a/EEex/copy/EEex_Opcode_Init.lua +++ /dev/null @@ -1,127 +0,0 @@ - -(function() - - local trivialStat = { - ["onConstruct"] = function(self, stats, aux) - aux[self.name] = {} - end, - ["onReload"] = function(self, stats, aux, sprite) - aux[self.name] = {} - end, - ["onEqu"] = function(self, stats, aux, otherStats, otherAux) - aux[self.name] = EEex_Utility_DeepCopy(otherAux[self.name]) - end, - ["onPlusEqu"] = function(self, stats, aux, otherStats, otherAux) - local insertI = #aux - for _, otherVal in ipairs(otherAux[self.name]) do - insertI = insertI + 1 - aux[insertI] = EEex_Utility_DeepCopy(otherVal) - end - end, - } - - trivialStat["__index"] = function(_, key) - return trivialStat[key] - end - - local registerStat = function(name, args) - args = args or {} - args.name = name - setmetatable(args, trivialStat) - EEex_Stats_Register(name, args) - end - - ----------------------------------------------------------------------- - -- Opcode #280 -- - -- param1 != 0 => Force wild surge number -- - -- special != 0 => Suppress wild surge feedback string and visuals -- - ----------------------------------------------------------------------- - - registerStat("EEex_Op280", { - ["onConstruct"] = function(self, stats, aux) - aux["EEex_Op280"] = nil - end, - ["onReload"] = function(self, stats, aux, sprite) - aux["EEex_Op280"] = nil - end, - ["onPlusEqu"] = function(self, stats, aux, otherStats, otherAux) end, - }) - - --------------------------------------- - -- New Opcode #401 (SetExtendedStat) -- - --------------------------------------- - - local statsIDS = EEex_Resource_LoadIDS("STATS") - local exStats2DA = EEex_Resource_Load2DA("X-STATS") - - local getExtendedStatField = function(name, id, field) - local val = exStats2DA:getAtLabels(field, name) - if val ~= "*" and not tonumber(val) then - print(string.format("[X-STATS.2DA] Invalid %s(#%d) %s value: \"%s\"", name, id, field, val)) - return "*" - end - return val - end - - for id = EEex_Stats_FirstExtendedStatID, statsIDS:getCount() - 1 do - if statsIDS:hasID(id) then - local name = statsIDS:getLine(id) - EEex_Stats_ExtendedInfo[id] = { - ["min"] = getExtendedStatField(name, id, "MIN"), - ["max"] = getExtendedStatField(name, id, "MAX"), - ["default"] = getExtendedStatField(name, id, "DEFAULT"), - } - end - end - - statsIDS:free() - exStats2DA:free() - - registerStat("EEex_ExtendedStats", { - ["onConstruct"] = function(self, stats, aux) - local t = {} - for id, _ in pairs(EEex_Stats_ExtendedInfo) do - t[id] = 0 - end - aux["EEex_ExtendedStats"] = t - end, - ["onReload"] = function(self, stats, aux, sprite) - local t = aux["EEex_ExtendedStats"] - for id, exStat in pairs(EEex_Stats_ExtendedInfo) do - EEex_Stats_Private_SetExtended(t, id, exStat.default ~= "*" and tonumber(exStat.default) or 0) - end - end, - ["onPlusEqu"] = function(self, stats, aux, otherStats, otherAux) - local exStats = aux["EEex_ExtendedStats"] - local otherExStats = otherAux["EEex_ExtendedStats"] - for id, _ in pairs(EEex_Stats_ExtendedInfo) do - EEex_Stats_Private_SetExtended(exStats, id, exStats[id] + otherExStats[id]) - end - end, - }) - - ------------------------------------- - -- New Opcode #403 (ScreenEffects) -- - ------------------------------------- - - registerStat("EEex_ScreenEffects") - - ----------------------------------------- - -- New Opcode #408 (ProjectileMutator) -- - ----------------------------------------- - - registerStat("EEex_ProjectileMutatorEffects") - - -------------------------------------------- - -- New Opcode #409 (EnableActionListener) -- - -------------------------------------------- - - registerStat("EEex_EnabledActionListeners", { - ["onPlusEqu"] = function(self, stats, aux, otherStats, otherAux) - for k, v in pairs(otherAux[self.name]) do - aux[k] = EEex_Utility_DeepCopy(v) - end - end, - }) - -end)() diff --git a/EEex/copy/EEex_Opcode_Patch.lua b/EEex/copy/EEex_Opcode_Patch.lua index 3a747aa..e6182af 100644 --- a/EEex/copy/EEex_Opcode_Patch.lua +++ b/EEex/copy/EEex_Opcode_Patch.lua @@ -3,112 +3,159 @@ EEex_DisableCodeProtection() - ------------------------------------------- - -- Clean up CGameEffect auxiliary values -- - ------------------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CGameEffect::Destruct()_FirstCall"), {[[ - mov rdx, rdi - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(EEex::DestroyUDAux) - ]]}) - - --------------------------------------- - -- Copy CGameEffect auxiliary values -- - --------------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CGameEffect::CopyFromBase()-FirstCall"), {[[ - - ; This is the only caller that isn't working with a CGameEffect. - mov rax, #$(1) ]], {EEex_Label("Data-CGameEffect::DecodeEffectFromBase()-After-CGameEffect::CopyFromBase()")}, [[ #ENDL - cmp qword ptr ss:[rsp+0x38], rax - je #L(return) - - mov r8, rdi ; targetPtr - lea rdx, qword ptr ds:[rsi-#$(1)] ]], {EEex_PtrSize}, [[ ; sourcePtr - mov rcx, #L(Hardcoded_InternalLuaState) - call #L(EEex::CopyUDAux) - ]]}) - - ----------------------------------------- - -- EEex_Opcode_Hook_AfterListsResolved -- - ----------------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::ProcessEffectList()-AfterListsResolved"), EEex_FlattenTable({ + --[[ + +--------------------------------------------------------------------------+ + | Clean up EEex data linked to a CGameEffect instance before it is deleted | + +--------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_OnDestruct(pEffect: CGameEffect*) | + +--------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameEffect::Destruct()_FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_AfterListsResolved", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), + mov rcx, rdi ; pEffect + call #L(EEex::Opcode_Hook_OnDestruct) + ]]} + ) + + --[[ + +---------------------------------------------------------------------------------------------+ + | Associate EEex data linked to a CGameEffect instance with a new CGameEffect instance (copy) | + +---------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_OnCopy(pSrcEffect: CGameEffect*, pDstEffect: CGameEffect*) | + +---------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameEffect::CopyFromBase()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - --+--------------------------------------------------------------------------------+ - --| Opcode #214 | - --+--------------------------------------------------------------------------------+ - --| param2 == 3 -> Call Lua function in resource field to get CButtonData iterator | - --+--------------------------------------------------------------------------------+ - --| Hook return: | - --| false -> Effect not handled | - --| true -> Effect handled (skip normal code) | - --+--------------------------------------------------------------------------------+ - - EEex_HookBeforeRestore(EEex_Label("Hook-CGameEffectSecondaryCastList::ApplyEffect()"), 0, 5, 5, EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(64) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OnOp214ApplyEffect", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), + ; This is the only caller that isn't working with a CGameEffect. + mov rax, #$(1) ]], {EEex_Label("Data-CGameEffect::DecodeEffectFromBase()-After-CGameEffect::CopyFromBase()")}, [[ #ENDL + cmp qword ptr ss:[rsp+0x38], rax + je #L(return) + + mov rdx, rdi ; pDstEffect + lea rcx, qword ptr ds:[rsi-#$(1)] ]], {EEex_PtrSize}, [[ ; pSrcEffect + call #L(EEex::Opcode_Hook_OnCopy) + ]]} + ) + + --[[ + +-------------------------------------------------------------------------------------+ + | Call a hook immediately after sprites have had both of their effect lists evaluated | + +-------------------------------------------------------------------------------------+ + | Used to implement listeners that act as "final" operations on a sprite | + +-------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_AfterListsResolved(pSprite: CGameSprite*) | + +-------------------------------------------------------------------------------------+ + | [Lua] EEex_Opcode_LuaHook_AfterListsResolved(sprite: CGameSprite) | + +-------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::ProcessEffectList()-AfterListsResolved"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - jmp no_error + mov rcx, rsi ; pSprite + call #L(EEex::Opcode_Hook_AfterListsResolved) + ]]} + ) + + -------------------------------------- + -- Opcode Changes -- + -------------------------------------- + + --[[ + +--------------------------------------------------------------------------------------------------+ + | Opcode #214 | + +--------------------------------------------------------------------------------------------------+ + | param2 == 3 -> Call the global Lua function with the name in `resource` to get a CButtonData | + | iterator. Then, use this iterator to determine which spells should be shown to | + | the player. Note that the function name must be 8 characters or less, and be | + | ALL UPPERCASE. | + | | + | resource -> Name of the global Lua function when `param2 == 3` | + +--------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Opcode_Hook_OnOp214ApplyEffect(effect: CGameEffect, sprite: CGameSprite) -> boolean | + | return: | + | -> false - Effect not handled | + | -> true - Effect handled (skip normal code) | + +--------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameEffectSecondaryCastList::ApplyEffect()"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(64) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + ]]}, + EEex_GenLuaCall("EEex_Opcode_Hook_OnOp214ApplyEffect", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error - call_error: - mov rax, 1 + call_error: + mov rax, 1 - no_error: - test rax, rax - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jz return - mov eax, 1 - ret - ]]}, - })) + no_error: + test rax, rax + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jz #L(return) - ------------------------------------------------------------ - -- Opcode #248 (Special BIT0 allows .EFF to bypass op120) -- - ------------------------------------------------------------ + mov eax, 1 + #MANUAL_HOOK_EXIT(1) + ret + ]]}, + }) + ) + -- Manually define the ignored registers for the unusual `ret` above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CGameEffectSecondaryCastList::ApplyEffect()"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) - EEex_HookAfterCall(EEex_Label("Hook-CGameEffectMeleeEffect::ApplyEffect()-AddTail"), EEex_FlattenTable({ + --[[ + +---------------------------------------------------------------------------------------------------------------------+ + | Opcode #248 | + +---------------------------------------------------------------------------------------------------------------------+ + | (special & 1) != 0 -> .EFF bypasses op120 | + +---------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_OnOp248AddTail(pOp248: CGameEffect*, pExtraEffect: CGameEffect*) | + +---------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp248(sprite: CGameSprite, targetSprite: CGameSprite, blocked: boolean) | + +---------------------------------------------------------------------------------------------------------------------+ + --]] + + --------------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_OnOp248AddTail() -- + --------------------------------------------------- + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameEffectMeleeEffect::ApplyEffect()-AddTail"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OnOp248AddTail", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameEffect" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + mov rdx, rbx ; pEffect + mov rcx, rdi ; pOp248 + call #L(EEex::Opcode_Hook_OnOp248AddTail) + ]]} + ) + + ------------------------------------------------------- + -- [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp248() -- + ------------------------------------------------------- EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Swing()-CImmunitiesWeapon::OnList()-Melee"), EEex_FlattenTable({ {[[ @@ -129,39 +176,44 @@ ]]}, })) - ------------------------------------------------------------ - -- Opcode #249 (Special BIT0 allows .EFF to bypass op120) -- - ------------------------------------------------------------ + --[[ + +---------------------------------------------------------------------------------------------------------------------+ + | Opcode #249 | + +---------------------------------------------------------------------------------------------------------------------+ + | (special & 1) != 0 -> .EFF bypasses op120 | + +---------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_OnOp249AddTail(pOp249: CGameEffect*, pExtraEffect: CGameEffect*) | + +---------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp249(sprite: CGameSprite, targetSprite: CGameSprite, blocked: boolean) | + +---------------------------------------------------------------------------------------------------------------------+ + --]] + + --------------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_OnOp249AddTail() -- + --------------------------------------------------- local op249SavedEffect = EEex_Malloc(EEex_PtrSize) EEex_HookBeforeRestore(EEex_Label("Hook-CGameEffectRangeEffect::ApplyEffect()"), 0, 6, 6, {[[ - mov qword ptr ds:[#$(1)], rcx - ]], {op249SavedEffect}}) + mov qword ptr ds:[#$(1)], rcx ]], {op249SavedEffect}, [[ #ENDL + ]]}) - EEex_HookAfterCall(EEex_Label("Hook-CGameEffectRangeEffect::ApplyEffect()-AddTail"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OnOp249AddTail", { - ["args"] = { - function(rspOffset) return {[[ - mov rax, qword ptr ss:[#$(1)] - mov qword ptr ss:[rsp+#$(2)], rax - ]], {op249SavedEffect, rspOffset}}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameEffect" end, - }, - }), + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameEffectRangeEffect::ApplyEffect()-AddTail"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + mov rdx, rbx ; pEffect + mov rcx, qword ptr ss:[#$(1)] ]], {op249SavedEffect}, [[ ; pOp249 + call #L(EEex::Opcode_Hook_OnOp249AddTail) + ]]} + ) - EEex_HookRelativeBranch(EEex_Label("Hook-CGameSprite::Swing()-CImmunitiesWeapon::OnList()-Ranged"), EEex_FlattenTable({ + ------------------------------------------------------- + -- [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp249() -- + ------------------------------------------------------- + + EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Swing()-CImmunitiesWeapon::OnList()-Ranged"), EEex_FlattenTable({ {[[ #MAKE_SHADOW_SPACE(64) - call #L(original) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax ]]}, EEex_GenLuaCall("EEex_Opcode_Hook_OnAfterSwingCheckedOp249", { @@ -179,6 +231,7 @@ test rax, rax jz #L(return) + #MANUAL_HOOK_EXIT(0) ; The consequence of not running the else block is that this boilerplate is not executed mov rsi, qword ptr ss:[rsp+0x70] lea r13, qword ptr ds:[r15+0xC] @@ -186,283 +239,318 @@ ]]}, })) - ----------------------------------------------------------------------- - -- Opcode #280 -- - -- param1 != 0 => Force wild surge number -- - -- special != 0 => Suppress wild surge feedback string and visuals -- - ----------------------------------------------------------------------- + --[[ + +------------------------------------------------------------------------------------------------------+ + | Opcode #280 | + +------------------------------------------------------------------------------------------------------+ + | param1 != 0 -> Force wild surge number to param1 | + | special != 0 -> Suppress wild surge feedback string and visuals | + +------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_Op280_BeforeApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) | + +------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_Op280_GetForcedWildSurgeNumber(pSprite: CGameSprite*) -> int | + | return -> Forced wild surge number | + +------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals(pSprite: CGameSprite*) -> bool | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Suppress wild surge feedback string and visuals | + +------------------------------------------------------------------------------------------------------+ + --]] + + ------------------------------------------------------------ + -- [EEex.dll] EEex::Opcode_Hook_Op280_BeforeApplyEffect() -- + ------------------------------------------------------------ -- Store op280 param1 and special as stats - EEex_HookBeforeRestore(EEex_Label("Hook-CGameEffectForceSurge::ApplyEffect()-FirstInstruction"), 0, 9, 9, EEex_FlattenTable({ + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameEffectForceSurge::ApplyEffect()-FirstInstruction"), 0, 9, 9, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(64) + #MAKE_SHADOW_SPACE(16) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OnOp280ApplyEffect", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), - {[[ - call_error: + + + ; rdx already pSprite + ; rcx already pEffect + call #L(EEex::Opcode_Hook_Op280_BeforeApplyEffect) + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) + ]]} + ) + + ------------------------------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_Op280_GetForcedWildSurgeNumber() -- + ------------------------------------------------------------------- -- Override wild surge number if op280 param1 is non-zero - EEex_HookBeforeRestore(EEex_Label("Hook-CGameSprite::WildSpell()-OverrideSurgeNumber"), 0, 9, 9, EEex_FlattenTable({ + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameSprite::WildSpell()-OverrideSurgeNumber"), 0, 9, 9, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11, EEex_HookIntegrityWatchdogRegister.R13 + }}}, {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OverrideWildSurgeNumber", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), - {[[ - jmp no_error + mov rcx, r14 ; pSprite + call #L(EEex::Opcode_Hook_Op280_GetForcedWildSurgeNumber) - call_error: - xor rax, rax - - no_error: - test rax, rax + test eax, eax cmovnz r13d, eax - #DESTROY_SHADOW_SPACE - ]]}, - })) + ]]} + ) - local checkSuppressVisual = EEex_JITNear(EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_SuppressWildSurgeVisuals", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), + ------------------------------------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals() -- + ------------------------------------------------------------------------- + + -- Suppress feedback string if op280 special is non-zero + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameSprite::WildSpell()-CGameSprite::Feedback()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11}}}, {[[ - jmp no_error + #MAKE_SHADOW_SPACE(32) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - call_error: - xor rax, rax + ; rcx already pSprite + call #L(EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals) + test al, al - no_error: + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ret - ]]}, - })) - - -- Suppress feedback string if op280 special is non-zero - EEex_HookBeforeCall(EEex_Label("Hook-CGameSprite::WildSpell()-CGameSprite::Feedback()"), {[[ - - #MAKE_SHADOW_SPACE(32) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - - mov rcx, r14 - call ]], checkSuppressVisual, [[ #ENDL - test rax, rax - jz normal - - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(return) - - normal: - #RESUME_SHADOW_ENTRY - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}) + jnz #L(return_skip) + ]]} + ) -- Suppress random visual effect if op280 special is non-zero - EEex_HookJumpOnFail(EEex_Label("Hook-CGameSprite::WildSpell()-SuppressVisualEffectJmp"), 0, {[[ - mov rcx, r14 - call ]], checkSuppressVisual, [[ #ENDL - test rax, rax - jnz #L(jmp_success) - ]]}) + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::WildSpell()-SuppressVisualEffectJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + {[[ + mov rcx, r14 ; pSprite + call #L(EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals) + test al, al + jnz #L(jmp_success) + ]]} + ) -- Suppress adding SPFLESHS CVisualEffect object to the area if op280 special is non-zero - EEex_HookBeforeCall(EEex_Label("Hook-CGameSprite::WildSpell()-CVisualEffect::Load()-SPFLESHS"), {[[ - - #MAKE_SHADOW_SPACE(32) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - - mov rcx, r14 - call ]], checkSuppressVisual, [[ #ENDL - test rax, rax - jz normal - - ; This is normally done by the CVisualEffect::Load() call - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - call #L(CString::Destruct) - - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(return) - - normal: - #RESUME_SHADOW_ENTRY - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}) - - -- Make SPFLESHS message nullptr if op280 special is non-zero - EEex_HookBeforeCall(EEex_Label("Hook-CGameSprite::WildSpell()-operator_new()-SPFLESHS"), {[[ - - #MAKE_SHADOW_SPACE(8) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - - mov rcx, r14 - call ]], checkSuppressVisual, [[ #ENDL - test rax, rax - jz normal - - xor rax, rax - - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp #L(return) - - normal: - #RESUME_SHADOW_ENTRY - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}) - - -- Only send SPFLESHS message if it is non-nullptr - EEex_HookBeforeCall(EEex_Label("Hook-CGameSprite::WildSpell()-CMessageHandler::AddMessage()-SPFLESHS"), {[[ - test rdx, rdx - jz #L(return) - ]]}) - - -------------------------------------------------------------------------- - -- Opcode #326 (Special BIT0 flips SPLPROT.2DA's "source" and "target") -- - -------------------------------------------------------------------------- - - EEex_HookAfterRestore(EEex_Label("Hook-CGameEffectApplySpell::ApplyEffect()-OverrideSplprotContext"), 0, 7, 7, EEex_FlattenTable({ + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameSprite::WildSpell()-CVisualEffect::Load()-SPFLESHS"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11}}}, {[[ - #MAKE_SHADOW_SPACE(72) + #MAKE_SHADOW_SPACE(32) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_ApplySpell_ShouldFlipSplprotSourceAndTarget", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - call_error: - xor rax, rax + mov rcx, r14 ; pSprite + call #L(EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals) + test al, al + jz normal + + ; This is normally done by the CVisualEffect::Load() call + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + call #L(CString::Destruct) - no_error: - test rax, rax mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - jz #L(return) + #DESTROY_SHADOW_SPACE(KEEP_ENTRY) + jmp #L(return_skip) - mov rax, r8 - mov r8, r9 - mov r9, rax - ]]}, - })) - - ------------------------------------------------------------------------------------------------- - -- Opcode #333 (param3 BIT0 allows "SPL" file not to terminate upon a successful saving throw) -- - ------------------------------------------------------------------------------------------------- - - EEex_HookAfterRestore(EEex_Label("Hook-CGameEffectStaticCharge::ApplyEffect()-CopyOp333Call"), 0, 9, 9, EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(56) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_OnOp333CopiedSelf", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax #ENDL", {rspOffset}}, "CGameEffect" end, - }, - }), - {[[ - call_error: + normal: + #RESUME_SHADOW_ENTRY + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) + ]]} + ) - ---------------------------------------------------- - -- Allow saving throw BIT23 to bypass opcode #101 -- - ---------------------------------------------------- - - EEex_HookBeforeRestore(EEex_Label("Hook-CImmunitiesEffect::OnList()-Entry"), 0, 5, 5, EEex_FlattenTable({ + -- Make SPFLESHS message nullptr if op280 special is non-zero + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameSprite::WildSpell()-operator_new()-SPFLESHS"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(56) + #MAKE_SHADOW_SPACE(8) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Opcode_Hook_CImmunitiesEffect_BypassOp101", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CGameEffect" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - call_error: - xor rax, rax + mov rcx, r14 ; pSprite + call #L(EEex::Opcode_Hook_Op280_ShouldSuppressWildSurgeVisuals) + test al, al - no_error: - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE + jz #L(return) - test rax, rax - jz no_bypass xor rax, rax - ret - no_bypass: - ]]}, - })) + jmp #L(return_skip) + ]]} + ) - ----------------- - -- New Opcodes -- - ----------------- + -- Only send SPFLESHS message if it is non-nullptr + EEex_HookBeforeCall(EEex_Label("Hook-CGameSprite::WildSpell()-CMessageHandler::AddMessage()-SPFLESHS"), {[[ + test rdx, rdx + jz #L(return_skip) + ]]}) + + --[[ + +----------------------------------------------------------------------------------------------------------+ + | Opcode #326 | + +----------------------------------------------------------------------------------------------------------+ + | (special & 1) != 0 -> Flip what SPLPROT.2DA considers the "source" and "target" sprites | + +----------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_ApplySpell_ShouldFlipSplprotSourceAndTarget(pEffect: CGameEffect*) -> int | + | return: | + | -> 0 - Don't alter engine behavior | + | -> !0 - Flip what SPLPROT.2DA considers the "source" and "target" sprites | + +----------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffectApplySpell::ApplyEffect()-OverrideSplprotContext"), 0, 7, 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}, + {"manual_return", true}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 + + mov rcx, rbx ; pEffect + call #L(EEex::Opcode_Hook_ApplySpell_ShouldFlipSplprotSourceAndTarget) + + test rax, rax + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + jnz #L(flip) + + #MANUAL_HOOK_EXIT(0) + jmp #L(return) + + flip: + mov rax, r8 + mov r8, r9 + mov r9, rax + #MANUAL_HOOK_EXIT(1) + jmp #L(return) + ]]}, + }) + ) + -- Manually define the ignored registers for the "flip" branch above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CGameEffectApplySpell::ApplyEffect()-OverrideSplprotContext"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }) + + --[[ + +-----------------------------------------------------------------+ + | Opcode #333 | + +-----------------------------------------------------------------+ + | (param3 & 1) != 0 -> Only check saving throw once | + +-----------------------------------------------------------------+ + | [Lua] EEex_Opcode_Hook_OnOp333CopiedSelf(effect: CGameEffect) | + +-----------------------------------------------------------------+ + --]] + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffectStaticCharge::ApplyEffect()-CopyOp333Call"), 0, 9, 9, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(56) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + ]]}, + EEex_GenLuaCall("EEex_Opcode_Hook_OnOp333CopiedSelf", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax #ENDL", {rspOffset}}, "CGameEffect" end, + }, + }), + {[[ + call_error: + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +------------------------------------------------------------------------------------------------+ + | Allow saving throw BIT23 to bypass opcode #101 | + +------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_Op101_ShouldEffectBypassImmunity(pEffect: CGameEffect*) -> bool | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Bypass opcode #101 | + +------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CImmunitiesEffect::OnList()-Entry"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(16) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + + mov rcx, rdx ; pEffect + call #L(EEex::Opcode_Hook_Op101_ShouldEffectBypassImmunity) + + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + + test al, al + jz #L(return) + + xor rax, rax + #MANUAL_HOOK_EXIT(1) + ret + ]]}, + }) + ) + -- Manually define the ignored registers for the unusual `ret` above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CImmunitiesEffect::OnList()-Entry"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) + + ----------------------------------- + -- New Opcodes -- + ----------------------------------- local genOpcodeDecode = function(args) @@ -518,7 +606,7 @@ _2: mov edx, 30h mov rcx, rbp - call #L(Hardcoded_free) ; SDL_FreeRW + call #L(Hardcoded_free) ; SDL_FreeRW test rsi, rsi lea rdx, qword ptr ds:[rsi+8h] mov rcx, rbx @@ -538,14 +626,14 @@ return {[[ mov ecx, #$(1) ]], {CGameEffect.sizeof}, [[ #ENDL call #L(operator_new) - mov rcx, rax ; this + mov rcx, rax ; this test rax, rax jz #L(Hook-CGameEffect::DecodeEffect()-Fail) - mov rax, qword ptr ds:[rsi] ; target + mov rax, qword ptr ds:[rsi] ; target mov qword ptr [rsp+20h], rax - mov r9d, ebp ; sourceID - mov r8, r14 ; source - mov rdx, rdi ; effect + mov r9d, ebp ; sourceID + mov r8, r14 ; source + mov rdx, rdi ; effect call #$(1) ]], {constructor}, [[ #ENDL jmp #L(Hook-CGameEffect::DecodeEffect()-Success) ]]} @@ -572,76 +660,93 @@ return genDecode(writeConstructor(newvtbl)) end - -------------------------------------------- - -- New Opcode #400 (SetTemporaryAIScript) -- - -------------------------------------------- + --[[ + +----------------------------------------------------------------------------------------------------------------------+ + | New Opcode #400 (SetTemporaryAIScript) | + +----------------------------------------------------------------------------------------------------------------------+ + | Temporarily set a script level and restore the old script when the effect is removed. | + | NOTE: This is dangerous! Script changes from any other mechanism will be lost when the effect expires. | + +----------------------------------------------------------------------------------------------------------------------+ + | param2 -> Script level to set | + | resource -> Script to set | + +----------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_SetTemporaryAIScript_ApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Halt effect list processing | + | -> !0 - Continue effect list processing | + +----------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_SetTemporaryAIScript_OnRemove(pEffect: CGameEffect*, pSprite: CGameSprite*) | + +----------------------------------------------------------------------------------------------------------------------+ + --]] local EEex_SetTemporaryAIScript = genOpcodeDecode({ - ["ApplyEffect"] = EEex_FlattenTable({[[ - + ["ApplyEffect"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_SetTemporaryAIScript_ApplyEffect", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_SetTemporaryAIScript_ApplyEffect) #DESTROY_SHADOW_SPACE - mov rax, 1 ret - ]]}), - - ["OnRemove"] = EEex_FlattenTable({[[ + ]]}, + ["OnRemove"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_SetTemporaryAIScript_OnRemove", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_SetTemporaryAIScript_OnRemove) #DESTROY_SHADOW_SPACE ret - ]]}), + ]]}, }) - --------------------------------------- - -- New Opcode #401 (SetExtendedStat) -- - --------------------------------------- + --[[ + +-----------------------------------------------------------------------------------------------------------------+ + | New Opcode #401 (SetExtendedStat) | + +-----------------------------------------------------------------------------------------------------------------+ + | Modify the value of an extended stat. All operations are clamped such that results outside of the stat's | + | range will resolve to the exceeded extrema. | + | | + | Extended stats are those with ids outside of the vanilla range in STATS.IDS. | + | Extended stat minimums, maximums, and defaults are defined in X-STATS.2DA. | + +-----------------------------------------------------------------------------------------------------------------+ + | param1 -> Modification amount | + | | + | param2 -> Modification type: | + | -> 0 (Sum) - stat = stat + param1 | + | -> 1 (Set) - stat = param1 | + | -> 2 (Percent) - stat = stat * param1 / 100 | + | | + | special -> Extended stat id | + +-----------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_SetExtendedStat_ApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Halt effect list processing | + | -> !0 - Continue effect list processing | + +-----------------------------------------------------------------------------------------------------------------+ + --]] local EEex_SetExtendedStat = genOpcodeDecode({ - - ["ApplyEffect"] = EEex_FlattenTable({[[ - + ["ApplyEffect"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_ApplySetExtendedStat", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_SetExtendedStat_ApplyEffect) #DESTROY_SHADOW_SPACE - mov rax, 1 ret - ]]}), + ]]}, }) - --------------------------------- - -- New Opcode #402 (InvokeLua) -- - --------------------------------- + --[[ + +-----------------------------------------------------------------------------------------------------------------+ + | New Opcode #402 (InvokeLua) | + +-----------------------------------------------------------------------------------------------------------------+ + | Invoke a global Lua function. Note that the function name must be 8 characters or less, and be ALL UPPERCASE. | + | | + | The function's signature is: FUNC(op402: CGameEffect, sprite: CGameSprite) | + +-----------------------------------------------------------------------------------------------------------------+ + | resource -> Global Lua function name | + +-----------------------------------------------------------------------------------------------------------------+ + | [JIT] | + +-----------------------------------------------------------------------------------------------------------------+ + --]] local EEex_InvokeLua = genOpcodeDecode({ @@ -674,153 +779,236 @@ ]]}), }) - ------------------------------------- - -- New Opcode #403 (ScreenEffects) -- - ------------------------------------- + --[[ + +---------------------------------------------------------------------------------------------------------------+ + | New Opcode #403 (ScreenEffects) | + +---------------------------------------------------------------------------------------------------------------+ + | Register a global Lua function that is called whenever an effect is added to the target creature. If this | + | function returns `true` the effect being added is blocked. Note that the function name must be 8 characters | + | or less, and be ALL UPPERCASE. | + | | + | The function's signature is: FUNC(op403: CGameEffect, effect: CGameEffect, sprite: CGameSprite) -> boolean | + +---------------------------------------------------------------------------------------------------------------+ + | resource -> Global Lua function name | + +---------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_ScreenEffects_ApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Halt effect list processing | + | -> !0 - Continue effect list processing | + +---------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_OnCheckAdd(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Don't alter engine behavior | + | -> !0 - Block effect | + +---------------------------------------------------------------------------------------------------------------+ + --]] + + -------------------------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_ScreenEffects_ApplyEffect() -- + -------------------------------------------------------------- local EEex_ScreenEffects = genOpcodeDecode({ - - ["ApplyEffect"] = EEex_FlattenTable({[[ - + ["ApplyEffect"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_ApplyScreenEffects", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_ScreenEffects_ApplyEffect) #DESTROY_SHADOW_SPACE - mov rax, 1 ret - ]]}), + ]]}, }) + ----------------------------------------------- + -- [EEex.dll] EEex::Opcode_Hook_OnCheckAdd() -- + ----------------------------------------------- + local effectBlockedHack = EEex_Malloc(0x8) - EEex_HookJumpOnFail(EEex_Label("Hook-CGameEffect::CheckAdd()-LastProbabilityJmp"), 0, EEex_FlattenTable({[[ + EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameEffect::CheckAdd()-LastProbabilityJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(8) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx - #MAKE_SHADOW_SPACE(56) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx + mov rdx, r14 ; pSprite + mov rcx, rdi ; pEffect + call #L(EEex::Opcode_Hook_OnCheckAdd) - ]], EEex_GenLuaCall("EEex_Opcode_Hook_OnCheckAdd", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ - jmp no_error - - call_error: - xor rax, rax - - no_error: - mov qword ptr ds:[#$(1)], rax ]], {{effectBlockedHack}}, [[ #ENDL - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - test rax, rax - jnz #L(Hook-CGameEffect::CheckAdd()-ProbabilityFailed) - ]]})) - - EEex_HookJumpOnSuccess(EEex_Label("Hook-CGameSprite::AddEffect()-noSave-Override"), 3, {[[ + mov qword ptr ds:[#$(1)], rax ]], {effectBlockedHack}, [[ #ENDL + + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + test rax, rax + jz #L(jmp_fail) + + #MANUAL_HOOK_EXIT(0) + jmp #L(Hook-CGameEffect::CheckAdd()-ProbabilityFailed) + ]]}, + }) + ) + + EEex_HookConditionalJumpOnSuccess(EEex_Label("Hook-CGameSprite::AddEffect()-noSave-Override"), 3, {[[ cmp qword ptr ds:[#$(1)], 0 ]], {effectBlockedHack}, [[ #ENDL - jnz jmp_fail + jnz #L(jmp_fail) ]]}) - ----------------------------------------- - -- New Opcode #408 (ProjectileMutator) -- - ----------------------------------------- + --[[ + +-------------------------------------------------------------------------------------------------------------------+ + | New Opcode #408 (ProjectileMutator) | + +-------------------------------------------------------------------------------------------------------------------+ + | Register a global Lua table that (potentially) contains several different functions that mutate projectiles. | + | Note that the table name must be 8 characters or less, and be ALL UPPERCASE. | + | | + | The function signatures are: | + | | + | typeMutator(context: table) -> number | + | | + | context: | + | | + | decodeSource: EEex_Projectile_DecodeSource - The source of the hook | + | | + | originatingEffect: CGameEffect | nil - The op408 effect that registered the mutator table | + | | + | originatingSprite: CGameSprite | nil - The sprite that is decoding (creating) the projectile | + | | + | projectileType: number - The projectile type about to be decoded. This is equivalent to the value | + | at .SPL->Ability Header->[+0x26]. Subtract one from this value to get the | + | corresponding PROJECTL.IDS index. | + | | + | return -> The new projectile type, or nil if the type should not be overridden. This is equivalent to | + | the value at .SPL->Ability Header->[+0x26]. Subtract one from this value to get the | + | corresponding PROJECTL.IDS index. | + | | + | projectileMutator(context: table) | + | | + | context: | + | | + | decodeSource: EEex_Projectile_DecodeSource - The source of the hook | + | | + | originatingEffect: CGameEffect | nil - The op408 effect that registered the mutator table | + | | + | originatingSprite: CGameSprite | nil - The sprite that is decoding (creating) the projectile | + | | + | projectile: CProjectile - The projectile about to be returned from the decoding process | + | | + | effectMutator(context: table) | + | | + | context: | + | | + | addEffectSource: EEex_Projectile_AddEffectSource - The source of the hook | + | | + | effect: CGameEffect - The effect that is being added to projectile | + | | + | originatingEffect: CGameEffect | nil - The op408 effect that registered the mutator table | + | | + | originatingSprite: CGameSprite | nil - The sprite that decoded (created) the projectile | + | | + | projectile: CProjectile - The projectile that `effect` is being added to | + | | + +-------------------------------------------------------------------------------------------------------------------+ + | resource -> Global Lua table name | + +-------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_ProjectileMutator_ApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Halt effect list processing | + | -> !0 - Continue effect list processing | + +-------------------------------------------------------------------------------------------------------------------+ + --]] local EEex_ProjectileMutator = genOpcodeDecode({ - - ["ApplyEffect"] = EEex_FlattenTable({[[ - + ["ApplyEffect"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_ProjectileMutator_ApplyEffect", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_ProjectileMutator_ApplyEffect) #DESTROY_SHADOW_SPACE - mov rax, 1 ret - ]]}), + ]]}, }) - -------------------------------------------- - -- New Opcode #409 (EnableActionListener) -- - -------------------------------------------- + --[[ + +----------------------------------------------------------------------------------------------------------------------+ + | New Opcode #409 (EnableActionListener) | + +----------------------------------------------------------------------------------------------------------------------+ + | Enable an action listener previously registered by EEex_Action_AddEnabledSpriteStartedActionListener(). The action | + | listener will then be called whenever the target sprite starts a new action. Note that the function name must be 8 | + | characters or less, and be ALL UPPERCASE. | + | | + | The function's signature is: listener(sprite: CGameSprite, action: CAIAction, op409: CGameEffect) | + +----------------------------------------------------------------------------------------------------------------------+ + | param1: | + | -> 0 - Action listener disabled | + | -> !0 - Action listener enabled | + | | + | resource -> The name of the function as registered by EEex_Action_AddEnabledSpriteStartedActionListener() | + +----------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Opcode_Hook_EnableActionListener_ApplyEffect(pEffect: CGameEffect*, pSprite: CGameSprite*) -> int | + | return: | + | -> 0 - Halt effect list processing | + | -> !0 - Continue effect list processing | + +----------------------------------------------------------------------------------------------------------------------+ + --]] local EEex_EnableActionListener = genOpcodeDecode({ - - ["ApplyEffect"] = EEex_FlattenTable({[[ - + ["ApplyEffect"] = {[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - - ]], EEex_GenLuaCall("EEex_Opcode_Hook_EnableActionListener_ApplyEffect", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"}, "CGameEffect" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx", {rspOffset}, "#ENDL"}, "CGameSprite" end, - }, - }), [[ - - call_error: + #MAKE_SHADOW_SPACE + call #L(EEex::Opcode_Hook_EnableActionListener_ApplyEffect) #DESTROY_SHADOW_SPACE - mov rax, 1 ret - ]]}), + ]]}, }) - ------------------- - -- Decode Switch -- - ------------------- - - EEex_HookJumpOnSuccess(EEex_Label("Hook-CGameEffect::DecodeEffect()-DefaultJmp"), 0, EEex_FlattenTable({[[ - - mov qword ptr ss:[rsp+60h], r15 ; save non-volatile register - - cmp eax, 367 - jbe jmp_fail - - cmp eax, 400 - jne _401 - ]], EEex_SetTemporaryAIScript, [[ - - _401: - cmp eax, 401 - jne _402 - ]], EEex_SetExtendedStat, [[ - - _402: - cmp eax, 402 - jne _403 - ]], EEex_InvokeLua, [[ - - _403: - cmp eax, 403 - jne _408 - ]], EEex_ScreenEffects, [[ - - _408: - cmp eax, 408 - jne _409 - ]], EEex_ProjectileMutator, [[ - - _409: - cmp eax, 409 - jne #L(jmp_success) - ]], EEex_EnableActionListener, [[ - ]]})) + --[[ + +-------------------------------------+ + | [JIT] Decode switch for new opcodes | + +-------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameEffect::DecodeEffect()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({[[ + + mov qword ptr ss:[rsp+60h], r15 ; save non-volatile register since I resume control flow from an EEex + ; opcode to somewhere that expects this stack location to be filled + + cmp eax, 400 + jne _401 + ]], EEex_SetTemporaryAIScript, [[ + + _401: + cmp eax, 401 + jne _402 + ]], EEex_SetExtendedStat, [[ + + _402: + cmp eax, 402 + jne _403 + ]], EEex_InvokeLua, [[ + + _403: + cmp eax, 403 + jne _408 + ]], EEex_ScreenEffects, [[ + + _408: + cmp eax, 408 + jne _409 + ]], EEex_ProjectileMutator, [[ + + _409: + cmp eax, 409 + jne #L(jmp_success) + ]], EEex_EnableActionListener, [[ + ]]}) + ) + EEex_HookIntegrityWatchdog_IgnoreStackSizes(EEex_Label("Hook-CGameEffect::DecodeEffect()-DefaultJmp"), {{0x60, 8}}) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Projectile.lua b/EEex/copy/EEex_Projectile.lua index 959df5f..9d87bfc 100644 --- a/EEex/copy/EEex_Projectile.lua +++ b/EEex/copy/EEex_Projectile.lua @@ -449,6 +449,7 @@ function EEex_Projectile_RegisterGlobalMutator(mutatorTableName) EEex_Error("[EEex_Projectile_RegisterGlobalMutator] Invalid mutatorTableName parameter value") end table.insert(EEex_Projectile_Private_GlobalMutators, mutatorTableName) + EEex.Projectile_LuaHook_GlobalMutators_Enabled = true end -- @bubb_doc { EEex_Projectile_CastUserType / alias=EEex_Projectile_CastUT } @@ -477,42 +478,43 @@ end -- -- }: See summary. +EEex_Projectile_Private_CastUserTypes = { + [EEex_Projectile_Type.CProjectile] = "CProjectile", + [EEex_Projectile_Type.CProjectileAmbiant] = "CProjectileAmbiant", + [EEex_Projectile_Type.CProjectileArea] = "CProjectileArea", + [EEex_Projectile_Type.CProjectileBAM] = "CProjectileBAM", + --[EEex_Projectile_Type.CProjectileCallLightning] = "CProjectileCallLightning", + --[EEex_Projectile_Type.CProjectileCastingGlow] = "CProjectileCastingGlow", + [EEex_Projectile_Type.CProjectileChain] = "CProjectileChain", + [EEex_Projectile_Type.CProjectileColorSpray] = "CProjectileColorSpray", + [EEex_Projectile_Type.CProjectileConeOfCold] = "CProjectileConeOfCold", + [EEex_Projectile_Type.CProjectileFall] = "CProjectileFall", + [EEex_Projectile_Type.CProjectileFireHands] = "CProjectileFireHands", + [EEex_Projectile_Type.CProjectileInstant] = "CProjectileInstant", + --[EEex_Projectile_Type.CProjectileInvisibleTravelling] = "CProjectileInvisibleTravelling", + --[EEex_Projectile_Type.CProjectileLightningBolt] = "CProjectileLightningBolt", + --[EEex_Projectile_Type.CProjectileLightningBoltGround] = "CProjectileLightningBoltGround", + --[EEex_Projectile_Type.CProjectileLightningBounce] = "CProjectileLightningBounce", + --[EEex_Projectile_Type.CProjectileLightningStorm] = "CProjectileLightningStorm", + --[EEex_Projectile_Type.CProjectileMagicMissileMulti] = "CProjectileMagicMissileMulti", + [EEex_Projectile_Type.CProjectileMulti] = "CProjectileMulti", + [EEex_Projectile_Type.CProjectileMushroom] = "CProjectileMushroom", + [EEex_Projectile_Type.CProjectileNewScorcher] = "CProjectileNewScorcher", + [EEex_Projectile_Type.CProjectileScorcher] = "CProjectileScorcher", + [EEex_Projectile_Type.CProjectileSegment] = "CProjectileSegment", + [EEex_Projectile_Type.CProjectileSkyStrike] = "CProjectileSkyStrike", + [EEex_Projectile_Type.CProjectileSkyStrikeBAM] = "CProjectileSkyStrikeBAM", + [EEex_Projectile_Type.CProjectileSpellHit] = "CProjectileSpellHit", + [EEex_Projectile_Type.CProjectileTravelDoor] = "CProjectileTravelDoor", +} + function EEex_Projectile_CastUserType(projectile) if not projectile then return nil end - local usertype = ({ - [EEex_Projectile_Type.CProjectile] = "CProjectile", - [EEex_Projectile_Type.CProjectileAmbiant] = "CProjectileAmbiant", - [EEex_Projectile_Type.CProjectileArea] = "CProjectileArea", - [EEex_Projectile_Type.CProjectileBAM] = "CProjectileBAM", - --[EEex_Projectile_Type.CProjectileCallLightning] = "CProjectileCallLightning", - --[EEex_Projectile_Type.CProjectileCastingGlow] = "CProjectileCastingGlow", - [EEex_Projectile_Type.CProjectileChain] = "CProjectileChain", - [EEex_Projectile_Type.CProjectileColorSpray] = "CProjectileColorSpray", - [EEex_Projectile_Type.CProjectileConeOfCold] = "CProjectileConeOfCold", - [EEex_Projectile_Type.CProjectileFall] = "CProjectileFall", - [EEex_Projectile_Type.CProjectileFireHands] = "CProjectileFireHands", - [EEex_Projectile_Type.CProjectileInstant] = "CProjectileInstant", - --[EEex_Projectile_Type.CProjectileInvisibleTravelling] = "CProjectileInvisibleTravelling", - --[EEex_Projectile_Type.CProjectileLightningBolt] = "CProjectileLightningBolt", - --[EEex_Projectile_Type.CProjectileLightningBoltGround] = "CProjectileLightningBoltGround", - --[EEex_Projectile_Type.CProjectileLightningBounce] = "CProjectileLightningBounce", - --[EEex_Projectile_Type.CProjectileLightningStorm] = "CProjectileLightningStorm", - --[EEex_Projectile_Type.CProjectileMagicMissileMulti] = "CProjectileMagicMissileMulti", - [EEex_Projectile_Type.CProjectileMulti] = "CProjectileMulti", - [EEex_Projectile_Type.CProjectileMushroom] = "CProjectileMushroom", - [EEex_Projectile_Type.CProjectileNewScorcher] = "CProjectileNewScorcher", - [EEex_Projectile_Type.CProjectileScorcher] = "CProjectileScorcher", - [EEex_Projectile_Type.CProjectileSegment] = "CProjectileSegment", - [EEex_Projectile_Type.CProjectileSkyStrike] = "CProjectileSkyStrike", - [EEex_Projectile_Type.CProjectileSkyStrikeBAM] = "CProjectileSkyStrikeBAM", - [EEex_Projectile_Type.CProjectileSpellHit] = "CProjectileSpellHit", - [EEex_Projectile_Type.CProjectileTravelDoor] = "CProjectileTravelDoor", - })[projectile:getType()] - + local usertype = EEex_Projectile_Private_CastUserTypes[projectile:getType()] return usertype and EEex_CastUD(projectile, usertype) or projectile end EEex_Projectile_CastUT = EEex_Projectile_CastUserType @@ -601,119 +603,3 @@ function EEex_Projectile_IsOfType(projectile, checkType) return EEex_BAnd(EEex_Projectile_Private_Inheritance[projType], checkType) ~= 0x0 end CProjectile.isOfType = EEex_Projectile_IsOfType - ------------ --- Hooks -- ------------ - -function EEex_Projectile_Private_CallMutatorFunction(mutatorTableName, mutatorFunctionName, arguments) - local mutatorTable = _G[mutatorTableName] - if mutatorTable ~= nil and type(mutatorTable) == "table" then - local mutatorFunc = mutatorTable[mutatorFunctionName] - if mutatorFunc then - if type(mutatorFunc) == "function" then - return mutatorFunc(arguments) - else - print("[!] op408 (ProjectileMutator) attempted to use an invalid \""..mutatorFunctionName.."\" value under: \""..mutatorTableName.."\"") - end - end - else - print("[!] op408 (ProjectileMutator) attempted to use an invalid mutator table: \""..mutatorTableName.."\"") - end -end - -function EEex_Projectile_Private_ProcessMutatorFunctions(aiBase, callProjectileMutatorFunc) - - for _, mutatorTableName in ipairs(EEex_Projectile_Private_GlobalMutators) do - local retVal = callProjectileMutatorFunc(mutatorTableName) - if retVal then - return retVal - end - end - - if EEex_GameObject_IsSprite(aiBase, true) then - local statsAux = EEex_GetUDAux(aiBase:getActiveStats()) - for _, originatingEffect in ipairs(statsAux["EEex_ProjectileMutatorEffects"] or {}) do - local retVal = callProjectileMutatorFunc(originatingEffect.m_res:get(), originatingEffect) - if retVal then - return retVal - end - end - end -end - -function EEex_Projectile_Hook_OnBeforeDecode(projectileType, aiBase, retPtr) - - local decodeSource = EEex_Projectile_Private_DecodeSources[retPtr] - - local callTypeMutator = function(mutatorTableName, originatingEffect) - - local newType = EEex_Projectile_Private_CallMutatorFunction(mutatorTableName, "typeMutator", { - ["decodeSource"] = decodeSource, - ["originatingEffect"] = originatingEffect, - ["originatingSprite"] = aiBase, - ["projectileType"] = projectileType, - }) - - if newType then - if type(newType) == "number" then - return newType - else - print("[!] op408 (ProjectileMutator) attempted to use an invalid return value \""..tostring(newType).."\" from typeMutator under: \""..mutatorTableName.."\"") - end - end - end - - return EEex_Projectile_Private_ProcessMutatorFunctions(aiBase, callTypeMutator) or -1 -end - -function EEex_Projectile_Hook_OnAfterDecode(projectile, aiBase, retPtr) - - local decodeSource = EEex_Projectile_Private_DecodeSources[retPtr] - - local callProjectileMutator = function(mutatorTableName, originatingEffect) - - local blockFurtherMutations = EEex_Projectile_Private_CallMutatorFunction(mutatorTableName, "projectileMutator", { - ["decodeSource"] = decodeSource, - ["originatingEffect"] = originatingEffect, - ["originatingSprite"] = aiBase, - ["projectile"] = projectile, - }) - - if blockFurtherMutations then - if type(blockFurtherMutations) == "boolean" then - return blockFurtherMutations - else - print("[!] op408 (ProjectileMutator) attempted to use an invalid return value \""..tostring(blockFurtherMutations).."\" from projectileMutator under: \""..mutatorTableName.."\"") - end - end - end - - EEex_Projectile_Private_ProcessMutatorFunctions(aiBase, callProjectileMutator) -end - -function EEex_Projectile_Hook_BeforeAddEffect(projectile, aiBase, effect, retPtr) - - local addEffectSource = EEex_Projectile_Private_AddEffectSources[retPtr] - - local callEffectMutator = function(mutatorTableName, originatingEffect) - - local blockFurtherMutations = EEex_Projectile_Private_CallMutatorFunction(mutatorTableName, "effectMutator", { - ["addEffectSource"] = addEffectSource, - ["effect"] = effect, - ["originatingEffect"] = originatingEffect, - ["originatingSprite"] = aiBase, - ["projectile"] = projectile, - }) - - if blockFurtherMutations then - if type(blockFurtherMutations) == "boolean" then - return blockFurtherMutations - else - print("[!] op408 (ProjectileMutator) attempted to use an invalid return value \""..tostring(blockFurtherMutations).."\" from effectMutator under: \""..mutatorTableName.."\"") - end - end - end - - EEex_Projectile_Private_ProcessMutatorFunctions(aiBase, callEffectMutator) -end diff --git a/EEex/copy/EEex_Projectile_Patch.lua b/EEex/copy/EEex_Projectile_Patch.lua index a136527..57e29ec 100644 --- a/EEex/copy/EEex_Projectile_Patch.lua +++ b/EEex/copy/EEex_Projectile_Patch.lua @@ -3,85 +3,92 @@ EEex_DisableCodeProtection() - ----------------------------------------- - -- EEex_Projectile_Hook_OnBeforeDecode -- - ----------------------------------------- - - EEex_HookBeforeRestore(EEex_Label("CProjectile::DecodeProjectile"), 0, 5, 5, EEex_FlattenTable({ + --[[ + +----------------------------------------------------------------------------------------------------------------------------------+ + | Implement Opcode #408 (ProjectileMutator) `typeMutator` functionality | + +----------------------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Projectile_Hook_OnBeforeDecode(nProjectileType: ushort, pDecoder: CGameAIBase*, pRetPtr: uintptr_t) -> ushort | + | return: | + | -> -1 - Don't alter engine behavior | + | -> !-1 - Override projectile type with the return value | + +----------------------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeRestoreWithLabels(EEex_Label("CProjectile::DecodeProjectile"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}, + {"manual_hook_integrity_exit", true}}, {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(72) + #MAKE_SHADOW_SPACE(16) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Projectile_Hook_OnBeforeDecode", { - ["args"] = { - function(rspOffset) return {[[ - and rcx, 0xFFFF - mov qword ptr ss:[rsp+#$(1)], rcx - ]], {rspOffset}} end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(0)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}} end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), - {[[ - cmp rax, -1 - je call_error + + mov r8, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; pRetPtr + ; rdx is already pDecoder + ; rcx is already nProjectileType + call #L(EEex::Projectile_Hook_OnBeforeDecode) + + cmp ax, -1 + je no_override + + mov cx, ax ; Override projectile type mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, rax #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - jmp return + #MANUAL_HOOK_EXIT(1) + jmp #L(return) - call_error: + no_override: #RESUME_SHADOW_ENTRY mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------- - -- EEex_Projectile_Hook_OnAfterDecode -- - ---------------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CProjectile::DecodeProjectile()-LastCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(56) - ]]}, - EEex_GenLuaCall("EEex_Projectile_Hook_OnAfterDecode", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CProjectile", "EEex_Projectile_CastUT" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(408)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}} end, - }, - }), + #MANUAL_HOOK_EXIT(0) + ]]} + ) + -- Manually define the ignored registers for the "override" branch above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("CProjectile::DecodeProjectile"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }) + + --[[ + +-------------------------------------------------------------------------------------------------------------------------+ + | Implement Opcode #408 (ProjectileMutator) `projectileMutator` functionality | + +-------------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Projectile_Hook_OnAfterDecode(pProjectile: CProjectile*, pDecoder: CGameAIBase*, pRetPtr: uintptr_t) | + +-------------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CProjectile::DecodeProjectile()-LastCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ------------------------------------------ - -- EEex_Projectile_Hook_BeforeAddEffect -- - ------------------------------------------ - - -- This is very ugly, but since CProjectile::AddEffect() isn't passed the - -- source aiBase, I have to go and manually define where the aiBase - -- is currently saved for the given CProjectile::AddEffect() call. + mov r8, qword ptr ss:[rsp+408] ; pRetPtr + mov rdx, rsi ; pDecoder + mov rcx, rbx ; pProjectile + call #L(EEex::Projectile_Hook_OnAfterDecode) + ]]} + ) + + --[[ + +----------------------------------------------------------------------------------------------------------------------------------------------------+ + | Implement Opcode #408 (ProjectileMutator) `effectMutator` functionality | + +----------------------------------------------------------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Projectile_Hook_OnBeforeAddEffect(pProjectile: CProjectile*, pDecoder: CGameAIBase*, pEffect: CGameEffect*, pRetPtr: uintptr_t) | + +----------------------------------------------------------------------------------------------------------------------------------------------------+ + --]] + + -- This is very ugly, but since CProjectile::AddEffect() isn't passed the source aiBase, I have to go and + -- manually define where the aiBase is currently saved for the given CProjectile::AddEffect() call. local getAddEffectAIBase = EEex_JITNear({[[ #STACK_MOD(8) ; This was called, the ret ptr broke alignment #MAKE_SHADOW_SPACE(24) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r8 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], r9 mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::ForceSpell()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016CE36 cmp rcx, rax @@ -181,44 +188,41 @@ mov rax, rsi return: - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE ret ]]}) - EEex_HookBeforeRestore(EEex_Label("CProjectile::AddEffect"), 0, 8, 8, EEex_FlattenTable({ + EEex_HookBeforeRestoreWithLabels(EEex_Label("CProjectile::AddEffect"), 0, 8, 8, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(80) + #MAKE_SHADOW_SPACE(16) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Projectile_Hook_BeforeAddEffect", { - ["args"] = { - function(rspOffset) return {[[ - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - mov qword ptr ss:[rsp+#$(1)], rcx - ]], {rspOffset}}, "CProjectile", "EEex_Projectile_CastUT" end, - function(rspOffset) return {[[ - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(0)] - call #$(1) ]], {getAddEffectAIBase}, [[ #ENDL - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(0)] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}} end, - }, - }), - {[[ - call_error: + + mov r9, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; pRetPtr + mov r8, rdx ; pEffect + + mov rcx, r9 ; pRetPtr + call #$(1) ]], {getAddEffectAIBase}, [[ #ENDL + mov rdx, rax ; pDecoder + + ; r9 already pRetPtr + ; r8 already pEffect + ; rdx already pDecoder + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; pProjectile + call #L(EEex::Projectile_Hook_OnBeforeAddEffect) + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_ReplaceLua.lua b/EEex/copy/EEex_ReplaceLua.lua new file mode 100644 index 0000000..5f07346 --- /dev/null +++ b/EEex/copy/EEex_ReplaceLua.lua @@ -0,0 +1,227 @@ + +(function() + + EEex_DisableCodeProtection() + + ------------------------------------------------------------------------------------------------------- + -- Initialize EEex.dll and replace the engine's bootstrap code to make it use the external Lua state -- + ------------------------------------------------------------------------------------------------------- + + EEex_InitLuaBindings("EEex") + + local override = function(funcName) + EEex_JITAt(EEex_Label(funcName), {[[ + jmp #L(EEex::Override_#$(1)) ]], {funcName} + }) + end + + override("bootstrapLua") + + ----------------------------------------------------------------------------------- + -- /START Lua function redirection -- + ----------------------------------------------------------------------------------- + -- Redirect in-engine Lua functions to point to the [General].LuaLibrary DLL -- + ----------------------------------------------------------------------------------- + + local redirectLuaProc = function(funcName) + EEex_JITAt(EEex_Label(funcName), {[[ + jmp ]], EEex_GetLuaLibraryProc(funcName) + }) + end + + redirectLuaProc("lua_atpanic") + redirectLuaProc("lua_callk") + redirectLuaProc("lua_checkstack") + redirectLuaProc("lua_concat") + redirectLuaProc("lua_createtable") + redirectLuaProc("lua_error") + redirectLuaProc("lua_gc") + redirectLuaProc("lua_getfield") + redirectLuaProc("lua_getglobal") + redirectLuaProc("lua_getinfo") + redirectLuaProc("lua_getlocal") + redirectLuaProc("lua_getmetatable") + redirectLuaProc("lua_gettable") + redirectLuaProc("lua_gettop") + + -- redirectLuaProc("lua_getuservalue") -- Only used in: + -- lpeg library [Program Options].Cucumber mode + + redirectLuaProc("lua_insert") + redirectLuaProc("lua_iscfunction") + redirectLuaProc("lua_isnumber") + redirectLuaProc("lua_isstring") + redirectLuaProc("lua_isuserdata") + redirectLuaProc("lua_len") + redirectLuaProc("lua_load") + redirectLuaProc("lua_newstate") + redirectLuaProc("lua_newuserdata") + redirectLuaProc("lua_next") + redirectLuaProc("lua_pcallk") + redirectLuaProc("lua_pushboolean") + redirectLuaProc("lua_pushcclosure") + redirectLuaProc("lua_pushfstring") + redirectLuaProc("lua_pushinteger") + redirectLuaProc("lua_pushlightuserdata") + redirectLuaProc("lua_pushlstring") + redirectLuaProc("lua_pushnil") + redirectLuaProc("lua_pushnumber") + redirectLuaProc("lua_pushstring") + redirectLuaProc("lua_pushvalue") + redirectLuaProc("lua_pushvfstring") + redirectLuaProc("lua_rawequal") + redirectLuaProc("lua_rawget") + redirectLuaProc("lua_rawgeti") + redirectLuaProc("lua_rawlen") + redirectLuaProc("lua_rawset") + redirectLuaProc("lua_rawseti") + redirectLuaProc("lua_remove") + redirectLuaProc("lua_resume") + redirectLuaProc("lua_setfield") + redirectLuaProc("lua_setglobal") + redirectLuaProc("lua_setmetatable") + redirectLuaProc("lua_settable") + redirectLuaProc("lua_settop") + redirectLuaProc("lua_setupvalue") + + -- redirectLuaProc("lua_setuservalue") -- Only used in: + -- debug library (replaced) + -- lpeg library ([Program Options].Cucumber mode) + + redirectLuaProc("lua_toboolean") + redirectLuaProc("lua_tocfunction") + redirectLuaProc("lua_tointegerx") + redirectLuaProc("lua_tolstring") + redirectLuaProc("lua_tonumberx") + + -- redirectLuaProc("lua_tounsignedx") -- Only used in: + -- bit32 library (removed) + -- luaL_setfuncs() (replaced) + -- math library (replaced) + + redirectLuaProc("lua_touserdata") + redirectLuaProc("lua_type") + redirectLuaProc("lua_typename") + redirectLuaProc("lua_xmove") + redirectLuaProc("luaL_addlstring") + redirectLuaProc("luaL_addstring") + redirectLuaProc("luaL_addvalue") + redirectLuaProc("luaL_argerror") + redirectLuaProc("luaL_checkinteger") + redirectLuaProc("luaL_checknumber") + redirectLuaProc("luaL_checkudata") + redirectLuaProc("luaL_error") + redirectLuaProc("luaL_getmetafield") + + -- redirectLuaProc("luaL_getsubtable") -- Only used in: + -- debug library (replaced) + -- luaL_requiref() (removed) + -- package library (replaced) + + redirectLuaProc("luaL_gsub") + redirectLuaProc("luaL_len") + redirectLuaProc("luaL_loadbufferx") + redirectLuaProc("luaL_loadfilex") + redirectLuaProc("luaL_loadstring") + redirectLuaProc("luaL_newmetatable") + redirectLuaProc("luaL_newstate") + redirectLuaProc("luaL_optlstring") + + -- redirectLuaProc("luaL_prepbuffsize") -- Only used in: + -- lpeg library ([Program Options].Cucumber mode) + -- luaL_addlstring() (replaced) + -- luaL_addstring() (replaced) + -- luaL_addvalue() (replaced) + -- luaL_gsub() (replaced) + -- mime_core library ([Program Options].Cucumber mode) + -- socket_core library ([Program Options].Cucumber mode) + -- string library (replaced) + + redirectLuaProc("luaL_pushresult") + redirectLuaProc("luaL_ref") + + -- redirectLuaProc("luaL_requiref") -- Only used in: + -- bootstrapLua() (calls to luaL_requiref() removed) + -- enableCucumberSupport() ([Program Options].Cucumber mode) + + redirectLuaProc("luaL_setfuncs") + + -- redirectLuaProc("luaL_tolstring") -- Only used in: + -- string library (replaced) + + redirectLuaProc("luaL_traceback") + redirectLuaProc("luaL_where") + redirectLuaProc("luaopen_base") + + -- redirectLuaProc("luaopen_bit32") -- Removed + + -- redirectLuaProc("luaopen_coroutine") -- Only used in: + -- enableCucumberSupport() ([Program Options].Cucumber mode) + + redirectLuaProc("luaopen_debug") + redirectLuaProc("luaopen_math") + redirectLuaProc("luaopen_package") + redirectLuaProc("luaopen_string") + redirectLuaProc("luaopen_table") + + ----------------------------------- + -- /END Lua function redirection -- + ----------------------------------- + + ---------------------------------------------------------------------------------------------------------------- + -- /START Lua INI handling fix -- + ---------------------------------------------------------------------------------------------------------------- + -- The engine uses a custom Lua function (luaL_loadfilexptr) to execute Baldur.lua (opened with _wfopen). -- + -- Since stdio functions have to be run within the same C runtime instance these functions must be -- + -- redirected to the Lua DLL procedures. -- + ---------------------------------------------------------------------------------------------------------------- + + -- Redirect the _wfopen() call in OpenIniFile() + EEex_ReplaceCall(EEex_Label("Hook-OpenIniFile()-_wfopen()"), EEex_GetLuaLibraryProc("wrapper_wfopen")) + + -- Redirect the luaL_loadfilexptr() call in chReadIniFile() + EEex_ReplaceCall(EEex_Label("Hook-chReadIniFile()-luaL_loadfilexptr()"), EEex_GetLuaLibraryProc("luaL_loadfilexptr")) + + -- A unique pattern cannot be established inside chWriteInifile() - replace it entirely to redirect its fclose() call + override("chWriteInifile") + + -- It's difficult to replace the fprintf() call in Infinity_WriteINILine() since a unique pattern can't be + -- established for that function. The only way to patch it is to grab its address after it has been + -- exported to Lua and replace it entirely. + + EEex_ReplaceLua_OnUIFunctionsLoaded = function() + EEex_DisableCodeProtection() + EEex_JITAt(EEex_CFuncToPtr(Infinity_WriteINILine), {[[ + jmp #L(EEex::Override_Infinity_WriteINILine) + ]]}) + EEex_EnableCodeProtection() + end + + EEex_HookAfterCall(EEex_Label("Hook-dimmInit()-uiLoadFunctions()"), EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(32) + ]]}, + EEex_GenLuaCall("EEex_ReplaceLua_OnUIFunctionsLoaded"), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + })) + + -- Disable backup INI processing, (runs if Baldur.lua had a syntax / runtime error), to prevent crash + -- due to the above stdio redirects for INI processing. The backup processing appears to parse + -- Baldur.lua as the old Baldur.ini SQL format. + EEex_ForceJump(EEex_Label("Hook-chReadIniFile()-CheckDoBackupProcessingJmp")) + + ------------------------------- + -- /END Lua INI handling fix -- + ------------------------------- + + -- Replace all inlined uses of LUA_REGISTRYINDEX = -1001000 (Lua 5.2) with LUA_REGISTRYINDEX = -10000 (LuaJIT) + for _, address in ipairs(EEex_Label("Data-LUA_REGISTRYINDEX")) do + EEex_Write32(address, -10000) + end + + EEex_EnableCodeProtection() + +end)() diff --git a/EEex/copy/EEex_Script.lua b/EEex/copy/EEex_Script.lua index 8f4b991..f02713b 100644 --- a/EEex/copy/EEex_Script.lua +++ b/EEex/copy/EEex_Script.lua @@ -1,7 +1,7 @@ function EEex_Script_IsPlayerScript(script) - local aux = EEex_TryGetUDAux(script) - return aux and aux["EEex_IsPlayerScript"] == true + -- [EEex.dll] + return EEex.IsPlayerScript(script) end CAIScript.isPlayerScript = EEex_Script_IsPlayerScript @@ -9,25 +9,3 @@ function EEex_Script_GetResRef(script) return script.cResRef:get() end CAIScript.getResRef = EEex_Script_GetResRef - ------------ --- Hooks -- ------------ - -function EEex_Script_Hook_OnRead(script, bPlayerScript) - EEex_GetUDAux(script)["EEex_IsPlayerScript"] = bPlayerScript ~= 0 -end - -function EEex_Script_Hook_OnCopy(srcScript, destScript) - - local srcAux = EEex_TryGetUDAux(srcScript) - if not srcAux then - EEex_Error("Unknown srcScript (this should never happen)!") - end - - EEex_GetUDAux(destScript)["EEex_IsPlayerScript"] = srcAux["EEex_IsPlayerScript"] -end - -function EEex_Script_Hook_OnDestruct(script) - EEex_DeleteUDAux(script) -end diff --git a/EEex/copy/EEex_Script_Patch.lua b/EEex/copy/EEex_Script_Patch.lua index d6d1aaa..bf12d5e 100644 --- a/EEex/copy/EEex_Script_Patch.lua +++ b/EEex/copy/EEex_Script_Patch.lua @@ -3,73 +3,71 @@ EEex_DisableCodeProtection() - ----------------------------- - -- EEex_Script_Hook_OnRead -- - ----------------------------- + --[[ + +-----------------------------------------------------------------------------------------------+ + | Maintain EEex data that flags whether a CAIScript was loaded as `bPlayerScript` (.BS vs .BCS) | + +-----------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Script_Hook_OnRead(pScript: CAIScript*, bPlayerScript: bool) | + +-----------------------------------------------------------------------------------------------+ + --]] - EEex_HookAfterCall(EEex_Label("Hook-CAIScript::Read()-OnRead"), EEex_FlattenTable({ + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIScript::Read()-OnRead"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Script_Hook_OnRead", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CAIScript" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}} end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + mov rdx, r14 ; bPlayerScript + mov rcx, rsi ; pScript + call #L(EEex::Script_Hook_OnRead) + ]]} + ) - ----------------------------- - -- EEex_Script_Hook_OnCopy -- - ----------------------------- + --[[ + +-----------------------------------------------------------------------------------------+ + | Associate EEex data linked to a CAIScript instance with a new CAIScript instance (copy) | + +-----------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Script_Hook_OnCopy(pSrcScript: CAIScript*, pDstScript: CAIScript*) | + +-----------------------------------------------------------------------------------------+ + --]] for _, entry in ipairs({ {"Hook-CAIScript::Construct()-OnCopy1", "rdi"}, {"Hook-CAIScript::Construct()-OnCopy2", "rsi"}, }) do - EEex_HookBeforeCall(EEex_Label(entry[1]), EEex_FlattenTable({ + EEex_HookBeforeCallWithLabels(EEex_Label(entry[1]), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - #MAKE_SHADOW_SPACE(64) + #MAKE_SHADOW_SPACE(16) mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - ]]}, - EEex_GenLuaCall("EEex_Script_Hook_OnCopy", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CAIScript" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], #$(2) #ENDL", {rspOffset, entry[2]}}, "CAIScript" end, - }, - }), - {[[ - call_error: + + mov rdx, #$(2) ]], entry, [[ ; pDstScript + ; rcx already pSrcScript + call #L(EEex::Script_Hook_OnCopy) + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) + ]]} + ) end - --------------------------------- - -- EEex_Script_Hook_OnDestruct -- - --------------------------------- + --[[ + +-----------------------------------------------------------------------+ + | Clean up EEex data linked to a CAIScript instance after it is deleted | + +-----------------------------------------------------------------------+ + | [EEex.dll] EEex::Script_Hook_OnDestruct(pScript: CAIScript*) | + +-----------------------------------------------------------------------+ + --]] - EEex_HookAfterCall(EEex_Label("Hook-CAIScript::Destruct()-OnDestruct"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Script_Hook_OnDestruct", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CAIScript" end, - }, - }), + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIScript::Destruct()-OnDestruct"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + mov rcx, rsi ; pScript + call #L(EEex::Script_Hook_OnDestruct) + ]]} + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Sprite.lua b/EEex/copy/EEex_Sprite.lua index a721812..3e767eb 100644 --- a/EEex/copy/EEex_Sprite.lua +++ b/EEex/copy/EEex_Sprite.lua @@ -452,8 +452,15 @@ function EEex_Sprite_GetActiveStats(sprite) end CGameSprite.getActiveStats = EEex_Sprite_GetActiveStats +-- @bubb_doc { EEex_Sprite_GetExtendedStat / instance_name=getExtendedStat } +-- @deprecated: Use ``EEex_Sprite_GetStat()`` instead. +-- @summary: Returns the value of the extended stat on the given ``sprite``. +-- @self { sprite / type=CGameSprite }: The sprite whose extended stat value is being fetched. +-- @return { type=number }: See summary. + function EEex_Sprite_GetExtendedStat(sprite, id) - return EEex_GetUDAux(sprite:getActiveStats())["EEex_ExtendedStats"][id] + -- [EEex.dll] + return EEex.GetExtendedStatValue(sprite, id) end CGameSprite.getExtendedStat = EEex_Sprite_GetExtendedStat @@ -844,13 +851,13 @@ function EEex_Sprite_Hook_OnResetQuickListCounts(sprite) end end -function EEex_Sprite_Hook_OnConstruct(sprite) - -end - -function EEex_Sprite_Hook_OnDestruct(sprite) +-- function EEex_Sprite_Hook_OnConstruct(sprite) +-- +-- end -end +-- function EEex_Sprite_Hook_OnDestruct(sprite) +-- +-- end EEex_Sprite_Private_MarshalHandlerFieldType = { ["TABLE_END"] = 0, diff --git a/EEex/copy/EEex_Sprite_Patch.lua b/EEex/copy/EEex_Sprite_Patch.lua index dcdde41..93654c5 100644 --- a/EEex/copy/EEex_Sprite_Patch.lua +++ b/EEex/copy/EEex_Sprite_Patch.lua @@ -3,202 +3,271 @@ EEex_DisableCodeProtection() - ------------------------------------------- - -- EEex_Sprite_Hook_CheckSuppressTooltip -- - ------------------------------------------- - - EEex_HookRelativeBranch(EEex_Label("Hook-CGameSprite::SetCursor()-SetCharacterToolTip()"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_CheckSuppressTooltip", { - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - xor rax, rax - - no_error: - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - test rax, rax - jnz #L(return) - call #L(original) - jmp #L(return) - ]]}, - })) + --[[ + +------------------------------------------------------------+ + | Call a hook that allows tooltips to be suppressed | + +------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_CheckSuppressTooltip() -> boolean | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Suppress tooltip from being opened | + +------------------------------------------------------------+ + --]] + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameSprite::SetCursor()-SetCharacterToolTip()"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_CheckSuppressTooltip", { + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error - ---------------------------------- - -- EEex_Sprite_Hook_OnConstruct -- - ---------------------------------- + call_error: + xor rax, rax - for _, labelName in ipairs({ - "Hook-CGameSprite::Construct1()-FirstCall", - "Hook-CGameSprite::Construct2()-FirstCall" - }) do - EEex_HookAfterCall(EEex_Label(labelName), EEex_FlattenTable({ + no_error: + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + test rax, rax + jnz #L(return_skip) + ]]}, + }) + ) + + --[[ + +------------------------------------------------------------+ + | [Unused] Call a hook whenever a CGameSprite is constructed | + +------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnConstruct(sprite: CGameSprite) | + +------------------------------------------------------------+ + --]] + + -- for _, labelName in ipairs({ + -- "Hook-CGameSprite::Construct1()-FirstCall", + -- "Hook-CGameSprite::Construct2()-FirstCall" + -- }) do + -- EEex_HookAfterCallWithLabels(EEex_Label(labelName), { + -- {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + -- EEex_FlattenTable({ + -- {[[ + -- #MAKE_SHADOW_SPACE(40) + -- ]]}, + -- EEex_GenLuaCall("EEex_Sprite_Hook_OnConstruct", { + -- ["args"] = { + -- function(rspOffset) return {[[ + -- mov qword ptr ss:[rsp+#$(1)], rsi + -- ]], {rspOffset}}, "CGameSprite" end, + -- }, + -- }), + -- {[[ + -- call_error: + -- #DESTROY_SHADOW_SPACE + -- ]]}, + -- }) + -- ) + -- end + + --[[ + +-----------------------------------------------------------+ + | [Unused] Call a hook whenever a CGameSprite is destructed | + +-----------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnDestruct(sprite: CGameSprite) | + +-----------------------------------------------------------+ + --]] + + -- EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Destruct()-FirstCall"), { + -- {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + -- EEex_FlattenTable({ + -- {[[ + -- #MAKE_SHADOW_SPACE(40) + -- ]]}, + -- EEex_GenLuaCall("EEex_Sprite_Hook_OnDestruct", { + -- ["args"] = { + -- function(rspOffset) return {[[ + -- mov qword ptr ss:[rsp+#$(1)], rbx + -- ]], {rspOffset}}, "CGameSprite" end, + -- }, + -- }), + -- {[[ + -- call_error: + -- #DESTROY_SHADOW_SPACE + -- ]]}, + -- }) + -- ) + + --[[ + +--------------------------------------------------------------------------------------------------------------+ + | Call a hook whenever the engine changes the number of times a spell can be cast | + +--------------------------------------------------------------------------------------------------------------+ + | Used to implement listeners that react to changes in a spell's castable count | + +--------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnCheckQuickLists(sprite: CGameSprite, abilityId: CAbilityId, changeAmount: number) | + +--------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameSprite::CheckQuickLists()-CallListeners"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ {[[ - #MAKE_SHADOW_SPACE(40) + #MAKE_SHADOW_SPACE(88) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnConstruct", { + EEex_GenLuaCall("EEex_Sprite_Hook_OnCheckQuickLists", { ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CAbilityId" end, function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rsi - ]], {rspOffset}}, "CGameSprite" end, + movsx rax, r8w + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}} end, }, }), {[[ call_error: + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE ]]}, - })) - end - - --------------------------------- - -- EEex_Sprite_Hook_OnDestruct -- - --------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Destruct()-FirstCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnDestruct", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rbx - ]], {rspOffset}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------- - -- EEex_Sprite_Hook_OnCheckQuickLists -- - ---------------------------------------- - - EEex_HookBeforeRestore(EEex_Label("Hook-CGameSprite::CheckQuickLists()-CallListeners"), 0, 5, 5, EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(88) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnCheckQuickLists", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CAbilityId" end, - function(rspOffset) return {[[ - movsx rax, r8w - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}} end, - }, - }), - {[[ - call_error: - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------- - -- EEex_Sprite_Hook_OnCheckQuickLists -- - ---------------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Rest()-OnResetQuickListCounts"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnResetQuickListCounts", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ----------------------------------------------- - -- CGameEffectList_Marshal_SavedSpritePtrMem -- - ----------------------------------------------- + }) + ) + + --[[ + +----------------------------------------------------------------------------------------+ + | Call a hook whenever the engine changes resets the number of times a spell can be cast | + +----------------------------------------------------------------------------------------+ + | Used to implement listeners that react to changes in a spell's castable count | + +----------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnResetQuickListCounts(sprite: CGameSprite) | + +----------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Rest()-OnResetQuickListCounts"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnResetQuickListCounts", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, + }, + }), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +--------------------------------------------------------------------------------------------------------------------------+ + | Implement custom creature-marshalling handlers that can store data about a sprite at the end of the sprite's effect list | + +--------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_CalculateExtraEffectListMarshalSize(sprite: CGameSprite) -> number | + | return -> The number of bytes required to store the extra data | + +--------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_WriteExtraEffectListMarshal(memory: number) | + +--------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_ReadExtraEffectListUnmarshal(sprite: CGameSprite, memory: number) | + +--------------------------------------------------------------------------------------------------------------------------+ + --]] + + ----------------------------------------------------- + -- [JIT] CGameEffectList_Marshal_SavedSpritePtrMem -- + ----------------------------------------------------- local CGameEffectList_Marshal_SavedSpritePtrMem = EEex_Malloc(EEex_PtrSize * 2) EEex_WritePtr(CGameEffectList_Marshal_SavedSpritePtrMem, 0x0) - EEex_HookRelativeBranch(EEex_Label("Hook-CGameSprite::Marshal()-CGameEffectList::Marshal()"), {[[ - mov qword ptr ds:[#$(1)], r13 ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL - call #L(original) - mov qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL - jmp #L(return) - ]]}) + EEex_HookBeforeAndAfterCall(EEex_Label("Hook-CGameSprite::Marshal()-CGameEffectList::Marshal()"), + {"mov qword ptr ds:[#$(1)], r13 #ENDL", {CGameEffectList_Marshal_SavedSpritePtrMem}}, + {"mov qword ptr ds:[#$(1)], 0 #ENDL", {CGameEffectList_Marshal_SavedSpritePtrMem}} + ) - ------------------------------------------------- - -- CGameEffectList_Unmarshal_SavedSpritePtrMem -- - ------------------------------------------------- + ------------------------------------------------------- + -- [JIT] CGameEffectList_Unmarshal_SavedSpritePtrMem -- + ------------------------------------------------------- local CGameEffectList_Unmarshal_SavedSpritePtrMem = CGameEffectList_Marshal_SavedSpritePtrMem + EEex_PtrSize EEex_WritePtr(CGameEffectList_Unmarshal_SavedSpritePtrMem, 0x0) - EEex_HookRelativeBranch(EEex_Label("Hook-CGameSprite::Unmarshal()-CGameEffectList::Unmarshal()"), {[[ - mov qword ptr ds:[#$(1)], rbx ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL - call #L(original) - mov qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL - jmp #L(return) - ]]}) + EEex_HookBeforeAndAfterCall(EEex_Label("Hook-CGameSprite::Unmarshal()-CGameEffectList::Unmarshal()"), + {"mov qword ptr ds:[#$(1)], rbx #ENDL", {CGameEffectList_Unmarshal_SavedSpritePtrMem}}, + {"mov qword ptr ds:[#$(1)], 0 #ENDL", {CGameEffectList_Unmarshal_SavedSpritePtrMem}} + ) ---------------------------------------------------------- - -- EEex_Sprite_Hook_CalculateExtraEffectListMarshalSize -- + -- [malloc] CGameEffectList_Marshal_OriginalMarshalSize -- ---------------------------------------------------------- local CGameEffectList_Marshal_OriginalMarshalSize = EEex_Malloc(EEex_PtrSize) EEex_WritePtr(CGameEffectList_Marshal_OriginalMarshalSize, 0x0) - EEex_HookJump(EEex_Label("Hook-CGameEffectList::Marshal()-OverrideSize"), 0, EEex_FlattenTable({ - {[[ - cmp qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL - jz jmp + ------------------------------------------------------------------ + -- [Lua] EEex_Sprite_Hook_CalculateExtraEffectListMarshalSize() -- + ------------------------------------------------------------------ + + EEex_HookBeforeConditionalJumpWithLabels(EEex_Label("Hook-CGameEffectList::Marshal()-OverrideSize"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RBX, EEex_HookIntegrityWatchdogRegister.RCX, + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + cmp qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL + jnz continue - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_CalculateExtraEffectListMarshalSize", { - ["args"] = { - -- sprite - function(rspOffset) return {[[ - mov rax, qword ptr ss:[#$(1)] ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), - {[[ - jmp no_error + test ebx, ebx ; Recalculates flags for the jle instruction being returned to + jmp #L(return) - call_error: - xor rax, rax + continue: + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_CalculateExtraEffectListMarshalSize", { + ["args"] = { + -- sprite + function(rspOffset) return {[[ + mov rax, qword ptr ss:[#$(1)] ]], {CGameEffectList_Marshal_SavedSpritePtrMem}, [[ #ENDL + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Number, + }), + {[[ + jmp no_error - no_error: - mov qword ptr ds:[#$(1)], rbx ]], {CGameEffectList_Marshal_OriginalMarshalSize}, [[ #ENDL - add rbx, rax + call_error: + xor rax, rax - #DESTROY_SHADOW_SPACE - ]]}, - })) + no_error: + mov qword ptr ds:[#$(1)], rbx ]], {CGameEffectList_Marshal_OriginalMarshalSize}, [[ #ENDL + add rbx, rax ; Sets flags for the jle instruction being returned to - -------------------------------------------------- - -- EEex_Sprite_Hook_WriteExtraEffectListMarshal -- - -------------------------------------------------- + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + ---------------------------------------------------------- + -- [Lua] EEex_Sprite_Hook_WriteExtraEffectListMarshal() -- + ---------------------------------------------------------- EEex_HookAfterCall(EEex_Label("Hook-CGameEffectList::Marshal()-WriteExtra"), EEex_FlattenTable({ {[[ @@ -224,181 +293,271 @@ ]]}, })) - --------------------------------------------------- - -- EEex_Sprite_Hook_ReadExtraEffectListUnmarshal -- - --------------------------------------------------- + ----------------------------------------------------------- + -- [Lua] EEex_Sprite_Hook_ReadExtraEffectListUnmarshal() -- + ----------------------------------------------------------- - EEex_HookRelativeBranch(EEex_Label("Hook-CGameEffectList::Unmarshal()-CGameEffect::DecodeEffectFromBase()"), EEex_FlattenTable({ - {[[ - cmp qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL - jz dont_do_anything + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameEffectList::Unmarshal()-CGameEffect::DecodeEffectFromBase()"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + cmp dword ptr ds:[rcx], 0x49422D58 ; Check if signature is "X-BI" + jne #L(return) - cmp dword ptr ds:[rcx], 0x49422D58 ; Check if signature is "X-BI" - je handle_EEex_binary + cmp qword ptr ds:[#$(1)], 0 ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL + jz dont_process_effect - dont_do_anything: - call #L(original) - jmp #L(return) + #MAKE_SHADOW_SPACE(48) + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_ReadExtraEffectListUnmarshal", { + ["args"] = { + -- sprite + function(rspOffset) return {[[ + mov rax, qword ptr ss:[#$(1)] ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]}, "CGameSprite" end, + -- memory + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"} end, + }, + }), + {[[ + call_error: + #DESTROY_SHADOW_SPACE - handle_EEex_binary: - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_ReadExtraEffectListUnmarshal", { - ["args"] = { - -- sprite - function(rspOffset) return {[[ - mov rax, qword ptr ss:[#$(1)] ]], {CGameEffectList_Unmarshal_SavedSpritePtrMem}, [[ #ENDL - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]}, "CGameSprite" end, - -- memory - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx", {rspOffset}, "#ENDL"} end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - jmp #L(Hook-CGameEffectList::Unmarshal()-Return) - ]]}, - })) + dont_process_effect: + #MANUAL_HOOK_EXIT(1) + jmp #L(Hook-CGameEffectList::Unmarshal()-Return) + ]]}, + }) + ) + -- Manually define the ignored registers for the unusual `dont_process_effect` branch above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CGameEffectList::Unmarshal()-CGameEffect::DecodeEffectFromBase()"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) + + --[[ + +------------------------------------------------------------------------------------------------------------------------------------------------+ + | Implement custom CONCENTR.2DA[VALUE,CHECK_MODE] - EEex-LuaFunction=. When `EEex-LuaFunction=` is specified, call | + | and use the return value to determine whether the spell was disrupted, (instead of running the normal concentration code). | + | | + | The function's signature is: (sprite: CGameSprite, damageData: table) -> boolean | + | | + | damageData: | + | | + | damageTaken: number - The number of hit points removed by the Opcode #12 | + | effect: CGameEffect - The Opcode #12 effect | + | sourceSprite: CGameSprite - The source sprite of the Opcode #12 effect | + | targetSprite: CGameSprite - The target sprite of the Opcode #12 effect | + +------------------------------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnLoadConcentrationCheckMode(checkMode: string) | + +------------------------------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnCheckConcentration(sprite: CGameSprite) -> boolean | + | return: | + | -> false - Spell NOT disrupted | + | -> true - Spell disrupted | + +------------------------------------------------------------------------------------------------------------------------------------------------+ + --]] + + ----------------------------------------------------------- + -- [Lua] EEex_Sprite_Hook_OnLoadConcentrationCheckMode() -- + ----------------------------------------------------------- + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CRuleTables::Construct()-CHECK_MODE-ConvertStrToInt"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnLoadConcentrationCheckMode", { + ["args"] = { + function(rspOffset) return {[[ + mov qword ptr ss:[rsp+#$(1)], rcx + ]], {rspOffset}}, "string" end, + }, + }), + {[[ + call_error: + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) --------------------------------------------------- - -- EEex_Sprite_Hook_OnLoadConcentrationCheckMode -- + -- [Lua] EEex_Sprite_Hook_OnCheckConcentration() -- --------------------------------------------------- - EEex_HookBeforeCall(EEex_Label("Hook-CRuleTables::Construct()-CHECK_MODE-ConvertStrToInt"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(48) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnLoadConcentrationCheckMode", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rcx - ]], {rspOffset}}, "string" end, - }, - }), - {[[ - call_error: - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ---------------------------------------------------------------------------------- - -- Call CONCENTR.2DA[VALUE,CHECK_MODE] EEex-LuaFunction=(sprite) -- - -- instead of running the normal concentration code. -- - -- Return: -- - -- false -> Spell NOT disrupted. -- - -- true -> Spell disrupted. -- - ---------------------------------------------------------------------------------- - EEex_Sprite_Private_RunCustomConcentrationCheckMem = EEex_Malloc(1) EEex_Write8(EEex_Sprite_Private_RunCustomConcentrationCheckMem, 0) - EEex_HookBeforeRestore(EEex_Label("Hook-CGameSprite::ConcentrationFailed()-CHECK_MODE-Redirect"), 0, 5, 5, EEex_FlattenTable({ - {[[ - cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL - jz return - - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnCheckConcentration", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - mov rax, 1 - - no_error: - #DESTROY_SHADOW_SPACE - ret - ]]}, - })) - - --------------------------------------------------------- - -- EEex_Sprite_Hook_OnDamageEffectStartingCalculations -- - --------------------------------------------------------- - - EEex_HookAfterRestore(EEex_Label("Hook-CGameEffectDamage::ApplyEffect()-StartingCalculations"), 0, 6, 6, EEex_FlattenTable({ - {[[ - cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL - jz #L(return) + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameSprite::ConcentrationFailed()-CHECK_MODE-Redirect"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL + jz #L(return) - #MAKE_SHADOW_SPACE(64) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnDamageEffectStartingCalculations", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rbp-0x41] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CGameSprite" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}, - })) + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnCheckConcentration", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error - ----------------------------------------- - -- EEex_Sprite_Hook_OnDamageEffectDone -- - ----------------------------------------- + call_error: + mov rax, 1 - EEex_HookAfterRestore(EEex_Label("Hook-CGameEffectDamage::ApplyEffect()-OnDone"), 0, 7, 7, EEex_FlattenTable({ - {[[ - cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL - jz #L(return) + no_error: + #DESTROY_SHADOW_SPACE + #MANUAL_HOOK_EXIT(1) + ret + ]]}, + }) + ) + -- Manually define the ignored registers for the unusual `ret` above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CGameSprite::ConcentrationFailed()-CHECK_MODE-Redirect"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) + + --[[ + +----------------------------------------------------------------------------------------------------------------------------------------+ + | Track Opcode #12 effects that have been applied to a sprite since it started its action. This is used to implement custom spell | + | disruption logic. | + +----------------------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnDamageEffectStartingCalculations(effect: CGameEffect, sourceSprite: CGameSprite, targetSprite: CGameSprite) | + +----------------------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnDamageEffectDone(effect: CGameEffect, sourceSprite: CGameSprite, targetSprite: CGameSprite) | + +----------------------------------------------------------------------------------------------------------------------------------------+ + --]] + + ----------------------------------------------------------------- + -- [Lua] EEex_Sprite_Hook_OnDamageEffectStartingCalculations() -- + ----------------------------------------------------------------- + + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffectDamage::ApplyEffect()-StartingCalculations"), 0, 6, 6, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL + jz #L(return) - #MAKE_SHADOW_SPACE(56) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnDamageEffectDone", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameEffect" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rbp-0x41] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CGameSprite" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + #MAKE_SHADOW_SPACE(64) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnDamageEffectStartingCalculations", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameEffect" end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rbp-0x41] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CGameSprite" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, + }, + }), + {[[ + call_error: + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) - -------------------------------------- - -- EEex_Sprite_Hook_OnSetCurrAction -- - -------------------------------------- + ------------------------------------------------- + -- [Lua] EEex_Sprite_Hook_OnDamageEffectDone() -- + ------------------------------------------------- - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::SetCurrAction()-FirstCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_OnSetCurrAction", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffectDamage::ApplyEffect()-OnDone"), 0, 7, 7, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, + EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + cmp byte ptr ss:[#$(1)], 0 ]], {EEex_Sprite_Private_RunCustomConcentrationCheckMem}, [[ #ENDL + jz #L(return) - -------------------------------------------------------------------------------------------- - -- Allow ITM header flag BIT18 to ignore weapon styles (as if the item were in SLOT_FIST) -- - -------------------------------------------------------------------------------------------- + #MAKE_SHADOW_SPACE(64) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnDamageEffectDone", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameEffect" end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rbp-0x41] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CGameSprite" end, + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, + }, + }), + {[[ + call_error: + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +---------------------------------------------------------------------+ + | On action change, clear EEex data associated with the ending action | + +---------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_OnSetCurrAction(sprite: CGameSprite) | + +---------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::SetCurrAction()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(40) + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_OnSetCurrAction", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CGameSprite" end, + }, + }), + {[[ + call_error: + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + + --[[ + +----------------------------------------------------------------------------------------+ + | Allow ITM header flag BIT18 to ignore weapon styles (as if the item were in SLOT_FIST) | + +----------------------------------------------------------------------------------------+ + | [Lua] EEex_Sprite_Hook_GetProfBonuses_IgnoreWeaponStyles(item: CItem, damR: number, | + | damL: number, thacR: number, thacL: number, ACB: number, ACM: number, | + | speed: number, crit: number) -> boolean | + | | + | return: | + | -> false - Don't alter engine behavior | + | -> true - Ignore weapon styles | + +----------------------------------------------------------------------------------------+ + --]] local getProfBonusesItemHack = EEex_Malloc(EEex_PtrSize) EEex_WritePtr(getProfBonusesItemHack, 0) @@ -429,77 +588,90 @@ ]]}) -- Main hook - EEex_HookBeforeRestore(EEex_Label("Hook-CRuleTables::GetProfBonuses()-IgnoreWeaponStyles"), 0, 5, 5, EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(136) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - mov dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], edx - mov dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8d - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 - ]]}, - EEex_GenLuaCall("EEex_Sprite_Hook_GetProfBonuses_IgnoreWeaponStyles", { - ["args"] = { - function(rspOffset) return {[[ - mov rax, qword ptr ds:[#$(1)] ]], {getProfBonusesItemHack}, [[ ; Global hack [item] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]}, "CItem" end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] ; Register arg 4 [damR] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(40)] ; Stack arg 1 [damL] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(48)] ; Stack arg 2 [thacR] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(56)] ; Stack arg 3 [thacL] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(64)] ; Stack arg 4 [ACB] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(72)] ; Stack arg 5 [ACM] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(80)] ; Stack arg 6 [speed] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - function(rspOffset) return {[[ - mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(88)] ; Stack arg 7 [crit] - mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL - ]]} end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), - {[[ - jmp no_error - - call_error: - xor rax, rax - - no_error: - test rax, rax - jz do_not_ignore_weapon_styles - - #DESTROY_SHADOW_SPACE(KEEP_ENTRY) - ret + EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CRuleTables::GetProfBonuses()-IgnoreWeaponStyles"), 0, 5, 5, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(136) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx + mov dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], edx + mov dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8d + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 + ]]}, + EEex_GenLuaCall("EEex_Sprite_Hook_GetProfBonuses_IgnoreWeaponStyles", { + ["args"] = { + function(rspOffset) return {[[ + mov rax, qword ptr ds:[#$(1)] ]], {getProfBonusesItemHack}, [[ ; Global hack [item] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]}, "CItem" end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] ; Register arg 4 [damR] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(40)] ; Stack arg 1 [damL] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(48)] ; Stack arg 2 [thacR] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(56)] ; Stack arg 3 [thacL] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(64)] ; Stack arg 4 [ACB] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(72)] ; Stack arg 5 [ACM] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(80)] ; Stack arg 6 [speed] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + function(rspOffset) return {[[ + mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(88)] ; Stack arg 7 [crit] + mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL + ]]} end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error - do_not_ignore_weapon_styles: - mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] - mov r8d, dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] - mov edx, dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] - mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] - #DESTROY_SHADOW_SPACE - ]]}, - })) + call_error: + xor rax, rax + + no_error: + test rax, rax + jz do_not_ignore_weapon_styles + + #DESTROY_SHADOW_SPACE(KEEP_ENTRY) + #MANUAL_HOOK_EXIT(1) + ret + + do_not_ignore_weapon_styles: + #RESUME_SHADOW_ENTRY + mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] + mov r8d, dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] + mov edx, dword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] + mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) + -- Manually define the ignored registers for the unusual `ret` above + EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("Hook-CRuleTables::GetProfBonuses()-IgnoreWeaponStyles"), 1, { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Stats.lua b/EEex/copy/EEex_Stats.lua index ee1b49e..1a438b2 100644 --- a/EEex/copy/EEex_Stats.lua +++ b/EEex/copy/EEex_Stats.lua @@ -20,76 +20,3 @@ EEex_DerivedStats_DisabledButtonType = { ["BUTTON_INNATEBUTTON"] = 13, ["SCREEN_INVENTORY"] = 14, } - ------------------------ --- General Functions -- ------------------------ - -EEex_Stats_Registered = {} - --- Needs to be updated if Beamdog ever adds a new stat -EEex_Stats_FirstExtendedStatID = 203 -EEex_Stats_ExtendedInfo = {} - -function EEex_Stats_Register(name, args) - EEex_Stats_Registered[name] = args -end - -function EEex_Stats_Private_SetExtended(exStats, id, v) - local exStatInfo = EEex_Stats_ExtendedInfo[id] - local min = exStatInfo.min - if min ~= "*" then v = math.max(v, tonumber(min)) end - local max = exStatInfo.max - if max ~= "*" then v = math.min(tonumber(max), v) end - exStats[id] = v -end - ------------ --- Hooks -- ------------ - -function EEex_Stats_Hook_OnConstruct(stats) - local aux = EEex_GetUDAux(stats) - for _, registered in pairs(EEex_Stats_Registered) do - EEex_Utility_CallIfExists(registered.onConstruct, registered, stats, aux) - end -end - -function EEex_Stats_Hook_OnDestruct(stats) - local aux = EEex_GetUDAux(stats) - for _, registered in pairs(EEex_Stats_Registered) do - EEex_Utility_CallIfExists(registered.onDestruct, registered, stats, aux) - end - EEex_DeleteUDAux(stats) -end - --- Assuming m_derivedStats is being reloaded (true as of v2.6) -function EEex_Stats_Hook_OnReload(sprite) - local stats = sprite.m_derivedStats - local aux = EEex_GetUDAux(stats) - for _, registered in pairs(EEex_Stats_Registered) do - EEex_Utility_CallIfExists(registered.onReload, registered, stats, aux, sprite) - end -end - -function EEex_Stats_Hook_OnEqu(stats, otherStats) - local aux = EEex_GetUDAux(stats) - local otherAux = EEex_GetUDAux(otherStats) - for _, registered in pairs(EEex_Stats_Registered) do - EEex_Utility_CallIfExists(registered.onEqu, registered, stats, aux, otherStats, otherAux) - end -end - -function EEex_Stats_Hook_OnPlusEqu(stats, otherStats) - local aux = EEex_GetUDAux(stats) - local otherAux = EEex_GetUDAux(otherStats) - for _, registered in pairs(EEex_Stats_Registered) do - EEex_Utility_CallIfExists(registered.onPlusEqu, registered, stats, aux, otherStats, otherAux) - end -end - -function EEex_Stats_Hook_OnGettingUnknown(stats, id) - return EEex_Stats_ExtendedInfo[id] - and EEex_GetUDAux(stats)["EEex_ExtendedStats"][id] - or 0 -end diff --git a/EEex/copy/EEex_Stats_Patch.lua b/EEex/copy/EEex_Stats_Patch.lua index d6bad17..cacdcbd 100644 --- a/EEex/copy/EEex_Stats_Patch.lua +++ b/EEex/copy/EEex_Stats_Patch.lua @@ -3,154 +3,165 @@ EEex_DisableCodeProtection() - --------------------------------- - -- EEex_Stats_Hook_OnConstruct -- - --------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CDerivedStats::Construct()-FirstCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnConstruct", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rsi - ]], {rspOffset}}, "CDerivedStats" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - -------------------------------- - -- EEex_Stats_Hook_OnDestruct -- - -------------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CDerivedStats::Destruct()-FirstCall"), EEex_FlattenTable({ + --[[ + +-------------------------------------------------------------+ + | Initialize EEex data linked to CDerivedStats instances | + +-------------------------------------------------------------+ + | [EEex.dll] Stats_Hook_OnConstruct(pStats: CDerivedStats*) | + +-------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::Construct()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnDestruct", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rdi - ]], {rspOffset}}, "CDerivedStats" end, - }, - }), + mov rcx, rsi ; pStats + call #L(EEex::Stats_Hook_OnConstruct) + ]]} + ) + + --[[ + +------------------------------------------------------------------+ + | Clean up EEex data linked to CDerivedStats instances | + +------------------------------------------------------------------+ + | [EEex.dll] EEex::Stats_Hook_OnDestruct(pStats: CDerivedStats*) | + +------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::Destruct()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) - - ------------------------------ - -- EEex_Stats_Hook_OnReload -- - ------------------------------ + mov rcx, rdi ; pStats + call #L(EEex::Stats_Hook_OnDestruct) + ]]} + ) + + --[[ + +----------------------------------------------------------------+ + | Reset EEex data linked to CDerivedStats instances | + +----------------------------------------------------------------+ + | [EEex.dll] EEex::Stats_Hook_OnReload(pStats: CDerivedStats*) | + +----------------------------------------------------------------+ + --]] local statsReloadTemplate = function(spriteRegStr) - return EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(40) - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnReload", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], #$(2) - ]], {rspOffset, spriteRegStr}}, "CGameSprite" end, - }, - }), - {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - }) + return {[[ + mov rcx, #$(1) ]], {spriteRegStr}, [[ ; pSprite + call #L(EEex::Stats_Hook_OnReload) + ]]} end - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::QuickLoad()-CDerivedStats::Reload()"), statsReloadTemplate("rdi")) - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-1"), statsReloadTemplate("rbx")) - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-2"), statsReloadTemplate("rbx")) - EEex_HookAfterCall(EEex_Label("Hook-CGameSprite::ProcessEffectList()-CDerivedStats::Reload()"), statsReloadTemplate("rsi")) - - --------------------------- - -- EEex_Stats_Hook_OnEqu -- - --------------------------- - - EEex_HookAfterCall(EEex_Label("Hook-CDerivedStats::operator_equ()-FirstCall"), EEex_FlattenTable({ + local callStatsReloadRbx = {"call #$(1) #ENDL", + { + EEex_JITNear(EEex_FlattenTable({ + {[[ + #STACK_MOD(8) ; This was called, the ret ptr broke alignment + #MAKE_SHADOW_SPACE + ]]}, + statsReloadTemplate("rbx"), + {[[ + #DESTROY_SHADOW_SPACE + ret + ]]}, + })), + }, + } + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::QuickLoad()-CDerivedStats::Reload()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + statsReloadTemplate("rdi") + ) + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-1"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + callStatsReloadRbx + ) + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-2"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + callStatsReloadRbx + ) + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::ProcessEffectList()-CDerivedStats::Reload()"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, + statsReloadTemplate("rsi") + ) + + --[[ + +-------------------------------------------------------------------------------------------------+ + | Associate EEex data linked to a CDerivedStats instance with a new CDerivedStats instance (copy) | + +-------------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Stats_Hook_OnEqu(pStats: CDerivedStats*, pOtherStats: CDerivedStats*) | + +-------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::operator_equ()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, {[[ - #MAKE_SHADOW_SPACE(48) - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnEqu", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], r14 - ]], {rspOffset}}, "CDerivedStats" end, - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rsi - ]], {rspOffset}}, "CDerivedStats" end, - }, - }), + mov rdx, rsi ; pOtherStats + mov rcx, r14 ; pStats + call #L(EEex::Stats_Hook_OnEqu) + ]]} + ) + + --[[ + +----------------------------------------------------------------------------------------------+ + | Apply bonus EEex CDerivedStats data to the regular EEex CDerivedStats data | + +----------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Stats_Hook_OnPlusEqu(pStats: CDerivedStats*, pOtherStats: CDerivedStats*) | + +----------------------------------------------------------------------------------------------+ + --]] + + EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CDerivedStats::operator_plus_equ()-FirstCall"), { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, {[[ - call_error: - #DESTROY_SHADOW_SPACE - ]]}, - })) + #MAKE_SHADOW_SPACE(8) + mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - ------------------------------- - -- EEex_Stats_Hook_OnPlusEqu -- - ------------------------------- + mov rdx, rdi ; pOtherStats + mov rcx, rbx ; pStats + call #L(EEex::Stats_Hook_OnPlusEqu) - EEex_HookBeforeCall(EEex_Label("Hook-CDerivedStats::operator_plus_equ()-FirstCall"), EEex_FlattenTable({ - {[[ - #MAKE_SHADOW_SPACE(56) - mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnPlusEqu", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rbx - ]], {rspOffset}}, "CDerivedStats" end, - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], rdi - ]], {rspOffset}}, "CDerivedStats" end, - }, - }), - {[[ - call_error: mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] #DESTROY_SHADOW_SPACE - ]]}, - })) - - -------------------------------------- - -- EEex_Stats_Hook_OnGettingUnknown -- - -------------------------------------- - - EEex_HookJumpOnSuccess(EEex_Label("Hook-CDerivedStats::GetAtOffset()-OutOfBoundsJmp"), 0, EEex_FlattenTable({ - {[[ - #STACK_MOD(8) ; This was called, the ret ptr broke alignment - #MAKE_SHADOW_SPACE(48) - inc eax - ]]}, - EEex_GenLuaCall("EEex_Stats_Hook_OnGettingUnknown", { - ["args"] = { - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CDerivedStats" end, - function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax #ENDL", {rspOffset}} end, - }, - ["returnType"] = EEex_LuaCallReturnType.Number, - }), - {[[ - jmp no_error + ]]} + ) + + --[[ + +---------------------------------------------------------------------------------------------+ + | Allow engine to fetch extended stat values | + +---------------------------------------------------------------------------------------------+ + | * Extended stats are those with ids outside of the vanilla range in STATS.IDS | + | * Extended stat minimums, maximums, and defaults are defined in X-STATS.2DA | + +---------------------------------------------------------------------------------------------+ + | [EEex.dll] EEex::Stats_Hook_OnGettingUnknown(pStats: CDerivedStats*, nStatId: int) -> int | + | return -> The value of the extended stat | + +---------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CDerivedStats::GetAtOffset()-OutOfBoundsJmp"), 0, { + {"stack_mod", 8}, + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, + EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE - call_error: - xor rax, rax + lea rdx, qword ptr ds:[rax+1] ; nStatId + ; rcx already pStats + call #L(EEex::Stats_Hook_OnGettingUnknown) - no_error: - #DESTROY_SHADOW_SPACE - ret - ]]}, - })) + #DESTROY_SHADOW_SPACE + #MANUAL_HOOK_EXIT(0) + ret + ]]}, + }) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_StutterDetector.lua b/EEex/copy/EEex_StutterDetector.lua index bc2f8b2..064bd24 100644 --- a/EEex/copy/EEex_StutterDetector.lua +++ b/EEex/copy/EEex_StutterDetector.lua @@ -26,7 +26,7 @@ function EEex_StutterDetector_Private_InstallFunctionWrappers() ( #k >= 5 and string.sub(k, 1, 5) == "EEex_" or #k >= 2 and string.sub(k, 1, 2) == "B3" ) - and v ~= EEex_GetMicroseconds + and v ~= EEex_StutterDetector_Private_Tick and v ~= EEex_GetMicroseconds then _G[k] = function(...) @@ -38,7 +38,13 @@ function EEex_StutterDetector_Private_InstallFunctionWrappers() local timeTaken = EEex_GetMicroseconds() - start topLevel = oldTopLevel - EEex_StutterDetector_Private_Times[k] = (EEex_StutterDetector_Private_Times[k] or 0) + timeTaken + local entry = EEex_StutterDetector_Private_Times[k] + if entry then + entry[1] = entry[1] + 1 + entry[2] = entry[2] + timeTaken + else + EEex_StutterDetector_Private_Times[k] = { 1, timeTaken } + end if topLevel then EEex_StutterDetector_Private_TopLevelTime = EEex_StutterDetector_Private_TopLevelTime + timeTaken @@ -46,15 +52,11 @@ function EEex_StutterDetector_Private_InstallFunctionWrappers() return table.unpack(retT) end + print("Installed stutter wrapper for "..k) end end end -function EEex_StutterDetector_Private_GameInitializedListener() - EEex_StutterDetector_Private_InstallFunctionWrappers() - EEex_StutterDetector_Private_PushMenuListener() -end - ---------- -- Code -- ---------- @@ -69,20 +71,20 @@ function EEex_StutterDetector_Private_Tick() if EEex_StutterDetector_OutputEnabled and EEex_StutterDetector_Private_LastMicroseconds then local target = 1000000 / EEex_CChitin.TIMER_UPDATES_PER_SECOND - local targetFudge = target + target * 0.05 + local targetFudge = target + target * 0.01 local diff = microseconds - EEex_StutterDetector_Private_LastMicroseconds - if diff > targetFudge and EEex_StutterDetector_Private_TopLevelTime > 1000 then + if diff >= targetFudge and EEex_StutterDetector_Private_TopLevelTime >= 400 then - print(" +--------------------+--------------------------+") - print(string.format("[!] | Stutter: %7.3fms | EEex Lua time: %7.3fms |", + print(" +--------------+-----+-----------------------------+") + print(string.format("[!] | Stutter: %7.3fms | EEex time: %7.3fms |", diff / 1000, EEex_StutterDetector_Private_TopLevelTime / 1000)) - print(" +--------------------+--------------------------+") + print(" +--------------+-----------------------------------+") EEex_Utility_IterateMapAsSorted(EEex_StutterDetector_Private_Times, - function(o1, o2) return o1[2] > o2[2] end, + function(o1, o2) return o1[2][2] > o2[2][2] end, function(i, k, v) - if v >= 1000 then - print(string.format(" | %7.3fms - %s", v / 1000, k)) + if v[2] >= 50 then + print(string.format(" | count: %5d | total: %7.3fms | avg: %7dns | %s", v[1], v[2] / 1000, v[2] / 1000 / v[1] * 1000000, k)) end end ) @@ -101,6 +103,10 @@ end end EEex_Menu_AddMainFileLoadedListener(EEex_StutterDetector_Private_LoadMenuListener) - EEex_GameState_AddInitializedListener(EEex_StutterDetector_Private_GameInitializedListener) EEex_Menu_AddAfterMainFileReloadedListener(EEex_StutterDetector_Private_PushMenuListener) + + EEex_StutterDetector_Private_InstallFunctionWrappers() + EEex_StutterDetector_Private_LoadMenuListener() + EEex_StutterDetector_Private_PushMenuListener() + end)() diff --git a/EEex/copy/EEex_Trigger.lua b/EEex/copy/EEex_Trigger.lua index c9f6233..80da96e 100644 --- a/EEex/copy/EEex_Trigger.lua +++ b/EEex/copy/EEex_Trigger.lua @@ -112,6 +112,7 @@ function EEex_Trigger_Hook_OnEvaluatingUnknown(aiBase, trigger) elseif triggerID == 0x4110 then -- EEex_MatchObject / EEex_MatchObjectEx + -- [EEex.dll] local matchedID = EEex.MatchObject(aiBase, trigger.m_string1.m_pchData:get(), trigger.m_specificID, trigger.m_specific2, trigger.m_specific3) diff --git a/EEex/copy/EEex_Trigger_Patch.lua b/EEex/copy/EEex_Trigger_Patch.lua index ec9b7e9..f5a191d 100644 --- a/EEex/copy/EEex_Trigger_Patch.lua +++ b/EEex/copy/EEex_Trigger_Patch.lua @@ -3,28 +3,53 @@ EEex_DisableCodeProtection() - EEex_HookJumpAutoSucceed(EEex_Label("Hook-CGameAIBase::EvaluateStatusTrigger()-DefaultJmp"), 0, EEex_FlattenTable({[[ - jbe jmp_fail - #MAKE_SHADOW_SPACE(48) - ]], EEex_GenLuaCall("EEex_Trigger_Hook_OnEvaluatingUnknown", { - ["args"] = { - function(rspOffset) return {[[ - mov qword ptr ss:[rsp+#$(1)], r14 - ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, - function(rspOffset) return {[[ - lea rax, qword ptr ss:[rbp+350h] - mov qword ptr ss:[rsp+#$(1)], rax - ]], {rspOffset}}, "CAITrigger" end, - }, - ["returnType"] = EEex_LuaCallReturnType.Boolean, - }), [[ - jmp no_error - call_error: - xor eax, eax - no_error: - mov esi, eax - #DESTROY_SHADOW_SPACE - ]]})) + --[[ + +---------------------------------------------------------------------------------------------------------------------------+ + | Implement new triggers | + +---------------------------------------------------------------------------------------------------------------------------+ + | 0x410D EEex_HasDispellableEffect(O:Object*) | + | 0x410E EEex_LuaTrigger(S:Chunk*) | + | 0x410F EEex_IsImmuneToOpcode(O:Object*,I:Opcode*) | + | 0x4110 EEex_MatchObject(S:Chunk*) | + | 0x4110 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) | + +---------------------------------------------------------------------------------------------------------------------------+ + | [Lua] EEex_Trigger_Hook_OnEvaluatingUnknown(aiBase: CGameAIBase|EEex_GameObject_CastUT, trigger: CAITrigger) -> boolean | + | return -> The trigger's evaluated value (false / true) | + +---------------------------------------------------------------------------------------------------------------------------+ + --]] + + EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameAIBase::EvaluateStatusTrigger()-DefaultJmp"), 0, { + {"hook_integrity_watchdog_ignore_registers", { + EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, + EEex_HookIntegrityWatchdogRegister.RSI, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, + EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 + }}}, + EEex_FlattenTable({ + {[[ + #MAKE_SHADOW_SPACE(48) + ]]}, + EEex_GenLuaCall("EEex_Trigger_Hook_OnEvaluatingUnknown", { + ["args"] = { + function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, + function(rspOffset) return {[[ + lea rax, qword ptr ss:[rbp+350h] + mov qword ptr ss:[rsp+#$(1)], rax + ]], {rspOffset}}, "CAITrigger" end, + }, + ["returnType"] = EEex_LuaCallReturnType.Boolean, + }), + {[[ + jmp no_error + + call_error: + xor eax, eax + + no_error: + mov esi, eax + #DESTROY_SHADOW_SPACE + ]]}, + }) + ) EEex_EnableCodeProtection() diff --git a/EEex/copy/EEex_Utility.lua b/EEex/copy/EEex_Utility.lua index e35fcae..f179b60 100644 --- a/EEex/copy/EEex_Utility.lua +++ b/EEex/copy/EEex_Utility.lua @@ -87,53 +87,11 @@ function EEex_Utility_GetOrCreateTable(t, key, fillFunc) return default end +-- @bubb_doc { EEex_Utility_DeepCopy } +-- @deprecated: Use ``EEex.DeepCopy`` instead. function EEex_Utility_DeepCopy(t) - - if type(t) ~= "table" then - return t - end - - local tCopy = {} - local processStack = {{tCopy, nil, t}} -- vCopy, iterK, toProcessT - local stackTop = 1 - - while true do - - ::continue:: - local toProcess = processStack[stackTop] - local toProcessT = toProcess[3] - local vCopy = toProcess[1] - - while true do - - local k, v = next(toProcessT, toProcess[2]) - if k == nil then - break - end - - toProcess[2] = k - - if type(v) == "table" then - stackTop = stackTop + 1 - processStack[stackTop] = {{}, nil, v} - goto continue - else - vCopy[k] = v - end - end - - processStack[stackTop] = nil - stackTop = stackTop - 1 - - if stackTop == 0 then - break - end - - local parent = processStack[stackTop] - parent[1][parent[2]] = vCopy - end - - return tCopy + -- [EEex.dll] + return EEex.DeepCopy(t) end function EEex_Utility_DumpSprite() diff --git a/EEex/loader/EEex.dll b/EEex/loader/EEex.dll new file mode 100644 index 0000000..7dc402d Binary files /dev/null and b/EEex/loader/EEex.dll differ diff --git a/EEex/loader/InfinityLoader.db b/EEex/loader/InfinityLoader.db index b2921be..055ed99 100644 --- a/EEex/loader/InfinityLoader.db +++ b/EEex/loader/InfinityLoader.db @@ -3,6 +3,14 @@ ;-- Bound Engine Functions -- ;---------------------------- +[!ExeSwitch-Baldur.exe-OpenIniFile] +Pattern=4889842450040000488BD9 +Operations=ADD -23 + +[!ExeSwitch-icewind.exe-OpenIniFile] +Pattern=4889842450040000488BD9 +Operations=ADD -36 + [C2DArray::Construct] Pattern=488D542420488937 Operations=ADD -51 @@ -123,6 +131,14 @@ Operations=ADD -25 [CChitin::OnResizeWindow] Pattern=48895C2420565741564883EC30 +[CDerivedStats::Construct] +Pattern=48894C24085741544155415641574883EC20488BF1 +Operations=ADD -15 + +[CDerivedStats::Destruct] +Pattern=488981500C0000 +Operations=ADD -34 + [CDerivedStats::GetAtOffset] Pattern=3DC9000000 Operations=ADD -5 @@ -400,6 +416,10 @@ Operations=ADD -10 [CompareCResByTypeThenName] Pattern=488B09488B12 +[OpenIniFile] +ExeSwitch=1 +ExeSwitchAlias=SiegeOfDragonspear.exe:Baldur.exe + [SDL_GetKeyFromName] Pattern=488BD94885C90F84F2000000 Operations=ADD -10 @@ -495,6 +515,10 @@ Operations=ADD -6, PUSH, READ DWORD, ADD, ADD 4 Pattern=41B800700000 Operations=ADD 59, PUSH, READ DWORD, ADD, ADD 4 +[optionsHaveChanged] +Pattern=4885C00F85DA000000 +Operations=ADD 219, PUSH, READ DWORD, ADD, ADD 8 + [resources] Pattern=488B1CFB4885DB745B Operations=ADD -4, PUSH, READ DWORD, ADD, ADD 4, ADD -8 @@ -732,6 +756,11 @@ Operations=ADD 48 Pattern=40554883EC30488D6C2430 Operations=ADD 13, PUSH, READ DWORD, ADD, ADD 5 +[Data-LUA_REGISTRYINDEX] +Pattern=BAD8B9F0FF +Operations=ADD 1 +Type=LIST + ;---------------------- ;-- Engine Functions -- ;---------------------- @@ -756,10 +785,29 @@ Operations=ADD -10 Pattern=488BF24889542468 Operations=ADD -49 +[Chitin_GetSectionCallback] +Pattern=4889742418574883EC20BA01000000 +Operations=ADD -10 + +[bootstrapLua] +Pattern=B918190000 +Operations=ADD -31, PUSH, READ DWORD, ADD, ADD 4 + +[chWriteInifile] +Pattern=4889742418574883EC20498BF1498BE8 +Operations=ADD 32, PUSH, READ DWORD, ADD, ADD 4 + +[l_log_print] +Pattern=BB010000008BF0 +Operations=ADD -23 + [operator_new] Pattern=488BD9EB0F Operations=ADD -6 +[panic] +Pattern=4883EC284533C0418D50FF + ;--------------------- ;-- Engine VFTables -- ;--------------------- @@ -860,13 +908,41 @@ Operations=ADD -38, PUSH, READ DWORD, ADD, ADD 4 ;-- Functions Referenced by Lua Bindings -- ;------------------------------------------ +[Hardcoded_luaL_error] +Pattern=4C894C242053574883EC28 +Operations=ADD -10 + +[Hardcoded_luaL_loadfilex] +Pattern=53415441564157488DAC24A0FEFFFF +Operations=ADD -2 + [Hardcoded_luaL_loadstring] Pattern=48894424404889542430 Operations=ADD -14 +[Hardcoded_luaL_ref] +Pattern=488B4910488D41F0 +Operations=ADD -26 + +[Hardcoded_lua_atpanic] +Pattern=498B8000010000 +Operations=ADD -4 + [Hardcoded_lua_callk] Pattern=48895C2408574883EC204C8B5110 +[Hardcoded_lua_createtable] +Pattern=418BF8488B4918 +Operations=ADD -18 + +[Hardcoded_lua_getfield] +Pattern=49C7C0FFFFFFFF0F1F840000000000 +Operations=ADD -33 + +[Hardcoded_lua_getglobal] +Pattern=4D8BC8488B5C2430 +Operations=ADD -112 + [Hardcoded_lua_getmetatable] Pattern=7411498B4218 Operations=ADD -28 @@ -875,6 +951,10 @@ Operations=ADD -28 Pattern=4D8BC84883C428 Operations=ADD -26 +[Hardcoded_lua_gettop] +Pattern=48C1F804C3 +Operations=ADD -15 + [Hardcoded_lua_insert] Pattern=498B53104C8BD0 Operations=ADD -12 @@ -899,9 +979,19 @@ Operations=ADD -13 Pattern=4C8B4310488BCB4983E810 Operations=ADD -14 +[Hardcoded_lua_pcallk] +Pattern=4053565741564883EC68 + [Hardcoded_lua_pushboolean] Pattern=4C8B411033C0 +[Hardcoded_lua_pushcclosure] +Pattern=4889742410574883EC304963F8 +Operations=ADD -5 + +[Hardcoded_lua_pushinteger] +Pattern=488B41100F57C0 + [Hardcoded_lua_pushlightuserdata] Pattern=488910C7400802000000 Operations=ADD -4 @@ -910,6 +1000,20 @@ Operations=ADD -4 Pattern=488B5310488B742438 Operations=ADD -83 +[Hardcoded_lua_pushnil] +Pattern=C74008000000004883411010 +Operations=ADD -4 + +[Hardcoded_lua_pushnumber] +Pattern=488B4110F20F1108 + +[Hardcoded_lua_pushstring] +Pattern=48895C2408574883EC20488BFA488BD94885D27519 + +[Hardcoded_lua_pushvalue] +Pattern=4189400849834210104883C428 +Operations=ADD -25 + [Hardcoded_lua_rawget] Pattern=488B53104883EA10488B08 Operations=ADD -14 @@ -917,6 +1021,14 @@ Operations=ADD -14 [Hardcoded_lua_rawgeti] Pattern=4883EC28458BD0 +[Hardcoded_lua_rawlen] +Pattern=498B16488BD8 +Operations=ADD -4, PUSH, READ DWORD, ADD, ADD 4 + +[Hardcoded_lua_rawset] +Pattern=488B7310488BF8 +Operations=ADD -34 + [Hardcoded_lua_rawseti] Pattern=4883EC20458BD0 Operations=ADD -6 @@ -925,6 +1037,10 @@ Operations=ADD -6 Pattern=498B4A104883C010 Operations=ADD -12 +[Hardcoded_lua_setglobal] +Pattern=C6862595000001 +Operations=ADD -11, PUSH, READ DWORD, ADD, ADD 4 + [Hardcoded_lua_setmetatable] Pattern=4C8B4B104C8BD0 Operations=ADD -14 @@ -933,10 +1049,26 @@ Operations=ADD -14 Pattern=48834310E04883C420 Operations=ADD -37 +[Hardcoded_lua_settop] +Pattern=7834488B4120 +Operations=ADD -2 + +[Hardcoded_lua_toboolean] +Pattern=8B480885C97414 +Operations=ADD -9 + [Hardcoded_lua_tocfunction] Pattern=83F9167508 Operations=ADD -12 +[Hardcoded_lua_tointegerx] +Pattern=80F9047555 +Operations=ADD -40 + +[Hardcoded_lua_tolstring] +Pattern=4883EC20498BD88BF2 +Operations=ADD -11 + [Hardcoded_lua_tonumberx] Pattern=80F9047554 Operations=ADD -40 @@ -946,11 +1078,8 @@ Pattern=83F9027418 Operations=ADD -15 [Hardcoded_lua_type] -Pattern=4883EC28E8????????488D0D????????483BC1740B - -[Hardcoded_tolua_beginmodule] -Pattern=488BD94885D27417 -Operations=ADD -6 +Pattern=740B8B4008 +Operations=ADD -19 [Hardcoded_tolua_bnd_cast] Pattern=488BCB488BF0418D5002 @@ -998,6 +1127,10 @@ Operations=ADD -38 Pattern=443BC07C43 Operations=ADD -43 +[Hardcoded_tolua_ismodulemetatable] +Pattern=488BD98D57FF +Operations=ADD -12 + [Hardcoded_tolua_isnumber] Pattern=443BC07C0E Operations=ADD -43 @@ -1009,9 +1142,9 @@ Operations=ADD -43 [Hardcoded_tolua_isusertype] Pattern=48895C240848896C24104889742418574883EC20498BE88BF2 -[Hardcoded_tolua_module] -Pattern=4889742410574883EC20418BF0488BFA -Operations=ADD -5 +[Hardcoded_tolua_moduleevents] +Pattern=85C0740DBAFEFFFFFF +Operations=ADD -17, PUSH, READ DWORD, ADD, ADD 4 [Hardcoded_tolua_newmetatable] Pattern=4883EC20488BFA488BD94C8BC2 @@ -1047,10 +1180,6 @@ Operations=ADD -43 Pattern=E9A90000008BD7 Operations=ADD -60 -[Hardcoded_tolua_usertype] -Pattern=488BFA89442420 -Operations=ADD -37 - [Hardcoded_tolua_variable] Pattern=488BF2498BF9 Operations=ADD -20 @@ -1079,22 +1208,42 @@ Operations=ADD -47 Pattern=83F8057566 Operations=ADD -47 +[tolua_beginmodule] +Pattern=488BD94885D27417 +Operations=ADD -6 + [tolua_cclass] Pattern=41574881EC38010000 Operations=ADD -11 +[tolua_module] +Pattern=4889742410574883EC20418BF0488BFA +Operations=ADD -5 + [tolua_open] Pattern=83F8010F84D7030000 Operations=ADD -61 +[tolua_usertype] +Pattern=488BFA89442420 +Operations=ADD -37 + ;----------- ;-- Hooks -- ;----------- +[!ExeSwitch-Baldur.exe-Hook-OpenIniFile()-_wfopen()] +Pattern=488BC3488B8C2450040000 +Operations=ADD -41 + [!ExeSwitch-Baldur.exe-Hook-dimmInit()-uiLoadMenu()] Pattern=74034C8B30 Operations=ADD 34 +[!ExeSwitch-icewind.exe-Hook-OpenIniFile()-_wfopen()] +Pattern=488BC3488B8C2450040000 +Operations=ADD -43 + [!ExeSwitch-icewind.exe-Hook-dimmInit()-uiLoadMenu()] Pattern=74034C8B30 Operations=ADD 37 @@ -1495,6 +1644,10 @@ Operations=ADD 17 Pattern=488BD0488BC8 Operations=ADD 182 +[Hook-OpenIniFile()-_wfopen()] +ExeSwitch=1 +ExeSwitchAlias=SiegeOfDragonspear.exe:Baldur.exe + [Hook-RenderListCallback()-drawItem()] Pattern=448BCB4C896C2420 Operations=ADD 27 @@ -1507,6 +1660,18 @@ Operations=ADD 28 Pattern=C7804010000001000000 Operations=ADD 10 +[Hook-chReadIniFile()-CheckDoBackupProcessingJmp] +Pattern=33F685C07524 +Operations=ADD 63 + +[Hook-chReadIniFile()-luaL_loadfilexptr()] +Pattern=33F685C07524 +Operations=ADD -5 + +[Hook-dimmInit()-uiLoadFunctions()] +Pattern=488B18EB03 +Operations=ADD 67 + [Hook-dimmInit()-uiLoadMenu()] ExeSwitch=1 ExeSwitchAlias=SiegeOfDragonspear.exe:Baldur.exe @@ -1534,3 +1699,380 @@ Operations=ADD 223 [Hook-uiRefreshMenu()-saveMenuStack()] Pattern=48C7C7FFFFFFFF6690 Operations=ADD -235 + +;------------------------------- +;-- Lua Replacement Functions -- +;------------------------------- + +[luaL_addlstring] +Pattern=488BDA498BF0 +Operations=ADD -15 + +[luaL_addstring] +Pattern=4883EC20488BFA488BF148C7C3FFFFFFFF +Operations=ADD -11 + +[luaL_addvalue] +Pattern=41574883EC20488B591833F6 +Operations=ADD -18 + +[luaL_argerror] +Pattern=48898424B0000000488BD9 +Operations=ADD -21 + +[luaL_checkinteger] +Pattern=80F904754F +Operations=ADD -46 + +[luaL_checknumber] +Pattern=80F904754E +Operations=ADD -46 + +[luaL_checkudata] +Pattern=4D8BF08BEA488BF1 +Operations=ADD -26 + +[luaL_error] +Pattern=4C894C242053574883EC28 +Operations=ADD -10 + +[luaL_getmetafield] +Pattern=744F488BD7 +Operations=ADD -23 + +[luaL_getsubtable] +Pattern=4883EC20BAD8B9F0FF +Operations=ADD -6 + +[luaL_gsub] +Pattern=41554157488DAC2478FEFFFF +Operations=ADD -6 + +[luaL_len] +Pattern=4883431010488B53108B42F8 +Operations=ADD -44 + +[luaL_loadbufferx] +Pattern=4833C44889442440488B842480000000 +Operations=ADD -11 + +[luaL_loadfilex] +Pattern=53415441564157488DAC24A0FEFFFF +Operations=ADD -2 + +[luaL_loadstring] +Pattern=48894424404889542430 +Operations=ADD -14 + +[luaL_newmetatable] +Pattern=48895C2418574883EC20488BFA4C8BC2 + +[luaL_newstate] +Pattern=7412488B4818 +Operations=ADD -21 + +[luaL_optlstring] +Pattern=48895C2408574883EC204D8BD9 + +[luaL_prepbuffsize] +Pattern=4889742418574883EC204C8B4108 +Operations=ADD -5 + +[luaL_pushresult] +Pattern=488B7918488BD9 +Operations=ADD -10 + +[luaL_ref] +Pattern=488B4910488D41F0 +Operations=ADD -26 + +[luaL_requiref] +Pattern=40535641574883EC20488B4110 + +[luaL_setfuncs] +Pattern=48895424105355564883EC50 +Operations=ADD -5 + +[luaL_tolstring] +Pattern=8D8227460F00 +Operations=ADD -20 + +[luaL_traceback] +Pattern=48895C2420555657415641574881ECB0000000 + +[luaL_where] +Pattern=48898424A0000000488BD985D2 +Operations=ADD -19 + +[lua_atpanic] +Pattern=498B8000010000 +Operations=ADD -4 + +[lua_callk] +Pattern=48895C2408574883EC204C8B5110 + +[lua_checkstack] +Pattern=4889442428488B7920 +Operations=ADD -20 + +[lua_concat] +Pattern=488BF983FA02 +Operations=ADD -12 + +[lua_createtable] +Pattern=418BF8488B4918 +Operations=ADD -18 + +[lua_error] +Pattern=488B5424484C8BC7 +Operations=ADD 33, PUSH, READ DWORD, ADD, ADD 4 + +[lua_gc] +Pattern=4889742410574883EC20488B5918 +Operations=ADD -5 + +[lua_getfield] +Pattern=49C7C0FFFFFFFF0F1F840000000000 +Operations=ADD -33 + +[lua_getglobal] +Pattern=4D8BC8488B5C2430 +Operations=ADD -112 + +[lua_getinfo] +Pattern=488944244080790A01 +Operations=ADD -23 + +[lua_getlocal] +Pattern=4883EC2080790A01 +Operations=ADD -2 + +[lua_getmetatable] +Pattern=7411498B4218 +Operations=ADD -28 + +[lua_gettable] +Pattern=4D8BC84883C428 +Operations=ADD -26 + +[lua_gettop] +Pattern=48C1F804C3 +Operations=ADD -15 + +[lua_getuservalue] +Pattern=488B10498B4210 +Operations=ADD -12 + +[lua_insert] +Pattern=498B53104C8BD0 +Operations=ADD -12 + +[lua_iscfunction] +Pattern=83F916740C +Operations=ADD -12 + +[lua_isnumber] +Pattern=83F9037428 +Operations=ADD -12 + +[lua_isstring] +Pattern=483BC17418 +Operations=ADD -16 + +[lua_isuserdata] +Pattern=83F947 +Operations=ADD -12 + +[lua_len] +Pattern=4585F60F8544010000 +Operations=ADD 48, PUSH, READ DWORD, ADD, ADD 4 + +[lua_load] +Pattern=4889442448498BC1 +Operations=ADD -16 + +[lua_newstate] +Pattern=4889442448488BEA +Operations=ADD -18 + +[lua_newuserdata] +Pattern=488BFA488B4918 +Operations=ADD -13 + +[lua_next] +Pattern=4C8B4310488BCB4983E810 +Operations=ADD -14 + +[lua_pcallk] +Pattern=4053565741564883EC68 + +[lua_pushboolean] +Pattern=4C8B411033C0 + +[lua_pushcclosure] +Pattern=4889742410574883EC304963F8 +Operations=ADD -5 + +[lua_pushfstring] +Pattern=4C894C2420534883EC20488BD9 +Operations=ADD -10 + +[lua_pushinteger] +Pattern=488B41100F57C0 + +[lua_pushlightuserdata] +Pattern=488910C7400802000000 +Operations=ADD -4 + +[lua_pushlstring] +Pattern=488B5310488B742438 +Operations=ADD -83 + +[lua_pushnil] +Pattern=C74008000000004883411010 +Operations=ADD -4 + +[lua_pushnumber] +Pattern=488B4110F20F1108 + +[lua_pushstring] +Pattern=488BFA488BD94885D27519 +Operations=ADD -10 + +[lua_pushvalue] +Pattern=4189400849834210104883C428 +Operations=ADD -25 + +[lua_pushvfstring] +Pattern=488BD6488BCB488B5C2430 +Operations=ADD -72 + +[lua_rawequal] +Pattern=40534883EC20458BD8 + +[lua_rawget] +Pattern=4883EA10488B08 +Operations=ADD -18 + +[lua_rawgeti] +Pattern=4883EC28458BD0 + +[lua_rawlen] +Pattern=498B16488BD8 +Operations=ADD -4, PUSH, READ DWORD, ADD, ADD 4 + +[lua_rawset] +Pattern=488B7310488BF8 +Operations=ADD -34 + +[lua_rawseti] +Pattern=4883EC20458BD0 +Operations=ADD -6 + +[lua_remove] +Pattern=498B4A104883C010 +Operations=ADD -12 + +[lua_resume] +Pattern=440FB77144 +Operations=ADD -14 + +[lua_setfield] +Pattern=49C7C0FFFFFFFF488D5710 +Operations=ADD -33 + +[lua_setglobal] +Pattern=4885C0740A488BD0 +Operations=ADD 35, PUSH, READ DWORD, ADD, ADD 4 + +[lua_setmetatable] +Pattern=4C8B4B104C8BD0 +Operations=ADD -14 + +[lua_settable] +Pattern=48834310E04883C420 +Operations=ADD -37 + +[lua_settop] +Pattern=7834488B4120 +Operations=ADD -2 + +[lua_setupvalue] +Pattern=4D63D04C8BD9 +Operations=ADD -4 + +[lua_setuservalue] +Pattern=4C8BD8498B4210 +Operations=ADD -12 + +[lua_toboolean] +Pattern=8B480885C97414 +Operations=ADD -9 + +[lua_tocfunction] +Pattern=83F9167508 +Operations=ADD -12 + +[lua_tointegerx] +Pattern=80F9047555 +Operations=ADD -40 + +[lua_tolstring] +Pattern=4883EC20498BD88BF2 +Operations=ADD -11 + +[lua_tonumberx] +Pattern=80F9047554 +Operations=ADD -40 + +[lua_tounsignedx] +Pattern=4889442440498BD8 +Operations=ADD -16 + +[lua_touserdata] +Pattern=83F9027418 +Operations=ADD -15 + +[lua_type] +Pattern=740B8B4008 +Operations=ADD -19 + +[lua_typename] +Pattern=4C8BC08BD7 +Operations=ADD -25, PUSH, READ DWORD, ADD, ADD 4 + +[lua_xmove] +Pattern=4C8BD1483BCA +Operations=ADD -3 + +[luaopen_base] +Pattern=488BF1BA02000000 +Operations=ADD -15 + +[luaopen_bit32] +Pattern=488B4B1041B90C000000 +Operations=ADD -61 + +[luaopen_coroutine] +Pattern=0F849A000000488BD0 +Operations=ADD -165, PUSH, READ DWORD, ADD, ADD 4 + +[luaopen_debug] +Pattern=488B4B1041B910000000 +Operations=ADD -61 + +[luaopen_math] +Pattern=574883EC20488B5118 +Operations=ADD -10 + +[luaopen_package] +Pattern=488BCEC7400816000000 +Operations=ADD -151 + +[luaopen_string] +Pattern=488BF1488B4918 +Operations=ADD -15 + +[luaopen_table] +Pattern=B918190000 +Operations=ADD -31, PUSH, READ DWORD, ADD, ADD 4, ADD 114, PUSH, READ DWORD, ADD, ADD 4 diff --git a/EEex/loader/InfinityLoader.exe b/EEex/loader/InfinityLoader.exe index 69b21bd..0c634a7 100644 Binary files a/EEex/loader/InfinityLoader.exe and b/EEex/loader/InfinityLoader.exe differ diff --git a/EEex/loader/InfinityLoader.ini b/EEex/loader/InfinityLoader.ini index f04ae5d..e31d8f1 100644 --- a/EEex/loader/InfinityLoader.ini +++ b/EEex/loader/InfinityLoader.ini @@ -36,12 +36,13 @@ KeepConsoleAttached=0 KeepCrtStreamsAttached=1 -; The Lua Library the loader uses when LuaPatchMode=EXTERNAL. Example: lua52.dll. +; The Lua library the loader uses when LuaPatchMode=EXTERNAL or LuaPatchMode=REPLACE_INTERNAL_WITH_EXTERNAL. Example: lua52.dll. LuaLibrary= -; INTERNAL -> InfinityLoader uses the internal Lua state / functions of the spawned program (default). -; EXTERNAL -> InfinityLoader holds its own Lua state and uses an external DLL for Lua functions. +; INTERNAL -> InfinityLoader uses the internal Lua state / functions of the spawned program (default). +; EXTERNAL -> InfinityLoader holds its own Lua state and uses an external DLL for Lua functions. +; REPLACE_INTERNAL_WITH_EXTERNAL -> InfinityLoader replaces the spawned program's internal Lua state / functions with those of LuaLibrary. LuaPatchMode=INTERNAL @@ -84,90 +85,6 @@ Operations=ADD -5 Pattern=743753 Operations=ADD -3 -[Hardcoded_luaL_error] -Pattern=4C894C242053574883EC28 -Operations=ADD -10 - -[Hardcoded_luaL_loadfilex] -Pattern=53415441564157488DAC24A0FEFFFF -Operations=ADD -2 - -[Hardcoded_luaL_ref] -Pattern=488B4910488D41F0 -Operations=ADD -26 - -[Hardcoded_lua_createtable] -Pattern=418BF8488B4918 -Operations=ADD -18 - -[Hardcoded_lua_getfield] -Pattern=49C7C0FFFFFFFF0F1F840000000000 -Operations=ADD -33 - -[Hardcoded_lua_getglobal] -Pattern=4D8BC8488B5C2430 -Operations=ADD -112 - -[Hardcoded_lua_gettop] -Pattern=48C1F804C3 -Operations=ADD -15 - -[Hardcoded_lua_pcallk] -Pattern=4053565741564883EC68 - -[Hardcoded_lua_pushcclosure] -Pattern=4889742410574883EC304963F8 -Operations=ADD -5 - -[Hardcoded_lua_pushinteger] -Pattern=488B41100F57C0 - -[Hardcoded_lua_pushnil] -Pattern=C74008000000004883411010 -Operations=ADD -4 - -[Hardcoded_lua_pushstring] -Pattern=48895C2408574883EC20488BFA488BD94885D27519 - -[Hardcoded_lua_pushvalue] -Pattern=4189400849834210104883C428 -Operations=ADD -25 - -[Hardcoded_lua_rawgeti] -Pattern=4883EC28458BD0 - -[Hardcoded_lua_rawset] -Pattern=488B7310488BF8 -Operations=ADD -34 - -[Hardcoded_lua_rawseti] -Pattern=4883EC20458BD0 -Operations=ADD -6 - -[Hardcoded_lua_setglobal] -Pattern=C6862595000001 -Operations=ADD -11, PUSH, READ DWORD, ADD, ADD 4 - -[Hardcoded_lua_settop] -Pattern=7834488B4120 -Operations=ADD -2 - -[Hardcoded_lua_toboolean] -Pattern=8B480885C97414 -Operations=ADD -9 - -[Hardcoded_lua_tointegerx] -Pattern=80F9047555 -Operations=ADD -40 - -[Hardcoded_lua_tolstring] -Pattern=4883EC20498BD88BF2 -Operations=ADD -11 - -[Hardcoded_lua_type] -Pattern=740B8B4008 -Operations=ADD -19 - [Hardcoded_malloc] Pattern=4883F9E0 Operations=ADD -9 diff --git a/EEex/loader/InfinityLoaderCommon.dll b/EEex/loader/InfinityLoaderCommon.dll new file mode 100644 index 0000000..5472498 Binary files /dev/null and b/EEex/loader/InfinityLoaderCommon.dll differ diff --git a/EEex/loader/InfinityLoaderDLL.dll b/EEex/loader/InfinityLoaderDLL.dll index 209c578..5ad4c00 100644 Binary files a/EEex/loader/InfinityLoaderDLL.dll and b/EEex/loader/InfinityLoaderDLL.dll differ diff --git a/EEex/loader/Lua52/LuaProvider.dll b/EEex/loader/Lua52/LuaProvider.dll new file mode 100644 index 0000000..24d13bf Binary files /dev/null and b/EEex/loader/Lua52/LuaProvider.dll differ diff --git a/EEex/loader/LuaBindings-v2.6.6.0.dll b/EEex/loader/LuaBindings-v2.6.6.0.dll index 442504d..c5745ff 100644 Binary files a/EEex/loader/LuaBindings-v2.6.6.0.dll and b/EEex/loader/LuaBindings-v2.6.6.0.dll differ diff --git a/EEex/loader/LuaBindingsCore.dll b/EEex/loader/LuaBindingsCore.dll new file mode 100644 index 0000000..22d1be4 Binary files /dev/null and b/EEex/loader/LuaBindingsCore.dll differ diff --git a/EEex/loader/LuaJIT/LuaProvider.dll b/EEex/loader/LuaJIT/LuaProvider.dll new file mode 100644 index 0000000..105b6f6 Binary files /dev/null and b/EEex/loader/LuaJIT/LuaProvider.dll differ diff --git a/EEex/loader/LuaJIT/lua51.dll b/EEex/loader/LuaJIT/lua51.dll new file mode 100644 index 0000000..5f6aa70 Binary files /dev/null and b/EEex/loader/LuaJIT/lua51.dll differ diff --git a/pdb/EEex.pdb b/pdb/EEex.pdb new file mode 100644 index 0000000..a6b8cde Binary files /dev/null and b/pdb/EEex.pdb differ diff --git a/pdb/InfinityLoader.pdb b/pdb/InfinityLoader.pdb index 640a33c..7b79847 100644 Binary files a/pdb/InfinityLoader.pdb and b/pdb/InfinityLoader.pdb differ diff --git a/pdb/InfinityLoaderCommon.pdb b/pdb/InfinityLoaderCommon.pdb new file mode 100644 index 0000000..16cbe3d Binary files /dev/null and b/pdb/InfinityLoaderCommon.pdb differ diff --git a/pdb/InfinityLoaderDLL.pdb b/pdb/InfinityLoaderDLL.pdb index 7108775..4607ec7 100644 Binary files a/pdb/InfinityLoaderDLL.pdb and b/pdb/InfinityLoaderDLL.pdb differ diff --git a/pdb/Lua52/LuaProvider.pdb b/pdb/Lua52/LuaProvider.pdb new file mode 100644 index 0000000..137fee3 Binary files /dev/null and b/pdb/Lua52/LuaProvider.pdb differ diff --git a/pdb/LuaBindings-v2.6.6.0.pdb b/pdb/LuaBindings-v2.6.6.0.pdb index dc2ab55..2c860e3 100644 Binary files a/pdb/LuaBindings-v2.6.6.0.pdb and b/pdb/LuaBindings-v2.6.6.0.pdb differ diff --git a/pdb/LuaBindingsCore.pdb b/pdb/LuaBindingsCore.pdb new file mode 100644 index 0000000..65b3de4 Binary files /dev/null and b/pdb/LuaBindingsCore.pdb differ diff --git a/pdb/LuaJIT/LuaProvider.pdb b/pdb/LuaJIT/LuaProvider.pdb new file mode 100644 index 0000000..083c8d3 Binary files /dev/null and b/pdb/LuaJIT/LuaProvider.pdb differ diff --git a/pdb/LuaJIT/lua51.pdb b/pdb/LuaJIT/lua51.pdb new file mode 100644 index 0000000..2133659 Binary files /dev/null and b/pdb/LuaJIT/lua51.pdb differ