Fix trampoline crash by replacing MenuCursor hooks
The previous MenuCursor detour hooks crashed at startup with 'REL/Trampoline.cpp: Failed to handle allocation request' because the plugin never allocated F4SE trampoline memory. Replaced detours with a MainMenu::AdvanceMovie vtable hook (which requires no trampoline) that re-expands cursor bounds immediately after vanilla title-screen parallax runs. Added UIAdvanceMenusFunctionCompleteEvent sink as additional safety net. Changed main menu disableFocusMenu to true to avoid fighting vanilla cursor/parallax state.
This commit is contained in:
@@ -50,6 +50,12 @@ namespace F4T::PrismaUI
|
||||
bool g_hasLastCursorPos = false;
|
||||
POINT g_lastCursorPos{};
|
||||
|
||||
using MainMenuAdvanceMovieFn = void(RE::IMenu*, float, std::uint64_t);
|
||||
|
||||
std::uintptr_t g_originalMainMenuAdvanceMovie = 0;
|
||||
bool g_mainMenuAdvanceHookInstalled = false;
|
||||
bool g_menuAdvanceSinkRegistered = false;
|
||||
|
||||
// Forward declarations (definitions appear later in this anonymous namespace;
|
||||
// several functions call each other out of definition order).
|
||||
void HideMainMenuInternal();
|
||||
@@ -60,6 +66,46 @@ namespace F4T::PrismaUI
|
||||
void CreateServerBrowserView();
|
||||
void SyncBrowserHover();
|
||||
void WatchMainMenuState();
|
||||
void InstallMainMenuCursorReleaseHook();
|
||||
void RegisterMenuAdvanceSink();
|
||||
void ReleaseMenuCursorConstraints();
|
||||
|
||||
[[nodiscard]] bool ShouldOverrideMenuCursorConstraints()
|
||||
{
|
||||
return g_mainMenuVisible || g_browserVisible;
|
||||
}
|
||||
|
||||
void MainMenuAdvanceMovieHook(RE::IMenu* a_this, float a_timeDelta, std::uint64_t a_time)
|
||||
{
|
||||
if (g_originalMainMenuAdvanceMovie != 0) {
|
||||
reinterpret_cast<MainMenuAdvanceMovieFn*>(g_originalMainMenuAdvanceMovie)(a_this, a_timeDelta, a_time);
|
||||
}
|
||||
|
||||
// MainMenu parallax updates MenuCursor bounds during AdvanceMovie; re-expand after vanilla runs.
|
||||
if (ShouldOverrideMenuCursorConstraints()) {
|
||||
ReleaseMenuCursorConstraints();
|
||||
}
|
||||
}
|
||||
|
||||
class MenuAdvanceSink :
|
||||
public RE::BSTEventSink<RE::UIAdvanceMenusFunctionCompleteEvent>
|
||||
{
|
||||
public:
|
||||
RE::BSEventNotifyControl ProcessEvent(
|
||||
const RE::UIAdvanceMenusFunctionCompleteEvent&,
|
||||
RE::BSTEventSource<RE::UIAdvanceMenusFunctionCompleteEvent>* a_source) override
|
||||
{
|
||||
(void)a_source;
|
||||
|
||||
if (ShouldOverrideMenuCursorConstraints()) {
|
||||
ReleaseMenuCursorConstraints();
|
||||
}
|
||||
|
||||
return RE::BSEventNotifyControl::kContinue;
|
||||
}
|
||||
};
|
||||
|
||||
MenuAdvanceSink g_menuAdvanceSink;
|
||||
|
||||
std::string GetLogPrefix()
|
||||
{
|
||||
@@ -78,6 +124,35 @@ namespace F4T::PrismaUI
|
||||
REX::WARN(std::string_view{ message });
|
||||
}
|
||||
|
||||
void InstallMainMenuCursorReleaseHook()
|
||||
{
|
||||
if (g_mainMenuAdvanceHookInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
REL::Relocation<std::uintptr_t> vtable{ RE::MainMenu::VTABLE[0] };
|
||||
g_originalMainMenuAdvanceMovie = vtable.write_vfunc(0x04, &MainMenuAdvanceMovieHook);
|
||||
if (g_originalMainMenuAdvanceMovie != 0) {
|
||||
g_mainMenuAdvanceHookInstalled = true;
|
||||
LogInfo("Installed MainMenu AdvanceMovie hook for MenuCursor release.");
|
||||
} else {
|
||||
LogWarning("MainMenu AdvanceMovie hook failed to install.");
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterMenuAdvanceSink()
|
||||
{
|
||||
if (g_menuAdvanceSinkRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* source = RE::UIAdvanceMenusFunctionCompleteEvent::GetEventSource()) {
|
||||
source->RegisterSink(&g_menuAdvanceSink);
|
||||
g_menuAdvanceSinkRegistered = true;
|
||||
LogInfo("Registered UIAdvanceMenusFunctionCompleteEvent sink for MenuCursor release.");
|
||||
}
|
||||
}
|
||||
|
||||
void SuppressNativeGameCursor()
|
||||
{
|
||||
auto* ui = RE::UI::GetSingleton();
|
||||
@@ -116,12 +191,15 @@ namespace F4T::PrismaUI
|
||||
return;
|
||||
}
|
||||
|
||||
// MainMenu keeps a tight MenuCursor box for title-screen parallax; clear it every frame
|
||||
// while our overlay owns input so the OS pointer can reach the full window.
|
||||
// MainMenu keeps a tight MenuCursor box for title-screen parallax; expand to the full
|
||||
// window while our overlay owns input so the OS pointer can reach every edge.
|
||||
menuCursor->SetCursorConstraintsRaw(0, 0, static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height));
|
||||
menuCursor->minCursorX = 0;
|
||||
menuCursor->minCursorY = 0;
|
||||
menuCursor->maxCursorX = width;
|
||||
menuCursor->maxCursorY = height;
|
||||
|
||||
::ClipCursor(nullptr);
|
||||
}
|
||||
|
||||
void ApplyBrowserPointerMode(bool a_mouseCursorVisible)
|
||||
@@ -136,12 +214,9 @@ namespace F4T::PrismaUI
|
||||
if (g_mainMenuVisible && g_api && g_api->IsValid(g_mainMenuView)) {
|
||||
activeView = g_mainMenuView;
|
||||
namespace_obj = "window.CO_App";
|
||||
// The main menu is the primary/standalone title-screen UI, not a panel opened
|
||||
// on top of another menu's cursor. Let PrismaUI open its own FocusMenu (kUsesCursor /
|
||||
// kCursor input context) instead of relying on vanilla MainMenu's cursor handling,
|
||||
// which re-centers the OS cursor each frame for its background camera parallax and
|
||||
// otherwise pins the pointer to the middle of the screen.
|
||||
disableFocusMenu = false;
|
||||
// MainMenu is already open with a working title-screen cursor. Keep PrismaUI's
|
||||
// FocusMenu overlay suppressed so we do not fight vanilla cursor/parallax state.
|
||||
disableFocusMenu = true;
|
||||
} else if (g_browserVisible && g_api && g_api->IsValid(g_serverBrowserView)) {
|
||||
activeView = g_serverBrowserView;
|
||||
namespace_obj = "window.CO_App";
|
||||
@@ -674,6 +749,8 @@ namespace F4T::PrismaUI
|
||||
void OnGameDataReady()
|
||||
{
|
||||
F4T::MainMenuInject::Install();
|
||||
InstallMainMenuCursorReleaseHook();
|
||||
RegisterMenuAdvanceSink();
|
||||
|
||||
g_api = PRISMA_UI_API::RequestPluginAPI<PRISMA_UI_API::IVPrismaUI4>();
|
||||
if (!g_api) {
|
||||
|
||||
Reference in New Issue
Block a user