└── EEex ├── EEex.ini ├── EEex.tp2 ├── copy ├── B3EffMen.lua ├── B3EffMen.menu ├── B3EmptyContainer.lua ├── B3Hotkey.lua ├── B3Invis.lua ├── B3Scale.lua ├── B3TAURA.BAM ├── B3TBACKG.BAM ├── B3TCONTI.BAM ├── B3TMODAL.BAM ├── B3TiStep.menu ├── B3TimeStep.lua ├── B3Timer.lua ├── B3Timer.menu ├── EEex_AIBase.lua ├── EEex_AIBase_Patch.lua ├── EEex_Action.lua ├── EEex_Action_Patch.lua ├── EEex_Actionbar.lua ├── EEex_Actionbar_Patch.lua ├── EEex_Alias.lua ├── EEex_Area.lua ├── EEex_Assembly.lua ├── EEex_Assembly_Patch.lua ├── EEex_Assembly_x86-64.lua ├── EEex_Assembly_x86-64_Patch.lua ├── EEex_Assembly_x86.lua ├── EEex_Assembly_x86_Patch.lua ├── EEex_Debug.lua ├── EEex_EarlyMain.lua ├── EEex_Fix.lua ├── EEex_Fix_Patch.lua ├── EEex_GameObject.lua ├── EEex_GameObject_Patch.lua ├── EEex_GameState.lua ├── EEex_GameState_Patch.lua ├── EEex_HookIntegrityWatchdog.lua ├── EEex_Key.lua ├── EEex_Key_Patch.lua ├── EEex_LuaBindings_Patch.lua ├── EEex_Main.lua ├── EEex_Marshal.lua ├── EEex_MemoryManagerDefinitions.lua ├── EEex_Menu.lua ├── EEex_Menu_Patch.lua ├── EEex_Mix_Patch.lua ├── EEex_Modules.lua ├── EEex_Object.lua ├── EEex_Object_Patch.lua ├── EEex_Opcode.lua ├── EEex_Opcode_Patch.lua ├── EEex_Projectile.lua ├── EEex_Projectile_Patch.lua ├── EEex_ReplaceLua.lua ├── EEex_Resource.lua ├── EEex_Script.lua ├── EEex_Script_Patch.lua ├── EEex_Sprite.lua ├── EEex_Sprite_Patch.lua ├── EEex_Stats.lua ├── EEex_Stats_Patch.lua ├── EEex_StutterDetector.lua ├── EEex_Test.lua ├── EEex_Trigger.lua ├── EEex_Trigger_Patch.lua ├── EEex_UserDataGlobals.lua ├── EEex_Utility.lua ├── EEex_Variable.lua ├── M___EEex.lua ├── X-CLSERG.2DA ├── X-MATOBJ.IDS ├── X-STATS.2DA └── X-STUTDE.menu ├── loader ├── EEex.dll ├── InfinityLoader.db ├── InfinityLoader.exe ├── InfinityLoader.ini ├── InfinityLoaderCommon.dll ├── InfinityLoaderDLL.dll ├── Lua52 │ └── LuaProvider.dll ├── LuaBindings-v2.6.6.0.dll ├── LuaBindingsCore.dll └── LuaJIT │ ├── LuaProvider.dll │ └── lua51.dll ├── patch ├── ACTION.IDS ├── OBJECT.IDS └── TRIGGER.IDS └── readme-EEex.html /EEex/EEex.ini: -------------------------------------------------------------------------------- 1 | # Filename must be the same as tp2 basename, placed at the same folder where 2 | # .tp2 file is located, use "UTF8 without BOM" encoding, everything is optional 3 | 4 | # ini section header is required to avoid false detection 5 | [Metadata] 6 | 7 | # Full name of the mod, without version number 8 | Name = EEex 9 | 10 | # Author name or nick, don't use email address 11 | Author = Bubb 12 | 13 | # Short description of the mod, main goals, features etc 14 | Description = An executable extender for Beamdog's Enhanced Edition of the Infinity Engine. 15 | 16 | # Web address of mod readme file (filename is case-sensitive!) 17 | Readme = https://eeex-docs.readthedocs.io/en/latest/ 18 | 19 | # Web address of mod dedicated forum or forum thread 20 | Forum = https://forums.beamdog.com/discussion/71798/mod-eeex-v0-6-0-alpha/p1 21 | 22 | # Web address of mod personal Homepage, no need to duplicate with mod dedicated forum 23 | # Homepage = https://www.example.com 24 | 25 | # if you use Github.com, simply use https://github.com/AccountOrOrgName/RepositoryName 26 | # read more about Delta Updates https://github.com/ALIENQuake/ProjectInfinity/wiki/Delta-Updates-for-mods-hosted-at-Github 27 | Download = https://github.com/Bubb13/EEex -------------------------------------------------------------------------------- /EEex/EEex.tp2: -------------------------------------------------------------------------------- 1 | BACKUP ~weidu_external/EEex/backup~ 2 | AUTHOR ~Bubb~ 3 | VERSION ~v0.10.3.1-alpha~ 4 | README ~EEex/readme-EEex.html~ 5 | 6 | BEGIN ~EEex~ LABEL ~B3-EEex-Main~ 7 | REQUIRE_PREDICATE (GAME_IS ~bgee bg2ee eet iwdee~ AND FILE_EXISTS ~data/PATCH26.BIF~) ~Game not supported.~ 8 | 9 | ACTION_FOR_EACH file IN ACTION OBJECT TRIGGER BEGIN 10 | COPY ~EEex/patch/%file%.IDS~ ~EEex/patch~ 11 | COUNT_2DA_ROWS 2 rows 12 | FOR (i = 0; i <= rows; ++i) BEGIN 13 | READ_2DA_ENTRY i 0 2 "col1" 14 | READ_2DA_ENTRY i 1 2 "col2" 15 | DEFINE_ASSOCIATIVE_ARRAY EEex_append_array BEGIN ~%file%~ , ~%col1%~ , ~%col2%~ => ~~ END 16 | END 17 | BUT_ONLY 18 | END 19 | 20 | DEFINE_ACTION_FUNCTION B3_ESCAPE_STRING 21 | STR_VAR 22 | str = ~~ 23 | RET 24 | escaped_str 25 | BEGIN 26 | ACTION_DEFINE_ASSOCIATIVE_ARRAY to_escape BEGIN ~$~ => 1 ~^~ => 1 ~.~ => 1 ~*~ => 1 ~+~ => 1 ~?~ => 1 ~[~ => 1 ~]~ => 1 ~\~ => 1 END 27 | OUTER_PATCH_SAVE escaped_str ~%str%~ BEGIN 28 | limit = BUFFER_LENGTH 29 | FOR (i = 0; i < limit; ++i) BEGIN 30 | READ_ASCII i character (1) 31 | PATCH_IF VARIABLE_IS_SET EVAL ~to_escape_%character%~ BEGIN 32 | INSERT_BYTES i 1 33 | WRITE_ASCII i ~\~ 34 | ++i 35 | ++limit 36 | END 37 | END 38 | END 39 | END 40 | 41 | ACTION_PHP_EACH EEex_append_array AS data => ~~ BEGIN 42 | LAF B3_ESCAPE_STRING STR_VAR str = EVAL ~%data_1%~ RET data_1_escaped = escaped_str END 43 | LAF B3_ESCAPE_STRING STR_VAR str = EVAL ~%data_2%~ RET data_2_escaped = escaped_str END 44 | APPEND ~%data%.IDS~ ~%data_1% %data_2%~ UNLESS ~^[ %TAB%]*%data_1_escaped%[ %TAB%]+%data_2_escaped%[ %TAB%]*%MNL%*$~ 45 | END 46 | 47 | COPY ~EEex/loader~ ~.~ 48 | COPY ~EEex/loader/Lua52~ ~.~ 49 | COPY ~EEex/copy~ ~override~ 50 | 51 | 52 | BEGIN ~Experimental - Use LuaJIT (can help stuttering)~ LABEL ~B3-EEex-LuaJIT~ 53 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 54 | 55 | COPY ~InfinityLoader.ini~ ~.~ 56 | REPLACE_TEXTUALLY ~^\(LuaLibrary=\).*~ ~\1lua51.dll~ 57 | REPLACE_TEXTUALLY ~^\(LuaPatchMode=\).*~ ~\1REPLACE_INTERNAL_WITH_EXTERNAL~ 58 | REPLACE_TEXTUALLY ~^\(LuaVersionExternal=\).*~ ~\15.1~ 59 | BUT_ONLY 60 | 61 | COPY ~EEex/loader/LuaJIT~ ~.~ 62 | 63 | 64 | BEGIN ~Enable effect menu module - LShift-on-hover to view spells affecting creature~ LABEL ~B3-EEex-Module-Effect-Menu~ 65 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 66 | 67 | COPY ~override/EEex_Modules.lua~ ~override~ 68 | REPLACE_TEXTUALLY ~^\([ %TAB%]+\["B3EffMen"\][ %TAB%]+=[ %TAB%]+\)false~ ~\1true~ 69 | BUT_ONLY 70 | 71 | 72 | BEGIN ~Enable empty container module - Highlight empty containers in gray instead of cyan~ LABEL ~B3-EEex-Module-Empty-Containers~ 73 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 74 | 75 | COPY ~override/EEex_Modules.lua~ ~override~ 76 | REPLACE_TEXTUALLY ~^\([ %TAB%]+\["B3EmptyContainer"\][ %TAB%]+=[ %TAB%]+\)false~ ~\1true~ 77 | BUT_ONLY 78 | 79 | 80 | BEGIN ~Enable timer module - Visual indicators for modal actions, contingencies, and spell/item cooldowns~ LABEL ~B3-EEex-Module-Timer-Main~ 81 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 82 | 83 | COPY ~override/EEex_Modules.lua~ ~override~ 84 | REPLACE_TEXTUALLY ~^\([ %TAB%]+\["B3Timer"\][ %TAB%]+=[ %TAB%]+\)false~ ~\1true~ 85 | BUT_ONLY 86 | 87 | 88 | BEGIN ~Timer module - Show modal actions (red bar)~ LABEL ~B3-EEex-Module-Timer-Modal~ 89 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Module-Timer-Main~) ~This component requires the "Enable timer module" component to be installed.~ 90 | 91 | COPY ~override/B3Timer.lua~ ~override~ 92 | REPLACE_TEXTUALLY ~^\(B3Timer_ShowModalTimer = \)[^ %TAB%%WNL%]+~ ~\1true~ 93 | BUT_ONLY 94 | 95 | 96 | BEGIN ~Timer module - Show contingencies (green bar)~ LABEL ~B3-EEex-Module-Timer-Contingency~ 97 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Module-Timer-Main~) ~This component requires the "Enable timer module" component to be installed.~ 98 | 99 | COPY ~override/B3Timer.lua~ ~override~ 100 | REPLACE_TEXTUALLY ~^\(B3Timer_ShowContingencyTimer = \)[^ %TAB%%WNL%]+~ ~\1true~ 101 | BUT_ONLY 102 | 103 | 104 | BEGIN ~Timer module - Show spell/item cooldowns (cyan bar)~ LABEL ~B3-EEex-Module-Timer-Aura~ 105 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Module-Timer-Main~) ~This component requires the "Enable timer module" component to be installed.~ 106 | 107 | COPY ~override/B3Timer.lua~ ~override~ 108 | REPLACE_TEXTUALLY ~^\(B3Timer_ShowCastTimer = \)[^ %TAB%%WNL%]+~ ~\1true~ 109 | BUT_ONLY 110 | 111 | 112 | BEGIN ~Enable time step module - Advance 1 game tick on keypress~ LABEL ~B3-EEex-Module-Time-Step~ 113 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 114 | 115 | COPY ~override/EEex_Modules.lua~ ~override~ 116 | REPLACE_TEXTUALLY ~^\([ %TAB%]+\["B3TimeStep"\][ %TAB%]+=[ %TAB%]+\)false~ ~\1true~ 117 | BUT_ONLY 118 | 119 | 120 | BEGIN ~Enable hotkey module - Edit override/B3Hotkey.lua to create advanced spell hotkeys~ LABEL ~B3-EEex-Module-Hotkey~ 121 | REQUIRE_PREDICATE MOD_IS_INSTALLED ~EEex.tp2~ (ID_OF_LABEL ~EEex.tp2~ ~B3-EEex-Main~) ~This component requires the main component to be installed.~ 122 | 123 | COPY ~override/EEex_Modules.lua~ ~override~ 124 | REPLACE_TEXTUALLY ~^\([ %TAB%]+\["B3Hotkey"\][ %TAB%]+=[ %TAB%]+\)false~ ~\1true~ 125 | BUT_ONLY 126 | -------------------------------------------------------------------------------- /EEex/copy/B3EffMen.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- Options -- 4 | ------------- 5 | 6 | B3EffectMenu_Key = EEex_Key_GetFromName("Left Shift") 7 | B3EffectMenu_RowCount = 4 8 | 9 | ------------- 10 | -- Globals -- 11 | ------------- 12 | 13 | B3EffectMenu_Menu_Enabled = false 14 | 15 | ----------------------- 16 | -- Hooks / Listeners -- 17 | ----------------------- 18 | 19 | B3EffectMenu_OldIsActorTooltipDisabled = EEex_Sprite_Hook_CheckSuppressTooltip 20 | EEex_Sprite_Hook_CheckSuppressTooltip = function() 21 | return B3EffectMenu_Menu_Enabled or B3EffectMenu_OldIsActorTooltipDisabled() 22 | end 23 | 24 | function B3EffectMenu_LoadMenu() 25 | 26 | EEex_Menu_LoadFile("B3EffMen") 27 | 28 | local rowTotal = 35 * B3EffectMenu_RowCount 29 | Infinity_SetArea("B3EffectMenu_Menu_Background", nil, nil, nil, rowTotal + 20) 30 | Infinity_SetArea("B3EffectMenu_Menu_List", nil, nil, nil, rowTotal) 31 | 32 | local actionbarMenu = EEex_Menu_Find("WORLD_ACTIONBAR") 33 | 34 | local oldActionbarOnOpen = EEex_Menu_GetItemFunction(actionbarMenu.reference_onOpen) 35 | EEex_Menu_SetItemFunction(actionbarMenu.reference_onOpen, function() 36 | local openResult = oldActionbarOnOpen() 37 | B3EffectMenu_Open() 38 | return openResult 39 | end) 40 | 41 | local oldActionbarOnClose = EEex_Menu_GetItemFunction(actionbarMenu.reference_onClose) 42 | EEex_Menu_SetItemFunction(actionbarMenu.reference_onClose, function() 43 | B3EffectMenu_Close() 44 | return oldActionbarOnClose() 45 | end) 46 | end 47 | EEex_Menu_AddMainFileLoadedListener(B3EffectMenu_LoadMenu) 48 | 49 | ---------- 50 | -- Main -- 51 | ---------- 52 | 53 | function B3EffectMenu_Init() 54 | B3EffectMenu_CurrentActorID = nil 55 | B3EffectMenu_EnableDelay = -1 56 | B3EffectMenu_Menu_Enabled = false 57 | end 58 | B3EffectMenu_Init() 59 | 60 | function B3EffectMenu_Open() 61 | B3EffectMenu_Init() 62 | Infinity_PushMenu("B3EffectMenu_Menu") 63 | end 64 | 65 | function B3EffectMenu_Close() 66 | Infinity_PopMenu("B3EffectMenu_Menu") 67 | B3EffectMenu_Init() 68 | end 69 | 70 | function B3EffectMenu_LaunchInfo() 71 | 72 | B3EffectMenu_Menu_List_Table = {} 73 | local sprite = EEex_GameObject_Get(B3EffectMenu_CurrentActorID) 74 | 75 | local pos = sprite.m_pos 76 | local screenX, screenY = EEex_Menu_TranslateXYFromGame(pos.x, pos.y) 77 | Infinity_SetOffset("B3EffectMenu_Menu", screenX, screenY) 78 | 79 | local seenSpells = {} 80 | 81 | EEex_Utility_IterateCPtrList(sprite.m_timedEffectList, function(effect) 82 | 83 | -- Only process spell effects 84 | local sourceType = effect.m_sourceType 85 | if sourceType == 2 then return end -- Continue EEex_Utility_IterateCPtrList 86 | 87 | local sourceResref = effect.m_sourceRes:get() 88 | -- Sanity check 89 | if sourceResref == "" then return end -- Continue EEex_Utility_IterateCPtrList 90 | 91 | -- Already added this spell 92 | if seenSpells[sourceResref] then return end -- Continue EEex_Utility_IterateCPtrList 93 | 94 | -- Skip completely permanent spells (to hide behind-the-scenes spells) 95 | local m_durationType = effect.m_durationType 96 | if m_durationType == 9 then return end -- Continue EEex_Utility_IterateCPtrList 97 | 98 | seenSpells[sourceResref] = true 99 | 100 | local spellHeader = EEex_Resource_Demand(sourceResref, "SPL") 101 | -- Sanity check 102 | if not spellHeader then return end -- Continue EEex_Utility_IterateCPtrList 103 | 104 | local casterLevel = effect.m_casterLevel 105 | if casterLevel <= 0 then casterLevel = 1 end 106 | 107 | local abilityData = spellHeader:getAbilityForLevel(casterLevel) 108 | 109 | -- The caster shouldn't have been able to cast this spell, just use the first ability 110 | if not abilityData then 111 | abilityData = spellHeader:getAbility(0) 112 | -- The spell didn't even have an ability... 113 | if not abilityData then return end -- Continue EEex_Utility_IterateCPtrList 114 | end 115 | 116 | local spellName = Infinity_FetchString(spellHeader.genericName) 117 | if spellName == "" then spellName = "(No Name)" end 118 | 119 | -- Skip no-icon spells (to hide behind-the-scenes spells) 120 | local spellIcon = abilityData.quickSlotIcon:get() 121 | if spellIcon == "" then return end -- Continue EEex_Utility_IterateCPtrList 122 | 123 | local listData = { 124 | ["bam"] = spellIcon, 125 | ["text"] = spellName, 126 | } 127 | 128 | table.insert(B3EffectMenu_Menu_List_Table, listData) 129 | end) 130 | 131 | -- Alphanumeric sort 132 | table.sort(B3EffectMenu_Menu_List_Table, function(a, b) 133 | local conv = function(s) 134 | local res, dot = "", "" 135 | for n, m, c in tostring(s):gmatch("(0*(%d*))(.?)") do 136 | if n == "" then 137 | dot, c = "", dot..c 138 | else 139 | res = res..(dot == "" and ("%03d%s"):format(#m, m) or "."..n) 140 | dot, c = c:match("(%.?)(.*)") 141 | end 142 | res = res..c:gsub(".", "\0%0") 143 | end 144 | return res 145 | end 146 | local ca, cb = conv(a.tooltip), conv(b.tooltip) 147 | return (a.text <= b.text) and (ca <= cb and a.text < b.text) 148 | end) 149 | 150 | B3EffectMenu_EnableDelay = 0 151 | end 152 | 153 | function B3EffectMenu_ClearWorldTooltip() 154 | EEex_EngineGlobal_CBaldurChitin.m_pObjectGame.m_tempCursor = 4 155 | end 156 | 157 | ------------------ 158 | -- UI Functions -- 159 | ------------------ 160 | 161 | ----------------------- 162 | -- B3EffectMenu_Menu -- 163 | ----------------------- 164 | 165 | function B3EffectMenu_Menu_Tick() 166 | 167 | if worldScreen ~= e:GetActiveEngine() then return end 168 | 169 | if B3EffectMenu_EnableDelay > -1 then 170 | B3EffectMenu_EnableDelay = B3EffectMenu_EnableDelay + 1 171 | if B3EffectMenu_EnableDelay == 1 then 172 | B3EffectMenu_Menu_Enabled = true 173 | B3EffectMenu_ClearWorldTooltip() 174 | B3EffectMenu_EnableDelay = -1 175 | end 176 | end 177 | 178 | local object = EEex_GameObject_GetUnderCursor() 179 | if EEex_Key_IsDown(B3EffectMenu_Key) and object and object:isSprite() then 180 | if object.m_id ~= B3EffectMenu_CurrentActorID then 181 | B3EffectMenu_CurrentActorID = object.m_id 182 | B3EffectMenu_LaunchInfo() 183 | end 184 | elseif (not EEex_Key_IsDown(B3EffectMenu_Key)) or (not EEex_Menu_IsCursorWithin("B3EffectMenu_Menu", "B3EffectMenu_Menu_Background")) then 185 | B3EffectMenu_Init() 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /EEex/copy/B3EffMen.menu: -------------------------------------------------------------------------------- 1 | 2 | ----------------------- 3 | -- B3EffectMenu_Menu -- 4 | ----------------------- 5 | 6 | menu 7 | { 8 | name "B3EffectMenu_Menu" 9 | ignoreesc 10 | 11 | label 12 | { 13 | enabled "B3EffectMenu_Menu_Tick()" 14 | area 0 0 2147483647 2147483647 15 | } 16 | 17 | label 18 | { 19 | area 0 0 220 160 20 | name "B3EffectMenu_Menu_Background" 21 | enabled "B3EffectMenu_Menu_Enabled" 22 | rectangle 5 23 | rectangle opacity 750 24 | } 25 | 26 | list 27 | { 28 | column 29 | { 30 | width 20 31 | label 32 | { 33 | area 0 0 32 32 34 | bam lua "B3EffectMenu_Menu_List_Table[rowNumber].bam" 35 | scaleToClip 36 | } 37 | } 38 | column 39 | { 40 | width 70 41 | label 42 | { 43 | area 0 0 147 30 44 | text lua "B3EffectMenu_Menu_List_Table[rowNumber].text" 45 | text align left center 46 | text useFontZoom 0 47 | } 48 | } 49 | 50 | area 10 10 210 140 51 | name "B3EffectMenu_Menu_List" 52 | enabled "B3EffectMenu_Menu_Enabled" 53 | table "B3EffectMenu_Menu_List_Table" 54 | var "B3EffectMenu_Menu_List_SelectedRow" 55 | scrollbar "GUISCRC" 56 | scrollbar clunkyScroll 35 57 | rowheight 35 58 | hidehighlight 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /EEex/copy/B3EmptyContainer.lua: -------------------------------------------------------------------------------- 1 | 2 | function B3EmptyContainer_OnBeforeContainerOutlineRender(container, color) 3 | if color.value ~= 0xFF00 and container.m_lstItems.m_nCount == 0 then 4 | color.value = 0x808080 5 | end 6 | end 7 | 8 | (function() 9 | 10 | EEex_DisableCodeProtection() 11 | 12 | EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CGameContainer::Render()-B3EmptyContainer"), { 13 | {"hook_integrity_watchdog_ignore_registers", { 14 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 15 | }}}, 16 | EEex_FlattenTable({ 17 | {[[ 18 | #MAKE_SHADOW_SPACE(80) 19 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 20 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 21 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r8 22 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 23 | ]]}, 24 | EEex_GenLuaCall("B3EmptyContainer_OnBeforeContainerOutlineRender", { 25 | ["args"] = { 26 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14", {rspOffset}, "#ENDL"}, "CGameContainer" end, 27 | function(rspOffset) return {[[ 28 | lea rax, qword ptr ds:[rsp+#LAST_FRAME_TOP(32)] 29 | mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL 30 | ]]}, "Primitive" end, 31 | }, 32 | }), 33 | {[[ 34 | call_error: 35 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 36 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 37 | mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 38 | mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] 39 | #DESTROY_SHADOW_SPACE 40 | ]]}, 41 | }) 42 | ) 43 | 44 | EEex_EnableCodeProtection() 45 | end)() 46 | -------------------------------------------------------------------------------- /EEex/copy/B3Hotkey.lua: -------------------------------------------------------------------------------- 1 | 2 | -- Format: {function / spell resref, {required modifier keys}, {main keys combination}}, 3 | 4 | B3Hotkey_Hotkeys = { 5 | {function() B3Hotkey_TogglePrintKeys() end, {}, {0x60}}, -- Key-Pressed Output Toggle ('`') 6 | --{"SPIN103", {}, {0x73, 0x61, 0x64}}, -- Old way of doing a spell keybinding 7 | --{function() B3Hotkey_AttemptToCastViaHotkey("SPWI112") end, {}, {0x73, 0x61, 0x64}}, -- Example of a spell keybinding 8 | --{function() B3Hotkey_AttemptToSelectCharacter(0) end, {0x400000E1}, {0x31}}, -- Example of a keybinding that uses shift mod 9 | --{function() B3Hotkey_CastTwoStep("SPWI124", "SPWI112") end, {}, {0x61, 0x73, 0x64}}, -- Example of casting Magic Missile through Nahal's 10 | --{function() B3Hotkey_CastTwoStep("SPWI510", "SPWI596") end, {}, {0x64, 0x73, 0x61}}, -- Example of casting Immunity : Necromancy 11 | } 12 | 13 | -- Initialize all stage counters to 1 14 | for _, hotkeyDef in ipairs(B3Hotkey_Hotkeys) do 15 | hotkeyDef[4] = 1 16 | end 17 | 18 | B3Hotkey_PrintKeys = false 19 | function B3Hotkey_TogglePrintKeys() 20 | if not B3Hotkey_PrintKeys then 21 | B3Hotkey_PrintKeys = true 22 | Infinity_DisplayString("[EEex] Enabled Key-Pressed Output") 23 | else 24 | B3Hotkey_PrintKeys = false 25 | Infinity_DisplayString("[EEex] Disabled Key-Pressed Output") 26 | end 27 | end 28 | 29 | -- Internal to this file 30 | function B3Hotkey_UseCGameButtonList(sprite, buttonList, resref, bOffInternal) 31 | 32 | local found = false 33 | 34 | EEex_Utility_IterateCPtrList(buttonList, function(buttonData) 35 | if buttonData.m_abilityId.m_res:get() == resref then 36 | if bOffInternal then 37 | sprite:ReadyOffInternalList(buttonData, false) 38 | else 39 | sprite:ReadySpell(buttonData, false) 40 | end 41 | found = true 42 | return true -- breaks out of EEex_Utility_IterateCPtrList() 43 | end 44 | end) 45 | 46 | EEex_Utility_FreeCPtrList(buttonList) 47 | return found 48 | end 49 | 50 | function B3Hotkey_AttemptToCastViaHotkey(resref) 51 | if worldScreen == e:GetActiveEngine() then 52 | local sprite = EEex_Sprite_GetSelected() 53 | if sprite then 54 | local spellButtonDataList = sprite:GetQuickButtons(2, false) 55 | if B3Hotkey_UseCGameButtonList(sprite, spellButtonDataList, resref, false) then return end 56 | local innateButtonDataList = sprite:GetQuickButtons(4, false) 57 | B3Hotkey_UseCGameButtonList(sprite, innateButtonDataList, resref, false) 58 | end 59 | end 60 | end 61 | 62 | function B3Hotkey_CastOffInternal(resref) 63 | if worldScreen == e:GetActiveEngine() then 64 | local sprite = EEex_Sprite_GetSelected() 65 | if sprite then 66 | local spellButtonDataList = sprite:GetInternalButtonList() 67 | if B3Hotkey_UseCGameButtonList(sprite, spellButtonDataList, resref, true) then 68 | EEex_Actionbar_RestoreLastState() 69 | return true 70 | end 71 | end 72 | end 73 | return false 74 | end 75 | 76 | function B3Hotkey_CastTwoStep(initial, second) 77 | B3Hotkey_AttemptToCastViaHotkey(initial) 78 | B3Hotkey_InternalCastResref = second 79 | end 80 | 81 | function B3Hotkey_AttemptToSelectCharacter(portraitNum, dontUnselect) 82 | 83 | local chitin = EngineGlobals.g_pBaldurChitin 84 | local activeEngine = e:GetActiveEngine() 85 | 86 | if worldScreen == activeEngine then 87 | 88 | local game = chitin.m_pObjectGame 89 | local spriteID = EEex_Sprite_GetInPortraitID(portraitNum) 90 | local cursorState = game.m_nState 91 | 92 | if cursorState == 0 then 93 | 94 | local doSelect = true 95 | 96 | local memberList = game.m_group.m_memberList 97 | if memberList.m_nCount == 1 and memberList.m_pNodeHead.data == spriteID then 98 | game:OnPortraitLDblClick(portraitNum) 99 | doSelect = false 100 | end 101 | 102 | if doSelect then 103 | 104 | if not dontUnselect then 105 | game:UnselectAll() 106 | end 107 | 108 | -- boolean bPlaySelectSound 109 | game:SelectCharacter(spriteID, true) 110 | game:SelectToolbar() 111 | end 112 | else 113 | local sprite = EEex_GameObject_Get(spriteID) 114 | if not sprite then 115 | return 116 | end 117 | 118 | local visibleArea = EEex_Area_GetVisible() 119 | if EEex_UserDataEqual(visibleArea, sprite.m_pArea) then 120 | if cursorState == 1 then 121 | visibleArea:OnActionButtonClickGround(sprite.m_pos) 122 | else 123 | sprite:virtual_OnActionButton(sprite.m_pos) 124 | end 125 | end 126 | end 127 | else 128 | EEex_CastUD(activeEngine, "EEex_CBaldurEngine"):virtual_OnPortraitLClick(portraitNum) 129 | end 130 | end 131 | 132 | B3Hotkey_LastSuccessfulHotkey = nil 133 | 134 | function B3Hotkey_KeyPressedListener(key) 135 | if B3Hotkey_PrintKeys then 136 | Infinity_DisplayString("[EEex] Pressed: "..EEex_ToHex(key)) 137 | end 138 | local completedMatch = false 139 | for _, hotkeyDef in ipairs(B3Hotkey_Hotkeys) do 140 | local stage = hotkeyDef[4] 141 | if stage ~= 0 then 142 | local hotkeyCombo = hotkeyDef[3] 143 | if hotkeyCombo[stage] == key then 144 | if stage ~= #hotkeyCombo then 145 | hotkeyDef[4] = stage + 1 -- Advance 146 | else 147 | hotkeyDef[4] = 0 -- Stop Processing 148 | local allFlagsDown = true 149 | for _, flag in ipairs(hotkeyDef[2]) do 150 | if not EEex_Key_IsDown(flag) then 151 | allFlagsDown = false 152 | break 153 | end 154 | end 155 | if allFlagsDown then 156 | -- Success 157 | B3Hotkey_LastSuccessfulHotkey = hotkeyDef 158 | completedMatch = true 159 | end 160 | end 161 | else 162 | local shouldFail = true 163 | for _, flag in ipairs(hotkeyDef[2]) do 164 | if key == flag then 165 | shouldFail = false 166 | break 167 | end 168 | end 169 | if shouldFail then 170 | -- Fail 171 | hotkeyDef[4] = 0 -- Stop Processing 172 | end 173 | end 174 | end 175 | end 176 | if not completedMatch then 177 | B3Hotkey_LastSuccessfulHotkey = nil 178 | end 179 | end 180 | EEex_Key_AddPressedListener(B3Hotkey_KeyPressedListener) 181 | 182 | function B3Hotkey_KeyReleasedListener(key) 183 | if B3Hotkey_LastSuccessfulHotkey ~= nil then 184 | local hotkeyValue = B3Hotkey_LastSuccessfulHotkey[1] 185 | local hotkeyValueType = type(hotkeyValue) 186 | if hotkeyValueType == "string" then 187 | B3Hotkey_AttemptToCastViaHotkey(hotkeyValue) 188 | elseif hotkeyValueType == "function" then 189 | hotkeyValue() 190 | end 191 | end 192 | B3Hotkey_LastSuccessfulHotkey = nil 193 | for _, hotkeyDef in ipairs(B3Hotkey_Hotkeys) do 194 | hotkeyDef[4] = 1 195 | end 196 | end 197 | EEex_Key_AddReleasedListener(B3Hotkey_KeyReleasedListener) 198 | 199 | B3Hotkey_InternalCastResref = nil 200 | 201 | function B3Hotkey_ActionbarListener(config, state) 202 | if config == 28 and B3Hotkey_InternalCastResref then 203 | local myCopy = B3Hotkey_InternalCastResref 204 | B3Hotkey_InternalCastResref = nil 205 | -- B3Hotkey_CastOffInternal() causes the engine to reapply config 28 if 206 | -- the ability target is the caster. We don't want other listeners to 207 | -- detect this, especially the spell menu. 208 | return EEex_Actionbar_RunWithListenersSuppressed(function() 209 | -- Prevent future listeners from processing the event if we handled it. 210 | -- Again, keep the spell menu from interfering. 211 | return B3Hotkey_CastOffInternal(myCopy) 212 | end) 213 | end 214 | end 215 | EEex_Actionbar_AddListener(B3Hotkey_ActionbarListener) 216 | -------------------------------------------------------------------------------- /EEex/copy/B3Invis.lua: -------------------------------------------------------------------------------- 1 | 2 | B3Invis_RenderAsInvisible = true 3 | 4 | function B3Invis_CanSelectedSeeInvis() 5 | local toReturn = false 6 | EEex_Sprite_IterateSelected(function(sprite) 7 | if sprite:getActiveStats().m_bSeeInvisible ~= 0 then 8 | toReturn = true 9 | return true 10 | end 11 | end) 12 | return toReturn 13 | end 14 | 15 | function B3Invis_Hook_CanSeeInvisible() 16 | return B3Invis_CanSelectedSeeInvis() 17 | end 18 | 19 | function B3Invis_Hook_ForceCircle(target) 20 | return target.m_bInvisible ~= 0 and B3Invis_CanSelectedSeeInvis() 21 | end 22 | 23 | (function() 24 | 25 | EEex_DisableCodeProtection() 26 | 27 | --[[ 28 | +------------------------------------------------------------------------------------------------------+ 29 | | Allow cursor to interact with invisible creatures if a selected creature has op193 | 30 | +------------------------------------------------------------------------------------------------------+ 31 | | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | 32 | | return: | 33 | | -> false - Don't alter engine behavior | 34 | | -> true - Allow the cursor to interact with the creature regardless of it being invisible | 35 | +------------------------------------------------------------------------------------------------------+ 36 | --]] 37 | 38 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::IsOver()-B3Invis"), 7, { 39 | {"hook_integrity_watchdog_ignore_registers", { 40 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 41 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 42 | EEex_HookIntegrityWatchdogRegister.R11 43 | }}}, 44 | EEex_FlattenTable({ 45 | {[[ 46 | #MAKE_SHADOW_SPACE(32) 47 | ]]}, 48 | EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { 49 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 50 | }), 51 | {[[ 52 | jmp no_error 53 | 54 | call_error: 55 | xor rax, rax 56 | 57 | no_error: 58 | test rax, rax 59 | 60 | #DESTROY_SHADOW_SPACE 61 | jnz #L(jmp_success) 62 | ]]}, 63 | }) 64 | ) 65 | 66 | --[[ 67 | +---------------------------------------------------------------------------------------+ 68 | | Allow invisible creature markers to render if a selected creature has op193 | 69 | +---------------------------------------------------------------------------------------+ 70 | | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | 71 | | return: | 72 | | -> false - Don't alter engine behavior | 73 | | -> true - Force the creature's marker to not be hidden due to invisibility | 74 | +---------------------------------------------------------------------------------------+ 75 | --]] 76 | 77 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis1"), 7, { 78 | {"hook_integrity_watchdog_ignore_registers", { 79 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 80 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 81 | }}}, 82 | EEex_FlattenTable({ 83 | {[[ 84 | #MAKE_SHADOW_SPACE(40) 85 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r9 86 | ]]}, 87 | EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { 88 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 89 | }), 90 | {[[ 91 | jmp no_error 92 | 93 | call_error: 94 | xor rax, rax 95 | 96 | no_error: 97 | test rax, rax 98 | 99 | mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 100 | #DESTROY_SHADOW_SPACE 101 | jnz #L(jmp_success) 102 | ]]}, 103 | }) 104 | ) 105 | 106 | if B3Invis_RenderAsInvisible then 107 | 108 | --[[ 109 | +---------------------------------------------------------------------------------------------------------------------+ 110 | | (Option #1) Render invisible creatures as transparent (like improved invisibility) if a selected creature has op193 | 111 | +---------------------------------------------------------------------------------------------------------------------+ 112 | | [Lua] B3Invis_Hook_CanSeeInvisible() -> boolean | 113 | | return: | 114 | | -> false - Don't alter engine behavior | 115 | | -> true - Force the creature to be rendered as transparent | 116 | +---------------------------------------------------------------------------------------------------------------------+ 117 | --]] 118 | 119 | EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameSprite::Render()-B3Invis"), 6, { 120 | {"hook_integrity_watchdog_ignore_registers", { 121 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 122 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 123 | }}}, 124 | EEex_FlattenTable({ 125 | {[[ 126 | #MAKE_SHADOW_SPACE(48) 127 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 128 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 129 | ]]}, 130 | EEex_GenLuaCall("B3Invis_Hook_CanSeeInvisible", { 131 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 132 | }), 133 | {[[ 134 | jmp no_error 135 | 136 | call_error: 137 | xor rax, rax 138 | 139 | no_error: 140 | test rax, rax 141 | 142 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 143 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 144 | #DESTROY_SHADOW_SPACE 145 | jnz #L(jmp_fail) 146 | ]]}, 147 | }) 148 | ) 149 | else 150 | 151 | --[[ 152 | +-------------------------------------------------------------------------------------------------------+ 153 | | (Option #2) Render invisible creatures with an always-visible marker if a selected creature has op193 | 154 | +-------------------------------------------------------------------------------------------------------+ 155 | | [Lua] B3Invis_Hook_ForceCircle(sprite: CGameSprite) -> boolean | 156 | | return: | 157 | | -> false - Don't alter engine behavior | 158 | | -> true - Force the creature's marker to render | 159 | +-------------------------------------------------------------------------------------------------------+ 160 | --]] 161 | 162 | EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameSprite::RenderMarkers()-B3Invis2"), 0, 6, 6, { 163 | {"hook_integrity_watchdog_ignore_registers", { 164 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 165 | }}}, 166 | EEex_FlattenTable({ 167 | {[[ 168 | jnz #L(return) 169 | 170 | #MAKE_SHADOW_SPACE(72) 171 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax 172 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx 173 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx 174 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r9 175 | ]]}, 176 | EEex_GenLuaCall("B3Invis_Hook_ForceCircle", { 177 | ["args"] = { 178 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, 179 | }, 180 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 181 | }), 182 | {[[ 183 | jmp no_error 184 | 185 | call_error: 186 | xor rax, rax 187 | 188 | no_error: 189 | test rax, rax 190 | 191 | mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] 192 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 193 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 194 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 195 | #DESTROY_SHADOW_SPACE 196 | ]]}, 197 | }) 198 | ) 199 | end 200 | 201 | EEex_EnableCodeProtection() 202 | 203 | end)() 204 | -------------------------------------------------------------------------------- /EEex/copy/B3Scale.lua: -------------------------------------------------------------------------------- 1 | 2 | B3Scale_Percentage = nil 3 | 4 | EEex_GameState_AddInitializedListener(function() 5 | B3Scale_Percentage = tonumber(Infinity_GetINIString("B3Scale", "Percentage", -1)) 6 | end) 7 | 8 | -- p = [0-1], where 0 is minimum scaling and 1 is maximum scaling 9 | function B3Scale_SetPercentage(p) 10 | B3Scale_Percentage = math.min(math.max(0, p), 1) 11 | Infinity_SetINIValue("B3Scale", "Percentage", B3Scale_Percentage) 12 | B3Scale_PokeEngine() 13 | end 14 | 15 | function B3Scale_GetVideoSize() 16 | local pVidMode = EEex_EngineGlobal_CBaldurChitin.cVideo.pCurrentMode 17 | return pVidMode.nWidth, pVidMode.nHeight 18 | end 19 | 20 | function B3Scale_PokeEngine() 21 | local w, h = B3Scale_GetVideoSize() 22 | EEex_EngineGlobal_CBaldurChitin:OnResizeWindow(w, h) 23 | end 24 | 25 | --[[ 26 | +-----------------------------------------------------------------------------+ 27 | | Tweak the UI scale whenever the window is resized (called directly by EEex) | 28 | +-----------------------------------------------------------------------------+ 29 | --]] 30 | 31 | function B3Scale_DoSizeChange() 32 | 33 | if B3Scale_Percentage == -1 then 34 | return 35 | end 36 | 37 | local w, h = B3Scale_GetVideoSize() 38 | local ratio = math.max(1.25, math.min(w / h, 2.6)) 39 | 40 | if ratio <= 4/3 then 41 | -- UI wasn't designed for this ratio, no scaling. 42 | return 43 | end 44 | 45 | local scaledH = w >= 1024 and h >= 768 46 | and 768 + (1 - B3Scale_Percentage) * (h - 768) 47 | or 768 48 | 49 | CVidMode.SCREENWIDTH = math.floor(scaledH * ratio) 50 | CVidMode.SCREENHEIGHT = math.floor(scaledH) 51 | end 52 | -------------------------------------------------------------------------------- /EEex/copy/B3TAURA.BAM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/copy/B3TAURA.BAM -------------------------------------------------------------------------------- /EEex/copy/B3TBACKG.BAM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/copy/B3TBACKG.BAM -------------------------------------------------------------------------------- /EEex/copy/B3TCONTI.BAM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/copy/B3TCONTI.BAM -------------------------------------------------------------------------------- /EEex/copy/B3TMODAL.BAM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/copy/B3TMODAL.BAM -------------------------------------------------------------------------------- /EEex/copy/B3TiStep.menu: -------------------------------------------------------------------------------- 1 | 2 | menu 3 | { 4 | name "B3TimeStep_Menu" 5 | ignoreEsc 6 | label 7 | { 8 | enabled "B3TimeStep_Tick()" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /EEex/copy/B3TimeStep.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- Options -- 4 | ------------- 5 | 6 | -- Key that, while paused, advances time 2 AsynchronousUpdate ticks, or 1 "game time" tick. 7 | -- Holding this key for half a second causes time to flow normally until it is released. 8 | B3TimeStep_Key = EEex_Key_GetFromName("d") 9 | 10 | ------------- 11 | -- Globals -- 12 | ------------- 13 | 14 | B3TimeStep_PauseScheduled = false 15 | B3TimeStep_PauseTick = -1 16 | 17 | B3TimeStep_Flowing = false 18 | B3TimeStep_FlowTick = -1 19 | 20 | ------------- 21 | -- Utility -- 22 | ------------- 23 | 24 | function B3TimeStep_TogglePause() 25 | -- byte visualPause, byte bSendMessage, int idPlayerPause, byte bLogPause, byte bRequireHostUnpause 26 | EngineGlobals.g_pBaldurChitin.m_pEngineWorld:TogglePauseGame(true, true, 0, false, false) 27 | end 28 | 29 | function B3TimeStep_GetGameTime() 30 | return EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_worldTime.m_gameTime 31 | end 32 | 33 | function B3TimeStep_SetGameTime(value) 34 | EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_worldTime.m_gameTime = value 35 | end 36 | 37 | --------------- 38 | -- Main Tick -- 39 | --------------- 40 | 41 | function B3TimeStep_Tick() 42 | 43 | local gameTime = B3TimeStep_GetGameTime() 44 | if B3TimeStep_PauseTick ~= -1 and gameTime >= B3TimeStep_PauseTick then 45 | B3TimeStep_PauseTick = -1 46 | B3TimeStep_TogglePause() 47 | -- For some reason the engine reverses m_gameTime by 1 tick on pause. 48 | -- Since this code only steps 1 tick, force the time to advance. 49 | B3TimeStep_SetGameTime(gameTime) 50 | end 51 | 52 | if not B3TimeStep_Flowing and B3TimeStep_FlowTick ~= -1 and Infinity_GetClockTicks() >= B3TimeStep_FlowTick then 53 | B3TimeStep_TogglePause() 54 | B3TimeStep_Flowing = true 55 | end 56 | 57 | return false 58 | end 59 | 60 | ------------------- 61 | -- Key Listeners -- 62 | ------------------- 63 | 64 | EEex_Key_AddPressedListener(function(key) 65 | if key == B3TimeStep_Key and worldScreen == e:GetActiveEngine() and Infinity_TextEditHasFocus() == 0 and worldScreen:CheckIfPaused() then 66 | B3TimeStep_TogglePause() 67 | B3TimeStep_PauseTick = B3TimeStep_GetGameTime() + 1 68 | B3TimeStep_FlowTick = Infinity_GetClockTicks() + 500 69 | end 70 | end) 71 | 72 | EEex_Key_AddReleasedListener(function(key) 73 | if key == B3TimeStep_Key then 74 | if B3TimeStep_Flowing and not worldScreen:CheckIfPaused() then 75 | B3TimeStep_TogglePause() 76 | end 77 | B3TimeStep_Flowing = false 78 | B3TimeStep_FlowTick = -1 79 | end 80 | end) 81 | 82 | ------------------------------ 83 | -- Maintain B3TimeStep_Menu -- 84 | ------------------------------ 85 | 86 | EEex_Menu_AddMainFileLoadedListener(function() 87 | EEex_Menu_LoadFile("B3TiStep") 88 | end) 89 | 90 | function B3TimeStep_PushMenu() 91 | Infinity_PushMenu("B3TimeStep_Menu") 92 | end 93 | EEex_GameState_AddInitializedListener(B3TimeStep_PushMenu) 94 | EEex_Menu_AddAfterMainFileReloadedListener(B3TimeStep_PushMenu) 95 | -------------------------------------------------------------------------------- /EEex/copy/B3Timer.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- Options -- 4 | ------------- 5 | 6 | B3Timer_ShowModalTimer = false 7 | B3Timer_ShowContingencyTimer = false 8 | B3Timer_ShowCastTimer = false 9 | 10 | B3Timer_HugPortraits = false 11 | 12 | ----------------------- 13 | -- Template Handling -- 14 | ----------------------- 15 | 16 | B3Timer_TemplateInstances = {} 17 | 18 | function B3Timer_CreateInstance(menuName, templateName, x, y, w, h) 19 | 20 | local menuEntry = B3Timer_TemplateInstances[menuName] 21 | if not menuEntry then 22 | menuEntry = {} 23 | B3Timer_TemplateInstances[menuName] = menuEntry 24 | end 25 | 26 | local entry = menuEntry[templateName] 27 | if not entry then 28 | entry = {["maxID"] = 0, ["instanceData"] = {}} 29 | menuEntry[templateName] = entry 30 | end 31 | 32 | local newID = entry.maxID + 1 33 | entry.maxID = newID 34 | 35 | local instanceEntry = {["id"] = newID} 36 | entry.instanceData[newID] = instanceEntry 37 | 38 | local oldAnimationID = currentAnimationID 39 | currentAnimationID = newID 40 | EEex_Menu_InjectTemplate(menuName, templateName, x, y, w, h) 41 | currentAnimationID = oldAnimationID 42 | 43 | return instanceEntry 44 | end 45 | 46 | B3Timer_TemplateInstancesByPortrait = {} 47 | 48 | function B3Timer_CreateInstanceForPortrait(menuName, templateName, x, y, w, h, portraitItem) 49 | local instanceEntry = B3Timer_CreateInstance(menuName, templateName, x, y, w, h) 50 | instanceEntry.portraitItem = portraitItem 51 | instanceEntry.portraitEnabledFunc = EEex_Menu_GetItemFunction(portraitItem.reference_enabled) 52 | instanceEntry.portraitIndex = EEex_Menu_GetItemVariant(portraitItem.button.portrait) 53 | instanceEntry.enabled = false 54 | local portraitEntry = EEex_Utility_GetOrCreate(B3Timer_TemplateInstancesByPortrait, instanceEntry.portraitIndex, {}) 55 | local instanceEntries = EEex_Utility_GetOrCreate(portraitEntry, templateName, {}) 56 | table.insert(instanceEntries, instanceEntry) 57 | end 58 | 59 | --------------- 60 | -- Listeners -- 61 | --------------- 62 | 63 | B3Timer_InjectingMenu = "RIGHT_SIDEBAR" 64 | 65 | function B3Timer_InstallBars() 66 | 67 | -- Dragonspear UI++ 68 | B3Timer_HugPortraits = B3Timer_HugPortraits or nameToItem["portrait1ButtonBig"] ~= nil 69 | 70 | EEex_Menu_LoadFile("B3Timer") 71 | 72 | local item = EEex_Menu_Find(B3Timer_InjectingMenu).items 73 | while item do 74 | local portrait = item.button.portrait 75 | if portrait then 76 | local area = item.area 77 | B3Timer_CreateInstanceForPortrait(B3Timer_InjectingMenu, "B3Timer_Menu_TEMPLATE_Background", nil, nil, 10, nil, item) 78 | B3Timer_CreateInstanceForPortrait(B3Timer_InjectingMenu, "B3Timer_Menu_TEMPLATE_TimerModal", nil, nil, 2, nil, item) 79 | B3Timer_CreateInstanceForPortrait(B3Timer_InjectingMenu, "B3Timer_Menu_TEMPLATE_TimerContingency", nil, nil, 2, nil, item) 80 | B3Timer_CreateInstanceForPortrait(B3Timer_InjectingMenu, "B3Timer_Menu_TEMPLATE_TimerCast", nil, nil, 2, nil, item) 81 | end 82 | item = item.next 83 | end 84 | end 85 | EEex_Menu_AddMainFileLoadedListener(B3Timer_InstallBars) 86 | 87 | function B3Timer_PushMenuListener() 88 | Infinity_PushMenu("B3Timer_Menu") 89 | end 90 | EEex_GameState_AddInitializedListener(B3Timer_PushMenuListener) 91 | EEex_Menu_AddAfterMainFileReloadedListener(B3Timer_PushMenuListener) 92 | 93 | -------------------- 94 | -- Menu Functions -- 95 | -------------------- 96 | 97 | B3Timer_NextUpdateTick = -1 98 | 99 | function B3Timer_Menu_Tick() 100 | 101 | -- Game lags when this function is spammed, limit to 30tps. 102 | local curTick = Infinity_GetClockTicks() 103 | if curTick < B3Timer_NextUpdateTick then 104 | return 105 | end 106 | B3Timer_NextUpdateTick = curTick + 33 107 | 108 | for portraitIndex = 0, 5, 1 do 109 | 110 | local portraitEntry = B3Timer_TemplateInstancesByPortrait[portraitIndex] 111 | local sprite = EEex_Sprite_GetInPortrait(portraitIndex) 112 | 113 | if sprite then 114 | 115 | local portraitInstanceStartX = {} 116 | local portraitInstanceCurX = {} 117 | 118 | local updateTimerBar = function(templateName, condition) 119 | 120 | for i, instanceEntry in ipairs(portraitEntry[templateName]) do 121 | 122 | local portraitArea = instanceEntry.portraitItem.area 123 | 124 | local curX = portraitInstanceCurX[i] 125 | if not curX then 126 | local startX = (B3Timer_HugPortraits and portraitArea.x or 0) - 3 127 | curX = startX 128 | portraitInstanceStartX[i] = startX 129 | portraitInstanceCurX[i] = curX 130 | end 131 | 132 | local portraitEnabledFunc = instanceEntry.portraitEnabledFunc 133 | if (not portraitEnabledFunc or portraitEnabledFunc()) and condition then 134 | instanceEntry.enabled = true 135 | portraitInstanceCurX[i] = curX - 3 136 | EEex_Menu_SetTemplateArea(B3Timer_InjectingMenu, templateName, instanceEntry.id, curX, portraitArea.y, nil, portraitArea.h) 137 | else 138 | instanceEntry.enabled = false 139 | end 140 | end 141 | end 142 | 143 | updateTimerBar( "B3Timer_Menu_TEMPLATE_TimerCast", B3Timer_ShowCastTimer and sprite:getCastTimerPercentage() > 0 ) 144 | updateTimerBar( "B3Timer_Menu_TEMPLATE_TimerContingency", B3Timer_ShowContingencyTimer and sprite:getActiveStats().m_cContingencyList.m_nCount > 0 ) 145 | updateTimerBar( "B3Timer_Menu_TEMPLATE_TimerModal", B3Timer_ShowModalTimer and sprite:getModalState() ~= 0 ) 146 | 147 | for i, backgroundEntry in ipairs(portraitEntry["B3Timer_Menu_TEMPLATE_Background"]) do 148 | local startX = portraitInstanceStartX[i] 149 | local curX = portraitInstanceCurX[i] 150 | if curX ~= startX then 151 | backgroundEntry.enabled = true 152 | local portraitArea = backgroundEntry.portraitItem.area 153 | EEex_Menu_SetTemplateArea(B3Timer_InjectingMenu, "B3Timer_Menu_TEMPLATE_Background", backgroundEntry.id, curX + 2, portraitArea.y, startX - curX + 1, portraitArea.h) 154 | else 155 | backgroundEntry.enabled = false 156 | end 157 | end 158 | else 159 | for _, templateName in ipairs({ 160 | "B3Timer_Menu_TEMPLATE_Background", 161 | "B3Timer_Menu_TEMPLATE_TimerModal", 162 | "B3Timer_Menu_TEMPLATE_TimerContingency", 163 | "B3Timer_Menu_TEMPLATE_TimerCast" 164 | }) 165 | do 166 | for _, instanceEntry in ipairs(portraitEntry[templateName]) do 167 | instanceEntry.enabled = false 168 | end 169 | end 170 | end 171 | end 172 | end 173 | 174 | function B3Timer_Menu_TEMPLATE_Background_Enabled() 175 | return B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_Background"].instanceData[instanceId].enabled 176 | end 177 | 178 | function B3Timer_Menu_TEMPLATE_TimerModal_Enabled() 179 | return B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerModal"].instanceData[instanceId].enabled 180 | end 181 | 182 | function B3Timer_Menu_TEMPLATE_TimerModal_Frame() 183 | local portraitIndex = B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerModal"].instanceData[instanceId].portraitIndex 184 | return math.floor(90 * EEex_Sprite_GetModalTimerPercentage(EEex_Sprite_GetInPortrait(portraitIndex)) + 0.5) 185 | end 186 | 187 | function B3Timer_Menu_TEMPLATE_TimerContingency_Enabled() 188 | return B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerContingency"].instanceData[instanceId].enabled 189 | end 190 | 191 | function B3Timer_Menu_TEMPLATE_TimerContingency_Frame() 192 | local portraitIndex = B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerContingency"].instanceData[instanceId].portraitIndex 193 | return math.floor(90 * EEex_Sprite_GetContingencyTimerPercentage(EEex_Sprite_GetInPortrait(portraitIndex)) + 0.5) 194 | end 195 | 196 | function B3Timer_Menu_TEMPLATE_TimerCast_Enabled() 197 | return B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerCast"].instanceData[instanceId].enabled 198 | end 199 | 200 | function B3Timer_Menu_TEMPLATE_TimerCast_Frame() 201 | local portraitIndex = B3Timer_TemplateInstances[B3Timer_InjectingMenu]["B3Timer_Menu_TEMPLATE_TimerCast"].instanceData[instanceId].portraitIndex 202 | return math.floor(90 * EEex_Sprite_GetCastTimerPercentage(EEex_Sprite_GetInPortrait(portraitIndex)) + 0.5) 203 | end 204 | -------------------------------------------------------------------------------- /EEex/copy/B3Timer.menu: -------------------------------------------------------------------------------- 1 | 2 | menu 3 | { 4 | name "B3Timer_Menu" 5 | ignoreesc 6 | 7 | -- The engine won't change the cursor type over templates if there isn't at 8 | -- least one "normal" element that occupies an xy of equal or greater value. 9 | label 10 | { 11 | area 0 0 2147483647 2147483647 12 | enabled "B3Timer_Menu_Tick()" 13 | } 14 | 15 | template 16 | { 17 | label 18 | { 19 | enabled "B3Timer_Menu_TEMPLATE_Background_Enabled()" 20 | bam "B3TBACKG" 21 | scaleToClip 22 | } 23 | name "B3Timer_Menu_TEMPLATE_Background" 24 | } 25 | 26 | template 27 | { 28 | label 29 | { 30 | enabled "B3Timer_Menu_TEMPLATE_TimerModal_Enabled()" 31 | bam "B3TMODAL" 32 | scaleToClip 33 | frame lua "B3Timer_Menu_TEMPLATE_TimerModal_Frame()" 34 | } 35 | name "B3Timer_Menu_TEMPLATE_TimerModal" 36 | } 37 | 38 | template 39 | { 40 | label 41 | { 42 | enabled "B3Timer_Menu_TEMPLATE_TimerContingency_Enabled()" 43 | bam "B3TCONTI" 44 | scaleToClip 45 | frame lua "B3Timer_Menu_TEMPLATE_TimerContingency_Frame()" 46 | } 47 | name "B3Timer_Menu_TEMPLATE_TimerContingency" 48 | } 49 | 50 | template 51 | { 52 | label 53 | { 54 | enabled "B3Timer_Menu_TEMPLATE_TimerCast_Enabled()" 55 | bam "B3TAURA" 56 | scaleToClip 57 | frame lua "B3Timer_Menu_TEMPLATE_TimerCast_Frame()" 58 | } 59 | name "B3Timer_Menu_TEMPLATE_TimerCast" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /EEex/copy/EEex_AIBase.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_AIBase_Private_ScriptingObjectUpdatedListeners = {} 3 | 4 | function EEex_AIBase_AddScriptingObjectUpdatedListener(func) 5 | table.insert(EEex_AIBase_Private_ScriptingObjectUpdatedListeners, func) 6 | end 7 | 8 | -- @bubb_doc { EEex_AIBase_GetScriptLevel / instance_name=getScriptLevel } 9 | -- 10 | -- @summary: Returns the ``aiBase``'s ``CAIScript`` for the given ``scriptLevel``. 11 | -- 12 | -- @self { aiBase / usertype=CGameAIBase }: The object whose script is being fetched. 13 | -- 14 | -- @param { scriptLevel / type=number }: The level of the script to fetch. Valid values identical to `SCRLEV.IDS`_. 15 | -- 16 | -- @return { usertype=CAIScript }: See summary. 17 | -- 18 | -- @extra_comment: 19 | -- 20 | -- ================================================================================================================= 21 | -- 22 | -- **SCRLEV.IDS** 23 | -- ************** 24 | -- 25 | -- +--------------+---------------+ 26 | -- | Script Level | Symbolic Name | 27 | -- +==============+===============+ 28 | -- | 0 | OVERRIDE | 29 | -- +--------------+---------------+ 30 | -- | 1 | AREA | 31 | -- +--------------+---------------+ 32 | -- | 2 | SPECIFICS | 33 | -- +--------------+---------------+ 34 | -- | 4 | CLASS | 35 | -- +--------------+---------------+ 36 | -- | 5 | RACE | 37 | -- +--------------+---------------+ 38 | -- | 6 | GENERAL | 39 | -- +--------------+---------------+ 40 | -- | 7 | DEFAULT | 41 | -- +--------------+---------------+ 42 | 43 | function EEex_AIBase_GetScriptLevel(aiBase, scriptLevel) 44 | return ({ 45 | [0] = aiBase.m_overrideScript, 46 | [1] = aiBase.m_areaScript, 47 | [2] = aiBase.m_specificsScript, 48 | [4] = aiBase.m_classScript, 49 | [5] = aiBase.m_raceScript, 50 | [6] = aiBase.m_generalScript, 51 | [7] = aiBase.m_defaultScript, 52 | })[scriptLevel] 53 | end 54 | CGameAIBase.getScriptLevel = EEex_AIBase_GetScriptLevel 55 | 56 | -- @bubb_doc { EEex_AIBase_GetScriptLevelResRef / instance_name=getScriptLevelResRef } 57 | -- 58 | -- @summary: Returns a string that represents the ``aiBase``'s ``CResRef`` for the given ``scriptLevel``. 59 | -- If the given ``scriptLevel`` is not populated, returns ``""``. 60 | -- 61 | -- @self { aiBase / usertype=CGameAIBase }: The object whose script resref is being fetched. 62 | -- 63 | -- @param { scriptLevel / type=number }: The level of the script resref to fetch. Valid values identical to `SCRLEV.IDS`_. 64 | -- 65 | -- @return { type=string }: See summary. 66 | 67 | function EEex_AIBase_GetScriptLevelResRef(aiBase, scriptLevel) 68 | local script = aiBase:getScriptLevel(scriptLevel) 69 | return script and script.cResRef:get() or "" 70 | end 71 | CGameAIBase.getScriptLevelResRef = EEex_AIBase_GetScriptLevelResRef 72 | 73 | -- @bubb_doc { EEex_AIBase_SetScriptLevel / instance_name=setScriptLevel } 74 | -- 75 | -- @summary: Sets the ``aiBase``'s ``CAIScript`` for the given ``scriptLevel`` to ``script``. 76 | -- 77 | -- @self { aiBase / usertype=CGameAIBase }: The object whose script level is being set. 78 | -- 79 | -- @param { scriptLevel / type=number }: The level of the script to set. Valid values identical to `SCRLEV.IDS`_. 80 | -- 81 | -- @param { script / usertype=CAIScript }: 82 | -- 83 | -- The script to assign to ``scriptLevel``. @EOL @EOL 84 | -- 85 | -- **Note:** ``aiBase`` **holds a reference to this parameter; do not free it.** 86 | 87 | function EEex_AIBase_SetScriptLevel(aiBase, scriptLevel, script) 88 | aiBase:virtual_SetScript(scriptLevel, script) 89 | end 90 | CGameAIBase.setScriptLevel = EEex_AIBase_SetScriptLevel 91 | 92 | -- @bubb_doc { EEex_AIBase_SetScriptLevelResRef / instance_name=setScriptLevelResRef } 93 | -- 94 | -- @summary: Loads the script with the given ``resref`` and sets the ``aiBase``'s ``CAIScript`` for the given ``scriptLevel`` to it. 95 | -- 96 | -- @self { aiBase / usertype=CGameAIBase }: The object whose script level is being set. 97 | -- 98 | -- @param { scriptLevel / type=number }: The level of the script to set. Valid values identical to `SCRLEV.IDS`_. 99 | -- 100 | -- @param { resref / type=string }: The script resref to assign to ``scriptLevel``. 101 | -- 102 | -- @param { bPlayerScript / type=boolean / default=false }: 103 | -- 104 | -- If ``true``, signifies that ``resref`` has the extension ``.BS`` instead of ``.BCS``. @EOL @EOL 105 | -- 106 | -- **Note:** Due to the enhanced edition’s use of script caching, the engine has trouble @EOL 107 | -- differentiating between ``.BS`` and ``.BCS`` files with the same name. If a script @EOL 108 | -- with the given ``resref`` has already been loaded by the engine, that script will be @EOL 109 | -- used, regardless of ``bPlayerScript``. 110 | 111 | function EEex_AIBase_SetScriptLevelResRef(aiBase, scriptLevel, resref, bPlayerScript) 112 | 113 | local newScript = EEex_NewUD("CAIScript") 114 | 115 | EEex_RunWithStackManager({ 116 | { ["name"] = "resref", ["struct"] = "CResRef", ["constructor"] = {["args"] = {resref} }}, }, 117 | function(manager) 118 | newScript:Construct1(manager:getUD("resref"), EEex_Utility_Default(bPlayerScript, false)) 119 | end) 120 | 121 | aiBase:setScriptLevel(scriptLevel, newScript) 122 | end 123 | CGameAIBase.setScriptLevelResRef = EEex_AIBase_SetScriptLevelResRef 124 | 125 | -- @bubb_doc { EEex_AIBase_SetStoredScriptingTarget / instance_name=setStoredScriptingTarget } 126 | -- 127 | -- @summary: Stores ``target`` on ``aiBase`` for use with the ``EEex_Target`` scripting object. 128 | -- 129 | -- @self { aiBase / usertype=CGameAIBase }: The object that the target is being stored on. 130 | -- 131 | -- @param { targetKey / type=string }: The name to be used to refer to the target being stored. 132 | -- 133 | -- @param { target / usertype=CGameObject }: The target being stored on ``aiBase`` as ``targetKey``. 134 | -- 135 | -- @extra_comment: 136 | -- 137 | -- ================================================================================================================ 138 | -- 139 | -- **Example** 140 | -- *********** 141 | -- 142 | -- A combination of ``EEex_AIBase_SetStoredScriptingTarget`` and ``EEex_LuaTrigger`` can be used to target specific 143 | -- objects programmatically. The following example shows how you could use this concept to have a creature start 144 | -- dialog once they see the current party leader: 145 | -- 146 | -- **In M_*.lua file:** 147 | -- """""""""""""""""""" 148 | -- 149 | -- .. code-block:: Lua 150 | -- 151 | -- function StoreAlivePartyLeader() 152 | -- 153 | -- local partyLeader = nil 154 | -- 155 | -- for i = 0, 5 do 156 | -- local partyMember = EEex_Sprite_GetInPortrait(i) 157 | -- if partyMember and EEex_BAnd(partyMember.m_baseStats.m_generalState, 0xFC0) == 0 then 158 | -- partyLeader = partyMember 159 | -- break 160 | -- end 161 | -- end 162 | -- 163 | -- EEex_LuaTrigger_Object:setStoredScriptingTarget("AlivePartyLeader", partyLeader) 164 | -- return partyLeader ~= nil 165 | -- end 166 | -- 167 | -- **In script:** 168 | -- """""""""""""" 169 | -- 170 | -- .. code-block:: text 171 | -- 172 | -- IF 173 | -- EEex_LuaTrigger("return StoreAlivePartyLeader()") 174 | -- See(EEex_Target("AlivePartyLeader")) 175 | -- THEN 176 | -- RESPONSE #100 177 | -- Dialog(EEex_Target("AlivePartyLeader")) 178 | -- END 179 | 180 | function EEex_AIBase_SetStoredScriptingTarget(aiBase, targetKey, target) 181 | local targetTable = EEex_Utility_GetOrCreateTable(EEex_GetUDAux(aiBase), "EEex_Target") 182 | targetTable[targetKey] = target and target.m_id or nil 183 | end 184 | CGameAIBase.setStoredScriptingTarget = EEex_AIBase_SetStoredScriptingTarget 185 | 186 | ----------- 187 | -- Hooks -- 188 | ----------- 189 | 190 | function EEex_AIBase_LuaHook_OnScriptingObjectUpdated(aiBase, scriptingObject) 191 | for _, listener in ipairs(EEex_AIBase_Private_ScriptingObjectUpdatedListeners) do 192 | listener(EEex_GameObject_CastUT(aiBase), scriptingObject) 193 | end 194 | end 195 | -------------------------------------------------------------------------------- /EEex/copy/EEex_AIBase_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +------------------------------------------------------------------------------------------------------------------+ 8 | | Call a hook that tracks when scripting objects are updated | 9 | +------------------------------------------------------------------------------------------------------------------+ 10 | | [EEex.dll] CGameAIBase::Override_ApplyTriggers() | 11 | | [EEex.dll] CGameAIBase::Override_SetTrigger(pTrigger: const CAITrigger*) | 12 | | [EEex.dll] CMessageSetLastObject::Override_Run() | 13 | | [Lua] EEex_AIBase_LuaHook_OnScriptingObjectUpdated(aiBase: CGameAIBase, scriptingObject: EEex_ScriptingObject) | 14 | +------------------------------------------------------------------------------------------------------------------+ 15 | --]] 16 | 17 | local override = function(label, replacementLabel) 18 | EEex_JITAt(EEex_Label(label), {"jmp #$(1) #ENDL", {EEex_Label(replacementLabel)}}) 19 | end 20 | 21 | override("Hook-CGameAIBase::ApplyTriggers()-FirstInstruction", "CGameAIBase::Override_ApplyTriggers") 22 | override("Hook-CGameAIBase::SetTrigger()-FirstInstruction", "CGameAIBase::Override_SetTrigger") 23 | override("Hook-CMessageSetLastObject::Run()-FirstInstruction", "CMessageSetLastObject::Override_Run") 24 | 25 | 26 | EEex_EnableCodeProtection() 27 | 28 | end)() 29 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Action_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +--------------------------------------------------------------------------------+ 8 | | Implement new actions | 9 | +--------------------------------------------------------------------------------+ 10 | | 472 EEex_LuaAction(S:Chunk*) | 11 | | 473 EEex_MatchObject(S:Chunk*) | 12 | | 473 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) | 13 | | 474 EEex_SetTarget(S:Name*,O:Target*) | 14 | | 476 EEex_SpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | 15 | | 476 EEex_SpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | 16 | | 477 EEex_SpellObjectOffsetNoDec(O:Target*,I:Spell*Spell,P:Offset*) | 17 | | 477 EEex_SpellObjectOffsetNoDecRES(S:RES*,O:Target*,P:Offset*) | 18 | | 478 EEex_ForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | 19 | | 478 EEex_ForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | 20 | | 479 EEex_ReallyForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) | 21 | | 479 EEex_ReallyForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) | 22 | +--------------------------------------------------------------------------------+ 23 | | [Lua] EEex_Action_Hook_OnEvaluatingUnknown(evaluator: CGameAIBase) -> number | 24 | | return -> Set as internal action return value: | 25 | | -> EEex_Action_ReturnType.ACTION_STOPPED | 26 | | -> EEex_Action_ReturnType.ACTION_ERROR | 27 | | -> EEex_Action_ReturnType.ACTION_DONE | 28 | | -> EEex_Action_ReturnType.ACTION_NORMAL | 29 | | -> EEex_Action_ReturnType.ACTION_INTERRUPTABLE | 30 | | -> EEex_Action_ReturnType.ACTION_NO_ACTION | 31 | +--------------------------------------------------------------------------------+ 32 | --]] 33 | 34 | EEex_HookBeforeRestoreWithLabels(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultCase"), 0, 7, 7, { 35 | {"hook_integrity_watchdog_ignore_registers", { 36 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 37 | EEex_HookIntegrityWatchdogRegister.RSI, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 38 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11, 39 | }}}, 40 | EEex_FlattenTable({ 41 | {[[ 42 | #MAKE_SHADOW_SPACE(40) 43 | ]]}, 44 | EEex_GenLuaCall("EEex_Action_Hook_OnEvaluatingUnknown", { 45 | ["args"] = { 46 | function(rspOffset) return {[[ 47 | mov qword ptr ss:[rsp+#$(1)], rbx 48 | ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, 49 | }, 50 | ["returnType"] = EEex_LuaCallReturnType.Number, 51 | }), 52 | {[[ 53 | mov esi, eax 54 | #DESTROY_SHADOW_SPACE(KEEP_ENTRY) 55 | #MANUAL_HOOK_EXIT(0) 56 | jmp #L(Hook-CGameAIBase::ExecuteAction()-NormalBranch) 57 | 58 | call_error: 59 | #RESUME_SHADOW_ENTRY 60 | #DESTROY_SHADOW_SPACE 61 | ]]} 62 | }) 63 | ) 64 | 65 | --[[ 66 | +----------------------------------------------------------------------------------+ 67 | | Implement "sprite started action" listeners | 68 | +----------------------------------------------------------------------------------+ 69 | | [EEex.dll] EEex::Action_Hook_OnAfterSpriteStartedAction(pSprite: CGameSprite*) | 70 | +----------------------------------------------------------------------------------+ 71 | | [Lua] EEex_Action_LuaHook_OnAfterSpriteStartedAction(sprite: CGameSprite) | 72 | +----------------------------------------------------------------------------------+ 73 | --]] 74 | 75 | EEex_HookAfterCallWithLabels(EEex_Label("CGameSprite::SetCurrAction()-LastCall"), { 76 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 77 | {[[ 78 | cmp word ptr ds:[r14], 0 ; Don't call the hook for NoAction() since the engine spams it 79 | jz #L(return) 80 | 81 | mov rcx, rdi ; pSprite 82 | call #L(EEex::Action_Hook_OnAfterSpriteStartedAction) 83 | ]]} 84 | ) 85 | 86 | EEex_EnableCodeProtection() 87 | 88 | end)() 89 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Actionbar_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +--------------------------------------------------------------------------+ 8 | | Implement actionbar listeners | 9 | +--------------------------------------------------------------------------+ 10 | | [Lua] EEex_Actionbar_Hook_StateUpdating(config: number, state: number) | 11 | | [Lua] EEex_Actionbar_Hook_ButtonsUpdated() | 12 | +--------------------------------------------------------------------------+ 13 | --]] 14 | 15 | EEex_HookBeforeCall(EEex_Label("Hook-CInfButtonArray::SetState()-SaveArg"), {[[ 16 | mov dword ptr ds:[rsp+70h], r15d ; store it in unused spill space 17 | ]]}) 18 | EEex_HookIntegrityWatchdog_IgnoreStackSizes(EEex_Label("Hook-CInfButtonArray::SetState()-SaveArg"), {{0x70, 4}}) 19 | 20 | EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CInfButtonArray::SetState()-CInfButtonArray::UpdateButtons()"), { 21 | {"hook_integrity_watchdog_ignore_registers", { 22 | EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 23 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 24 | }}}, 25 | EEex_FlattenTable({ 26 | {[[ 27 | mov eax, dword ptr ds:[rsp+70h] 28 | dec eax 29 | cmp eax, 71h 30 | ja NoConfig 31 | 32 | mov rdx, #L(Data-CInfButtonArray::SetState()-IndirectJumpTable) 33 | movzx eax, byte ptr ds:[rdx+rax] 34 | jmp CallHook 35 | 36 | NoConfig: 37 | mov rax, -1 38 | 39 | CallHook: 40 | #MAKE_SHADOW_SPACE(48) 41 | ]]}, 42 | EEex_GenLuaCall("EEex_Actionbar_Hook_StateUpdating", { 43 | ["args"] = { 44 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax", {rspOffset}, "#ENDL"} end, 45 | function(rspOffset) return {[[ 46 | mov edx, dword ptr ds:[rsp+#LAST_FRAME_TOP(70h)] 47 | mov qword ptr ss:[rsp+#$(1)], rdx ]], {rspOffset}, [[ #ENDL 48 | ]]} end, 49 | }, 50 | }), 51 | {[[ 52 | call_error: 53 | #DESTROY_SHADOW_SPACE 54 | mov rcx, r14 55 | ]]}, 56 | }) 57 | ) 58 | 59 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CInfButtonArray::UpdateButtons()-LastCall"), { 60 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 61 | EEex_FlattenTable({ 62 | {[[ 63 | #MAKE_SHADOW_SPACE(32) 64 | ]]}, 65 | EEex_GenLuaCall("EEex_Actionbar_Hook_ButtonsUpdated"), 66 | {[[ 67 | call_error: 68 | #DESTROY_SHADOW_SPACE 69 | ]]}, 70 | }) 71 | ) 72 | 73 | --[[ 74 | +--------------------------------------------------------------------------------------------------+ 75 | | Make it possible to grant non-thieves full thieving capabilities | 76 | +--------------------------------------------------------------------------------------------------+ 77 | | [Lua] EEex_Actionbar_Hook_HasFullThieving(sprite: CGameSprite) -> boolean | 78 | | return: | 79 | | -> false - The creature is limited to pickpocketing (cannot pick locks / disarm traps) | 80 | | -> true - The creature can take all thieving actions | 81 | +--------------------------------------------------------------------------------------------------+ 82 | --]] 83 | 84 | EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CInfButtonArray::OnLButtonPressed()-HasFullThieving"), 0, 7, 11, { 85 | {"hook_integrity_watchdog_ignore_registers", { 86 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, 87 | EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 88 | }}}, 89 | EEex_FlattenTable({ 90 | {[[ 91 | #MAKE_SHADOW_SPACE(48) 92 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 93 | ]]}, 94 | EEex_GenLuaCall("EEex_Actionbar_Hook_HasFullThieving", { 95 | ["args"] = { 96 | function(rspOffset) return {[[ 97 | mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(48h)] 98 | mov qword ptr ss:[rsp+#$(1)], rax 99 | ]], {rspOffset}}, "CGameSprite" end, 100 | }, 101 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 102 | }), 103 | {[[ 104 | jmp no_error 105 | 106 | call_error: 107 | mov rax, 1 108 | 109 | no_error: 110 | mov dl, 24h 111 | test rax, rax 112 | jnz full_thieving 113 | 114 | mov dl, 28h 115 | 116 | full_thieving: 117 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 118 | #DESTROY_SHADOW_SPACE 119 | ]]}, 120 | }) 121 | ) 122 | 123 | --[[ 124 | +---------------------------------------------------------------------------------------------------------+ 125 | | Allow non-party-members with EEex_Actionbar_Hook_HasFullThieving() == true to pick locks / disarm traps | 126 | +---------------------------------------------------------------------------------------------------------+ 127 | | [Lua] EEex_Actionbar_Hook_IsPartyLeader(sprite: CGameSprite) -> boolean | 128 | | return: | 129 | | false -> The creature is treated as a non-party-member for certain cursor mechanics | 130 | | true -> The creature is treated as a party member for certain cursor mechanics | 131 | +---------------------------------------------------------------------------------------------------------+ 132 | --]] 133 | 134 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIGroup::IsPartyLeader()-Override"), { 135 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 136 | EEex_FlattenTable({ 137 | {[[ 138 | test rax, rax 139 | jnz #L(return) 140 | 141 | #MAKE_SHADOW_SPACE(40) 142 | ]]}, 143 | EEex_GenLuaCall("EEex_Actionbar_Hook_IsPartyLeader", { 144 | ["args"] = { 145 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, 146 | }, 147 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 148 | }), 149 | {[[ 150 | jmp no_error 151 | 152 | call_error: 153 | xor rax, rax 154 | 155 | no_error: 156 | #DESTROY_SHADOW_SPACE 157 | ]]}, 158 | }) 159 | ) 160 | 161 | --[[ 162 | +------------------------------------------------------------------------------------------------------------------+ 163 | | Set a Lua global that flags whether the engine has opened the special abilities menu to find the thieving button | 164 | +------------------------------------------------------------------------------------------------------------------+ 165 | | [Lua Global] EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities: boolean | 166 | +------------------------------------------------------------------------------------------------------------------+ 167 | --]] 168 | 169 | EEex_HookBeforeAndAfterCallWithLabels(EEex_Label("Hook-CScreenWorld::OnKeyDown()-ThievingHotkeyPressSpecialAbilitiesCall"), { 170 | {"hook_integrity_watchdog_ignore_registers_0", { 171 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 172 | EEex_HookIntegrityWatchdogRegister.R11 173 | }}, 174 | {"hook_integrity_watchdog_ignore_registers_1", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 175 | {[[ 176 | #MAKE_SHADOW_SPACE(16) 177 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 178 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 179 | 180 | mov rdx, 1 181 | mov rcx, #L(Hardcoded_InternalLuaState) 182 | call #L(Hardcoded_lua_pushboolean) 183 | mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL 184 | mov rcx, #L(Hardcoded_InternalLuaState) 185 | call #L(Hardcoded_lua_setglobal) 186 | 187 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 188 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 189 | #DESTROY_SHADOW_SPACE 190 | ]]}, 191 | {[[ 192 | mov rdx, 0 193 | mov rcx, #L(Hardcoded_InternalLuaState) 194 | call #L(Hardcoded_lua_pushboolean) 195 | mov rdx, #$(1) ]], {EEex_WriteStringCache("EEex_Actionbar_HookGlobal_IsThievingHotkeyOpeningSpecialAbilities")}, [[ #ENDL 196 | mov rcx, #L(Hardcoded_InternalLuaState) 197 | call #L(Hardcoded_lua_setglobal) 198 | ]]} 199 | ) 200 | 201 | EEex_EnableCodeProtection() 202 | 203 | end)() 204 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Alias.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | local alias = function(func) 5 | if not func then error("Missing alias source") end 6 | return func 7 | end 8 | 9 | --------------- 10 | -- CAIAction -- 11 | --------------- 12 | 13 | CAIAction.Construct1 = alias(CAIAction.Construct_Overload_ActionID_DestPoint_SpecificID_SpecificID2) 14 | CAIAction.ConstructCopy = alias(CAIAction.Construct_Overload_Copy) 15 | CAIAction.operator_equ = alias(CAIAction.AssignmentOperator) 16 | 17 | --------------- 18 | -- CAIIdList -- 19 | --------------- 20 | 21 | CAIIdList.Construct1 = alias(CAIIdList.Construct_Overload_Default) 22 | CAIIdList.FindID = alias(CAIIdList.Find_Overload_ID) 23 | CAIIdList.LoadList2 = alias(CAIIdList.LoadList_Overload_Resref) 24 | 25 | ------------------- 26 | -- CAIObjectType -- 27 | ------------------- 28 | 29 | CAIObjectType.Construct1 = alias(CAIObjectType.Construct_Overload_Manual) 30 | CAIObjectType.ConstructCopy = alias(CAIObjectType.Construct_Overload_Copy) 31 | 32 | --------------- 33 | -- CAIScript -- 34 | --------------- 35 | 36 | CAIScript.Construct1 = alias(CAIScript.Construct_Overload_Manual) 37 | 38 | ---------------- 39 | -- CAITrigger -- 40 | ---------------- 41 | 42 | CAITrigger.ConstructCopy = alias(CAITrigger.Construct_Overload_Copy) 43 | 44 | ----------------- 45 | -- CGameAIBase -- 46 | ----------------- 47 | 48 | CGameAIBase.GetTargetShareType1 = alias(CGameAIBase.GetTargetShareType_Overload_AIType_ObjectType) 49 | CGameAIBase.GetTargetShareType2 = alias(CGameAIBase.GetTargetShareType_Overload_ObjectType) 50 | 51 | --------------- 52 | -- CGameArea -- 53 | --------------- 54 | 55 | CGameArea.GetAllInRange1 = alias(CGameArea.GetAllInRange_Overload_Point) 56 | CGameArea.GetAllInRange2 = alias(CGameArea.GetAllInRange_Overload_VertListPos) 57 | CGameArea.GetNearest2 = alias(CGameArea.GetNearest_Overload_Point) 58 | 59 | ----------- 60 | -- CItem -- 61 | ----------- 62 | 63 | CItem.Construct3 = alias(CItem.Construct_Overload_Manual) 64 | 65 | ------------- 66 | -- CString -- 67 | ------------- 68 | 69 | CString.ConstructFromChars = alias(CString.Construct_Overload_String) 70 | CString.ConstructFromCString = alias(CString.Construct_Overload_CString) 71 | CString.SetFromChars = alias(CString.AssignmentOperator_Overload_String) 72 | CString.SetFromCString = alias(CString.AssignmentOperator_Overload_CString) 73 | 74 | end)() 75 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Assembly_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | if EEex_Architecture == "x86" then 3 | EEex_DoFile("EEex_Assembly_x86_Patch") 4 | elseif EEex_Architecture == "x86-64" then 5 | EEex_DoFile("EEex_Assembly_x86-64_Patch") 6 | else 7 | EEex_Error(string.format("Unhandled EEex_Architecture: \"%s\"", EEex_Architecture)) 8 | end 9 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Assembly_x86-64_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_JITNearAsLabel("EEex_PrintPopLuaString", {[[ 3 | 4 | mov qword ptr ss:[rsp+8], rbx 5 | mov qword ptr ss:[rsp+10h], rbp 6 | 7 | #MAKE_SHADOW_SPACE(16) 8 | 9 | mov rbx, rcx 10 | 11 | #ALIGN 12 | mov r8, 0 ; len 13 | mov rdx, -1 ; index 14 | mov rcx, rbx ; L 15 | call #L(Hardcoded_lua_tolstring) 16 | #ALIGN_END 17 | 18 | ; L(Hardcoded_lua_pushstring) arg 19 | mov rbp, rax 20 | 21 | #ALIGN 22 | mov rdx, ]], EEex_WriteStringCache("print"), [[ ; name 23 | mov rcx, rbx ; L 24 | call #L(Hardcoded_lua_getglobal) 25 | #ALIGN_END 26 | 27 | #ALIGN 28 | mov rdx, rbp ; s 29 | mov rcx, rbx ; L 30 | call #L(Hardcoded_lua_pushstring) 31 | #ALIGN_END 32 | 33 | #ALIGN 34 | mov qword ptr ss:[rsp+8], 0 ; k 35 | mov qword ptr ss:[rsp], 0 ; ctx 36 | mov r9, 0 ; errfunc 37 | mov r8, 0 ; nresults 38 | mov rdx, 1 ; nargs 39 | mov rcx, rbx ; L 40 | call #L(Hardcoded_lua_pcallk) 41 | #ALIGN_END 42 | 43 | ; Clear error string off of stack 44 | #ALIGN 45 | mov rdx, -2 ; index 46 | mov rcx, rbx ; L 47 | call #L(Hardcoded_lua_settop) 48 | #ALIGN_END 49 | 50 | #DESTROY_SHADOW_SPACE 51 | 52 | mov rbp, qword ptr ss:[rsp+10h] 53 | mov rbx, qword ptr ss:[rsp+8] 54 | ret 55 | ]]}) 56 | 57 | EEex_JITNearAsLabel("EEex_CheckCallError", {[[ 58 | 59 | test rax, rax 60 | jnz error 61 | ret 62 | 63 | error: 64 | #MAKE_SHADOW_SPACE 65 | 66 | #ALIGN 67 | call #L(EEex_PrintPopLuaString) 68 | #ALIGN_END 69 | 70 | mov rax, 1 71 | #DESTROY_SHADOW_SPACE 72 | ret 73 | ]]}) 74 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Assembly_x86_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_JITNearAsLabel("EEex_PrintPopLuaString", {[[ 3 | 4 | push 0 ; len 5 | push -1 ; index 6 | push ebx ; L 7 | call #L(Hardcoded_lua_tolstring) 8 | add esp, 0xC 9 | 10 | ; used in #L(Hardcoded_lua_pushstring) below 11 | push eax 12 | 13 | push ]], EEex_WriteStringCache("print"), [[ ; name 14 | push ebx ; L 15 | call #L(Hardcoded_lua_getglobal) 16 | add esp, 8 17 | 18 | push ebx ; L 19 | call #L(Hardcoded_lua_pushstring) 20 | add esp, 8 21 | 22 | push 0 ; k 23 | push 0 ; ctx 24 | push 0 ; errfunc 25 | push 0 ; nresults 26 | push 1 ; nargs 27 | push ebx ; L 28 | call #L(Hardcoded_lua_pcallk) 29 | add esp, 0x18 30 | 31 | ; Clear error string off of stack 32 | push -2 ; index 33 | push ebx ; L 34 | call #L(Hardcoded_lua_settop) 35 | add esp, 8 36 | ret 37 | ]]}) 38 | 39 | EEex_JITNearAsLabel("EEex_CheckCallError", {[[ 40 | 41 | test eax, eax 42 | jnz error 43 | ret 44 | 45 | error: 46 | call #L(EEex_PrintPopLuaString) 47 | mov eax, 1 48 | ret 49 | ]]}) 50 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Debug.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_Debug_DisableExtraCreatureMarshalling = false 3 | EEex_Debug_LogActions = false 4 | 5 | function EEex_Debug_DumpScriptEncoding(resref, bPlayerScript) 6 | local script = EEex_Resource_LoadScript(resref, bPlayerScript) 7 | local dumpAiType = function(name, aiType, indent) 8 | indent = indent or "" 9 | print(string.format("%s%s:", indent, name)) 10 | print(string.format("%s m_name: \"%s\"", indent, aiType.m_name.m_pchData:get())) 11 | print(string.format("%s m_EnemyAlly: %d", indent, aiType.m_EnemyAlly)) 12 | print(string.format("%s m_General: %d", indent, aiType.m_General)) 13 | print(string.format("%s m_Race: %d", indent, aiType.m_Race)) 14 | print(string.format("%s m_Class: %d", indent, aiType.m_Class)) 15 | print(string.format("%s m_Instance: 0x%X", indent, aiType.m_Instance)) 16 | for i = 0, aiType.m_SpecialCase.lastIndex do 17 | print(string.format("%s m_SpecialCase[%d]: %d", indent, i, aiType.m_SpecialCase:get(i))) 18 | end 19 | print(string.format("%s m_Specifics: %d", indent, aiType.m_Specifics)) 20 | print(string.format("%s m_Gender: %d", indent, aiType.m_Gender)) 21 | print(string.format("%s m_Alignment: %d", indent, aiType.m_Alignment)) 22 | end 23 | EEex_Utility_IterateCPtrList(script.m_caList, function(conditionResponse) 24 | print("------------------------------------------") 25 | print("triggers:") 26 | print("------------------------------------------") 27 | EEex_Utility_IterateCPtrList(conditionResponse.m_condition.m_triggerList, function(trigger) 28 | print(string.format(" m_triggerID: 0x%X", trigger.m_triggerID)) 29 | print(string.format(" m_specificID: %d", trigger.m_specificID)) 30 | dumpAiType("m_triggerCause", trigger.m_triggerCause, " ") 31 | print(string.format(" m_flags: 0x%X", trigger.m_flags)) 32 | print(string.format(" m_specific2: %d", trigger.m_specific2)) 33 | print(string.format(" m_specific3: %d", trigger.m_specific3)) 34 | print(string.format(" m_string1: \"%s\"", trigger.m_string1.m_pchData:get())) 35 | print(string.format(" m_string2: \"%s\"", trigger.m_string2.m_pchData:get())) 36 | print("------------------------------------------") 37 | end) 38 | print("actions:") 39 | print("------------------------------------------") 40 | EEex_Utility_IterateCPtrList(conditionResponse.m_responseSet.m_responseList, function(response) 41 | EEex_Utility_IterateCPtrList(response.m_actionList, function(action) 42 | print(string.format(" m_actionID: %d", action.m_actionID)) 43 | dumpAiType("m_actorID", action.m_actorID, " ") 44 | dumpAiType("m_acteeID", action.m_acteeID, " ") 45 | dumpAiType("m_acteeID2", action.m_acteeID2, " ") 46 | print(string.format(" m_specificID: %d", action.m_specificID)) 47 | print(string.format(" m_specificID2: %d", action.m_specificID2)) 48 | print(string.format(" m_specificID3: %d", action.m_specificID3)) 49 | print(string.format(" m_string1: \"%s\"", action.m_string1.m_pchData:get())) 50 | print(string.format(" m_string2: \"%s\"", action.m_string2.m_pchData:get())) 51 | print(string.format(" m_dest.x: %d", action.m_dest.x)) 52 | print(string.format(" m_dest.y: %d", action.m_dest.y)) 53 | print(string.format(" m_internalFlags: 0x%X", action.m_internalFlags)) 54 | print(string.format(" m_source: \"%s\"", action.m_source.m_pchData:get())) 55 | print("------------------------------------------") 56 | end) 57 | end) 58 | end) 59 | end 60 | 61 | (function() 62 | 63 | if EEex_Debug_LogActions then 64 | 65 | local actionNames = {} 66 | 67 | EEex_GameState_AddInitializedListener(function() 68 | local actions = EEex_Resource_LoadIDS("ACTION") 69 | for i = 0, actions:getCount() - 1 do 70 | actionNames[i] = actions:getStart(i) 71 | end 72 | actions:free() 73 | end) 74 | 75 | EEex_Debug_LogAction = function(aiBase, bFromAIBase) 76 | 77 | local objectType = aiBase.m_objectType 78 | 79 | -- Don't double log certain actions 80 | if bFromAIBase == 1 and objectType == CGameObjectType.SPRITE then 81 | return 82 | end 83 | 84 | local objectName = "Unknown" 85 | 86 | if objectType == CGameObjectType.SPRITE then 87 | objectName = aiBase.m_sName.m_pchData:get().." ("..aiBase.m_resref:get()..")" 88 | elseif objectType == CGameObjectType.AREA_AI then 89 | objectName = "Area script ("..aiBase.m_pArea.m_resref:get()..")" 90 | elseif objectType == CGameObjectType.GAME_AI then 91 | objectName = "Game script (unknown)" 92 | elseif objectType == CGameObjectType.DOOR then 93 | objectName = "Door (unknown)" 94 | elseif objectType == CGameObjectType.CONTAINER then 95 | objectName = "Container (unknown)" 96 | elseif objectType == CGameObjectType.TRIGGER then 97 | objectName = "Trigger (unknown)" 98 | end 99 | 100 | local actionID = aiBase.m_curAction.m_actionID 101 | if actionID ~= 0 then 102 | local curScriptNum = aiBase.m_curScriptNum 103 | print(string.format("%s executing action %d (%s), script level %d (%s), block %d, response %d", 104 | objectName, actionID, actionNames[actionID] or "unknown", curScriptNum, 105 | aiBase:getScriptLevelResRef(aiBase, curScriptNum >= 3 and curScriptNum + 1 or curScriptNum), 106 | aiBase.m_curResponseSetNum, aiBase.m_curResponseNum)) 107 | end 108 | end 109 | 110 | EEex_DisableCodeProtection() 111 | 112 | --[[ 113 | +---------------------------------------------------------------------------------------------------------+ 114 | | Debug-log details about a CGameAIBase's action before it is executed | 115 | +---------------------------------------------------------------------------------------------------------+ 116 | | [Lua] EEex_Debug_LogAction(executingObject: CGameAIBase|EEex_GameObject_CastUT, bFromAIBase: boolean) | 117 | +---------------------------------------------------------------------------------------------------------+ 118 | --]] 119 | 120 | EEex_HookBeforeConditionalJumpWithLabels(EEex_Label("Hook-CGameAIBase::ExecuteAction()-DefaultJmp"), 0, { 121 | {"hook_integrity_watchdog_ignore_registers", { 122 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 123 | EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 124 | }}}, 125 | EEex_FlattenTable({ 126 | {[[ 127 | #MAKE_SHADOW_SPACE(56) 128 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r8 129 | ]]}, 130 | EEex_GenLuaCall("EEex_Debug_LogAction", { 131 | ["args"] = { 132 | function(rspOffset) return {[[ 133 | mov qword ptr ss:[rsp+#$(1)], rbx 134 | ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, 135 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 1", {rspOffset}, "#ENDL"} end, 136 | }, 137 | }), 138 | {[[ 139 | call_error: 140 | mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 141 | #DESTROY_SHADOW_SPACE 142 | cmp r8d, 0x1D5 143 | ]]}, 144 | }) 145 | ) 146 | 147 | EEex_HookBeforeConditionalJumpWithLabels(EEex_Label("Hook-CGameSprite::ExecuteAction()-DefaultJmp"), 0, { 148 | {"hook_integrity_watchdog_ignore_registers", { 149 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R11 150 | }}}, 151 | EEex_FlattenTable({ 152 | {[[ 153 | #MAKE_SHADOW_SPACE(80) 154 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 155 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 156 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], r9 157 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r10 158 | ]]}, 159 | EEex_GenLuaCall("EEex_Debug_LogAction", { 160 | ["args"] = { 161 | function(rspOffset) return {[[ 162 | mov qword ptr ss:[rsp+#$(1)], rdi 163 | ]], {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, 164 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], 0", {rspOffset}, "#ENDL"} end, 165 | }, 166 | }), 167 | {[[ 168 | call_error: 169 | mov r10, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)] 170 | mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 171 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 172 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 173 | #DESTROY_SHADOW_SPACE 174 | cmp ecx, 0x1D7 175 | ]]}, 176 | }) 177 | ) 178 | 179 | EEex_EnableCodeProtection() 180 | end 181 | 182 | end)() 183 | -------------------------------------------------------------------------------- /EEex/copy/EEex_EarlyMain.lua: -------------------------------------------------------------------------------- 1 | 2 | -- This is the early startup file for EEex. InfinityLoader calls this file before the game is 3 | -- resumed when [General].LuaPatchMode = REPLACE_INTERNAL_WITH_EXTERNAL. This file replaces 4 | -- in-engine Lua functions before they are used. 5 | 6 | (function() 7 | -- Contains most of the code editing functions. This file is the core of EEex. 8 | EEex_DoFile("EEex_Assembly") 9 | EEex_DoFile("EEex_Assembly_Patch") 10 | -- Replaces the statically compiled, in-exe Lua version with LuaLibrary. 11 | EEex_DoFile("EEex_ReplaceLua") 12 | end)() 13 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Fix.lua: -------------------------------------------------------------------------------- 1 | 2 | -- The engine doesn't update quick lists when a special ability is added, 3 | -- such as from op171 or act279. 4 | function EEex_Fix_Hook_OnAddSpecialAbility(sprite, spell) 5 | EEex_RunWithStackManager({ 6 | { ["name"] = "abilityId", ["struct"] = "CAbilityId" } }, 7 | function(manager) 8 | local abilityId = manager:getUD("abilityId") 9 | abilityId.m_itemType = 1 -- spell, not an item 10 | abilityId.m_res:copy(spell.cResRef) 11 | -- CAbilityId* ab, short changeAmount, int remove, int removeSpellIfZero 12 | sprite:CheckQuickLists(abilityId, 1, 0, 0); 13 | end) 14 | end 15 | 16 | ---------------------------------------------------------------------------------- 17 | -- Fix Spell() and SpellPoint() not being disruptable if the creature is facing -- 18 | -- SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), NEE(11), SEE(13), or SSE(15) -- 19 | ---------------------------------------------------------------------------------- 20 | 21 | function EEex_Fix_Hook_ShouldForceMainSpellActionCode(sprite, point) 22 | 23 | local forcing = EEex_GetUDAux(sprite)["EEex_Fix_HasSpellOrSpellPointStartedCasting"] == 1 24 | 25 | -- If I force the main spell action code, the direction-setting code 26 | -- isn't run. Manually do that here so sprites still turn to face 27 | -- their target after they have started the casting glow. 28 | if forcing then 29 | local message = EEex_NewUD("CMessageSetDirection") 30 | message:Construct(point, sprite.m_id, sprite.m_id) 31 | EngineGlobals.g_pBaldurChitin.m_cMessageHandler:AddMessage(message, 0) 32 | end 33 | 34 | return forcing 35 | end 36 | 37 | function EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow(sprite) 38 | EEex_GetUDAux(sprite)["EEex_Fix_HasSpellOrSpellPointStartedCasting"] = 1 39 | end 40 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Fix_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +----------------------------------------------------------------------------------------------------+ 8 | | BUG: v2.6.6.0 - op206/318/324 incorrectly indexes the source object's item list if the incoming | 9 | | effect's source spell has a name strref of -1 without first checking if the source was a sprite | 10 | +----------------------------------------------------------------------------------------------------+ 11 | | [EEex.dll] EEex::Fix_Hook_SpellImmunityShouldSkipItemIndexing(pGameObject: CGameObject*) -> bool | 12 | | return: | 13 | | -> false - Don't alter engine behavior | 14 | | -> true - Force the engine to skip its item list check | 15 | +----------------------------------------------------------------------------------------------------+ 16 | --]] 17 | 18 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameEffect::CheckAdd()-FixSpellImmunityShouldSkipItemIndexing"), 4, { 19 | {"hook_integrity_watchdog_ignore_registers", { 20 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 21 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 22 | EEex_HookIntegrityWatchdogRegister.R11 23 | }}}, 24 | {[[ 25 | mov rcx, qword ptr ds:[rsp+#LAST_FRAME_TOP(50h)] ; pGameObject 26 | call #L(EEex::Fix_Hook_SpellImmunityShouldSkipItemIndexing) 27 | test al, al 28 | jnz #L(jmp_success) 29 | ]]} 30 | ) 31 | 32 | --[[ 33 | +------------------------------------------------------------------------------------------------------+ 34 | | Fix quick spell slots not updating when a special ability is added (for example, by op171 or act279) | 35 | +------------------------------------------------------------------------------------------------------+ 36 | | [Lua] EEex_Fix_Hook_OnAddSpecialAbility(sprite: CGameSprite, spell: CSpell) | 37 | +------------------------------------------------------------------------------------------------------+ 38 | --]] 39 | 40 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::AddSpecialAbility()-LastCall"), { 41 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 42 | EEex_FlattenTable({ 43 | {[[ 44 | #MAKE_SHADOW_SPACE(48) 45 | ]]}, 46 | EEex_GenLuaCall("EEex_Fix_Hook_OnAddSpecialAbility", { 47 | ["args"] = { 48 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rsi #ENDL", {rspOffset}}, "CGameSprite" end, 49 | function(rspOffset) return {[[ 50 | lea rax, qword ptr ds:[rsp+#LAST_FRAME_TOP(48h)] 51 | mov qword ptr ss:[rsp+#$(1)], rax 52 | ]], {rspOffset}}, "CSpell" end, 53 | }, 54 | }), 55 | {[[ 56 | call_error: 57 | #DESTROY_SHADOW_SPACE 58 | ]]}, 59 | }) 60 | ) 61 | 62 | --[[ 63 | +----------------------------------------------------------------------------------------------------------------------+ 64 | | Fix Spell() and SpellPoint() not being disruptable if the creature is facing SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), | 65 | | NEE(11), SEE(13), or SSE(15) | 66 | +----------------------------------------------------------------------------------------------------------------------+ 67 | | [Lua] EEex_Fix_Hook_ShouldForceMainSpellActionCode(sprite: CGameSprite, point: CPoint) -> boolean | 68 | | return: | 69 | | -> false - Don't alter engine behavior | 70 | | -> true - Force the engine to run the main spell action code regardless of the sprite's orientation | 71 | | (which includes spell disruption handling) | 72 | +----------------------------------------------------------------------------------------------------------------------+ 73 | | [Lua] EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow(sprite: CGameSprite) | 74 | +----------------------------------------------------------------------------------------------------------------------+ 75 | --]] 76 | 77 | ---------------------------------------------------------- 78 | -- [Lua] EEex_Fix_Hook_ShouldForceMainSpellActionCode() -- 79 | ---------------------------------------------------------- 80 | 81 | local callShouldForceMainSpellActionCode = EEex_JITNear(EEex_FlattenTable({ 82 | {[[ 83 | #STACK_MOD(8) ; This was called, the ret ptr broke alignment 84 | #MAKE_SHADOW_SPACE(48) 85 | ]]}, 86 | EEex_GenLuaCall("EEex_Fix_Hook_ShouldForceMainSpellActionCode", { 87 | ["args"] = { 88 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, 89 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdx #ENDL", {rspOffset}}, "CPoint" end, 90 | }, 91 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 92 | }), 93 | {[[ 94 | jmp no_error 95 | 96 | call_error: 97 | xor rax, rax 98 | 99 | no_error: 100 | #DESTROY_SHADOW_SPACE 101 | ret 102 | ]]}, 103 | })) 104 | 105 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::Spell()-CheckDirectionJmp"), 3, { 106 | {"hook_integrity_watchdog_ignore_registers", { 107 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 108 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 109 | EEex_HookIntegrityWatchdogRegister.R11 110 | }}}, 111 | {[[ 112 | mov rdx, r14 ; point 113 | mov rcx, rbx ; sprite 114 | call #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL 115 | test rax, rax 116 | jnz #L(jmp_success) 117 | ]]} 118 | ) 119 | 120 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CGameSprite::SpellPoint()-CheckDirectionJmp"), 5, { 121 | {"hook_integrity_watchdog_ignore_registers", { 122 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 123 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 124 | EEex_HookIntegrityWatchdogRegister.R11 125 | }}}, 126 | {[[ 127 | lea rdx, qword ptr ss:[rsp+0x60] ; point 128 | mov rcx, rbx ; sprite 129 | call #$(1) ]], {callShouldForceMainSpellActionCode}, [[ #ENDL 130 | test rax, rax 131 | jnz #L(jmp_success) 132 | ]]} 133 | ) 134 | 135 | ----------------------------------------------------------------- 136 | -- [Lua] EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow() -- 137 | ----------------------------------------------------------------- 138 | 139 | local callOnSpellOrSpellPointStartedCastingGlow = EEex_JITNear(EEex_FlattenTable({ 140 | {[[ 141 | #STACK_MOD(8) ; This was called, the ret ptr broke alignment 142 | #MAKE_SHADOW_SPACE(40) 143 | ]]}, 144 | EEex_GenLuaCall("EEex_Fix_Hook_OnSpellOrSpellPointStartedCastingGlow", { 145 | ["args"] = { 146 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rcx #ENDL", {rspOffset}}, "CGameSprite" end, 147 | }, 148 | }), 149 | {[[ 150 | call_error: 151 | #DESTROY_SHADOW_SPACE 152 | ret 153 | ]]}, 154 | })) 155 | 156 | for _, address in ipairs({ 157 | EEex_Label("Hook-CGameSprite::Spell()-ApplyCastingEffect()"), 158 | EEex_Label("Hook-CGameSprite::SpellPoint()-ApplyCastingEffect()") 159 | }) do 160 | EEex_HookAfterCallWithLabels(address, { 161 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 162 | {[[ 163 | mov rcx, rbx ; sprite 164 | call #$(1) ]], {callOnSpellOrSpellPointStartedCastingGlow}, [[ #ENDL 165 | ]]} 166 | ) 167 | end 168 | 169 | --[[ 170 | +----------------------------------------------------------------------------------------------------------------+ 171 | | [JIT] Opcode #182 should consider -1 (instead of 0) the fail return value from CGameSprite::FindItemPersonal() | 172 | +----------------------------------------------------------------------------------------------------------------+ 173 | --]] 174 | 175 | EEex_HookBeforeConditionalJump(EEex_Label("Hook-CGameEffectApplyEffectEquipItem::ApplyEffect()-CheckRetVal"), 0, {[[ 176 | cmp ax, -1 177 | ]]}) 178 | 179 | --[[ 180 | +--------------------------------------------------------------------------------------------------------------------------------+ 181 | | Fix a couple of regressions in v2.6 regarding op206/op232/op256 | 182 | +--------------------------------------------------------------------------------------------------------------------------------+ 183 | | 1) op206's param1 only works for values 0xF00074 and 0xF00080 | 184 | | 2) op232 and op256's "you cannot cast multiple instances" message fails to display | 185 | +--------------------------------------------------------------------------------------------------------------------------------+ 186 | | [EEex.dll] EEex::Fix_Hook_ShouldTransformSpellImmunityStrref(pEffect: CGameEffect*, pImmunitySpell: CImmunitySpell*) -> bool | 187 | | return: | 188 | | -> false - Don't transform immunity strref | 189 | | -> true - Transform immunity strref | 190 | +--------------------------------------------------------------------------------------------------------------------------------+ 191 | --]] 192 | 193 | EEex_HookAfterRestoreWithLabels(EEex_Label("Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrref"), 0, 5, 5, { 194 | {"hook_integrity_watchdog_ignore_registers", { 195 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 196 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 197 | EEex_HookIntegrityWatchdogRegister.R11 198 | }}, 199 | {"manual_return", true}}, 200 | {[[ 201 | mov rdx, r12 ; pImmunitySpell 202 | mov rcx, rdi ; pEffect 203 | call #L(EEex::Fix_Hook_ShouldTransformSpellImmunityStrref) 204 | test al, al 205 | 206 | #MANUAL_HOOK_EXIT(0) 207 | jnz #L(Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrrefBody) 208 | jmp #L(Hook-CGameEffect::CheckAdd()-FixShouldTransformSpellImmunityStrrefElse) 209 | ]]} 210 | ) 211 | 212 | EEex_EnableCodeProtection() 213 | 214 | end)() 215 | -------------------------------------------------------------------------------- /EEex/copy/EEex_GameObject_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +---------------------------------------------------------------------+ 8 | | Clean up any EEex data linked to a game object before it is deleted | 9 | +---------------------------------------------------------------------+ 10 | | [Lua] EEex_GameObject_Hook_OnDeleting(objectID: number) | 11 | +---------------------------------------------------------------------+ 12 | --]] 13 | 14 | EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameObjectArray::Delete()-DeleteJmp"), 5, { 15 | {"hook_integrity_watchdog_ignore_registers", { 16 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 17 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 18 | }}}, 19 | EEex_FlattenTable({ 20 | {[[ 21 | #MAKE_SHADOW_SPACE(56) 22 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 23 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 24 | ]]}, 25 | EEex_GenLuaCall("EEex_GameObject_Hook_OnDeleting", { 26 | ["args"] = { 27 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbp", {rspOffset}, "#ENDL"} end, 28 | }, 29 | }), 30 | {[[ 31 | call_error: 32 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 33 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 34 | #DESTROY_SHADOW_SPACE 35 | ]]}, 36 | }) 37 | ) 38 | 39 | EEex_EnableCodeProtection() 40 | 41 | end)() 42 | -------------------------------------------------------------------------------- /EEex/copy/EEex_GameState.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- General -- 4 | ------------- 5 | 6 | -- @bubb_doc { EEex_GameState_GetGlobalInt } 7 | -- 8 | -- @summary: Returns the integer value of the ``variableName`` Global scoped to ``GLOBAL``. 9 | -- If no variable named ``variableName`` exists, returns ``0``. 10 | -- 11 | -- @param { variableName / type=string }: The name of the variable to fetch. 12 | -- 13 | -- @return { type=number }: See summary. 14 | 15 | function EEex_GameState_GetGlobalInt(variableName) 16 | return EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_variables:getInt(variableName) 17 | end 18 | 19 | -- @bubb_doc { EEex_GameState_GetGlobalString } 20 | -- 21 | -- @summary: Returns the string value of the ``variableName`` Global scoped to ``GLOBAL``. 22 | -- If no variable named ``variableName`` exists, returns ``""``. 23 | -- 24 | -- @note: Global string values can only be accessed through EEex functions. 25 | -- 26 | -- @param { variableName / type=string }: The name of the variable to fetch. 27 | -- 28 | -- @return { type=string }: See summary. 29 | 30 | function EEex_GameState_GetGlobalString(variableName) 31 | return EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_variables:getString(variableName) 32 | end 33 | 34 | -- @bubb_doc { EEex_GameState_SetGlobalInt } 35 | -- 36 | -- @summary: Sets the integer value of the ``variableName`` Global scoped to ``GLOBAL`` to ``value``. 37 | -- 38 | -- @param { variableName / type=string }: The name of the variable to set. 39 | -- 40 | -- @param { value / type=number }: The value to set the variable to. 41 | 42 | function EEex_GameState_SetGlobalInt(variableName, value) 43 | EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_variables:setInt(variableName, value) 44 | end 45 | 46 | -- @bubb_doc { EEex_GameState_SetGlobalString } 47 | -- 48 | -- @summary: Sets the string value of the ``variableName`` Global scoped to ``GLOBAL`` to ``value``. 49 | -- 50 | -- @note: Global string values can only be accessed through EEex functions. 51 | -- 52 | -- @warning: Global string values can be a maximum of 32 characters. Attempting to set a value 53 | -- that is longer than 32 characters will result in the value being truncated. 54 | -- 55 | -- @param { variableName / type=string }: The name of the variable to set. 56 | -- 57 | -- @param { value / type=string }: The value to set the variable to. 58 | 59 | function EEex_GameState_SetGlobalString(variableName, value) 60 | EngineGlobals.g_pBaldurChitin.m_pObjectGame.m_variables:setString(variableName, value) 61 | end 62 | 63 | --------------- 64 | -- Listeners -- 65 | --------------- 66 | 67 | EEex_GameState_Private_AlreadyInitialized = false 68 | EEex_GameState_Private_InitializedListeners = {} 69 | 70 | -- @bubb_doc { EEex_GameState_AddInitializedListener } 71 | -- 72 | -- @summary: Registers a listener function that is called immediately after the engine's Lua environment has been initialized. 73 | -- This only occurs once during the engine's early start up process. If the engine has already been initialized, 74 | -- ``listener`` is called immediately. 75 | -- 76 | -- @param { listener / type=function }: The listener to register. 77 | 78 | function EEex_GameState_AddInitializedListener(listener) 79 | if EEex_GameState_Private_AlreadyInitialized then 80 | listener() 81 | else 82 | table.insert(EEex_GameState_Private_InitializedListeners, listener) 83 | end 84 | end 85 | 86 | EEex_GameState_DestroyedListeners = {} 87 | 88 | -- @bubb_doc { EEex_GameState_AddDestroyedListener } 89 | -- 90 | -- @summary: Registers a listener function that is called immediately after the engine has cleaned up a game session. 91 | -- Examples of when this occurs include the user quitting to the main menu, loading a save, etc. 92 | -- 93 | -- @param { listener / type=function }: The listener to register. 94 | 95 | function EEex_GameState_AddDestroyedListener(listener) 96 | table.insert(EEex_GameState_DestroyedListeners, listener) 97 | end 98 | 99 | ----------- 100 | -- Hooks -- 101 | ----------- 102 | 103 | function EEex_GameState_LuaHook_OnInitialized() 104 | for _, listener in ipairs(EEex_GameState_Private_InitializedListeners) do 105 | listener() 106 | end 107 | EEex_GameState_Private_AlreadyInitialized = true 108 | EEex_GameState_Private_InitializedListeners = {} 109 | end 110 | 111 | function EEex_GameState_Hook_OnDestroyed() 112 | for _, listener in ipairs(EEex_GameState_DestroyedListeners) do 113 | listener() 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /EEex/copy/EEex_GameState_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +---------------------------------------------------------------------------+ 8 | | Call a hook after the engine has completed most of its initialization | 9 | +---------------------------------------------------------------------------+ 10 | | Used to implement listeners that need the game state to be initialized, | 11 | | yet also require early execution during engine startup | 12 | +---------------------------------------------------------------------------+ 13 | | [EEex.dll] EEex::GameState_Hook_OnInitialized() | 14 | +---------------------------------------------------------------------------+ 15 | | [Lua] EEex_GameState_LuaHook_OnInitialized() | 16 | +---------------------------------------------------------------------------+ 17 | --]] 18 | 19 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-SDL_main()-CLUAConsole::LuaInit()"), { 20 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 21 | {[[ 22 | call #L(EEex::GameState_Hook_OnInitialized) 23 | ]]} 24 | ) 25 | 26 | --[[ 27 | +---------------------------------------------------------------------------+ 28 | | Call a hook after the engine has "destroyed" a game instance | 29 | +---------------------------------------------------------------------------+ 30 | | Used to implement listeners that need to react to a game session ending | 31 | +---------------------------------------------------------------------------+ 32 | | [Lua] EEex_GameState_Hook_OnDestroyed() | 33 | +---------------------------------------------------------------------------+ 34 | --]] 35 | 36 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CInfGame::DestroyGame()-LastCall"), { 37 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 38 | EEex_FlattenTable({ 39 | {[[ 40 | #MAKE_SHADOW_SPACE(32) 41 | ]]}, 42 | EEex_GenLuaCall("EEex_GameState_Hook_OnDestroyed"), 43 | {[[ 44 | call_error: 45 | #DESTROY_SHADOW_SPACE 46 | ]]}, 47 | }) 48 | ) 49 | 50 | --[[ 51 | +------------------------------------------------------------------------+ 52 | | Immediately read special values from GLOBALS (like EEEX_NEXTUUID) | 53 | +------------------------------------------------------------------------+ 54 | | [EEex.dll] EEex::GameState_Hook_OnAfterGlobalVariablesUnmarshalled() | 55 | +------------------------------------------------------------------------+ 56 | --]] 57 | 58 | EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CInfGame::Unmarshal()-AfterGlobalVariablesUnmarshalled"), { 59 | {"hook_integrity_watchdog_ignore_registers", { 60 | EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 61 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 62 | }}}, 63 | {[[ 64 | #MAKE_SHADOW_SPACE(8) 65 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 66 | 67 | call #L(EEex::GameState_Hook_OnAfterGlobalVariablesUnmarshalled) 68 | 69 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 70 | #DESTROY_SHADOW_SPACE 71 | ]]} 72 | ) 73 | 74 | 75 | EEex_EnableCodeProtection() 76 | 77 | end)() 78 | -------------------------------------------------------------------------------- /EEex/copy/EEex_HookIntegrityWatchdog.lua: -------------------------------------------------------------------------------- 1 | 2 | -- This flag instructs EEex to write integrity watchdog checks before and after all of its hooks. 3 | -- If any CPU registers / stack values change unexpectedly during a hook's execution, these changes will be logged to the console. 4 | EEex_HookIntegrityWatchdog_Load = false 5 | 6 | (function() 7 | 8 | if not EEex_HookIntegrityWatchdog_Load then 9 | return 10 | end 11 | 12 | local hookIntegrityWatchdogEnter = EEex_JITNear({[[ 13 | 14 | #STACK_MOD(8) 15 | #MAKE_SHADOW_SPACE(184) 16 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax 17 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx 18 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx 19 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r8 20 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)], r9 21 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)], r10 22 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)], r11 23 | 24 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)], rax 25 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-176)], rbx 26 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-168)], rcx 27 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-160)], rdx 28 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-152)], rbp 29 | mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(48)] ; 8 + 32 + 8 30 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-144)], rax ; rsp 31 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-136)], rsi 32 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-128)], rdi 33 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-120)], r8 34 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-112)], r9 35 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-104)], r10 36 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-96)], r11 37 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-88)], r12 38 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-80)], r13 39 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-72)], r14 40 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-64)], r15 41 | 42 | lea rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)] 43 | mov rcx, qword ptr ss:[rsp+#LAST_FRAME_TOP(40)] ; 8 + 32 + 0 44 | call #L(EEex::HookIntegrityWatchdogEnter) 45 | 46 | mov r11, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)] 47 | mov r10, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)] 48 | mov r9, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)] 49 | mov r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)] 50 | mov rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 51 | mov rcx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 52 | mov rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 53 | #DESTROY_SHADOW_SPACE 54 | ret 55 | ]]}) 56 | 57 | local hookIntegrityWatchdogExit = EEex_JITNear({[[ 58 | 59 | #STACK_MOD(8) 60 | #MAKE_SHADOW_SPACE(184) 61 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax 62 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rcx 63 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rdx 64 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)], r8 65 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)], r9 66 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)], r10 67 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)], r11 68 | 69 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)], rax 70 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-176)], rbx 71 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-168)], rcx 72 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-160)], rdx 73 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-152)], rbp 74 | mov rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(56)] ; 8 + 32 + 16 75 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-144)], rax ; rsp 76 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-136)], rsi 77 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-128)], rdi 78 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-120)], r8 79 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-112)], r9 80 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-104)], r10 81 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-96)], r11 82 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-88)], r12 83 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-80)], r13 84 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-72)], r14 85 | mov qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-64)], r15 86 | 87 | lea r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-184)] 88 | mov rdx, qword ptr ds:[rsp+#LAST_FRAME_TOP(48)] ; 8 + 32 + 8 89 | mov rcx, qword ptr ss:[rsp+#LAST_FRAME_TOP(40)] ; 8 + 32 + 0 90 | call #L(EEex::HookIntegrityWatchdogExit) 91 | 92 | mov r11, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-56)] 93 | mov r10, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-48)] 94 | mov r9, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-40)] 95 | mov r8, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-32)] 96 | mov rdx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 97 | mov rcx, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 98 | mov rax, qword ptr ds:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 99 | #DESTROY_SHADOW_SPACE 100 | ret 101 | ]]}) 102 | 103 | EEex_HookIntegrityWatchdog_HookEnter = {[[ 104 | 105 | #MAKE_SHADOW_SPACE(32) 106 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax ; Save RAX 107 | pushfq #STACK_MOD(8) ; Save status flags 108 | pop rax #STACK_MOD(-8) 109 | and rax, 0x8D5 110 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rax 111 | 112 | lea rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; Save previous frame's rsp as second stack arg 113 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rax 114 | 115 | mov rax, #L(hook_address) ; Save hook address as first stack arg 116 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], rax 117 | 118 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX 119 | call ]], hookIntegrityWatchdogEnter, [[ #ENDL 120 | 121 | pushfq #STACK_MOD(8) ; Restore status flags 122 | and qword ptr ss:[rsp], 0xFFFFFFFFFFFFF72A 123 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 124 | or qword ptr ss:[rsp], rax 125 | popfq #STACK_MOD(-8) 126 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX (again) 127 | #DESTROY_SHADOW_SPACE 128 | ]]} 129 | 130 | local cachedHookExit = {} 131 | EEex_HookIntegrityWatchdog_HookExit = function(instance) 132 | local cached = cachedHookExit[instance] 133 | if cached then return cached end 134 | local t = {[[ 135 | 136 | #MAKE_SHADOW_SPACE(40) 137 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax ; Save RAX 138 | pushfq #STACK_MOD(8) ; Save status flags 139 | pop rax #STACK_MOD(-8) 140 | and rax, 0x8D5 141 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rax 142 | 143 | lea rax, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; Save previous frame's rsp as third stack arg 144 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)], rax 145 | 146 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-32)], ]], instance, [[ ; Save instance as second stack arg 147 | 148 | mov rax, #L(hook_address) ; Save hook address as first stack arg 149 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-40)], rax 150 | 151 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX 152 | call ]], hookIntegrityWatchdogExit, [[ #ENDL 153 | 154 | pushfq #STACK_MOD(8) ; Restore status flags 155 | and qword ptr ss:[rsp], 0xFFFFFFFFFFFFF72A 156 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 157 | or qword ptr ss:[rsp], rax 158 | popfq #STACK_MOD(-8) 159 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; Restore RAX (again) 160 | #DESTROY_SHADOW_SPACE 161 | ]]} 162 | cachedHookExit[instance] = t 163 | return t 164 | end 165 | 166 | EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance = function(address, instance, defaultRegisters) 167 | local ignoreFlags = EEex_Flags(defaultRegisters) 168 | local ignoredRegisters = EEex_TryLabel("hook_integrity_watchdog_ignore_registers_"..instance) 169 | if ignoredRegisters then 170 | ignoreFlags = EEex_BOr(ignoreFlags, EEex_Flags(ignoredRegisters)) 171 | end 172 | EEex.HookIntegrityWatchdogIgnoreRegisters(address, instance, ignoreFlags) 173 | end 174 | 175 | EEex_HookIntegrityWatchdog_IgnoreRegisters = function(address, defaultRegisters) 176 | local ignoreFlags = EEex_Flags(defaultRegisters) 177 | local ignoredRegisters = EEex_TryLabel("hook_integrity_watchdog_ignore_registers") 178 | if ignoredRegisters then 179 | ignoreFlags = EEex_BOr(ignoreFlags, EEex_Flags(ignoredRegisters)) 180 | end 181 | EEex.HookIntegrityWatchdogIgnoreRegisters(address, 0, ignoreFlags) 182 | end 183 | 184 | local ignoreStackSize = function(address, instance, offset, size) 185 | EEex.HookIntegrityWatchdogIgnoreStackRange(address, instance, offset, offset + size - 1) 186 | end 187 | 188 | EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance = function(address, instance) 189 | -- Stack mod indicates that the hook isn't operating in the usual stack frame. None of 190 | -- the stack should be ignored if stack_mod is defined, as the shadow space at the top 191 | -- of the stack actually belongs to the called function that's being hooked. 192 | if not EEex_TryLabel("stack_mod") then 193 | ignoreStackSize(address, instance, 0, 32) 194 | end 195 | end 196 | 197 | EEex_HookIntegrityWatchdog_DefaultIgnoreStack = function(address) 198 | EEex_HookIntegrityWatchdog_DefaultIgnoreStackForInstance(address, 0) 199 | end 200 | 201 | EEex_HookIntegrityWatchdog_IgnoreStackSizesForInstance = function(address, instance, stackSizes) 202 | for _, stackSize in ipairs(stackSizes) do 203 | ignoreStackSize(address, instance, stackSize[1], stackSize[2]) 204 | end 205 | end 206 | 207 | EEex_HookIntegrityWatchdog_IgnoreStackSizes = function(address, stackSizes) 208 | EEex_HookIntegrityWatchdog_IgnoreStackSizesForInstance(address, 0, stackSizes) 209 | end 210 | 211 | end)() 212 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Key_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +--------------------------------------------------------------------+ 8 | | React to input by intercepting SDL events | 9 | +--------------------------------------------------------------------+ 10 | | [Lua] EEex_Key_Hook_AfterEventsPoll(event: SDL_Event) -> boolean | 11 | | return: | 12 | | -> false - Don't alter engine behavior | 13 | | -> true - Suppress event | 14 | +--------------------------------------------------------------------+ 15 | --]] 16 | 17 | local afterEventsPollHook = EEex_JITNear(EEex_FlattenTable({ 18 | {[[ 19 | #STACK_MOD(8) ; This was called, the ret ptr broke alignment 20 | 21 | test eax, eax 22 | jnz call_hook 23 | ret 24 | 25 | call_hook: 26 | #MAKE_SHADOW_SPACE(40) 27 | ]]}, 28 | EEex_GenLuaCall("EEex_Key_Hook_AfterEventsPoll", { 29 | ["args"] = { 30 | function(rspOffset) return {[[ 31 | lea rcx, qword ptr ds:[rbp-51h] 32 | mov qword ptr ss:[rsp+#$(1)], rcx 33 | ]], {rspOffset}}, "SDL_Event" end, 34 | }, 35 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 36 | }), 37 | {[[ 38 | xor rax, 1 39 | #DESTROY_SHADOW_SPACE(KEEP_ENTRY) 40 | ret 41 | 42 | call_error: 43 | #RESUME_SHADOW_ENTRY 44 | mov rax, 1 45 | #DESTROY_SHADOW_SPACE 46 | ret 47 | ]]}, 48 | })) 49 | 50 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-1"), { 51 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 52 | {[[ 53 | call ]], afterEventsPollHook, [[ #ENDL 54 | ]]} 55 | ) 56 | 57 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CChitin::ProcessEvents()-SDL_PollEvent()-2"), { 58 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 59 | {[[ 60 | call ]], afterEventsPollHook, [[ #ENDL 61 | ]]} 62 | ) 63 | 64 | EEex_EnableCodeProtection() 65 | 66 | end)() 67 | -------------------------------------------------------------------------------- /EEex/copy/EEex_LuaBindings_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | local override = function(patternName) 7 | EEex_JITAt(EEex_Label(patternName), {[[ 8 | jmp #L(override_#$(1)) ]], {patternName} 9 | }) 10 | end 11 | 12 | override("class_index_event") 13 | override("class_newindex_event") 14 | override("module_index_event") 15 | override("module_newindex_event") 16 | override("tolua_beginmodule") 17 | override("tolua_cclass") 18 | override("tolua_module") 19 | override("tolua_open") 20 | override("tolua_usertype") 21 | 22 | EEex_EnableCodeProtection() 23 | 24 | end)() 25 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Main.lua: -------------------------------------------------------------------------------- 1 | 2 | -- This is the main startup file for EEex. InfinityLoader redirects control flow after the CALL instruction 3 | -- located at [Hardcoded_InternalPatchLocation] in order to (potentially) initialize Lua, initialize 4 | -- hardcoded EEex state, and call this file. 5 | 6 | ------------- 7 | -- Options -- 8 | ------------- 9 | 10 | EEex_Main_MinimalStutterStartup = false 11 | 12 | ---------------------------------- 13 | -- Startup Config (Do not edit) -- 14 | ---------------------------------- 15 | 16 | EEex_Main_Private_NormalStartupFiles = { 17 | "EEex_Action", 18 | "EEex_Action_Patch", 19 | "EEex_Actionbar", 20 | "EEex_Actionbar_Patch", 21 | "EEex_AIBase", 22 | "EEex_AIBase_Patch", 23 | "EEex_Area", 24 | "EEex_Fix", 25 | "EEex_Fix_Patch", 26 | "EEex_GameObject", 27 | "EEex_GameObject_Patch", 28 | "EEex_GameState", 29 | "EEex_GameState_Patch", 30 | "EEex_Key", 31 | "EEex_Key_Patch", 32 | "EEex_Menu", 33 | "EEex_Menu_Patch", 34 | "EEex_Mix_Patch", 35 | "EEex_Object", 36 | "EEex_Object_Patch", 37 | "EEex_Opcode", 38 | "EEex_Opcode_Patch", 39 | "EEex_Projectile", 40 | "EEex_Projectile_Patch", 41 | "EEex_Resource", 42 | "EEex_Script", 43 | "EEex_Script_Patch", 44 | "EEex_Sprite", 45 | "EEex_Sprite_Patch", 46 | "EEex_Stats", 47 | "EEex_Stats_Patch", 48 | "EEex_Test", 49 | "EEex_Trigger", 50 | "EEex_Trigger_Patch", 51 | "EEex_Utility", 52 | "EEex_Variable", 53 | "EEex_Marshal", 54 | "EEex_Debug", 55 | } 56 | 57 | EEex_Main_Private_MinimalStutterStartupFiles = { 58 | "EEex_GameState", 59 | "EEex_GameState_Patch", 60 | "EEex_Menu", 61 | "EEex_Menu_Patch", 62 | "EEex_Resource", 63 | "EEex_Utility", 64 | } 65 | 66 | ---------- 67 | -- Main -- 68 | ---------- 69 | 70 | (function() 71 | 72 | -- Contains most of the code editing functions. This file is the core of EEex. 73 | EEex_DoFile("EEex_Assembly") 74 | EEex_DoFile("EEex_Assembly_Patch") 75 | 76 | -- Contains Lua bindings that map engine structures to Lua 77 | EEex_OpenLuaBindings("LuaBindings-v2.6.6.0", function() 78 | -- Patches in-engine tolua functions with EEex versions. 79 | -- These are required for proper bindings operation. 80 | EEex_DoFile("EEex_LuaBindings_Patch") 81 | end) 82 | 83 | -- Contains EEex's C++ functionality 84 | EEex_OpenLuaBindings("EEex") 85 | EEex_DoFile("EEex_HookIntegrityWatchdog") 86 | 87 | -- Defines aliases for EEex functions to preserve API compatibility if internal names change 88 | EEex_DoFile("EEex_Alias") 89 | 90 | -- Defines information about usertypes for the EEex_MemoryManager helper 91 | EEex_DoFile("EEex_MemoryManagerDefinitions") 92 | 93 | -- Run EEex's other files (which each pertain to a specific category) 94 | for _, fileName in ipairs(not EEex_Main_MinimalStutterStartup 95 | and EEex_Main_Private_NormalStartupFiles 96 | or EEex_Main_Private_MinimalStutterStartupFiles) 97 | do 98 | EEex_DoFile(fileName) 99 | end 100 | 101 | if not EEex_Main_MinimalStutterStartup then 102 | 103 | -- This file may run before the game is initialized. 104 | -- The following listener runs files that need to 105 | -- wait for the game to be somewhat initialized. 106 | EEex_GameState_AddInitializedListener(function() 107 | EEex_DoFile("EEex_UserDataGlobals") 108 | EEex_DoFile("EEex_StutterDetector") 109 | end) 110 | 111 | -- Run EEex_Modules.lua, which determines the enabled EEex modules 112 | EEex_DoFile("EEex_Modules") 113 | for moduleName, enabled in pairs(EEex_Modules) do 114 | if enabled then 115 | -- Load the enabled modules 116 | EEex_DoFile(moduleName) 117 | end 118 | end 119 | end 120 | 121 | -- Stops a call to SDL_LogOutput() higher in this file 122 | -- preventing the console from attaching later on 123 | EEex_Write32(EEex_Label("Data-EngineConsoleAttachedPtr"), 0) 124 | 125 | EEex_Active = true 126 | 127 | end)() 128 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Marshal.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_Sprite_AddMarshalHandlers("EEex", 3 | function(sprite) 4 | local toMarshal = {} 5 | if not EEex.IsMarshallingCopy() then 6 | toMarshal["SummonerUUID"] = EEex_GetUDAux(sprite)["EEex_SummonerUUID"] 7 | end 8 | return toMarshal 9 | end, 10 | function(sprite, read) 11 | local summonerUUID = read["SummonerUUID"] 12 | if summonerUUID then 13 | EEex_GetUDAux(sprite)["EEex_SummonerUUID"] = summonerUUID 14 | end 15 | end 16 | ) 17 | 18 | function EEex_Marshal_Private_OnSummonerLoaded(sprite, loadedSprite) 19 | sprite.m_lSummonedBy:Set(loadedSprite:virtual_GetAIType()) 20 | end 21 | 22 | EEex_Sprite_AddLoadedListener(function(sprite) 23 | local summonerUUID = EEex_GetUDAux(sprite)["EEex_SummonerUUID"] 24 | if summonerUUID then 25 | sprite:loadedWithUUIDCallback(summonerUUID, EEex_Marshal_Private_OnSummonerLoaded) 26 | end 27 | end) 28 | 29 | EEex_AIBase_AddScriptingObjectUpdatedListener(function(aiBase, scriptingObject) 30 | 31 | if not aiBase:isSprite(true) then 32 | return 33 | end 34 | 35 | if scriptingObject == EEex_ScriptingObject.SUMMONED_BY then 36 | local summoner = EEex_GameObject_Get(aiBase.m_lSummonedBy.m_Instance) 37 | if EEex_GameObject_IsSprite(summoner, true) then 38 | EEex_GetUDAux(aiBase)["EEex_SummonerUUID"] = summoner:getUUID() 39 | end 40 | end 41 | end) 42 | -------------------------------------------------------------------------------- /EEex/copy/EEex_MemoryManagerDefinitions.lua: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------- 3 | -- Memory Manager Definitions -- 4 | -------------------------------- 5 | 6 | EEex_MemoryManagerStructDefinitions["C2DArray"] = { 7 | ["constructors"] = { 8 | ["#default"] = C2DArray.Construct, 9 | }, 10 | ["destructor"] = C2DArray.Destruct, 11 | } 12 | 13 | EEex_MemoryManagerStructDefinitions["CAbilityId"] = { 14 | ["constructors"] = { 15 | ["#default"] = CAbilityId.Construct, 16 | }, 17 | } 18 | 19 | EEex_MemoryManagerStructDefinitions["CAIAction"] = { 20 | ["constructors"] = { 21 | ["copy"] = CAIAction.ConstructCopy, 22 | }, 23 | ["destructor"] = CAIAction.Destruct, 24 | } 25 | 26 | EEex_MemoryManagerStructDefinitions["CAIObjectType"] = { 27 | ["constructors"] = { 28 | ["#default"] = function(objectType) 29 | objectType:Construct1(0, 0, 0, 0, 0, 0, 0, -1) 30 | end, 31 | ["copy"] = CAIObjectType.ConstructCopy, 32 | }, 33 | ["destructor"] = CAIObjectType.Destruct, 34 | } 35 | 36 | EEex_MemoryManagerStructDefinitions["CAIScriptFile"] = { 37 | ["constructors"] = { 38 | ["#default"] = CAIScriptFile.Construct, 39 | }, 40 | ["destructor"] = CAIScriptFile.Destruct, 41 | } 42 | 43 | EEex_MemoryManagerStructDefinitions["CButtonData"] = { 44 | ["constructors"] = { 45 | ["#default"] = CButtonData.Construct, 46 | }, 47 | } 48 | 49 | EEex_MemoryManagerStructDefinitions["CPoint"] = { 50 | ["constructors"] = { 51 | ["fromXY"] = function(point, x, y) 52 | point.x = x 53 | point.y = y 54 | end, 55 | }, 56 | } 57 | 58 | EEex_MemoryManagerStructDefinitions["CResRef"] = { 59 | ["constructors"] = { 60 | ["#default"] = CResRef.set, 61 | }, 62 | } 63 | 64 | EEex_MemoryManagerStructDefinitions["CSpell"] = { 65 | ["constructors"] = { 66 | ["#default"] = CSpell.Construct, 67 | }, 68 | } 69 | 70 | EEex_MemoryManagerStructDefinitions["CString"] = { 71 | ["constructors"] = { 72 | ["#default"] = CString.ConstructFromChars, 73 | }, 74 | ["destructor"] = CString.Destruct, 75 | } 76 | 77 | EEex_MemoryManagerStructDefinitions["CTypedPtrList"] = { 78 | ["constructors"] = { 79 | ["#default"] = function(self) 80 | self:Construct(10) 81 | end 82 | }, 83 | ["destructor"] = _G["CTypedPtrList"].Destruct, 84 | } 85 | 86 | EEex_MemoryManagerStructDefinitions["CVariable"] = { 87 | ["constructors"] = { 88 | ["#default"] = CVariable.Construct, 89 | }, 90 | } 91 | 92 | EEex_MemoryManagerStructDefinitions["string"] = { 93 | ["constructors"] = { 94 | ["#default"] = function(address, luaString) 95 | EEex_WriteString(address, luaString) 96 | end, 97 | }, 98 | ["size"] = function(luaString) 99 | return #luaString + 1 100 | end, 101 | } 102 | 103 | EEex_MemoryManagerStructDefinitions["uninitialized"] = { 104 | ["size"] = function(size) 105 | return size 106 | end, 107 | } 108 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Mix_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------------------------------------------------------------------------------------------------------------- 3 | -- This file contains patches that hook the same executable location, but call out to different EEex_*.lua files -- 4 | ------------------------------------------------------------------------------------------------------------------- 5 | 6 | (function() 7 | 8 | EEex_DisableCodeProtection() 9 | 10 | ------------------------------------------------------- 11 | -- [Lua] EEex_Sprite_Hook_CheckBlockWeaponHit() -- 12 | -- [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp248() -- 13 | ------------------------------------------------------- 14 | 15 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Swing()-CImmunitiesWeapon::OnList()-Melee"), { 16 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 17 | EEex_FlattenTable({ 18 | {[[ 19 | #MAKE_SHADOW_SPACE(72) 20 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax 21 | ]]}, 22 | EEex_GenLuaCall("EEex_Sprite_Hook_CheckBlockWeaponHit", { 23 | ["labelSuffix"] = "_1", 24 | ["args"] = { 25 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, -- attackingSprite 26 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, -- targetSprite 27 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r12 #ENDL", {rspOffset}}, "CItem" end, -- weapon 28 | -- weaponAbility 29 | function(rspOffset) return {[[ 30 | mov rax, qword ptr ds:[rsp+#LAST_FRAME_TOP(68h)] 31 | mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL 32 | ]]}, "Item_ability_st" end, 33 | }, 34 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 35 | }), 36 | {[[ 37 | jmp no_error_1 38 | 39 | call_error_1: 40 | xor rax, rax 41 | 42 | no_error_1: 43 | test rax, rax 44 | jz do_not_block_base_weapon_damage_and_onhit_effects 45 | 46 | #DESTROY_SHADOW_SPACE(KEEP_ENTRY) 47 | jmp #L(return) 48 | 49 | do_not_block_base_weapon_damage_and_onhit_effects: 50 | #RESUME_SHADOW_ENTRY 51 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 52 | ]]}, 53 | EEex_GenLuaCall("EEex_Opcode_Hook_OnAfterSwingCheckedOp248", { 54 | ["labelSuffix"] = "_2", 55 | ["args"] = { 56 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, 57 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, 58 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax #ENDL", {rspOffset}}, "boolean" end, 59 | }, 60 | }), 61 | {[[ 62 | call_error_2: 63 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 64 | #DESTROY_SHADOW_SPACE 65 | ]]}, 66 | }) 67 | ) 68 | 69 | ------------------------------------------------------- 70 | -- [Lua] EEex_Sprite_Hook_CheckBlockWeaponHit() -- 71 | -- [Lua] EEex_Opcode_Hook_OnAfterSwingCheckedOp249() -- 72 | ------------------------------------------------------- 73 | 74 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Swing()-CImmunitiesWeapon::OnList()-Ranged"), { 75 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 76 | EEex_FlattenTable({ 77 | {[[ 78 | #MAKE_SHADOW_SPACE(72) 79 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rax 80 | ]]}, 81 | EEex_GenLuaCall("EEex_Sprite_Hook_CheckBlockWeaponHit", { 82 | ["labelSuffix"] = "_1", 83 | ["args"] = { 84 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, -- attackingSprite 85 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, -- targetSprite 86 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rdi #ENDL", {rspOffset}}, "CItem" end, -- weapon 87 | -- weaponAbility 88 | function(rspOffset) return {[[ 89 | mov rax, qword ptr ds:[rsp+#LAST_FRAME_TOP(68h)] 90 | mov qword ptr ss:[rsp+#$(1)], rax ]], {rspOffset}, [[ #ENDL 91 | ]]}, "Item_ability_st" end, 92 | }, 93 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 94 | }), 95 | {[[ 96 | jmp no_error_1 97 | 98 | call_error_1: 99 | xor rax, rax 100 | 101 | no_error_1: 102 | test rax, rax 103 | jz do_not_block_base_weapon_damage_and_onhit_effects 104 | 105 | #DESTROY_SHADOW_SPACE(KEEP_ENTRY) 106 | jmp #L(return) 107 | 108 | do_not_block_base_weapon_damage_and_onhit_effects: 109 | #RESUME_SHADOW_ENTRY 110 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 111 | ]]}, 112 | EEex_GenLuaCall("EEex_Opcode_Hook_OnAfterSwingCheckedOp249", { 113 | ["labelSuffix"] = "_2", 114 | ["args"] = { 115 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rbx #ENDL", {rspOffset}}, "CGameSprite" end, 116 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}}, "CGameSprite" end, 117 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], rax #ENDL", {rspOffset}}, "boolean" end, 118 | }, 119 | }), 120 | {[[ 121 | call_error_2: 122 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 123 | #DESTROY_SHADOW_SPACE 124 | 125 | test rax, rax 126 | jz #L(return) 127 | 128 | #MANUAL_HOOK_EXIT(0) 129 | ; The consequence of not running the else block is that this boilerplate is not executed 130 | mov rsi, qword ptr ss:[rsp+0x70] 131 | lea r13, qword ptr ds:[r15+0xC] 132 | jmp #L(Hook-CGameSprite::Swing()-NoCImmunitiesWeaponElseContinue) 133 | ]]}, 134 | }) 135 | ) 136 | 137 | EEex_EnableCodeProtection() 138 | 139 | end)() 140 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Modules.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_Modules = { 3 | ["B3EffMen"] = false, 4 | ["B3EmptyContainer"] = false, 5 | ["B3Hotkey"] = false, 6 | ["B3Invis"] = false, 7 | ["B3Scale"] = false, 8 | ["B3Timer"] = false, 9 | ["B3TimeStep"] = false, 10 | } 11 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Object.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_LuaObject = nil 3 | 4 | ------------- 5 | -- General -- 6 | ------------- 7 | 8 | -- Compiles the given object string and returns the resulting CAIObjectType. 9 | -- If the string contains errors, the resulting type acts as "Myself". 10 | -- Call :free() on the returned CAIObjectType when it is no longer needed. 11 | function EEex_Object_ParseString(string) 12 | local toReturn = EEex_NewUD("CAIObjectType") 13 | toReturn.free = function(objectType) 14 | objectType:Destruct() 15 | EEex_FreeUD(objectType) 16 | end 17 | EEex_RunWithStackManager({ 18 | { ["name"] = "scriptFile", ["struct"] = "CAIScriptFile" }, 19 | { ["name"] = "cstring", ["struct"] = "CString", ["constructor"] = { ["args"] = { string } } } }, 20 | function(manager) 21 | manager:getUD("scriptFile"):ParseObjectType(toReturn, manager:getUD("cstring")) 22 | end) 23 | return toReturn 24 | end 25 | 26 | -- Evaluates the given CAIObjectType in the context of aiBase and returns the found object (or nil). 27 | function EEex_Object_EvalAsAIBase(objectType, aiBase, checkBackList) 28 | return EEex_RunWithStackManager({ 29 | { ["name"] = "objectTypeCopy", ["struct"] = "CAIObjectType", 30 | ["constructor"] = { ["variant"] = "copy", ["args"] = { objectType } } 31 | } }, 32 | function(manager) 33 | local objectTypeCopy = manager:getUD("objectTypeCopy") 34 | objectTypeCopy:Decode(aiBase) 35 | return EEex_GameObject_CastUT(objectTypeCopy:GetShare(aiBase, checkBackList and 1 or 0)) 36 | end) 37 | end 38 | CAIObjectType.evalAsAIBase = EEex_Object_EvalAsAIBase 39 | 40 | -- Evaluates the given object string in the context of aiBase and returns the found object (or nil). 41 | -- Prefer using compiled object types when efficiency is required. 42 | function EEex_Object_EvalStringAsAIBase(string, aiBase, checkBackList) 43 | return EEex_RunWithStackManager({ 44 | { ["name"] = "scriptFile", ["struct"] = "CAIScriptFile" }, 45 | { ["name"] = "cstring", ["struct"] = "CString", ["constructor"] = { ["args"] = { string } } }, 46 | { ["name"] = "objectType", ["struct"] = "CAIObjectType" } }, 47 | function(manager) 48 | local objectType = manager:getUD("objectType") 49 | manager:getUD("scriptFile"):ParseObjectType(objectType, manager:getUD("cstring")) 50 | objectType:Decode(aiBase) 51 | return EEex_GameObject_CastUT(objectType:GetShare(aiBase, checkBackList and 1 or 0)) 52 | end) 53 | end 54 | 55 | ----------- 56 | -- Hooks -- 57 | ----------- 58 | 59 | function EEex_Object_Hook_ForceIgnoreActorScriptName(aiType) 60 | local firstEvaluatedObject = aiType.m_SpecialCase:get(0) 61 | return firstEvaluatedObject == 117 or firstEvaluatedObject == 118 62 | end 63 | 64 | EEex_Object_Hook_OnEvaluatingUnknown_ReturnType = { 65 | ["HANDLED_CONTINUE"] = 0, 66 | ["HANDLED_DONE"] = 1, 67 | ["UNHANDLED"] = 2, 68 | } 69 | 70 | function EEex_Object_Hook_OnEvaluatingUnknown(decodingAIType, caller, nSpecialCaseI, curAIType) 71 | 72 | local nObjectIDS = decodingAIType.m_SpecialCase:get(nSpecialCaseI) 73 | 74 | local fail = function() 75 | decodingAIType:Set(CAIObjectType.NOONE) 76 | return EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE 77 | end 78 | 79 | local setObject = function(object) 80 | if object then 81 | curAIType:Set(object:virtual_GetAIType()) 82 | return EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE 83 | else 84 | return fail() 85 | end 86 | end 87 | 88 | local setInstance = function(objectID) 89 | return setObject(objectID and EEex_GameObject_Get(objectID) or nil) 90 | end 91 | 92 | if nObjectIDS == 115 then -- EEex_LuaObject 93 | 94 | return setObject(EEex_LuaObject) 95 | 96 | elseif nObjectIDS == 116 then -- EEex_MatchObject 97 | 98 | return setInstance(EEex_GetUDAux(caller)["EEex_MatchObject"]) 99 | 100 | elseif nObjectIDS == 117 then -- EEex_Target 101 | 102 | local targetTable = EEex_Utility_GetOrCreateTable(EEex_GetUDAux(caller), "EEex_Target") 103 | return setInstance(targetTable[curAIType.m_name.m_pchData:get()]) 104 | 105 | elseif nObjectIDS == 118 then -- EEex_LuaDecode 106 | 107 | EEex_LuaDecode_Object = caller 108 | 109 | local success, retVal = EEex_Utility_Eval("EEex_LuaDecode", curAIType.m_name.m_pchData:get()) 110 | return success and setObject(retVal) or fail() 111 | end 112 | 113 | return EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED 114 | end 115 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Object_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +----------------------------------------------------------------------------------------------+ 8 | | Prevent certain OBJECT.IDS entries from interpreting their string parameter as a script name | 9 | +----------------------------------------------------------------------------------------------+ 10 | | 117 EEex_Target() | 11 | | 118 EEex_LuaDecode() | 12 | +----------------------------------------------------------------------------------------------+ 13 | | [Lua] EEex_Object_Hook_ForceIgnoreActorScriptName(aiType: CAIObjectType) -> boolean | 14 | | return: | 15 | | -> false - Don't alter engine behavior | 16 | | -> true - Even though the script object was defined with a string parameter, | 17 | | this string should not be treated as a script name | 18 | +----------------------------------------------------------------------------------------------+ 19 | --]] 20 | 21 | EEex_HookConditionalJumpOnFailWithLabels(EEex_Label("Hook-CAIObjectType::Decode()-TargetNameOverride"), 0, { 22 | {"hook_integrity_watchdog_ignore_registers", { 23 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 24 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 25 | EEex_HookIntegrityWatchdogRegister.R11 26 | }}}, 27 | EEex_FlattenTable({ 28 | {[[ 29 | #MAKE_SHADOW_SPACE(40) 30 | ]]}, 31 | EEex_GenLuaCall("EEex_Object_Hook_ForceIgnoreActorScriptName", { 32 | ["args"] = { 33 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, 34 | }, 35 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 36 | }), 37 | {[[ 38 | jmp no_error 39 | 40 | call_error: 41 | xor rax, rax 42 | 43 | no_error: 44 | test rax, rax 45 | 46 | #DESTROY_SHADOW_SPACE 47 | jnz #L(jmp_success) 48 | ]]}, 49 | }) 50 | ) 51 | 52 | --[[ 53 | +------------------------------------------------------------------------------------------------------------------------------+ 54 | | Implement new OBJECT.IDS entries | 55 | +------------------------------------------------------------------------------------------------------------------------------+ 56 | | 117 EEex_Target() | 57 | | 118 EEex_LuaDecode() | 58 | +------------------------------------------------------------------------------------------------------------------------------+ 59 | | [Lua] EEex_Object_Hook_OnEvaluatingUnknown(decodingAIType: CAIObjectType, caller CGameAIBase|EEex_GameObject_CastUT, | 60 | | nSpecialCaseI: number, curAIType: CAIObjectType) -> number | 61 | | return: | 62 | | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE - Continue evaluating outer objects | 63 | | | 64 | | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE - Halt OBJECT.IDS processing and use curAIType | 65 | | | 66 | | -> EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED - Engine falls back to "Myself" and continues | 67 | | evaluating outer objects | 68 | +------------------------------------------------------------------------------------------------------------------------------+ 69 | --]] 70 | 71 | EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CAIObjectType::Decode()-DefaultJmp"), 0, { 72 | {"hook_integrity_watchdog_ignore_registers", { 73 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, 74 | EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 75 | }}}, 76 | EEex_FlattenTable({ 77 | {[[ 78 | #MAKE_SHADOW_SPACE(72) 79 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rdx 80 | ]]}, 81 | EEex_GenLuaCall("EEex_Object_Hook_OnEvaluatingUnknown", { 82 | ["args"] = { 83 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r13 #ENDL", {rspOffset}}, "CAIObjectType" end, 84 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r12 #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, 85 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r15 #ENDL", {rspOffset}} end, 86 | function(rspOffset) return {[[ 87 | lea rax, qword ptr ss:[rbp-11h] 88 | mov qword ptr ss:[rsp+#$(1)], rax 89 | ]], {rspOffset}}, "CAIObjectType" end, 90 | }, 91 | ["returnType"] = EEex_LuaCallReturnType.Number, 92 | }), 93 | {[[ 94 | jmp no_error 95 | 96 | call_error: 97 | mov rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.UNHANDLED}, [[ #ENDL 98 | 99 | no_error: 100 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 101 | #DESTROY_SHADOW_SPACE 102 | 103 | cmp rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_CONTINUE}, [[ #ENDL 104 | je normal_return 105 | 106 | cmp rax, #$(1) ]], {EEex_Object_Hook_OnEvaluatingUnknown_ReturnType.HANDLED_DONE}, [[ #ENDL 107 | jne #L(jmp_success) 108 | 109 | #MANUAL_HOOK_EXIT(0) 110 | jmp #L(Hook-CAIObjectType::Decode()-ReturnBranch) 111 | 112 | normal_return: 113 | #MANUAL_HOOK_EXIT(0) 114 | jmp #L(Hook-CAIObjectType::Decode()-NormalBranch) 115 | ]]}, 116 | }) 117 | ) 118 | 119 | 120 | EEex_EnableCodeProtection() 121 | 122 | end)() 123 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Opcode.lua: -------------------------------------------------------------------------------- 1 | 2 | --------------- 3 | -- Listeners -- 4 | --------------- 5 | 6 | EEex_Opcode_ListsResolvedListeners = {} 7 | 8 | function EEex_Opcode_AddListsResolvedListener(func) 9 | -- [EEex.dll] 10 | EEex.Opcode_LuaHook_AfterListsResolved_Enabled = true 11 | table.insert(EEex_Opcode_ListsResolvedListeners, func) 12 | end 13 | 14 | ----------------------- 15 | -- Private Functions -- 16 | ----------------------- 17 | 18 | function EEex_Opcode_Private_ApplyExtraMeleeEffects(sprite, targetSprite) 19 | 20 | EEex_Utility_IterateCPtrList(sprite:getActiveStats().m_cExtraMeleeEffects, function(effect) 21 | 22 | -- [EEex.dll] 23 | if not EEex.ShouldEffectBypassOp120(effect) then 24 | return -- continue 25 | end 26 | 27 | if EEex_BAnd(effect.m_special, 4) ~= 0 and sprite.m_equipment.m_selectedWeapon ~= 10 then 28 | return -- continue 29 | end 30 | 31 | pExtraEffect = effect:virtual_Copy() 32 | 33 | pExtraEffect.m_sourceId = sprite.m_id 34 | pExtraEffect.m_source.x = sprite.m_pos.x 35 | pExtraEffect.m_source.y = sprite.m_pos.y 36 | 37 | pExtraEffect.m_sourceTarget = targetSprite.m_id 38 | pExtraEffect.m_target.x = targetSprite.m_pos.x 39 | pExtraEffect.m_target.y = targetSprite.m_pos.y 40 | 41 | local addEffectMessage = EEex_NewUD("CMessageAddEffect") 42 | -- CGameEffect* effect, bool noSave, short commType, int caller, int target 43 | addEffectMessage:Construct(pExtraEffect, false, 1, sprite.m_id, targetSprite.m_id) 44 | EngineGlobals.g_pBaldurChitin.m_cMessageHandler:AddMessage(addEffectMessage, false) 45 | end) 46 | end 47 | 48 | function EEex_Opcode_Private_ApplyExtraRangedEffects(sprite, targetSprite) 49 | 50 | EEex_Utility_IterateCPtrList(sprite:getActiveStats().m_cExtraRangedEffects, function(effect) 51 | 52 | -- [EEex.dll] 53 | if not EEex.ShouldEffectBypassOp120(effect) then 54 | return -- continue 55 | end 56 | 57 | pExtraEffect = effect:virtual_Copy() 58 | 59 | pExtraEffect.m_sourceId = sprite.m_id 60 | pExtraEffect.m_source.x = sprite.m_pos.x 61 | pExtraEffect.m_source.y = sprite.m_pos.y 62 | 63 | pExtraEffect.m_sourceTarget = targetSprite.m_id 64 | pExtraEffect.m_target.x = targetSprite.m_pos.x 65 | pExtraEffect.m_target.y = targetSprite.m_pos.y 66 | 67 | EEex_Utility_Switch(pExtraEffect.m_targetType, { 68 | [1] = function() 69 | local addEffectMessage = EEex_NewUD("CMessageAddEffect") 70 | -- CGameEffect* effect, bool noSave, short commType, int caller, int target 71 | addEffectMessage:Construct(pExtraEffect, false, 1, sprite.m_id, sprite.m_id) 72 | EngineGlobals.g_pBaldurChitin.m_cMessageHandler:AddMessage(addEffectMessage, false) 73 | end, 74 | [3] = function() 75 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 76 | sprite:ApplyEffectToParty(pExtraEffect) 77 | pExtraEffect:virtual_Destruct(true) 78 | end, 79 | [4] = function() 80 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 81 | -- CGameEffect* effect, int ignoreParty, int useSpecifics, byte specifics, CGameObject* pIgnore 82 | sprite.m_pArea:ApplyEffect(pExtraEffect, false, false, 0, nil) 83 | pExtraEffect:virtual_Destruct(true) 84 | end, 85 | [5] = function() 86 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 87 | -- CGameEffect* effect, int ignoreParty, int useSpecifics, byte specifics, CGameObject* pIgnore 88 | sprite.m_pArea:ApplyEffect(pExtraEffect, true, false, 0, nil) 89 | pExtraEffect:virtual_Destruct(true) 90 | end, 91 | [6] = function() 92 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 93 | if sprite:getPortraitIndex() == -1 then 94 | -- CGameEffect* effect, int ignoreParty, int useSpecifics, byte specifics, CGameObject* pIgnore 95 | sprite.m_pArea:ApplyEffect(pExtraEffect, false, true, sprite.m_typeAI.m_Specifics, nil) 96 | else 97 | sprite:ApplyEffectToParty(pExtraEffect) 98 | end 99 | pExtraEffect:virtual_Destruct(true) 100 | end, 101 | [7] = function() 102 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 103 | pExtraEffect:virtual_Destruct(true) 104 | end, 105 | [8] = function() 106 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 4) 107 | -- CGameEffect* effect, int ignoreParty, int useSpecifics, byte specifics, CGameObject* pIgnore 108 | sprite.m_pArea:ApplyEffect(pExtraEffect, false, false, 0, sprite) 109 | pExtraEffect:virtual_Destruct(true) 110 | end, 111 | [9] = function() 112 | pExtraEffect.m_flags = EEex_BOr(pExtraEffect, 8) 113 | sprite.m_curProjectile:AddEffect(pExtraEffect) 114 | end }, 115 | function() 116 | sprite.m_curProjectile:AddEffect(pExtraEffect) 117 | end 118 | ) 119 | end) 120 | end 121 | 122 | ----------- 123 | -- Hooks -- 124 | ----------- 125 | 126 | function EEex_Opcode_LuaHook_AfterListsResolved(sprite) 127 | for _, func in ipairs(EEex_Opcode_ListsResolvedListeners) do 128 | func(sprite) 129 | end 130 | end 131 | 132 | --[[ 133 | +--------------------------------------------------------------------------------+ 134 | | Opcode #214 | 135 | +--------------------------------------------------------------------------------+ 136 | | param2 == 3 -> Call Lua function in resource field to get CButtonData iterator | 137 | +--------------------------------------------------------------------------------+ 138 | | Hook return: | 139 | | false -> Effect not handled | 140 | | true -> Effect handled (skip normal code) | 141 | +--------------------------------------------------------------------------------+ 142 | --]] 143 | 144 | function EEex_Opcode_Hook_OnOp214ApplyEffect(effect, sprite) 145 | 146 | local param2 = effect.m_dWFlags 147 | if param2 ~= 3 then 148 | return false 149 | end 150 | 151 | effect.m_done = true 152 | 153 | local func = _G[effect.m_res:get()] 154 | if func == nil then 155 | return false 156 | end 157 | 158 | sprite:openOp214Interface(func(effect, sprite)) 159 | return true 160 | end 161 | 162 | ------------------------------------------------------------ 163 | -- Opcode #248 (Special BIT0 allows .EFF to bypass op120) -- 164 | ------------------------------------------------------------ 165 | 166 | function EEex_Opcode_Hook_OnAfterSwingCheckedOp248(sprite, targetSprite, bBlocked) 167 | if bBlocked then 168 | EEex_Opcode_Private_ApplyExtraMeleeEffects(sprite, targetSprite) 169 | end 170 | end 171 | 172 | ------------------------------------------------------------ 173 | -- Opcode #249 (Special BIT0 allows .EFF to bypass op120) -- 174 | ------------------------------------------------------------ 175 | 176 | -- This function replaces the `else` block (hit blocked) of the op120 check for ranged attacks. 177 | -- [Bubb]: Why did I replace the entire else block instead of hooking after its CProjectile::ClearEffects() call? 178 | function EEex_Opcode_Hook_OnAfterSwingCheckedOp249(sprite, targetSprite, bBlocked) 179 | 180 | if bBlocked then 181 | 182 | sprite.m_curProjectile:ClearEffects() 183 | EEex_Opcode_Private_ApplyExtraRangedEffects(sprite, targetSprite) 184 | 185 | if sprite.m_curAction.m_actionID ~= 98 then 186 | EEex_RunWithStackManager({ 187 | { ["name"] = "stringIn", ["struct"] = "CString", ["constructor"] = { ["args"] = { "" } } } }, 188 | function(manager) 189 | sprite:FeedBack(37, 0, 0, 0, -1, 0, manager:getUD("stringIn")) 190 | end 191 | ) 192 | end 193 | end 194 | end 195 | 196 | -------------------------------------------------------------- 197 | -- Opcode #333 (param3 BIT0 - only check saving throw once) -- 198 | -------------------------------------------------------------- 199 | 200 | function EEex_Opcode_Hook_OnOp333CopiedSelf(effect) 201 | if EEex_IsBitSet(effect.m_effectAmount2, 0) then 202 | effect.m_savingThrow = 0 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Projectile_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +----------------------------------------------------------------------------------------------------------------------------------+ 8 | | Implement Opcode #408 (ProjectileMutator) `typeMutator` functionality | 9 | +----------------------------------------------------------------------------------------------------------------------------------+ 10 | | [EEex.dll] EEex::Projectile_Hook_OnBeforeDecode(nProjectileType: ushort, pDecoder: CGameAIBase*, pRetPtr: uintptr_t) -> ushort | 11 | | return: | 12 | | -> -1 - Don't alter engine behavior | 13 | | -> !-1 - Override projectile type with the return value | 14 | +----------------------------------------------------------------------------------------------------------------------------------+ 15 | --]] 16 | 17 | EEex_HookBeforeRestoreWithLabels(EEex_Label("CProjectile::DecodeProjectile"), 0, 5, 5, { 18 | {"stack_mod", 8}, 19 | {"hook_integrity_watchdog_ignore_registers", { 20 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 21 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 22 | }}, 23 | {"manual_hook_integrity_exit", true}}, 24 | {[[ 25 | #MAKE_SHADOW_SPACE(16) 26 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 27 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 28 | 29 | mov r8, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; pRetPtr 30 | ; rdx is already pDecoder 31 | ; rcx is already nProjectileType 32 | call #L(EEex::Projectile_Hook_OnBeforeDecode) 33 | 34 | cmp ax, -1 35 | je no_override 36 | 37 | mov cx, ax ; Override projectile type 38 | 39 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 40 | #DESTROY_SHADOW_SPACE(KEEP_ENTRY) 41 | #MANUAL_HOOK_EXIT(1) 42 | jmp #L(return) 43 | 44 | no_override: 45 | #RESUME_SHADOW_ENTRY 46 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 47 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 48 | #DESTROY_SHADOW_SPACE 49 | #MANUAL_HOOK_EXIT(0) 50 | ]]} 51 | ) 52 | -- Manually define the ignored registers for the "override" branch above 53 | EEex_HookIntegrityWatchdog_IgnoreRegistersForInstance(EEex_Label("CProjectile::DecodeProjectile"), 1, { 54 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.R8, 55 | EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 56 | }) 57 | 58 | --[[ 59 | +-------------------------------------------------------------------------------------------------------------------------+ 60 | | Implement Opcode #408 (ProjectileMutator) `projectileMutator` functionality | 61 | +-------------------------------------------------------------------------------------------------------------------------+ 62 | | [EEex.dll] EEex::Projectile_Hook_OnAfterDecode(pProjectile: CProjectile*, pDecoder: CGameAIBase*, pRetPtr: uintptr_t) | 63 | +-------------------------------------------------------------------------------------------------------------------------+ 64 | --]] 65 | 66 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CProjectile::DecodeProjectile()-LastCall"), { 67 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 68 | {[[ 69 | mov r8, qword ptr ss:[rsp+408] ; pRetPtr 70 | mov rdx, rsi ; pDecoder 71 | mov rcx, rbx ; pProjectile 72 | call #L(EEex::Projectile_Hook_OnAfterDecode) 73 | ]]} 74 | ) 75 | 76 | --[[ 77 | +----------------------------------------------------------------------------------------------------------------------------------------------------+ 78 | | Implement Opcode #408 (ProjectileMutator) `effectMutator` functionality | 79 | +----------------------------------------------------------------------------------------------------------------------------------------------------+ 80 | | [EEex.dll] EEex::Projectile_Hook_OnBeforeAddEffect(pProjectile: CProjectile*, pDecoder: CGameAIBase*, pEffect: CGameEffect*, pRetPtr: uintptr_t) | 81 | +----------------------------------------------------------------------------------------------------------------------------------------------------+ 82 | --]] 83 | 84 | -- This is very ugly, but since CProjectile::AddEffect() isn't passed the source aiBase, I have to go and 85 | -- manually define where the aiBase is currently saved for the given CProjectile::AddEffect() call. 86 | local getAddEffectAIBase = EEex_JITNear({[[ 87 | 88 | #STACK_MOD(8) ; This was called, the ret ptr broke alignment 89 | #MAKE_SHADOW_SPACE(24) 90 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], r8 91 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], r9 92 | 93 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::ForceSpell()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016CE36 94 | cmp rcx, rax 95 | je in_rbx 96 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::ForceSpell()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x14016CE53 97 | cmp rcx, rax 98 | je in_rbx 99 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::ForceSpellPoint()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016DEA4 100 | cmp rcx, rax 101 | je in_rbx 102 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::Spell()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1403B5238 103 | cmp rcx, rax 104 | je in_rbx 105 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::Spell()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x1403B5259 106 | cmp rcx, rax 107 | je in_rbx 108 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::SpellPoint()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1403B6DE2 109 | cmp rcx, rax 110 | je in_rbx 111 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::Swing()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1403B88B3 112 | cmp rcx, rax 113 | je in_rbx 114 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::Swing()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x1403B9291 115 | cmp rcx, rax 116 | je in_rbx 117 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::UseItem()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1403BB59A 118 | cmp rcx, rax 119 | je in_rbx 120 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::UseItem()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x1403BB5BF 121 | cmp rcx, rax 122 | je in_rbx 123 | mov rax, #$(1) ]], {EEex_Label("Data-CGameSprite::UseItemPoint()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1403BC0DA 124 | cmp rcx, rax 125 | je in_rbx 126 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireSpell()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016AE6F 127 | cmp rcx, rax 128 | je in_r14 129 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireSpell()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x14016AFE2 130 | cmp rcx, rax 131 | je in_r14 132 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireSpellPoint()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016BC3B 133 | cmp rcx, rax 134 | je in_r14 135 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireSpellPoint()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x14016BCAD 136 | cmp rcx, rax 137 | je in_r14 138 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireItem()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016A465 139 | cmp rcx, rax 140 | je in_rbp 141 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireItem()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x14016A487 142 | cmp rcx, rax 143 | je in_rbp 144 | mov rax, #$(1) ]], {EEex_Label("Data-CBounceList::Add()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14014C7DB 145 | cmp rcx, rax 146 | je in_r15 147 | mov rax, #$(1) ]], {EEex_Label("Data-CBounceList::Add()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x14014C82E 148 | cmp rcx, rax 149 | je in_r15 150 | mov rax, #$(1) ]], {EEex_Label("Data-CGameEffect::FireSpell()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x1401E4715 151 | cmp rcx, rax 152 | je source_id_on_stack 153 | mov rax, #$(1) ]], {EEex_Label("Data-CGameEffect::FireSpell()-CProjectile::AddEffect()-RetPtr-2")}, [[ ; 0x1401E4793 154 | cmp rcx, rax 155 | je source_id_on_stack 156 | mov rax, #$(1) ]], {EEex_Label("Data-CGameAIBase::FireItemPoint()-CProjectile::AddEffect()-RetPtr")}, [[ ; 0x14016A7F3 157 | cmp rcx, rax 158 | je in_rsi 159 | 160 | xor rax, rax 161 | jmp return 162 | 163 | in_rbx: 164 | mov rax, rbx 165 | jmp return 166 | 167 | in_r14: 168 | mov rax, r14 169 | jmp return 170 | 171 | in_rbp: 172 | mov rax, rbp 173 | jmp return 174 | 175 | in_r15: 176 | mov rax, r15 177 | jmp return 178 | 179 | source_id_on_stack: 180 | mov ecx, dword ptr ss:[rbp+0x7F] 181 | lea rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 182 | mov qword ptr ss:[rdx], 0 183 | call #L(CGameObjectArray::GetShare) 184 | mov rax, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-24)] 185 | jmp return 186 | 187 | in_rsi: 188 | mov rax, rsi 189 | 190 | return: 191 | mov r9, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 192 | mov r8, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 193 | #DESTROY_SHADOW_SPACE 194 | ret 195 | ]]}) 196 | 197 | EEex_HookBeforeRestoreWithLabels(EEex_Label("CProjectile::AddEffect"), 0, 8, 8, { 198 | {"stack_mod", 8}, 199 | {"hook_integrity_watchdog_ignore_registers", { 200 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 201 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 202 | }}}, 203 | {[[ 204 | #MAKE_SHADOW_SPACE(16) 205 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 206 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 207 | 208 | mov r9, qword ptr ss:[rsp+#LAST_FRAME_TOP(0)] ; pRetPtr 209 | mov r8, rdx ; pEffect 210 | 211 | mov rcx, r9 ; pRetPtr 212 | call #$(1) ]], {getAddEffectAIBase}, [[ #ENDL 213 | mov rdx, rax ; pDecoder 214 | 215 | ; r9 already pRetPtr 216 | ; r8 already pEffect 217 | ; rdx already pDecoder 218 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] ; pProjectile 219 | call #L(EEex::Projectile_Hook_OnBeforeAddEffect) 220 | 221 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 222 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 223 | #DESTROY_SHADOW_SPACE 224 | ]]} 225 | ) 226 | 227 | EEex_EnableCodeProtection() 228 | 229 | end)() 230 | -------------------------------------------------------------------------------- /EEex/copy/EEex_ReplaceLua.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | ------------------------------------------------------------------------------------------------------- 7 | -- Initialize EEex.dll and replace the engine's bootstrap code to make it use the external Lua state -- 8 | ------------------------------------------------------------------------------------------------------- 9 | 10 | EEex_InitLuaBindings("EEex") 11 | 12 | local override = function(funcName) 13 | EEex_JITAt(EEex_Label(funcName), {[[ 14 | jmp #L(EEex::Override_#$(1)) ]], {funcName} 15 | }) 16 | end 17 | 18 | override("bootstrapLua") 19 | 20 | ----------------------------------------------------------------------------------- 21 | -- /START Lua function redirection -- 22 | ----------------------------------------------------------------------------------- 23 | -- Redirect in-engine Lua functions to point to the [General].LuaLibrary DLL -- 24 | ----------------------------------------------------------------------------------- 25 | 26 | local redirectLuaProc = function(funcName) 27 | EEex_JITAt(EEex_Label(funcName), {[[ 28 | jmp ]], EEex_GetLuaLibraryProc(funcName) 29 | }) 30 | end 31 | 32 | redirectLuaProc("lua_atpanic") 33 | redirectLuaProc("lua_callk") 34 | redirectLuaProc("lua_checkstack") 35 | redirectLuaProc("lua_concat") 36 | redirectLuaProc("lua_createtable") 37 | redirectLuaProc("lua_error") 38 | redirectLuaProc("lua_gc") 39 | redirectLuaProc("lua_getfield") 40 | redirectLuaProc("lua_getglobal") 41 | redirectLuaProc("lua_getinfo") 42 | redirectLuaProc("lua_getlocal") 43 | redirectLuaProc("lua_getmetatable") 44 | redirectLuaProc("lua_gettable") 45 | redirectLuaProc("lua_gettop") 46 | 47 | -- redirectLuaProc("lua_getuservalue") -- Only used in: 48 | -- lpeg library [Program Options].Cucumber mode 49 | 50 | redirectLuaProc("lua_insert") 51 | redirectLuaProc("lua_iscfunction") 52 | redirectLuaProc("lua_isnumber") 53 | redirectLuaProc("lua_isstring") 54 | redirectLuaProc("lua_isuserdata") 55 | redirectLuaProc("lua_len") 56 | redirectLuaProc("lua_load") 57 | redirectLuaProc("lua_newstate") 58 | redirectLuaProc("lua_newuserdata") 59 | redirectLuaProc("lua_next") 60 | redirectLuaProc("lua_pcallk") 61 | redirectLuaProc("lua_pushboolean") 62 | redirectLuaProc("lua_pushcclosure") 63 | redirectLuaProc("lua_pushfstring") 64 | redirectLuaProc("lua_pushinteger") 65 | redirectLuaProc("lua_pushlightuserdata") 66 | redirectLuaProc("lua_pushlstring") 67 | redirectLuaProc("lua_pushnil") 68 | redirectLuaProc("lua_pushnumber") 69 | redirectLuaProc("lua_pushstring") 70 | redirectLuaProc("lua_pushvalue") 71 | redirectLuaProc("lua_pushvfstring") 72 | redirectLuaProc("lua_rawequal") 73 | redirectLuaProc("lua_rawget") 74 | redirectLuaProc("lua_rawgeti") 75 | redirectLuaProc("lua_rawlen") 76 | redirectLuaProc("lua_rawset") 77 | redirectLuaProc("lua_rawseti") 78 | redirectLuaProc("lua_remove") 79 | redirectLuaProc("lua_resume") 80 | redirectLuaProc("lua_setfield") 81 | redirectLuaProc("lua_setglobal") 82 | redirectLuaProc("lua_setmetatable") 83 | redirectLuaProc("lua_settable") 84 | redirectLuaProc("lua_settop") 85 | redirectLuaProc("lua_setupvalue") 86 | 87 | -- redirectLuaProc("lua_setuservalue") -- Only used in: 88 | -- debug library (replaced) 89 | -- lpeg library ([Program Options].Cucumber mode) 90 | 91 | redirectLuaProc("lua_toboolean") 92 | redirectLuaProc("lua_tocfunction") 93 | redirectLuaProc("lua_tointegerx") 94 | redirectLuaProc("lua_tolstring") 95 | redirectLuaProc("lua_tonumberx") 96 | 97 | -- redirectLuaProc("lua_tounsignedx") -- Only used in: 98 | -- bit32 library (removed) 99 | -- luaL_setfuncs() (replaced) 100 | -- math library (replaced) 101 | 102 | redirectLuaProc("lua_touserdata") 103 | redirectLuaProc("lua_type") 104 | redirectLuaProc("lua_typename") 105 | redirectLuaProc("lua_xmove") 106 | redirectLuaProc("luaL_addlstring") 107 | redirectLuaProc("luaL_addstring") 108 | redirectLuaProc("luaL_addvalue") 109 | redirectLuaProc("luaL_argerror") 110 | redirectLuaProc("luaL_checkinteger") 111 | redirectLuaProc("luaL_checknumber") 112 | redirectLuaProc("luaL_checkudata") 113 | redirectLuaProc("luaL_error") 114 | redirectLuaProc("luaL_getmetafield") 115 | 116 | -- redirectLuaProc("luaL_getsubtable") -- Only used in: 117 | -- debug library (replaced) 118 | -- luaL_requiref() (removed) 119 | -- package library (replaced) 120 | 121 | redirectLuaProc("luaL_gsub") 122 | redirectLuaProc("luaL_len") 123 | redirectLuaProc("luaL_loadbufferx") 124 | redirectLuaProc("luaL_loadfilex") 125 | redirectLuaProc("luaL_loadstring") 126 | redirectLuaProc("luaL_newmetatable") 127 | redirectLuaProc("luaL_newstate") 128 | redirectLuaProc("luaL_optlstring") 129 | 130 | -- redirectLuaProc("luaL_prepbuffsize") -- Only used in: 131 | -- lpeg library ([Program Options].Cucumber mode) 132 | -- luaL_addlstring() (replaced) 133 | -- luaL_addstring() (replaced) 134 | -- luaL_addvalue() (replaced) 135 | -- luaL_gsub() (replaced) 136 | -- mime_core library ([Program Options].Cucumber mode) 137 | -- socket_core library ([Program Options].Cucumber mode) 138 | -- string library (replaced) 139 | 140 | redirectLuaProc("luaL_pushresult") 141 | redirectLuaProc("luaL_ref") 142 | 143 | -- redirectLuaProc("luaL_requiref") -- Only used in: 144 | -- bootstrapLua() (calls to luaL_requiref() removed) 145 | -- enableCucumberSupport() ([Program Options].Cucumber mode) 146 | 147 | redirectLuaProc("luaL_setfuncs") 148 | 149 | -- redirectLuaProc("luaL_tolstring") -- Only used in: 150 | -- string library (replaced) 151 | 152 | redirectLuaProc("luaL_traceback") 153 | redirectLuaProc("luaL_where") 154 | redirectLuaProc("luaopen_base") 155 | 156 | -- redirectLuaProc("luaopen_bit32") -- Removed 157 | 158 | -- redirectLuaProc("luaopen_coroutine") -- Only used in: 159 | -- enableCucumberSupport() ([Program Options].Cucumber mode) 160 | 161 | redirectLuaProc("luaopen_debug") 162 | redirectLuaProc("luaopen_math") 163 | redirectLuaProc("luaopen_package") 164 | redirectLuaProc("luaopen_string") 165 | redirectLuaProc("luaopen_table") 166 | 167 | ----------------------------------- 168 | -- /END Lua function redirection -- 169 | ----------------------------------- 170 | 171 | ---------------------------------------------------------------------------------------------------------------- 172 | -- /START Lua INI handling fix -- 173 | ---------------------------------------------------------------------------------------------------------------- 174 | -- The engine uses a custom Lua function (luaL_loadfilexptr) to execute Baldur.lua (opened with _wfopen). -- 175 | -- Since stdio functions have to be run within the same C runtime instance these functions must be -- 176 | -- redirected to the Lua DLL procedures. -- 177 | ---------------------------------------------------------------------------------------------------------------- 178 | 179 | -- Redirect the _wfopen() call in OpenIniFile() 180 | EEex_ReplaceCall(EEex_Label("Hook-OpenIniFile()-_wfopen()"), EEex_GetLuaLibraryProc("wrapper_wfopen")) 181 | 182 | -- Redirect the luaL_loadfilexptr() call in chReadIniFile() 183 | EEex_ReplaceCall(EEex_Label("Hook-chReadIniFile()-luaL_loadfilexptr()"), EEex_GetLuaLibraryProc("luaL_loadfilexptr")) 184 | 185 | -- A unique pattern cannot be established inside chWriteInifile() - replace it entirely to redirect its fclose() call 186 | override("chWriteInifile") 187 | 188 | -- It's difficult to replace the fprintf() call in Infinity_WriteINILine() since a unique pattern can't be 189 | -- established for that function. The only way to patch it is to grab its address after it has been 190 | -- exported to Lua and replace it entirely. 191 | 192 | EEex_ReplaceLua_OnUIFunctionsLoaded = function() 193 | EEex_DisableCodeProtection() 194 | EEex_JITAt(EEex_CFuncToPtr(Infinity_WriteINILine), {[[ 195 | jmp #L(EEex::Override_Infinity_WriteINILine) 196 | ]]}) 197 | EEex_EnableCodeProtection() 198 | end 199 | 200 | EEex_HookAfterCall(EEex_Label("Hook-dimmInit()-uiLoadFunctions()"), EEex_FlattenTable({ 201 | {[[ 202 | #MAKE_SHADOW_SPACE(32) 203 | ]]}, 204 | EEex_GenLuaCall("EEex_ReplaceLua_OnUIFunctionsLoaded"), 205 | {[[ 206 | call_error: 207 | #DESTROY_SHADOW_SPACE 208 | ]]}, 209 | })) 210 | 211 | -- Disable backup INI processing, (runs if Baldur.lua had a syntax / runtime error), to prevent crash 212 | -- due to the above stdio redirects for INI processing. The backup processing appears to parse 213 | -- Baldur.lua as the old Baldur.ini SQL format. 214 | EEex_ForceJump(EEex_Label("Hook-chReadIniFile()-CheckDoBackupProcessingJmp")) 215 | 216 | ------------------------------- 217 | -- /END Lua INI handling fix -- 218 | ------------------------------- 219 | 220 | -- Replace all inlined uses of LUA_REGISTRYINDEX = -1001000 (Lua 5.2) with LUA_REGISTRYINDEX = -10000 (LuaJIT) 221 | for _, address in ipairs(EEex_Label("Data-LUA_REGISTRYINDEX")) do 222 | EEex_Write32(address, -10000) 223 | end 224 | 225 | EEex_EnableCodeProtection() 226 | 227 | end)() 228 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Script.lua: -------------------------------------------------------------------------------- 1 | 2 | function EEex_Script_IsPlayerScript(script) 3 | -- [EEex.dll] 4 | return EEex.IsPlayerScript(script) 5 | end 6 | CAIScript.isPlayerScript = EEex_Script_IsPlayerScript 7 | 8 | function EEex_Script_GetResRef(script) 9 | return script.cResRef:get() 10 | end 11 | CAIScript.getResRef = EEex_Script_GetResRef 12 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Script_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +-----------------------------------------------------------------------------------------------+ 8 | | Maintain EEex data that flags whether a CAIScript was loaded as `bPlayerScript` (.BS vs .BCS) | 9 | +-----------------------------------------------------------------------------------------------+ 10 | | [EEex.dll] EEex::Script_Hook_OnRead(pScript: CAIScript*, bPlayerScript: bool) | 11 | +-----------------------------------------------------------------------------------------------+ 12 | --]] 13 | 14 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIScript::Read()-OnRead"), { 15 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 16 | {[[ 17 | mov rdx, r14 ; bPlayerScript 18 | mov rcx, rsi ; pScript 19 | call #L(EEex::Script_Hook_OnRead) 20 | ]]} 21 | ) 22 | 23 | --[[ 24 | +-----------------------------------------------------------------------------------------+ 25 | | Associate EEex data linked to a CAIScript instance with a new CAIScript instance (copy) | 26 | +-----------------------------------------------------------------------------------------+ 27 | | [EEex.dll] EEex::Script_Hook_OnCopy(pSrcScript: CAIScript*, pDstScript: CAIScript*) | 28 | +-----------------------------------------------------------------------------------------+ 29 | --]] 30 | 31 | for _, entry in ipairs({ 32 | {"Hook-CAIScript::Construct()-OnCopy1", "rdi"}, 33 | {"Hook-CAIScript::Construct()-OnCopy2", "rsi"}, }) 34 | do 35 | EEex_HookBeforeCallWithLabels(EEex_Label(entry[1]), { 36 | {"hook_integrity_watchdog_ignore_registers", { 37 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 38 | EEex_HookIntegrityWatchdogRegister.R11 39 | }}}, 40 | {[[ 41 | #MAKE_SHADOW_SPACE(16) 42 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 43 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)], rdx 44 | 45 | mov rdx, #$(2) ]], entry, [[ ; pDstScript 46 | ; rcx already pSrcScript 47 | call #L(EEex::Script_Hook_OnCopy) 48 | 49 | mov rdx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-16)] 50 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 51 | #DESTROY_SHADOW_SPACE 52 | ]]} 53 | ) 54 | end 55 | 56 | --[[ 57 | +-----------------------------------------------------------------------+ 58 | | Clean up EEex data linked to a CAIScript instance after it is deleted | 59 | +-----------------------------------------------------------------------+ 60 | | [EEex.dll] EEex::Script_Hook_OnDestruct(pScript: CAIScript*) | 61 | +-----------------------------------------------------------------------+ 62 | --]] 63 | 64 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CAIScript::Destruct()-OnDestruct"), { 65 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 66 | {[[ 67 | mov rcx, rsi ; pScript 68 | call #L(EEex::Script_Hook_OnDestruct) 69 | ]]} 70 | ) 71 | 72 | EEex_EnableCodeProtection() 73 | 74 | end)() 75 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Stats.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- Globals -- 4 | ------------- 5 | 6 | EEex_DerivedStats_DisabledButtonType = { 7 | ["BUTTON_STEALTH"] = 0, 8 | ["BUTTON_THIEVING"] = 1, 9 | ["BUTTON_CASTSPELL"] = 2, 10 | ["BUTTON_QUICKSPELL0"] = 3, 11 | ["BUTTON_QUICKSPELL1"] = 4, 12 | ["BUTTON_QUICKSPELL2"] = 5, 13 | ["BUTTON_TURNUNDEAD"] = 6, 14 | ["BUTTON_DIALOG"] = 7, 15 | ["BUTTON_USEITEM"] = 8, 16 | ["BUTTON_QUICKITEM1"] = 9, 17 | ["BUTTON_BATTLESONG"] = 10, 18 | ["BUTTON_QUICKITEM2"] = 11, 19 | ["BUTTON_QUICKITEM3"] = 12, 20 | ["BUTTON_INNATEBUTTON"] = 13, 21 | ["SCREEN_INVENTORY"] = 14, 22 | } 23 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Stats_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +-------------------------------------------------------------+ 8 | | Initialize EEex data linked to CDerivedStats instances | 9 | +-------------------------------------------------------------+ 10 | | [EEex.dll] Stats_Hook_OnConstruct(pStats: CDerivedStats*) | 11 | +-------------------------------------------------------------+ 12 | --]] 13 | 14 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::Construct()-FirstCall"), { 15 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 16 | {[[ 17 | mov rcx, rsi ; pStats 18 | call #L(EEex::Stats_Hook_OnConstruct) 19 | ]]} 20 | ) 21 | 22 | --[[ 23 | +------------------------------------------------------------------+ 24 | | Clean up EEex data linked to CDerivedStats instances | 25 | +------------------------------------------------------------------+ 26 | | [EEex.dll] EEex::Stats_Hook_OnDestruct(pStats: CDerivedStats*) | 27 | +------------------------------------------------------------------+ 28 | --]] 29 | 30 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::Destruct()-FirstCall"), { 31 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 32 | {[[ 33 | mov rcx, rdi ; pStats 34 | call #L(EEex::Stats_Hook_OnDestruct) 35 | ]]} 36 | ) 37 | 38 | --[[ 39 | +----------------------------------------------------------------+ 40 | | Reset EEex data linked to CDerivedStats instances | 41 | +----------------------------------------------------------------+ 42 | | [EEex.dll] EEex::Stats_Hook_OnReload(pStats: CDerivedStats*) | 43 | +----------------------------------------------------------------+ 44 | --]] 45 | 46 | local statsReloadTemplate = function(spriteRegStr) 47 | return {[[ 48 | mov rcx, #$(1) ]], {spriteRegStr}, [[ ; pSprite 49 | call #L(EEex::Stats_Hook_OnReload) 50 | ]]} 51 | end 52 | 53 | local callStatsReloadRbx = {"call #$(1) #ENDL", 54 | { 55 | EEex_JITNear(EEex_FlattenTable({ 56 | {[[ 57 | #STACK_MOD(8) ; This was called, the ret ptr broke alignment 58 | #MAKE_SHADOW_SPACE 59 | ]]}, 60 | statsReloadTemplate("rbx"), 61 | {[[ 62 | #DESTROY_SHADOW_SPACE 63 | ret 64 | ]]}, 65 | })), 66 | }, 67 | } 68 | 69 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::QuickLoad()-CDerivedStats::Reload()"), { 70 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 71 | statsReloadTemplate("rdi") 72 | ) 73 | 74 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-1"), { 75 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 76 | callStatsReloadRbx 77 | ) 78 | 79 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::Unmarshal()-CDerivedStats::Reload()-2"), { 80 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 81 | callStatsReloadRbx 82 | ) 83 | 84 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CGameSprite::ProcessEffectList()-CDerivedStats::Reload()"), { 85 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 86 | statsReloadTemplate("rsi") 87 | ) 88 | 89 | --[[ 90 | +-------------------------------------------------------------------------------------------------+ 91 | | Associate EEex data linked to a CDerivedStats instance with a new CDerivedStats instance (copy) | 92 | +-------------------------------------------------------------------------------------------------+ 93 | | [EEex.dll] EEex::Stats_Hook_OnEqu(pStats: CDerivedStats*, pOtherStats: CDerivedStats*) | 94 | +-------------------------------------------------------------------------------------------------+ 95 | --]] 96 | 97 | EEex_HookAfterCallWithLabels(EEex_Label("Hook-CDerivedStats::operator_equ()-FirstCall"), { 98 | {"hook_integrity_watchdog_ignore_registers", {EEex_HookIntegrityWatchdogRegister.RAX}}}, 99 | {[[ 100 | mov rdx, rsi ; pOtherStats 101 | mov rcx, r14 ; pStats 102 | call #L(EEex::Stats_Hook_OnEqu) 103 | ]]} 104 | ) 105 | 106 | --[[ 107 | +----------------------------------------------------------------------------------------------+ 108 | | Apply bonus EEex CDerivedStats data to the regular EEex CDerivedStats data | 109 | +----------------------------------------------------------------------------------------------+ 110 | | [EEex.dll] EEex::Stats_Hook_OnPlusEqu(pStats: CDerivedStats*, pOtherStats: CDerivedStats*) | 111 | +----------------------------------------------------------------------------------------------+ 112 | --]] 113 | 114 | EEex_HookBeforeCallWithLabels(EEex_Label("Hook-CDerivedStats::operator_plus_equ()-FirstCall"), { 115 | {"hook_integrity_watchdog_ignore_registers", { 116 | EEex_HookIntegrityWatchdogRegister.RDX, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 117 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 118 | }}}, 119 | {[[ 120 | #MAKE_SHADOW_SPACE(8) 121 | mov qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)], rcx 122 | 123 | mov rdx, rdi ; pOtherStats 124 | mov rcx, rbx ; pStats 125 | call #L(EEex::Stats_Hook_OnPlusEqu) 126 | 127 | mov rcx, qword ptr ss:[rsp+#SHADOW_SPACE_BOTTOM(-8)] 128 | #DESTROY_SHADOW_SPACE 129 | ]]} 130 | ) 131 | 132 | --[[ 133 | +---------------------------------------------------------------------------------------------+ 134 | | Allow engine to fetch extended stat values | 135 | +---------------------------------------------------------------------------------------------+ 136 | | * Extended stats are those with ids outside of the vanilla range in STATS.IDS | 137 | | * Extended stat minimums, maximums, and defaults are defined in X-STATS.2DA | 138 | +---------------------------------------------------------------------------------------------+ 139 | | [EEex.dll] EEex::Stats_Hook_OnGettingUnknown(pStats: CDerivedStats*, nStatId: int) -> int | 140 | | return -> The value of the extended stat | 141 | +---------------------------------------------------------------------------------------------+ 142 | --]] 143 | 144 | EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CDerivedStats::GetAtOffset()-OutOfBoundsJmp"), 0, { 145 | {"stack_mod", 8}, 146 | {"hook_integrity_watchdog_ignore_registers", { 147 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 148 | EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, EEex_HookIntegrityWatchdogRegister.R10, 149 | EEex_HookIntegrityWatchdogRegister.R11 150 | }}}, 151 | EEex_FlattenTable({ 152 | {[[ 153 | #MAKE_SHADOW_SPACE 154 | 155 | lea rdx, qword ptr ds:[rax+1] ; nStatId 156 | ; rcx already pStats 157 | call #L(EEex::Stats_Hook_OnGettingUnknown) 158 | 159 | #DESTROY_SHADOW_SPACE 160 | #MANUAL_HOOK_EXIT(0) 161 | ret 162 | ]]}, 163 | }) 164 | ) 165 | 166 | EEex_EnableCodeProtection() 167 | 168 | end)() 169 | -------------------------------------------------------------------------------- /EEex/copy/EEex_StutterDetector.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- Options -- 4 | ------------- 5 | 6 | EEex_StutterDetector_Load = false 7 | EEex_StutterDetector_OutputEnabled = true 8 | 9 | --------------- 10 | -- Listeners -- 11 | --------------- 12 | 13 | function EEex_StutterDetector_Private_LoadMenuListener() 14 | EEex_Menu_LoadFile("X-STUTDE") 15 | end 16 | 17 | function EEex_StutterDetector_Private_PushMenuListener() 18 | Infinity_PushMenu("EEex_StutterDetector_Menu") 19 | end 20 | 21 | function EEex_StutterDetector_Private_InstallFunctionWrappers() 22 | local topLevel = true 23 | for k, v in pairs(_G) do 24 | if type(k) == "string" and type(v) == "function" 25 | and 26 | ( #k >= 5 and string.sub(k, 1, 5) == "EEex_" 27 | or #k >= 2 and string.sub(k, 1, 2) == "B3" 28 | ) 29 | and v ~= EEex_StutterDetector_Private_Tick and v ~= EEex_GetMicroseconds 30 | then 31 | _G[k] = function(...) 32 | 33 | local oldTopLevel = topLevel 34 | topLevel = false 35 | 36 | local start = EEex_GetMicroseconds() 37 | local retT = {v(...)} 38 | local timeTaken = EEex_GetMicroseconds() - start 39 | 40 | topLevel = oldTopLevel 41 | local entry = EEex_StutterDetector_Private_Times[k] 42 | if entry then 43 | entry[1] = entry[1] + 1 44 | entry[2] = entry[2] + timeTaken 45 | else 46 | EEex_StutterDetector_Private_Times[k] = { 1, timeTaken } 47 | end 48 | 49 | if topLevel then 50 | EEex_StutterDetector_Private_TopLevelTime = EEex_StutterDetector_Private_TopLevelTime + timeTaken 51 | end 52 | 53 | return table.unpack(retT) 54 | end 55 | print("Installed stutter wrapper for "..k) 56 | end 57 | end 58 | end 59 | 60 | ---------- 61 | -- Code -- 62 | ---------- 63 | 64 | EEex_StutterDetector_Private_TopLevelTime = 0 65 | EEex_StutterDetector_Private_Times = {} 66 | EEex_StutterDetector_Private_LastMicroseconds = nil 67 | 68 | function EEex_StutterDetector_Private_Tick() 69 | 70 | local microseconds = EEex_GetMicroseconds() 71 | 72 | if EEex_StutterDetector_OutputEnabled and EEex_StutterDetector_Private_LastMicroseconds then 73 | local target = 1000000 / EEex_CChitin.TIMER_UPDATES_PER_SECOND 74 | local targetFudge = target + target * 0.01 75 | local diff = microseconds - EEex_StutterDetector_Private_LastMicroseconds 76 | if diff >= targetFudge and EEex_StutterDetector_Private_TopLevelTime >= 400 then 77 | 78 | print(" +--------------+-----+-----------------------------+") 79 | print(string.format("[!] | Stutter: %7.3fms | EEex time: %7.3fms |", 80 | diff / 1000, EEex_StutterDetector_Private_TopLevelTime / 1000)) 81 | print(" +--------------+-----------------------------------+") 82 | 83 | EEex_Utility_IterateMapAsSorted(EEex_StutterDetector_Private_Times, 84 | function(o1, o2) return o1[2][2] > o2[2][2] end, 85 | function(i, k, v) 86 | if v[2] >= 50 then 87 | print(string.format(" | count: %5d | total: %7.3fms | avg: %7dns | %s", v[1], v[2] / 1000, v[2] / 1000 / v[1] * 1000000, k)) 88 | end 89 | end 90 | ) 91 | end 92 | end 93 | 94 | EEex_StutterDetector_Private_TopLevelTime = 0 95 | EEex_StutterDetector_Private_Times = {} 96 | EEex_StutterDetector_Private_LastMicroseconds = microseconds 97 | end 98 | 99 | (function() 100 | 101 | if not EEex_StutterDetector_Load then 102 | return 103 | end 104 | 105 | EEex_Menu_AddMainFileLoadedListener(EEex_StutterDetector_Private_LoadMenuListener) 106 | EEex_Menu_AddAfterMainFileReloadedListener(EEex_StutterDetector_Private_PushMenuListener) 107 | 108 | EEex_StutterDetector_Private_InstallFunctionWrappers() 109 | EEex_StutterDetector_Private_LoadMenuListener() 110 | EEex_StutterDetector_Private_PushMenuListener() 111 | 112 | end)() 113 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Test.lua: -------------------------------------------------------------------------------- 1 | 2 | function EEex_Test_IDSFunctions() 3 | 4 | local doTest = function(cacheAsArray) 5 | 6 | local action = EEex_Resource_LoadIDS("ACTION", cacheAsArray) 7 | 8 | print(string.format(" [%s] %d", "EEex_Resource_GetIDSCount", EEex_Resource_GetIDSCount(action))) 9 | print(string.format(" [%s] %s", "EEex_Resource_GetIDSEntry", tostring(EEex_Resource_GetIDSEntry(action, 3)))) 10 | print(string.format(" [%s] %s", "EEex_Resource_GetIDSLine", EEex_Resource_GetIDSLine(action, 1))) 11 | print(string.format(" [%s] %s", "EEex_Resource_GetIDSStart", EEex_Resource_GetIDSStart(action, 1))) 12 | print(string.format(" [%s] %s", "EEex_Resource_IDSHasID(3)", tostring(EEex_Resource_IDSHasID(action, 3)))) 13 | print(string.format(" [%s] %s", "EEex_Resource_IDSHasID(4)", tostring(EEex_Resource_IDSHasID(action, 4)))) 14 | 15 | print(string.format(" [%s]", "EEex_Resource_IterateIDSEntries")) 16 | EEex_Resource_IterateIDSEntries(action, function(entry) 17 | print(string.format(" %s", tostring(entry))) 18 | end) 19 | 20 | print(string.format(" [%s]", "EEex_Resource_IterateUnpackedIDSEntries")) 21 | EEex_Resource_IterateUnpackedIDSEntries(action, function(id, line, start) 22 | print(string.format(" %d, %s, %s", id, line, start)) 23 | end) 24 | end 25 | 26 | print(string.format("[cacheAsArray=false]")) 27 | doTest(false) 28 | print(string.format("[cacheAsArray=true]")) 29 | doTest(true) 30 | end 31 | 32 | function EEex_Test_2DAFunctions() 33 | 34 | local kitlist = EEex_Resource_Load2DA("KITLIST") 35 | 36 | print(string.format("[%s] %d", "EEex_Resource_Find2DAColumnIndex", EEex_Resource_Find2DAColumnIndex(kitlist, 1, "CLABFI02"))) 37 | print(string.format("[%s] %d", "EEex_Resource_Find2DAColumnLabel", EEex_Resource_Find2DAColumnLabel(kitlist, "ABILITIES"))) 38 | print(string.format("[%s] %d", "EEex_Resource_Find2DARowIndex", EEex_Resource_Find2DARowIndex(kitlist, 0, "SHAPESHIFTER"))) 39 | print(string.format("[%s] %d", "EEex_Resource_Find2DARowLabel", EEex_Resource_Find2DARowLabel(kitlist, "32"))) 40 | print(string.format("[%s] %s", "EEex_Resource_Get2DAColumnLabel", EEex_Resource_Get2DAColumnLabel(kitlist, 2))) 41 | print(string.format("[%s] %s", "EEex_Resource_Get2DADefault", EEex_Resource_Get2DADefault(kitlist))) 42 | 43 | local dimX, dimY = EEex_Resource_Get2DADimensions(kitlist) 44 | print(string.format("[%s] %d, %d", "EEex_Resource_Get2DADimensions", dimX, dimY)) 45 | 46 | print(string.format("[%s] %s", "EEex_Resource_Get2DARowLabel", EEex_Resource_Get2DARowLabel(kitlist, 5))) 47 | print(string.format("[%s] %s", "EEex_Resource_GetAt2DALabels", EEex_Resource_GetAt2DALabels(kitlist, "ABILITIES", "31"))) 48 | print(string.format("[%s] %s", "EEex_Resource_GetAt2DAPoint", EEex_Resource_GetAt2DAPoint(kitlist, 3, 3))) 49 | 50 | local maxX, maxY = EEex_Resource_GetMax2DAIndices(kitlist) 51 | print(string.format("[%s] %d, %d", "EEex_Resource_GetMax2DAIndices", maxX, maxY)) 52 | 53 | print(string.format("[%s]", "EEex_Resource_Iterate2DAColumnIndex")) 54 | EEex_Resource_Iterate2DAColumnIndex(kitlist, 1, function(i, str) 55 | print(string.format(" [%d] = %s", i, str)) 56 | end) 57 | 58 | print(string.format("[%s]", "EEex_Resource_Iterate2DAColumnLabel")) 59 | EEex_Resource_Iterate2DAColumnLabel(kitlist, "UNUSABLE", function(i, str) 60 | print(string.format(" [%d] = %s", i, str)) 61 | end) 62 | 63 | print(string.format("[%s]", "EEex_Resource_Iterate2DARowIndex")) 64 | EEex_Resource_Iterate2DARowIndex(kitlist, 5, function(i, str) 65 | print(string.format(" [%d] = %s", i, str)) 66 | end) 67 | 68 | print(string.format("[%s]", "EEex_Resource_Iterate2DARowLabel")) 69 | EEex_Resource_Iterate2DARowLabel(kitlist, "41", function(i, str) 70 | print(string.format(" [%d] = %s", i, str)) 71 | end) 72 | end 73 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Trigger.lua: -------------------------------------------------------------------------------- 1 | 2 | ------------- 3 | -- General -- 4 | ------------- 5 | 6 | -- Parses the given string as a .BS trigger block and returns the compiled script object, (only filled with triggers). 7 | -- Call :free() on the returned CAIScriptFile when it is no longer needed. 8 | function EEex_Trigger_ParseConditionalString(string) 9 | local toReturn = EEex_NewUD("CAIScriptFile") 10 | toReturn:Construct() 11 | toReturn.free = function(scriptFile) 12 | scriptFile:Destruct() 13 | EEex_FreeUD(scriptFile) 14 | end 15 | EEex_RunWithStackManager({ 16 | { ["name"] = "cstring", ["struct"] = "CString", ["constructor"] = { ["args"] = { string } }, ["noDestruct"] = true } }, 17 | function(manager) 18 | toReturn:ParseConditionalString(manager:getUD("cstring")) 19 | end) 20 | return toReturn 21 | end 22 | 23 | -- Evaluates compiled triggers returned by EEex_Trigger_ParseConditionalString() in the context of aiBase. 24 | function EEex_Trigger_EvalScriptFileConditionalAsAIBase(scriptFile, aiBase) 25 | return scriptFile.m_curCondition:Hold(aiBase.m_pendingTriggers, aiBase) ~= 0 26 | end 27 | CAIScriptFile.evalConditionalAsAIBase = EEex_Trigger_EvalScriptFileConditionalAsAIBase 28 | 29 | -- Same as EEex_Trigger_EvalScriptFileConditionalAsAIBase() but takes a string instead of a compiled script object. 30 | -- Prefer using compiled triggers when efficiency is required. 31 | function EEex_Trigger_EvalConditionalStringAsAIBase(string, aiBase) 32 | return EEex_RunWithStackManager({ 33 | { ["name"] = "scriptFile", ["struct"] = "CAIScriptFile" }, 34 | { ["name"] = "cstring", ["struct"] = "CString", ["constructor"] = { ["args"] = { string } }, ["noDestruct"] = true } }, 35 | function(manager) 36 | local scriptFile = manager:getUD("scriptFile") 37 | scriptFile:ParseConditionalString(manager:getUD("cstring")) 38 | return scriptFile.m_curCondition:Hold(aiBase.m_pendingTriggers, aiBase) ~= 0 39 | end) 40 | end 41 | 42 | ----------- 43 | -- Hooks -- 44 | ----------- 45 | 46 | function EEex_Trigger_Hook_OnEvaluatingUnknown(aiBase, trigger) 47 | 48 | local triggerID = trigger.m_triggerID 49 | 50 | local quickDecode = function() 51 | return EEex_RunWithStackManager({ 52 | { ["name"] = "pointer", ["struct"] = "Pointer" }, }, 53 | function(manager) 54 | local pointer = manager:getUD("pointer") 55 | aiBase:virtual_QuickDecode(trigger, pointer) 56 | return pointer.reference 57 | end) 58 | end 59 | 60 | if triggerID == 0x410D then -- EEex_HasDispellableEffect 61 | 62 | local targetSprite = quickDecode() 63 | if not targetSprite then 64 | return false 65 | end 66 | 67 | local found = false 68 | 69 | local testBit = function(effect) 70 | if EEex_BAnd(effect.m_flags, 0x1) ~= 0 then 71 | found = true 72 | return true 73 | end 74 | end 75 | 76 | EEex_Utility_IterateCPtrList(targetSprite.m_timedEffectList, testBit) 77 | if found then 78 | return true 79 | end 80 | 81 | EEex_Utility_IterateCPtrList(targetSprite.m_equipedEffectList, testBit) 82 | return found 83 | 84 | elseif triggerID == 0x410E then -- EEex_LuaTrigger 85 | 86 | EEex_LuaTrigger_Object = aiBase 87 | EEex_LuaTrigger_Trigger = trigger 88 | 89 | local success, retVal = EEex_Utility_Eval("EEex_LuaTrigger", trigger.m_string1.m_pchData:get()) 90 | if success then 91 | return retVal 92 | end 93 | 94 | elseif triggerID == 0x410F then -- EEex_IsImmuneToOpcode 95 | 96 | local targetSprite = quickDecode() 97 | if not targetSprite then 98 | return false 99 | end 100 | 101 | local found = false 102 | 103 | local lookingForID = trigger.m_specificID 104 | EEex_Utility_IterateCPtrList(targetSprite:getActiveStats().m_cImmunitiesEffect, function(effect) 105 | if effect.m_effectId == lookingForID then 106 | found = true 107 | return true 108 | end 109 | end) 110 | 111 | return found 112 | 113 | elseif triggerID == 0x4110 then -- EEex_MatchObject / EEex_MatchObjectEx 114 | 115 | -- [EEex.dll] 116 | local matchedID = EEex.MatchObject(aiBase, trigger.m_string1.m_pchData:get(), 117 | trigger.m_specificID, trigger.m_specific2, trigger.m_specific3) 118 | 119 | EEex_GetUDAux(aiBase)["EEex_MatchObject"] = matchedID 120 | return matchedID ~= -1 121 | end 122 | 123 | return false 124 | end 125 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Trigger_Patch.lua: -------------------------------------------------------------------------------- 1 | 2 | (function() 3 | 4 | EEex_DisableCodeProtection() 5 | 6 | --[[ 7 | +---------------------------------------------------------------------------------------------------------------------------+ 8 | | Implement new triggers | 9 | +---------------------------------------------------------------------------------------------------------------------------+ 10 | | 0x410D EEex_HasDispellableEffect(O:Object*) | 11 | | 0x410E EEex_LuaTrigger(S:Chunk*) | 12 | | 0x410F EEex_IsImmuneToOpcode(O:Object*,I:Opcode*) | 13 | | 0x4110 EEex_MatchObject(S:Chunk*) | 14 | | 0x4110 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) | 15 | +---------------------------------------------------------------------------------------------------------------------------+ 16 | | [Lua] EEex_Trigger_Hook_OnEvaluatingUnknown(aiBase: CGameAIBase|EEex_GameObject_CastUT, trigger: CAITrigger) -> boolean | 17 | | return -> The trigger's evaluated value (false / true) | 18 | +---------------------------------------------------------------------------------------------------------------------------+ 19 | --]] 20 | 21 | EEex_HookConditionalJumpOnSuccessWithLabels(EEex_Label("Hook-CGameAIBase::EvaluateStatusTrigger()-DefaultJmp"), 0, { 22 | {"hook_integrity_watchdog_ignore_registers", { 23 | EEex_HookIntegrityWatchdogRegister.RAX, EEex_HookIntegrityWatchdogRegister.RCX, EEex_HookIntegrityWatchdogRegister.RDX, 24 | EEex_HookIntegrityWatchdogRegister.RSI, EEex_HookIntegrityWatchdogRegister.R8, EEex_HookIntegrityWatchdogRegister.R9, 25 | EEex_HookIntegrityWatchdogRegister.R10, EEex_HookIntegrityWatchdogRegister.R11 26 | }}}, 27 | EEex_FlattenTable({ 28 | {[[ 29 | #MAKE_SHADOW_SPACE(48) 30 | ]]}, 31 | EEex_GenLuaCall("EEex_Trigger_Hook_OnEvaluatingUnknown", { 32 | ["args"] = { 33 | function(rspOffset) return {"mov qword ptr ss:[rsp+#$(1)], r14 #ENDL", {rspOffset}}, "CGameAIBase", "EEex_GameObject_CastUT" end, 34 | function(rspOffset) return {[[ 35 | lea rax, qword ptr ss:[rbp+350h] 36 | mov qword ptr ss:[rsp+#$(1)], rax 37 | ]], {rspOffset}}, "CAITrigger" end, 38 | }, 39 | ["returnType"] = EEex_LuaCallReturnType.Boolean, 40 | }), 41 | {[[ 42 | jmp no_error 43 | 44 | call_error: 45 | xor eax, eax 46 | 47 | no_error: 48 | mov esi, eax 49 | #DESTROY_SHADOW_SPACE 50 | ]]}, 51 | }) 52 | ) 53 | 54 | EEex_EnableCodeProtection() 55 | 56 | end)() 57 | -------------------------------------------------------------------------------- /EEex/copy/EEex_UserDataGlobals.lua: -------------------------------------------------------------------------------- 1 | 2 | EEex_EngineGlobal_CBaldurChitin = EEex_CastUD(e, "EEex_CBaldurChitin") 3 | -------------------------------------------------------------------------------- /EEex/copy/EEex_Variable.lua: -------------------------------------------------------------------------------- 1 | 2 | function EEex_Variable_Find(variableHash, variableName) 3 | return EEex_RunWithStackManager({ 4 | { ["name"] = "variableName", ["struct"] = "CString", ["constructor"] = {["args"] = {variableName} }, ["noDestruct"] = true }, }, 5 | function(manager) 6 | return variableHash:FindKey(manager:getUD("variableName")) 7 | end) 8 | end 9 | CVariableHash.find = EEex_Variable_Find 10 | 11 | function EEex_Variable_GetInt(variableHash, variableName) 12 | local variable = variableHash:find(variableName) 13 | return variable and variable.m_intValue or 0 14 | end 15 | CVariableHash.getInt = EEex_Variable_GetInt 16 | 17 | function EEex_Variable_GetString(variableHash, variableName) 18 | local variable = variableHash:find(variableName) 19 | return variable and variable.m_stringValue:get() or "" 20 | end 21 | CVariableHash.getString = EEex_Variable_GetString 22 | 23 | function EEex_Variable_GetOrCreate(variableHash, variableName) 24 | local variable = variableHash:find(variableName) 25 | if variable then 26 | return variable 27 | else 28 | EEex_RunWithStackManager({ 29 | { ["name"] = "variableParam", ["struct"] = "CVariable" }, }, 30 | function(manager) 31 | local variableParam = manager:getUD("variableParam") 32 | variableParam.m_name:set(variableName) 33 | variableHash:AddKey(variableParam) 34 | end) 35 | return variableHash:find(variableName) 36 | end 37 | end 38 | CVariableHash.getOrCreate = EEex_Variable_GetOrCreate 39 | 40 | function EEex_Variable_SetInt(variableHash, variableName, value) 41 | variableHash:getOrCreate(variableName).m_intValue = value 42 | end 43 | CVariableHash.setInt = EEex_Variable_SetInt 44 | 45 | function EEex_Variable_SetString(variableHash, variableName, value) 46 | variableHash:getOrCreate(variableName).m_stringValue:set(value) 47 | end 48 | CVariableHash.setString = EEex_Variable_SetString 49 | -------------------------------------------------------------------------------- /EEex/copy/M___EEex.lua: -------------------------------------------------------------------------------- 1 | 2 | if not EEex_Active then 3 | error("[!] ERROR: EEex not active.\n\nDid you forget to start the game with InfinityLoader.exe?") 4 | end 5 | -------------------------------------------------------------------------------- /EEex/copy/X-CLSERG.2DA: -------------------------------------------------------------------------------- 1 | 2DA V1.0 2 | 0 3 | BOW DAGGER MACE SLING SMSWORD BGSWORD HAMMER MSTAR FLAIL DART AXE STAFF XBOW FIST SPEAR POLEARM 4 | BERSERKER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 | WIZARD_SLAYER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 | KENSAI 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 | CAVALIER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 | INQUISITOR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 | UNDEAD_HUNTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 | FERALAN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 | STALKER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 | BEASTMASTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13 | ASSASIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 | BOUNTY_HUNTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 | SWASHBUCKLER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 | BLADE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 17 | JESTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 | SKALD 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 | TOTEMIC_DRUID 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 | SHAPESHIFTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 | BEAST_FRIEND 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 22 | TALOS 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 | HELM 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 24 | LATHANDER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 | ABJURER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 26 | CONJURER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 27 | DIVINER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 | ENCHANTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 29 | ILLUSIONIST 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 30 | INVOKER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 31 | NECROMANCER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 | TRANSMUTER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 33 | WILDMAGE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 34 | BARBARIAN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 | Blackguard 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 | SHADOWDANCER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 37 | DWARVEN_DEFENDER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 38 | DRAGON_DISCIPLE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 39 | DARK_MOON 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 | SUN_SOUL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 41 | LATHANDER_X 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 42 | GRIZZLY_BEAR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 43 | OHTYR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 44 | OHTEMPUS 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 45 | -------------------------------------------------------------------------------- /EEex/copy/X-MATOBJ.IDS: -------------------------------------------------------------------------------- 1 | * 2 | 0x00000001 IGNORE_LOS 3 | 0x00000002 MATCH_NON_SPRITES 4 | 0x00000004 IGNORE_INVISIBLE 5 | 0x00000008 MATCH_INVISIBLE 6 | 0x00000010 MATCH_SLEEPING 7 | 0x00000020 MATCH_DEAD 8 | 0x00000040 MATCH_BACKLIST 9 | 0x00000080 PRIORITIZE_BACKLIST 10 | 0x00000100 MATCH_ONLY_BACKLIST 11 | 0x00000200 FARTHEST -------------------------------------------------------------------------------- /EEex/copy/X-STATS.2DA: -------------------------------------------------------------------------------- 1 | 2DA V1.0 2 | * 3 | MIN MAX DEFAULT 4 | -------------------------------------------------------------------------------- /EEex/copy/X-STUTDE.menu: -------------------------------------------------------------------------------- 1 | 2 | ------------------------------- 3 | -- EEex_StutterDetector_Menu -- 4 | ------------------------------- 5 | 6 | menu 7 | { 8 | name "EEex_StutterDetector_Menu" 9 | ignoreesc 10 | 11 | label 12 | { 13 | enabled "EEex_StutterDetector_Private_Tick()" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EEex/loader/EEex.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/EEex.dll -------------------------------------------------------------------------------- /EEex/loader/InfinityLoader.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/InfinityLoader.exe -------------------------------------------------------------------------------- /EEex/loader/InfinityLoader.ini: -------------------------------------------------------------------------------- 1 | 2 | [General] 3 | 4 | ; 0 -> Normal behavior (default). 5 | ; 1 -> Enables additional output to help debug the loader. This is only useful to a developer. 6 | 7 | Debug=0 8 | 9 | ; The prefix used by the temporary print function that is exposed to Lua during the execution of EarlyMain.lua (run when LuaPatchMode != INTERNAL). 10 | 11 | EarlyPrintPrefix="INFO: LPRINT: " 12 | 13 | ; The list of executables InfinityLoader will attempt to spawn. The first executable that exists is selected. 14 | 15 | ExeNames=Baldur.exe,icewind.exe,SiegeOfDragonspear.exe 16 | 17 | ; Patterns can change based on what executable is being spawned. When setting up InfinityLoader to spawn a renamed executable, 18 | ; it is important to map that renamed executable back to the original name so the correct patterns are used. 19 | ; 20 | ; This option is a list in the form of: 21 | ; :,: ... 22 | ; 23 | ; To configure InfinityLoader to spawn a renamed executable, the following should be done: 24 | ; 1) Above in ExeNames, either rename the original executable or add the renamed executable to the list. 25 | ; 2) Add an alias to this list that maps the renamed executable to the original executable. 26 | ; 27 | ; For example, if InfinityLoader should spawn BaldurReal.exe, a renamed Baldur.exe: 28 | ; ExeNames=BaldurReal.exe ... 29 | ; ExeSwitchAlias=BaldurReal.exe:Baldur.exe ... 30 | 31 | ExeSwitchAlias= 32 | 33 | ; Used internally to identify the loader in specific circumstances, e.g. when displaying a message box. 34 | 35 | ExtenderName=EEex 36 | 37 | ; 0 -> Loader detatches from the console after initialization so the game can attach to it without problems (default). 38 | ; 1 -> Loader stays attached to the console. Use this option if the game does not attach to the console. 39 | 40 | KeepConsoleAttached=0 41 | 42 | ; 0 -> Loader sends the standard io streams to nul after detatching from the console (default). 43 | ; 1 -> Loader keeps the standard io streams attached. Use this option if the game attaches to the console. 44 | 45 | KeepCrtStreamsAttached=1 46 | 47 | ; The name of the log file where all console output will be mirrored. If this field is blank no log is automatically written. 48 | 49 | LogFile= 50 | 51 | ; This prefix is applied to all Lua globals provided by the loader, and is used when executing EarlyMain.lua / Main.lua. 52 | 53 | LuaGlobalsPrefix=EEex_ 54 | 55 | ; The Lua library the loader uses when LuaPatchMode=EXTERNAL or LuaPatchMode=REPLACE_INTERNAL_WITH_EXTERNAL. Example: lua52.dll. 56 | 57 | LuaLibrary= 58 | 59 | ; INTERNAL -> InfinityLoader uses the internal Lua state / functions of the spawned program (default). 60 | ; EXTERNAL -> InfinityLoader holds its own Lua state and uses an external DLL for Lua functions. 61 | ; REPLACE_INTERNAL_WITH_EXTERNAL -> InfinityLoader replaces the spawned program's internal Lua state / functions with those of LuaLibrary. 62 | 63 | LuaPatchMode=INTERNAL 64 | 65 | ; The Lua version of the external LuaLibrary. This alters how LuaProvider.dll links to LuaLibrary, and whether it provides specific compatibility wrappers. 66 | 67 | LuaVersionExternal= 68 | 69 | ; The Lua version embedded in the target executable. This alters how LuaProvider.dll links to the in-executable functions, and whether it provides specific compatibility wrappers. 70 | 71 | LuaVersionInternal=5.2 72 | 73 | ; 0 -> Normal behavior (default). 74 | ; 1 -> Spawns a message box immediately before InfinityLoader resumes the game, and immediately after InfinityLoaderDLL.dll's initialization has started. 75 | ; This is useful for attaching a debugger before the game launches, and before the DLL is initialized. 76 | 77 | Pause=0 78 | 79 | ; These options are auto-generated by InfinityLoader. Do not manually edit! 80 | 81 | [Auto-Generated] 82 | 83 | ;------------------------------------------------ 84 | ;-- Patterns directly referenced by the loader -- 85 | ;------------------------------------------------ 86 | 87 | [Hardcoded_InternalLuaState] 88 | Pattern=4885C00F85DA000000 89 | Operations=ADD 12, PUSH, READ DWORD, ADD, ADD 4 90 | 91 | [Hardcoded_InternalPatchLocation] 92 | Pattern=B918190000 93 | Operations=ADD -32 94 | 95 | [Hardcoded_SDL_LogOutput()_fprintf] 96 | Pattern=488BC84C8BCF 97 | Operations=ADD 6 98 | 99 | [Hardcoded_WinMainPatchLocation] 100 | Pattern=4C8D0C45020000004C8BC3 101 | Operations=ADD -5 102 | 103 | [Hardcoded_free] 104 | Pattern=743753 105 | Operations=ADD -3 106 | 107 | [Hardcoded_malloc] 108 | Pattern=4883F9E0 109 | Operations=ADD -9 110 | -------------------------------------------------------------------------------- /EEex/loader/InfinityLoaderCommon.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/InfinityLoaderCommon.dll -------------------------------------------------------------------------------- /EEex/loader/InfinityLoaderDLL.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/InfinityLoaderDLL.dll -------------------------------------------------------------------------------- /EEex/loader/Lua52/LuaProvider.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/Lua52/LuaProvider.dll -------------------------------------------------------------------------------- /EEex/loader/LuaBindings-v2.6.6.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/LuaBindings-v2.6.6.0.dll -------------------------------------------------------------------------------- /EEex/loader/LuaBindingsCore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/LuaBindingsCore.dll -------------------------------------------------------------------------------- /EEex/loader/LuaJIT/LuaProvider.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/LuaJIT/LuaProvider.dll -------------------------------------------------------------------------------- /EEex/loader/LuaJIT/lua51.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bubb13/EEex/ae370a68b20079f3f255dd7d64cd60b534944748/EEex/loader/LuaJIT/lua51.dll -------------------------------------------------------------------------------- /EEex/patch/ACTION.IDS: -------------------------------------------------------------------------------- 1 | 472 EEex_LuaAction(S:Chunk*) 2 | 473 EEex_MatchObject(S:Chunk*) 3 | 473 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) 4 | 474 EEex_SetTarget(S:Name*,O:Target*) 5 | 476 EEex_SpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) 6 | 476 EEex_SpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) 7 | 477 EEex_SpellObjectOffsetNoDec(O:Target*,I:Spell*Spell,P:Offset*) 8 | 477 EEex_SpellObjectOffsetNoDecRES(S:RES*,O:Target*,P:Offset*) 9 | 478 EEex_ForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) 10 | 478 EEex_ForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) 11 | 479 EEex_ReallyForceSpellObjectOffset(O:Target*,I:Spell*Spell,P:Offset*) 12 | 479 EEex_ReallyForceSpellObjectOffsetRES(S:RES*,O:Target*,P:Offset*) -------------------------------------------------------------------------------- /EEex/patch/OBJECT.IDS: -------------------------------------------------------------------------------- 1 | 115 EEex_LuaObject 2 | 116 EEex_MatchObject 3 | 117 EEex_Target 4 | 118 EEex_LuaDecode -------------------------------------------------------------------------------- /EEex/patch/TRIGGER.IDS: -------------------------------------------------------------------------------- 1 | 0x410D EEex_HasDispellableEffect(O:Object*) 2 | 0x410E EEex_LuaTrigger(S:Chunk*) 3 | 0x410F EEex_IsImmuneToOpcode(O:Object*,I:Opcode*) 4 | 0x4110 EEex_MatchObject(S:Chunk*) 5 | 0x4110 EEex_MatchObjectEx(S:Chunk*,I:Nth*,I:Range*,I:Flags*X-MATOBJ) -------------------------------------------------------------------------------- /EEex/readme-EEex.html: -------------------------------------------------------------------------------- 1 | You will be redirected to EEex's Documentation shortly. If nothing happens, you can find EEex's Documentation: Here. 2 | 3 | 4 | --------------------------------------------------------------------------------