Skip to content

Commit bc07f0f

Browse files
authored
Merge pull request #78 from Septirage/feature/executeenhancedscript_support
Add easy support for ExecuteEnhancedScript call
2 parents 69fee57 + 851e3b7 commit bc07f0f

13 files changed

+401
-2
lines changed

include/nwnx_cplugin.h

+43
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,49 @@ struct NWNXCPlugin_InitInfo {
2222
const char* nwn2_module_path;
2323
/// Path to the NWNX4 user directory, where nwnx4_controller.exe is located.
2424
const char* nwnx_install_path;
25+
/// Function pointers to interact with the nwn2server instance
26+
const struct NWNXCPlugin_NWN2Hooks* nwn2_hooks;
27+
};
28+
29+
/// Bound to NWScript ExecuteScript function
30+
/// @param outExecuted If not null, the bool will be set to true if the script
31+
/// has been successfully executed.
32+
typedef void(ExecuteScriptFn)(const char* sScript, uint32_t oTarget, bool* outExecuted);
33+
/// Bound to NWScript ExecuteScriptEnhanced function
34+
/// @param outExecuted If not null, the bool will be set to true if the script
35+
/// has been successfully executed. This is used to differenciate between a
36+
/// script returning the value -1 and a script not being executed.
37+
typedef int32_t(ExecuteScriptEnhancedFn)(const char* sScriptName,
38+
uint32_t oTarget,
39+
bool bClearParams,
40+
bool* outExecuted);
41+
/// Bound to NWScript AddScriptParameterInt function. Bound values are not
42+
/// shared between the NWScript and CPlugin environments
43+
typedef void(AddScriptParameterIntFn)(int32_t nParam);
44+
/// Bound to NWScript AddScriptParameterString function. Bound values are not
45+
/// shared between the NWScript and CPlugin environments.
46+
/// @warning The string value is only borrowed and must live at least until
47+
/// ExecuteScriptEnhanced is called
48+
typedef void(AddScriptParameterStringFn)(const char* sParam);
49+
/// Bound to NWScript AddScriptParameterFloat function. Bound values are not
50+
/// shared between the NWScript and CPlugin environments
51+
typedef void(AddScriptParameterFloatFn)(float fParam);
52+
/// Bound to NWScript AddScriptParameterObject function. Bound values are not
53+
/// shared between the NWScript and CPlugin environments
54+
typedef void(AddScriptParameterObjectFn)(uint32_t oParam);
55+
/// Bound to NWScript ClearScriptParams function. Only clears the bound values
56+
/// from the CPlugin environment
57+
typedef void(ClearScriptParamsFn)();
58+
59+
/// Function pointers to interact with the nwn2server instance
60+
struct NWNXCPlugin_NWN2Hooks {
61+
ExecuteScriptFn* ExecuteScript;
62+
ExecuteScriptEnhancedFn* ExecuteScriptEnhanced;
63+
AddScriptParameterIntFn* AddScriptParameterInt;
64+
AddScriptParameterStringFn* AddScriptParameterString;
65+
AddScriptParameterFloatFn* AddScriptParameterFloat;
66+
AddScriptParameterObjectFn* AddScriptParameterObject;
67+
ClearScriptParamsFn* ClearScriptParams;
2568
};
2669

2770
//

src/hook/CPlugin.h

+20
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
11
#ifndef NWNX4_CPLUGIN_H
22
#define NWNX4_CPLUGIN_H
33

4+
#include "scriptManagement.h"
45
#include <functional>
56
#include <string>
67
#include <windows.h>
78

89
class CPlugin {
910
public:
11+
struct NWN2Hooks {
12+
using ExecuteScriptFn = decltype(NWScript::ExecuteScript);
13+
using ExecuteScriptEnhancedFn = decltype(NWScript::ExecuteScriptEnhanced);
14+
using AddScriptParameterIntFn = decltype(NWScript::AddScriptParameterInt);
15+
using AddScriptParameterStringFn = decltype(NWScript::AddScriptParameterString);
16+
using AddScriptParameterFloatFn = decltype(NWScript::AddScriptParameterFloat);
17+
using AddScriptParameterObjectFn = decltype(NWScript::AddScriptParameterObject);
18+
using ClearScriptParamsFn = decltype(NWScript::ClearScriptParams);
19+
20+
ExecuteScriptFn* ExecuteScript = NWScript::ExecuteScript;
21+
ExecuteScriptEnhancedFn* ExecuteScriptEnhanced = NWScript::ExecuteScriptEnhanced;
22+
AddScriptParameterIntFn* AddScriptParameterInt = NWScript::AddScriptParameterInt;
23+
AddScriptParameterStringFn* AddScriptParameterString = NWScript::AddScriptParameterString;
24+
AddScriptParameterFloatFn* AddScriptParameterFloat = NWScript::AddScriptParameterFloat;
25+
AddScriptParameterObjectFn* AddScriptParameterObject = NWScript::AddScriptParameterObject;
26+
ClearScriptParamsFn* ClearScriptParams = NWScript::ClearScriptParams;
27+
};
28+
1029
struct InitInfo {
1130
const char* dll_path;
1231
const char* nwnx_user_path;
1332
const char* nwn2_install_path;
1433
const char* nwn2_home_path;
1534
const char* nwn2_module_path;
1635
const char* nwnx_install_path;
36+
const struct NWN2Hooks* nwn2_hooks;
1737
};
1838

1939
CPlugin(HINSTANCE hDLL, const InitInfo& initInfo);

src/hook/hook.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "../misc/windows_utils.h"
2323
#include "../nwnx_version.h"
2424
#include "scorcohook.h"
25+
#include "scriptManagement.h"
2526
#include <Shlobj.h>
2627
#include <codecvt>
2728
#include <filesystem>
@@ -744,6 +745,9 @@ void loadPlugins()
744745
= (std::filesystem::path(nwn2HomeDir) / "modules" / serverArgs["moduledir"]).string();
745746
const char* nwn2ModulePathCStr = nwn2ModulePath.size() > 0 ? nwn2ModulePath.c_str() : nullptr;
746747

748+
// Get NWN2 hooks
749+
struct CPlugin::NWN2Hooks hooks;
750+
747751
// Start loading plugins
748752
for (auto& pluginPath : pluginList) {
749753
if (++pluginPath.begin() == pluginPath.end()) {
@@ -788,6 +792,7 @@ void loadPlugins()
788792
.nwn2_home_path = nwn2HomeDir.c_str(),
789793
.nwn2_module_path = nwn2ModulePathCStr,
790794
.nwnx_install_path = nwnxInstallDirStr.c_str(),
795+
.nwn2_hooks = &hooks,
791796
};
792797

793798
// Instantiate & initialize CPlugin

src/hook/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ NWNX4_Hook_lib = shared_library('NWNX4_Hook',
55
'hook.cpp',
66
'scorcohook.cpp',
77
'crashdump.cpp',
8+
'scriptManagement.cpp',
89
'CPlugin.cpp',
910
'../plugins/plugin.cpp',
1011
'../plugins/legacy_plugin.cpp',

src/hook/scriptManagement.cpp

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
2+
#include "scriptManagement.h"
3+
#include <bit>
4+
#include <cstdint>
5+
#include <cstring>
6+
#include <string>
7+
8+
#include "../misc/log.h"
9+
extern std::unique_ptr<LogNWNX> logger;
10+
11+
constexpr uint32_t NWN_DEFAULT_EXECUTESCRIPT_ENH_PARAMS_LEN = 32;
12+
13+
struct NWN2Param {
14+
uint32_t _;
15+
uint32_t value;
16+
uint32_t ptr;
17+
uint32_t size;
18+
uint32_t type;
19+
};
20+
static_assert(sizeof(NWN2Param) == 5 * 4);
21+
22+
static std::vector<NWN2Param> scriptparams;
23+
24+
// 1.23
25+
// g_pVirtualMachine
26+
constexpr uint32_t NWN2_OFFSET_CVIRTUALMACHINE = 0x00864424;
27+
// CVirtualMachine::ExecuteScript
28+
constexpr uint32_t NWN2_OFFSET_EXECUTESCRIPT = 0x0072B380;
29+
constexpr uint32_t NWN2_OFFSET_EXECUTESCRIPT_ENH = 0x0072B050;
30+
31+
constexpr uint32_t NWN2_OFFSET_InitParam = 0x0055EA40;
32+
constexpr uint32_t NWN2_OFFSET_CleanParam = 0x006b5cd0;
33+
34+
struct NWN2ParamsList {
35+
struct NWN2Param* list;
36+
size_t size;
37+
};
38+
static_assert(sizeof(NWN2ParamsList) == 8);
39+
40+
static struct NWN2ParamsList* nwn2_scriptparams = (struct NWN2ParamsList*)(0x0086F15C);
41+
// static size_t scriptparams_count = 0;
42+
43+
struct CVirtualMachine {
44+
uint32_t _;
45+
uint32_t execscript_ret_value;
46+
};
47+
static CVirtualMachine** nwn2_vm
48+
= std::bit_cast<struct CVirtualMachine**>(NWN2_OFFSET_CVIRTUALMACHINE);
49+
50+
// Function hooks
51+
using CVirtualMachine_ExecuteScript_t = BOOL(__thiscall*)(CVirtualMachine* thisVM,
52+
const NWN::CExoString& scriptName,
53+
NWN::OBJECTID objectId,
54+
uint32_t unknown1,
55+
uint32_t unknown2);
56+
static CVirtualMachine_ExecuteScript_t CVirtualMachine_ExecuteScript
57+
= std::bit_cast<CVirtualMachine_ExecuteScript_t>(NWN2_OFFSET_EXECUTESCRIPT);
58+
59+
//
60+
using CVirtualMachine_ExecuteScriptEnhanced_t
61+
= int32_t(__thiscall*)(CVirtualMachine* thisVM,
62+
const NWN::CExoString& scriptName,
63+
NWN::OBJECTID objectID,
64+
void* ParamList,
65+
uint32_t unknow1,
66+
uint32_t unknow2);
67+
static CVirtualMachine_ExecuteScriptEnhanced_t CVirtualMachine_ExecuteScriptEnhanced
68+
= std::bit_cast<CVirtualMachine_ExecuteScriptEnhanced_t>(NWN2_OFFSET_EXECUTESCRIPT_ENH);
69+
70+
// //
71+
// using CVirtualMachine_InitParam_t = void(__thiscall*)(void* paramLst, uint32_t iNb);
72+
// static CVirtualMachine_InitParam_t CVirtualMachine_InitParam
73+
// = std::bit_cast<CVirtualMachine_InitParam_t>(NWN2_OFFSET_InitParam);
74+
75+
// //
76+
// using CVirtualMachine_CleanParam_t = void(__thiscall*)(void* paramLst);
77+
// static CVirtualMachine_CleanParam_t CVirtualMachine_CleanParam
78+
// = std::bit_cast<CVirtualMachine_CleanParam_t>(NWN2_OFFSET_CleanParam);
79+
80+
namespace NWScript {
81+
82+
void ExecuteScript(const char* sScript, NWN::OBJECTID oTarget, bool* outExecuted)
83+
{
84+
logger->Trace("ExecuteScript %s, %lu", sScript, oTarget);
85+
auto executed = CVirtualMachine_ExecuteScript(
86+
*nwn2_vm,
87+
NWN::CExoString {.m_sString = (char*)sScript, // un-const cast, safe as param is read only
88+
.m_nBufferLength = strlen(sScript)},
89+
oTarget, 1, 1);
90+
if (outExecuted != nullptr)
91+
*outExecuted = executed;
92+
}
93+
94+
int32_t ExecuteScriptEnhanced(const char* sScriptName,
95+
NWN::OBJECTID oTarget,
96+
bool bClearParams,
97+
bool* outExecuted)
98+
{
99+
logger->Trace("ExecuteScriptEnhanced %s, %lu", sScriptName, oTarget);
100+
101+
const NWN::CExoString script
102+
= {.m_sString = (char*)sScriptName, .m_nBufferLength = strlen(sScriptName)};
103+
104+
NWN2ParamsList save = *nwn2_scriptparams;
105+
106+
nwn2_scriptparams->list = scriptparams.data();
107+
nwn2_scriptparams->size = scriptparams.size();
108+
109+
// call the script
110+
int retValue
111+
= CVirtualMachine_ExecuteScriptEnhanced(*nwn2_vm, script, oTarget, nwn2_scriptparams, 1, 1);
112+
113+
if (outExecuted != nullptr)
114+
*outExecuted = retValue != 0;
115+
116+
// Is the script ok?
117+
if (retValue != 0)
118+
retValue = (*nwn2_vm)->execscript_ret_value;
119+
else
120+
retValue = -1;
121+
122+
*nwn2_scriptparams = save;
123+
124+
return retValue;
125+
}
126+
void AddScriptParameterInt(int32_t nParam)
127+
{
128+
logger->Trace("AddScriptParameterInt %d", nParam);
129+
130+
scriptparams.push_back(NWN2Param {
131+
._ = 0,
132+
.value = std::bit_cast<uint32_t>(nParam),
133+
.ptr = 0,
134+
.size = 0,
135+
.type = 0,
136+
});
137+
}
138+
void AddScriptParameterString(const char* sParam)
139+
{
140+
logger->Trace("AddScriptParameterString %s", sParam);
141+
142+
scriptparams.push_back(NWN2Param {
143+
._ = 0,
144+
.value = 0,
145+
.ptr = std::bit_cast<uint32_t>(sParam),
146+
.size = strlen(sParam) + 1,
147+
.type = 2,
148+
});
149+
}
150+
void AddScriptParameterFloat(float fParam)
151+
{
152+
logger->Trace("AddScriptParameterFloat %f", fParam);
153+
154+
scriptparams.push_back(NWN2Param {
155+
._ = 0,
156+
.value = std::bit_cast<uint32_t>(fParam),
157+
.ptr = 0,
158+
.size = 0,
159+
.type = 1,
160+
});
161+
}
162+
void AddScriptParameterObject(NWN::OBJECTID oParam)
163+
{
164+
logger->Trace("AddScriptParameterObject %lu", oParam);
165+
166+
scriptparams.push_back(NWN2Param {
167+
._ = 0,
168+
.value = oParam,
169+
.ptr = 0,
170+
.size = 0,
171+
.type = 4,
172+
});
173+
}
174+
175+
void ClearScriptParams()
176+
{
177+
logger->Trace("ClearScriptParams");
178+
scriptparams.clear();
179+
}
180+
} // namespace NWScript

src/hook/scriptManagement.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#if !defined(SCRIPTMANAGEMENT_H_INCLUDED)
2+
#define SCRIPTMANAGEMENT_H_INCLUDED
3+
4+
#define DLLEXPORT extern "C" __declspec(dllexport)
5+
6+
#include "../NWN2Lib/NWN2.h"
7+
#include "../NWN2Lib/NWN2Common.h"
8+
#include <cstdint>
9+
10+
namespace NWScript {
11+
12+
extern "C" {
13+
void ExecuteScript(const char* sScript, NWN::OBJECTID oTarget, bool* outExecuted = NULL);
14+
int32_t ExecuteScriptEnhanced(const char* sScriptName,
15+
NWN::OBJECTID oTarget,
16+
bool bClearParams,
17+
bool* outExecuted = NULL);
18+
void AddScriptParameterInt(int32_t nParam);
19+
void AddScriptParameterString(const char* sParam);
20+
void AddScriptParameterFloat(float fParam);
21+
void AddScriptParameterObject(NWN::OBJECTID oParam);
22+
void ClearScriptParams();
23+
}
24+
25+
} // namespace NWScript
26+
27+
#endif

0 commit comments

Comments
 (0)