diff --git a/sksevr_plugin/plugin/Globals.h b/sksevr_plugin/plugin/Globals.h new file mode 100644 index 00000000..d1d63ff2 --- /dev/null +++ b/sksevr_plugin/plugin/Globals.h @@ -0,0 +1,7 @@ +#pragma once +#include "skse64/PluginAPI.h" +extern PluginHandle g_pluginHandle; +extern SKSEPapyrusInterface* g_papyrus; +extern SKSEScaleformInterface* g_scaleform; +extern SKSETaskInterface* g_SkseTaskInterface; +extern SKSEObjectInterface* g_SkseObjectInterface; diff --git a/sksevr_plugin/plugin/PapyrusUIFix.cpp b/sksevr_plugin/plugin/PapyrusUIFix.cpp new file mode 100644 index 00000000..d6f9744f --- /dev/null +++ b/sksevr_plugin/plugin/PapyrusUIFix.cpp @@ -0,0 +1,196 @@ +#include "PapyrusUIFix.h" + +#include "skse64/GameMenus.h" +#include "skse64/ScaleformCallbacks.h" +#include "skse64/ScaleformMovie.h" +#include "skse64/PapyrusNativeFunctions.h" + +#include "skse64/PapyrusUI.h" +#include "skse64/Hooks_UI.h" +#include "skse64/Serialization.h" + +#include "Globals.h" + +namespace PapyrusUIFix +{ + template void SetGFxValue(GFxValue* val, T arg); + template <> void SetGFxValue(GFxValue* val, bool arg) { val->SetBool(arg); } + template <> void SetGFxValue(GFxValue* val, float arg) { val->SetNumber(arg); } + + // Disabled so we don't accidentally use this + //template <> void SetGFxValue(GFxValue* val, UInt32 arg) { val->SetNumber(arg); } + + template <> void SetGFxValue(GFxValue* val, SInt32 arg) { val->SetNumber(arg); } + template <> void SetGFxValue(GFxValue* val, BSFixedString arg) + { + // lifetime of this string will not be managed by the scaleform runtime + val->SetString(arg.data); + } + + class UIInvokeDelegate : public UIDelegate_v1, public ISKSEObject + { + public: + UIInvokeDelegate(const char* nameBuf, const char* targetBuf) : + menuName_(nameBuf), + target_(targetBuf) + {} + + explicit UIInvokeDelegate(SerializationTag tag) { + + } + + virtual const char* ClassName() const { return "SkyUI-UIInvokeDelegate"; } + virtual UInt32 ClassVersion() const { return 1; } + + virtual void Run() override + { + MenuManager* mm = MenuManager::GetSingleton(); + if (!mm) + return; + + BSFixedString t(menuName_.c_str()); + GFxMovieView* view = mm->GetMovieView(&t); + if (!view) + return; + + GFxValue* value = NULL; + if (args.size() > 0) + value = &args[0]; + + view->Invoke(target_.c_str(), NULL, value, args.size()); + } + virtual void Dispose() override + { + delete this; + } + + virtual bool Save(SKSESerializationInterface* intfc) + { + using namespace Serialization; + + if (!WriteData(intfc, &menuName_)) + return false; + + if (!WriteData(intfc, &target_)) + return false; + + if (!WriteData(intfc, &type_)) + return false; + + if (!WriteData(intfc, &handle_)) + return false; + + return true; + } + + virtual bool Load(SKSESerializationInterface* intfc, UInt32 version) + { + using namespace Serialization; + + if (!ReadData(intfc, &menuName_)) + return false; + + if (!ReadData(intfc, &target_)) + return false; + + if (!ReadData(intfc, &type_)) + return false; + + if (!ReadData(intfc, &handle_)) + return false; + + UInt64 fixedHandle; + if (intfc->ResolveHandle(handle_, &fixedHandle)) + handle_ = fixedHandle; + + return true; + } + + + std::vector args; + + private: + std::string menuName_; + std::string target_; + + UInt32 type_; + UInt64 handle_; + }; + + template + void SetT(StaticFunctionTag* thisInput, BSFixedString menuName, BSFixedString targetStr, T value) + { + if (!menuName.data || !targetStr.data) + return; + + MenuManager* mm = MenuManager::GetSingleton(); + if (!mm) + return; + + GFxMovieView* view = mm->GetMovieView(&menuName); + if (!view) + return; + + GFxValue fxValue; + PapyrusUIFix::SetGFxValue(&fxValue, value); + + view->SetVariable(targetStr.data, &fxValue, 1); + } + + template + void InvokeArgT(StaticFunctionTag* thisInput, BSFixedString menuName, BSFixedString targetStr, T arg) + { + if (!menuName.data || !targetStr.data) + return; + + if (!g_SkseTaskInterface) + return; + + UIInvokeDelegate* cmd = new UIInvokeDelegate(menuName.data, targetStr.data); + + cmd->args.resize(1); + PapyrusUIFix::SetGFxValue(&cmd->args[0], arg); + + g_SkseTaskInterface->AddUITask(cmd); + } + + template + void InvokeArrayT(StaticFunctionTag* thisInput, BSFixedString menuName, BSFixedString targetStr, VMArray args) + { + if (!menuName.data || !targetStr.data) + return; + + if (!g_SkseTaskInterface) + return; + + UInt32 argCount = args.Length(); + + UIInvokeDelegate* cmd = new UIInvokeDelegate(menuName.data, targetStr.data); + + cmd->args.resize(argCount); + for (UInt32 i = 0; i < argCount; i++) + { + T arg; + args.Get(&arg, i); + PapyrusUIFix::SetGFxValue(&cmd->args[i], arg); + } + + g_SkseTaskInterface->AddUITask(cmd); + } + + bool RegisterPapyrusFuncs(VMClassRegistry* registry) { + SKSEObjectRegistry& objectRegistry = g_SkseObjectInterface->GetObjectRegistry(); + objectRegistry.RegisterClass(); + + registry->RegisterFunction( + new NativeFunction3 ("SetInt", "UI", SetT, registry)); + + registry->RegisterFunction( + new NativeFunction3 ("InvokeInt", "UI", InvokeArgT, registry)); + + registry->RegisterFunction( + new NativeFunction3 >("InvokeIntA", "UI", InvokeArrayT, registry)); + + return true; + } +} diff --git a/sksevr_plugin/plugin/PapyrusUIFix.h b/sksevr_plugin/plugin/PapyrusUIFix.h new file mode 100644 index 00000000..3329f04e --- /dev/null +++ b/sksevr_plugin/plugin/PapyrusUIFix.h @@ -0,0 +1,8 @@ +#pragma once + +#include "skse64/PapyrusObjects.h" + +namespace PapyrusUIFix +{ + bool RegisterPapyrusFuncs(VMClassRegistry* registry); +} diff --git a/sksevr_plugin/plugin/main.cpp b/sksevr_plugin/plugin/main.cpp index f0abd71e..9fdfcebc 100644 --- a/sksevr_plugin/plugin/main.cpp +++ b/sksevr_plugin/plugin/main.cpp @@ -3,14 +3,20 @@ #include // CSIDL_MYCODUMENTS #include "FormDB.h" -#include "ScaleformExtendedDataFix.h" #include "Keyboard.h" #include "ControllerStateHook.h" #include "Settings.h" -static PluginHandle g_pluginHandle = kPluginHandle_Invalid; -static SKSEPapyrusInterface* g_papyrus = nullptr; -static SKSEScaleformInterface* g_scaleform = nullptr; +// SKSE patches +#include "ScaleformExtendedDataFix.h" +#include "PapyrusUIFix.h" +#include "skse64/Hooks_UI.h" + +PluginHandle g_pluginHandle = kPluginHandle_Invalid; +SKSEPapyrusInterface* g_papyrus = nullptr; +SKSEScaleformInterface* g_scaleform = nullptr; +SKSETaskInterface* g_SkseTaskInterface = nullptr; +SKSEObjectInterface* g_SkseObjectInterface = nullptr; void WaitForDebugger(bool should_break = false) { while (!IsDebuggerPresent()) @@ -61,9 +67,15 @@ extern "C" { // We're storing all our data in lua FormDB::InitGlobalLuaVM(); + // Setup pointers to various SKSE interfaces + // Parts of the program may need to access these sparatically. + g_SkseTaskInterface = (SKSETaskInterface*)skse->QueryInterface(kInterface_Task); + g_SkseObjectInterface = (SKSEObjectInterface*)skse->QueryInterface(kInterface_Object); + // Register additional Papyrus functions g_papyrus = (SKSEPapyrusInterface *)skse->QueryInterface(kInterface_Papyrus); g_papyrus->Register(FormDB::RegisterPapyrusFuncs); // Expose FormDB lua functions to Papyrus + g_papyrus->Register(PapyrusUIFix::RegisterPapyrusFuncs); // Fix Papyrus=>SKSE=>Scaleform SInt32 conversion bug // Register Inventory item hooks g_scaleform = (SKSEScaleformInterface*)skse->QueryInterface(kInterface_Scaleform); @@ -82,6 +94,9 @@ extern "C" { // Start receiving constroller state updates ControllerStateHook::Init(); + + + _MESSAGE("Plugin loaded"); return true; diff --git a/sksevr_plugin/plugin/plugin.vcxproj b/sksevr_plugin/plugin/plugin.vcxproj index ada07ece..a5c34be5 100644 --- a/sksevr_plugin/plugin/plugin.vcxproj +++ b/sksevr_plugin/plugin/plugin.vcxproj @@ -144,8 +144,10 @@ + + @@ -154,6 +156,7 @@ + diff --git a/sksevr_plugin/plugin/plugin.vcxproj.filters b/sksevr_plugin/plugin/plugin.vcxproj.filters new file mode 100644 index 00000000..0a3cdb3f --- /dev/null +++ b/sksevr_plugin/plugin/plugin.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + + SKSE Patches + + + + + + + SKSE Patches + + + + + SKSE Patches + + + + + + + SKSE Patches + + + + + + + + + {6d3ec361-f98a-4172-b913-8e3f183dad14} + + + \ No newline at end of file