Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions BG3Extender/GameDefinitions/PropertyMaps/UI.inl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ INHERIT(Noesis::DependencyObject)
P_FREE_GETTER(VisualParent, Noesis::VisualHelpers::GetVisualParent)
P_FREE_GETTER(VisualChildrenCount, Noesis::VisualHelpers::GetVisualChildrenCount)
P_FUN(VisualChild, Noesis::VisualHelpers::GetVisualChild)
P_FUN(PointFromScreen, Noesis::VisualHelpers::PointFromScreen)
P_FUN(PointToScreen, Noesis::VisualHelpers::PointToScreen)
P_FUN(HitTest, Noesis::VisualHelpers::HitTest)
END_CLS()


Expand Down
3 changes: 3 additions & 0 deletions BG3Extender/GameDefinitions/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ namespace bg3se
void* Noesis__GUI__LoadXaml{ nullptr };
void* Noesis__Visual__AddVisualChild{ nullptr };
void* Noesis__Visual__RemoveVisualChild{ nullptr };
void* Noesis__Visual__PointFromScreen{ nullptr };
void* Noesis__Visual__PointToScreen{ nullptr };
void* Noesis__VisualTreeHelper__HitTest{ nullptr };

GlobalSwitches** ls__GlobalSwitches{ nullptr };

Expand Down
6 changes: 6 additions & 0 deletions BG3Extender/GameDefinitions/UI.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <NsGui/ObservableCollection.h>
#include <NsGui/UIElementCollection.h>
#include <NsGui/ContentControl.h>
#include <NsGui/VisualTreeHelper.h>

BEGIN_BARE_NS(Noesis)

Expand All @@ -29,6 +30,8 @@ struct GridLengthHelper
};

using LoadXamlProc = Ptr<BaseComponent>* (Ptr<BaseComponent>& ret, char const* path);
using VisualPointConversionProc = Point (Visual::*)(Point const& point) const;
using VisualTreeHelperHitTestProc = HitTestResult (*)(Visual* visual, Point const& point);

struct SymbolManagerInternals
{
Expand Down Expand Up @@ -213,6 +216,9 @@ struct VisualHelpers
static Visual* GetVisualParent(Visual const* o);
static uint32_t GetVisualChildrenCount(lua_State* L, Visual const* o);
static Visual* GetVisualChild(Visual const* o, uint32_t index);
static Point PointFromScreen(lua_State* L, Visual const* o, Point const& point);
static Point PointToScreen(lua_State* L, Visual const* o, Point const& point);
static Visual* HitTest(lua_State* L, Visual* o, Point const& point);
};

struct UIElementDataHelpers
Expand Down
36 changes: 36 additions & 0 deletions BG3Extender/GameHooks/BinaryMappings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,42 @@
<Target Type="Indirect" Offset="@ref2" Symbol="Noesis__Visual__AddVisualChild" />
</Mapping>

<Mapping Name="Noesis::Visual::PointToScreen" AllowFail="true">
@target 48 89 5c 24 18 // mov [rsp+18h], rbx
57 // push rdi
b8 00 01 00 00 // mov eax, 100h
e8 ?? ?? ?? ?? // call __alloca_probe
48 2b e0 // sub rsp, rax
0f 29 b4 24 f0 00 00 00 // movaps [rsp+0F0h], xmm6
0f 29 bc 24 e0 00 00 00 // movaps [rsp+0E0h], xmm7
48 8b 05 ?? ?? ?? ?? // mov rax, [__security_cookie]
48 33 c4 // xor rax, rsp
48 89 84 24 d0 00 00 00 // mov [rsp+0D0h], rax
f3 41 0f 10 30 // movss xmm6, dword ptr [r8]
48 8b fa // mov rdi, rdx; Point return buffer
f3 41 0f 10 78 04 // movss xmm7, dword ptr [r8+4]
48 8d 54 24 60 // lea rdx, [rsp+60h]
48 8b d9 // mov rbx, rcx; Visual this
<Target Type="Absolute" Offset="@target"
Symbol="Noesis__Visual__PointToScreen" />
</Mapping>

<Mapping Name="Noesis::VisualTreeHelper::HitTest" AllowFail="true">
4c 8d 47 18 // lea r8, [rdi+18h]; screen Point
48 8d 54 24 20 // lea rdx, [rsp+20h]; Point return buffer
@ref0 e8 ?? ?? ?? ?? // call Noesis::Visual::PointFromScreen
4c 8b c0 // mov r8, rax; local Point
48 8b 97 a8 00 00 00 // mov rdx, [rdi+0A8h]; Visual
48 8d 4c 24 28 // lea rcx, [rsp+28h]; HitTestResult return buffer
@ref1 e8 ?? ?? ?? ?? // call Noesis::VisualTreeHelper::HitTest
48 83 38 00 // cmp qword ptr [rax], 0; result.visualHit
0f 85 ?? ?? ?? ?? // jnz
<Target Type="Indirect" Offset="@ref0"
Symbol="Noesis__Visual__PointFromScreen" />
<Target Type="Indirect" Offset="@ref1"
Symbol="Noesis__VisualTreeHelper__HitTest" />
</Mapping>

<!-- Sigs: exposed as "IsReachable" -->
<Mapping Name="eoc::AiGrid::FindPath">
48 8b 83 a8 01 00 00 // mov rax, [rbx+1A8h]
Expand Down
3 changes: 3 additions & 0 deletions BG3Extender/GameHooks/DataLibrariesBG3Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ namespace bg3se
SYM_OFF(Noesis__GUI__LoadXaml);
SYM_OFF(Noesis__Visual__AddVisualChild);
SYM_OFF(Noesis__Visual__RemoveVisualChild);
SYM_OFF(Noesis__Visual__PointFromScreen);
SYM_OFF(Noesis__Visual__PointToScreen);
SYM_OFF(Noesis__VisualTreeHelper__HitTest);

SYM_OFF(AppInstance);

Expand Down
67 changes: 67 additions & 0 deletions BG3Extender/Lua/Libs/ClientUI/NsHelpers.inl
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,73 @@ Visual* VisualHelpers::GetVisualChild(Visual const* o, uint32_t index)
}
}

namespace
{
void EnsureVisualClientThread(lua_State* L, char const* functionName)
{
auto state = lua::State::FromLua(L);
if (state == nullptr
|| !state->IsClient()
|| GetCurrentContextType() != ContextType::Client) {
luaL_error(L, "%s() can only be called from Client Lua", functionName);
return;
}

if (state->GetExtensionState().GetOwningThread() != GetCurrentThreadId()) {
luaL_error(L, "%s() can only be called on the Client owning thread", functionName);
}
}

void* RequireNoesisMapping(lua_State* L, void* address, char const* symbol)
{
if (address == nullptr) {
luaL_error(L, "%s mapping is unavailable for the current game version", symbol);
return nullptr;
}

return address;
}

Point InvokeVisualPointConversion(Visual const* visual, Point const& point, void* address)
{
// Visual has multiple inheritance in its ancestry, so a MSVC member-function
// pointer may be wider than a code pointer. Zero-initialize its adjustment
// fields and copy only the mapped function address into its code slot.
VisualPointConversionProc proc{};
static_assert(sizeof(proc) >= sizeof(address));
memcpy(&proc, &address, sizeof(address));
return (visual->*proc)(point);
}
}

Point VisualHelpers::PointFromScreen(lua_State* L, Visual const* o, Point const& point)
{
EnsureVisualClientThread(L, "PointFromScreen");
auto address = RequireNoesisMapping(L,
GetStaticSymbols().Noesis__Visual__PointFromScreen,
"Noesis::Visual::PointFromScreen");
return InvokeVisualPointConversion(o, point, address);
}

Point VisualHelpers::PointToScreen(lua_State* L, Visual const* o, Point const& point)
{
EnsureVisualClientThread(L, "PointToScreen");
auto address = RequireNoesisMapping(L,
GetStaticSymbols().Noesis__Visual__PointToScreen,
"Noesis::Visual::PointToScreen");
return InvokeVisualPointConversion(o, point, address);
}

Visual* VisualHelpers::HitTest(lua_State* L, Visual* o, Point const& point)
{
EnsureVisualClientThread(L, "HitTest");
auto address = RequireNoesisMapping(L,
GetStaticSymbols().Noesis__VisualTreeHelper__HitTest,
"Noesis::VisualTreeHelper::HitTest");
auto proc = reinterpret_cast<VisualTreeHelperHitTestProc>(address);
return proc(o, point).visualHit;
}

RoutedEvent* UIElementDataHelpers::GetEvent(UIElementData const* o, Symbol evt)
{
auto it = o->mEvents.Find(evt);
Expand Down
8 changes: 8 additions & 0 deletions BG3Extender/Lua/LuaSerializers.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ namespace bg3se::lua
inline LuaSerializer& serialize(LuaSerializer& s, STDWString& v) { return s.Visit(v); }
inline LuaSerializer& serialize(LuaSerializer& s, StringView& v) { return s.Visit(v); }
inline LuaSerializer& serialize(LuaSerializer& s, Noesis::Symbol& v) { return s.Visit(v); }
inline LuaSerializer& serialize(LuaSerializer& s, Noesis::Point& v)
{
s.BeginObject();
s.VisitProperty("X", v.x);
s.VisitProperty("Y", v.y);
s.EndObject();
return s;
}
inline LuaSerializer& serialize(LuaSerializer& s, ImguiHandle& v) { return s.Visit(v); }
inline LuaSerializer& serialize(LuaSerializer& s, Path& v) { return s.Visit(v); }
inline LuaSerializer& serialize(LuaSerializer& s, Guid& v) { return s.Visit(v); }
Expand Down