Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add easy support for ExecuteEnhancedScript call #78

Merged
merged 4 commits into from
Mar 11, 2024
Merged
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
43 changes: 43 additions & 0 deletions include/nwnx_cplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,49 @@ struct NWNXCPlugin_InitInfo {
const char* nwn2_module_path;
/// Path to the NWNX4 user directory, where nwnx4_controller.exe is located.
const char* nwnx_install_path;
/// Function pointers to interact with the nwn2server instance
const struct NWNXCPlugin_NWN2Hooks* nwn2_hooks;
};

/// Bound to NWScript ExecuteScript function
/// @param outExecuted If not null, the bool will be set to true if the script
/// has been successfully executed.
typedef void(ExecuteScriptFn)(const char* sScript, uint32_t oTarget, bool* outExecuted);
/// Bound to NWScript ExecuteScriptEnhanced function
/// @param outExecuted If not null, the bool will be set to true if the script
/// has been successfully executed. This is used to differenciate between a
/// script returning the value -1 and a script not being executed.
typedef int32_t(ExecuteScriptEnhancedFn)(const char* sScriptName,
uint32_t oTarget,
bool bClearParams,
bool* outExecuted);
/// Bound to NWScript AddScriptParameterInt function. Bound values are not
/// shared between the NWScript and CPlugin environments
typedef void(AddScriptParameterIntFn)(int32_t nParam);
/// Bound to NWScript AddScriptParameterString function. Bound values are not
/// shared between the NWScript and CPlugin environments.
/// @warning The string value is only borrowed and must live at least until
/// ExecuteScriptEnhanced is called
typedef void(AddScriptParameterStringFn)(const char* sParam);
/// Bound to NWScript AddScriptParameterFloat function. Bound values are not
/// shared between the NWScript and CPlugin environments
typedef void(AddScriptParameterFloatFn)(float fParam);
/// Bound to NWScript AddScriptParameterObject function. Bound values are not
/// shared between the NWScript and CPlugin environments
typedef void(AddScriptParameterObjectFn)(uint32_t oParam);
/// Bound to NWScript ClearScriptParams function. Only clears the bound values
/// from the CPlugin environment
typedef void(ClearScriptParamsFn)();

/// Function pointers to interact with the nwn2server instance
struct NWNXCPlugin_NWN2Hooks {
ExecuteScriptFn* ExecuteScript;
ExecuteScriptEnhancedFn* ExecuteScriptEnhanced;
AddScriptParameterIntFn* AddScriptParameterInt;
AddScriptParameterStringFn* AddScriptParameterString;
AddScriptParameterFloatFn* AddScriptParameterFloat;
AddScriptParameterObjectFn* AddScriptParameterObject;
ClearScriptParamsFn* ClearScriptParams;
};

//
Expand Down
20 changes: 20 additions & 0 deletions src/hook/CPlugin.h
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
#ifndef NWNX4_CPLUGIN_H
#define NWNX4_CPLUGIN_H

#include "scriptManagement.h"
#include <functional>
#include <string>
#include <windows.h>

class CPlugin {
public:
struct NWN2Hooks {
using ExecuteScriptFn = decltype(NWScript::ExecuteScript);
using ExecuteScriptEnhancedFn = decltype(NWScript::ExecuteScriptEnhanced);
using AddScriptParameterIntFn = decltype(NWScript::AddScriptParameterInt);
using AddScriptParameterStringFn = decltype(NWScript::AddScriptParameterString);
using AddScriptParameterFloatFn = decltype(NWScript::AddScriptParameterFloat);
using AddScriptParameterObjectFn = decltype(NWScript::AddScriptParameterObject);
using ClearScriptParamsFn = decltype(NWScript::ClearScriptParams);

ExecuteScriptFn* ExecuteScript = NWScript::ExecuteScript;
ExecuteScriptEnhancedFn* ExecuteScriptEnhanced = NWScript::ExecuteScriptEnhanced;
AddScriptParameterIntFn* AddScriptParameterInt = NWScript::AddScriptParameterInt;
AddScriptParameterStringFn* AddScriptParameterString = NWScript::AddScriptParameterString;
AddScriptParameterFloatFn* AddScriptParameterFloat = NWScript::AddScriptParameterFloat;
AddScriptParameterObjectFn* AddScriptParameterObject = NWScript::AddScriptParameterObject;
ClearScriptParamsFn* ClearScriptParams = NWScript::ClearScriptParams;
};

struct InitInfo {
const char* dll_path;
const char* nwnx_user_path;
const char* nwn2_install_path;
const char* nwn2_home_path;
const char* nwn2_module_path;
const char* nwnx_install_path;
const struct NWN2Hooks* nwn2_hooks;
};

CPlugin(HINSTANCE hDLL, const InitInfo& initInfo);
Expand Down
5 changes: 5 additions & 0 deletions src/hook/hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "../misc/windows_utils.h"
#include "../nwnx_version.h"
#include "scorcohook.h"
#include "scriptManagement.h"
#include <Shlobj.h>
#include <codecvt>
#include <filesystem>
Expand Down Expand Up @@ -744,6 +745,9 @@ void loadPlugins()
= (std::filesystem::path(nwn2HomeDir) / "modules" / serverArgs["moduledir"]).string();
const char* nwn2ModulePathCStr = nwn2ModulePath.size() > 0 ? nwn2ModulePath.c_str() : nullptr;

// Get NWN2 hooks
struct CPlugin::NWN2Hooks hooks;

// Start loading plugins
for (auto& pluginPath : pluginList) {
if (++pluginPath.begin() == pluginPath.end()) {
Expand Down Expand Up @@ -788,6 +792,7 @@ void loadPlugins()
.nwn2_home_path = nwn2HomeDir.c_str(),
.nwn2_module_path = nwn2ModulePathCStr,
.nwnx_install_path = nwnxInstallDirStr.c_str(),
.nwn2_hooks = &hooks,
};

// Instantiate & initialize CPlugin
Expand Down
1 change: 1 addition & 0 deletions src/hook/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ NWNX4_Hook_lib = shared_library('NWNX4_Hook',
'hook.cpp',
'scorcohook.cpp',
'crashdump.cpp',
'scriptManagement.cpp',
'CPlugin.cpp',
'../plugins/plugin.cpp',
'../plugins/legacy_plugin.cpp',
Expand Down
180 changes: 180 additions & 0 deletions src/hook/scriptManagement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@

#include "scriptManagement.h"
#include <bit>
#include <cstdint>
#include <cstring>
#include <string>

#include "../misc/log.h"
extern std::unique_ptr<LogNWNX> logger;

constexpr uint32_t NWN_DEFAULT_EXECUTESCRIPT_ENH_PARAMS_LEN = 32;

struct NWN2Param {
uint32_t _;
uint32_t value;
uint32_t ptr;
uint32_t size;
uint32_t type;
};
static_assert(sizeof(NWN2Param) == 5 * 4);

static std::vector<NWN2Param> scriptparams;

// 1.23
// g_pVirtualMachine
constexpr uint32_t NWN2_OFFSET_CVIRTUALMACHINE = 0x00864424;
// CVirtualMachine::ExecuteScript
constexpr uint32_t NWN2_OFFSET_EXECUTESCRIPT = 0x0072B380;
constexpr uint32_t NWN2_OFFSET_EXECUTESCRIPT_ENH = 0x0072B050;

constexpr uint32_t NWN2_OFFSET_InitParam = 0x0055EA40;
constexpr uint32_t NWN2_OFFSET_CleanParam = 0x006b5cd0;

struct NWN2ParamsList {
struct NWN2Param* list;
size_t size;
};
static_assert(sizeof(NWN2ParamsList) == 8);

static struct NWN2ParamsList* nwn2_scriptparams = (struct NWN2ParamsList*)(0x0086F15C);
// static size_t scriptparams_count = 0;

struct CVirtualMachine {
uint32_t _;
uint32_t execscript_ret_value;
};
static CVirtualMachine** nwn2_vm
= std::bit_cast<struct CVirtualMachine**>(NWN2_OFFSET_CVIRTUALMACHINE);

// Function hooks
using CVirtualMachine_ExecuteScript_t = BOOL(__thiscall*)(CVirtualMachine* thisVM,
const NWN::CExoString& scriptName,
NWN::OBJECTID objectId,
uint32_t unknown1,
uint32_t unknown2);
static CVirtualMachine_ExecuteScript_t CVirtualMachine_ExecuteScript
= std::bit_cast<CVirtualMachine_ExecuteScript_t>(NWN2_OFFSET_EXECUTESCRIPT);

//
using CVirtualMachine_ExecuteScriptEnhanced_t
= int32_t(__thiscall*)(CVirtualMachine* thisVM,
const NWN::CExoString& scriptName,
NWN::OBJECTID objectID,
void* ParamList,
uint32_t unknow1,
uint32_t unknow2);
static CVirtualMachine_ExecuteScriptEnhanced_t CVirtualMachine_ExecuteScriptEnhanced
= std::bit_cast<CVirtualMachine_ExecuteScriptEnhanced_t>(NWN2_OFFSET_EXECUTESCRIPT_ENH);

// //
// using CVirtualMachine_InitParam_t = void(__thiscall*)(void* paramLst, uint32_t iNb);
// static CVirtualMachine_InitParam_t CVirtualMachine_InitParam
// = std::bit_cast<CVirtualMachine_InitParam_t>(NWN2_OFFSET_InitParam);

// //
// using CVirtualMachine_CleanParam_t = void(__thiscall*)(void* paramLst);
// static CVirtualMachine_CleanParam_t CVirtualMachine_CleanParam
// = std::bit_cast<CVirtualMachine_CleanParam_t>(NWN2_OFFSET_CleanParam);

namespace NWScript {

void ExecuteScript(const char* sScript, NWN::OBJECTID oTarget, bool* outExecuted)
{
logger->Trace("ExecuteScript %s, %lu", sScript, oTarget);
auto executed = CVirtualMachine_ExecuteScript(
*nwn2_vm,
NWN::CExoString {.m_sString = (char*)sScript, // un-const cast, safe as param is read only
.m_nBufferLength = strlen(sScript)},
oTarget, 1, 1);
if (outExecuted != nullptr)
*outExecuted = executed;
}

int32_t ExecuteScriptEnhanced(const char* sScriptName,
NWN::OBJECTID oTarget,
bool bClearParams,
bool* outExecuted)
{
logger->Trace("ExecuteScriptEnhanced %s, %lu", sScriptName, oTarget);

const NWN::CExoString script
= {.m_sString = (char*)sScriptName, .m_nBufferLength = strlen(sScriptName)};

NWN2ParamsList save = *nwn2_scriptparams;

nwn2_scriptparams->list = scriptparams.data();
nwn2_scriptparams->size = scriptparams.size();

// call the script
int retValue
= CVirtualMachine_ExecuteScriptEnhanced(*nwn2_vm, script, oTarget, nwn2_scriptparams, 1, 1);

if (outExecuted != nullptr)
*outExecuted = retValue != 0;

// Is the script ok?
if (retValue != 0)
retValue = (*nwn2_vm)->execscript_ret_value;
else
retValue = -1;

*nwn2_scriptparams = save;

return retValue;
}
void AddScriptParameterInt(int32_t nParam)
{
logger->Trace("AddScriptParameterInt %d", nParam);

scriptparams.push_back(NWN2Param {
._ = 0,
.value = std::bit_cast<uint32_t>(nParam),
.ptr = 0,
.size = 0,
.type = 0,
});
}
void AddScriptParameterString(const char* sParam)
{
logger->Trace("AddScriptParameterString %s", sParam);

scriptparams.push_back(NWN2Param {
._ = 0,
.value = 0,
.ptr = std::bit_cast<uint32_t>(sParam),
.size = strlen(sParam) + 1,
.type = 2,
});
}
void AddScriptParameterFloat(float fParam)
{
logger->Trace("AddScriptParameterFloat %f", fParam);

scriptparams.push_back(NWN2Param {
._ = 0,
.value = std::bit_cast<uint32_t>(fParam),
.ptr = 0,
.size = 0,
.type = 1,
});
}
void AddScriptParameterObject(NWN::OBJECTID oParam)
{
logger->Trace("AddScriptParameterObject %lu", oParam);

scriptparams.push_back(NWN2Param {
._ = 0,
.value = oParam,
.ptr = 0,
.size = 0,
.type = 4,
});
}

void ClearScriptParams()
{
logger->Trace("ClearScriptParams");
scriptparams.clear();
}
} // namespace NWScript
27 changes: 27 additions & 0 deletions src/hook/scriptManagement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if !defined(SCRIPTMANAGEMENT_H_INCLUDED)
#define SCRIPTMANAGEMENT_H_INCLUDED

#define DLLEXPORT extern "C" __declspec(dllexport)

#include "../NWN2Lib/NWN2.h"
#include "../NWN2Lib/NWN2Common.h"
#include <cstdint>

namespace NWScript {

extern "C" {
void ExecuteScript(const char* sScript, NWN::OBJECTID oTarget, bool* outExecuted = NULL);
int32_t ExecuteScriptEnhanced(const char* sScriptName,
NWN::OBJECTID oTarget,
bool bClearParams,
bool* outExecuted = NULL);
void AddScriptParameterInt(int32_t nParam);
void AddScriptParameterString(const char* sParam);
void AddScriptParameterFloat(float fParam);
void AddScriptParameterObject(NWN::OBJECTID oParam);
void ClearScriptParams();
}

} // namespace NWScript

#endif
Loading
Loading