Skip to content

Commit

Permalink
feat: VM native functions & more (#259)
Browse files Browse the repository at this point in the history
This is a semi-complete port of the BSScriptUtil native functions
implementation from CLibF4. I've tested native function registration
with primitives, strings, arrays, forms, and structs. Also fixed some
issues that prevented existing Papyrus functions from being invoked +
some other miscellaneous fixes that I had on my fork.
  • Loading branch information
Deweh authored Jul 24, 2024
1 parent 8a8a71d commit fd25015
Show file tree
Hide file tree
Showing 14 changed files with 1,423 additions and 141 deletions.
1,089 changes: 1,089 additions & 0 deletions include/RE/B/BSScriptUtil.h

Large diffs are not rendered by default.

85 changes: 77 additions & 8 deletions include/RE/B/BSTArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ namespace RE
class BSTArrayAllocatorFunctor : public BSTArrayBase::IAllocatorFunctor
{
public:
using propagate_on_container_move_assignment = std::true_type;

// members
Allocator* _allocator; // 00
};
Expand All @@ -66,6 +68,8 @@ namespace RE
class BSTArrayHeapAllocator
{
public:
using propagate_on_container_move_assignment = std::true_type;

[[nodiscard]] void* allocate(std::size_t a_size)
{
const auto mem = malloc(a_size);
Expand All @@ -83,16 +87,43 @@ namespace RE
class BSScrapArrayAllocator
{
public:
using propagate_on_container_move_assignment = std::false_type;

BSScrapArrayAllocator() noexcept = default;
BSScrapArrayAllocator(const BSScrapArrayAllocator&) = delete;
BSScrapArrayAllocator(BSScrapArrayAllocator&&) = delete;

~BSScrapArrayAllocator() noexcept = default;

BSScrapArrayAllocator& operator=(const BSScrapArrayAllocator&) = delete;
BSScrapArrayAllocator& operator=(BSScrapArrayAllocator&&) = delete;

void* allocate(std::size_t a_size)
{
const auto mem = _allocator->Allocate(a_size, 0);
if (!_allocator) {
_allocator = MemoryManager::GetSingleton()->GetThreadScrapHeap();
}

if (!_allocator) {
stl::report_and_fail("failed to get thread scrap heap"sv);
}

const auto mem = _allocator->Allocate(a_size, alignof(void*));
if (!mem) {
stl::report_and_fail("out of memory"sv);
stl::report_and_fail("failed to handle allocation request"sv);
} else {
return mem;
}
}

void deallocate(void* a_ptr)
{
if (_allocator) {
_allocator->Deallocate(a_ptr, 0);
} else {
stl::report_and_fail("failed to deallocate block"sv);
}
std::memset(mem, 0, a_size);
return mem;
}
void deallocate(void* a_ptr) { _allocator->Deallocate(a_ptr, 0); }

protected:
// members
Expand Down Expand Up @@ -125,11 +156,49 @@ namespace RE
// 4)
explicit BSTArray(size_type a_count) { resize(a_count); }

BSTArray(const BSTArray& a_rhs)
{
clear();
reserve_exact(a_rhs.size());
for (const auto& i : a_rhs) {
emplace_back(i);
}
}

BSTArray(BSTArray&& a_rhs)
{
clear();
reserve_exact(a_rhs.size());
for (const auto& i : a_rhs) {
emplace_back(i);
}
a_rhs.clear();
}

BSTArray& operator=(const BSTArray& a_rhs)
{
clear();
reserve_exact(a_rhs.size());
for (const auto& i : a_rhs) {
emplace_back(i);
}
}

BSTArray& operator=(BSTArray&& a_rhs)
{
clear();
reserve_exact(a_rhs.size());
for (const auto& i : a_rhs) {
emplace_back(i);
}
a_rhs.clear();
}

~BSTArray()
{
if (capacity() > 0) {
clear();
allocator_type().deallocate(data());
allocator_type::deallocate(data());
set_data(nullptr);
set_capacity(0, 0);
}
Expand Down Expand Up @@ -270,12 +339,12 @@ namespace RE

const auto ndata =
static_cast<pointer>(
allocator_type().allocate(a_capacity * sizeof(value_type)));
allocator_type::allocate(a_capacity * sizeof(value_type)));
const auto odata = data();
if (ndata != odata) {
std::uninitialized_move_n(odata, size(), ndata);
std::destroy_n(odata, size());
allocator_type().deallocate(odata);
allocator_type::deallocate(odata);
set_data(ndata);
set_capacity(a_capacity, a_capacity * sizeof(value_type));
}
Expand Down
5 changes: 5 additions & 0 deletions include/RE/G/GameVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ namespace RE
return *singleton;
}

inline BSScript::IVirtualMachine* GetVM() const
{
return impl.get();
}

// members
std::uint64_t unkB0; // 00B0
std::uint64_t unkB8; // 00B8
Expand Down
73 changes: 45 additions & 28 deletions include/RE/I/IFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "RE/B/BSFixedString.h"
#include "RE/B/BSIntrusiveRefCounted.h"
#include "RE/T/TypeInfo.h"
#include "RE/S/StackFrame.h"
#include "RE/E/ErrorLogger.h"

namespace RE::BSScript
{
Expand All @@ -11,48 +13,63 @@ namespace RE::BSScript
class VirtualMachine;
}

class StackFrame;
class Variable;
class IVirtualMachine;
class VMClassInfo;
class VMClassRegistry;
class VMState;
class VMValue;

class IFunction
class IFunction :
public BSIntrusiveRefCounted
{
public:
IFunction() {}
virtual ~IFunction() {}

enum class CallResult : std::uint32_t
{
kCompleted,
kSetupForVM,
kInProgress,
kFailedRetry,
kFailedAbort
};

enum class FunctionType : std::uint32_t
{
kNormal,
kPropertyGetter,
kPropertySetter
};

struct Unk13
{
std::uint64_t unk00;
std::uint32_t unk08;
};

virtual BSFixedString* GetName(void) = 0;
virtual BSFixedString* GetClassName(void) = 0;
virtual BSFixedString* GetStateName(void) = 0;
virtual TypeInfo* GetReturnType(TypeInfo* a_typeInfo) = 0;
virtual std::uint64_t GetNumParams(void) = 0;
virtual std::uint64_t* GetParam(std::uint32_t a_idx, BSFixedString* a_nameOut, std::uint64_t* a_typeOut) = 0;
virtual std::uint64_t GetNumParams2(void) = 0;
virtual bool IsNative(void) = 0;
virtual bool IsStatic(void) = 0;
virtual bool Unk_0A(void) = 0;
virtual std::uint32_t Unk_0B(void) = 0;
virtual std::uint32_t GetUserFlags(void) = 0;
virtual BSFixedString* GetDocString(void) = 0;
virtual void Unk_0E(std::uint32_t a_unk) = 0;
virtual std::uint32_t Invoke(std::uint64_t a_unk0, std::uint64_t a_unk1, IVirtualMachine* a_vm, StackFrame* a_frame) = 0;
virtual BSFixedString* Unk_10(void) = 0; // file/line number?
virtual BSFixedString& GetName() = 0;
virtual BSFixedString& GetObjectTypeName() = 0;
virtual BSFixedString& GetStateName() = 0;
virtual TypeInfo* GetReturnType(TypeInfo* a_dst) = 0;
virtual std::uint64_t GetParamCount() = 0;
virtual TypeInfo* GetParam(std::uint32_t a_idx, BSFixedString* a_nameOut, TypeInfo* a_typeOut) = 0;
virtual std::uint64_t GetStackFrameSize() = 0;
virtual bool GetIsNative() = 0;
virtual bool GetIsStatic() = 0;
virtual bool GetIsEmpty() = 0;
virtual FunctionType GetFunctionType() = 0;
virtual std::uint32_t GetUserFlags() = 0;
virtual BSFixedString& GetDocString() = 0;
virtual void InsertLocals(std::uint32_t a_frame) = 0;
virtual CallResult Call(const BSTSmartPointer<Stack>& a_stack, ErrorLogger& a_errorLogger, Internal::VirtualMachine& a_vm, StackFrame* a_frame) = 0;
virtual BSFixedString& GetSourceFilename() = 0;
virtual bool TranslateIPToLineNumber(std::uint32_t a_instructionPointer, std::uint32_t* r_lineNumber) = 0;
virtual std::uint64_t* Unk_12(std::uint64_t* a_out) = 0; // new, might be type reflection
virtual Unk13* Unk_13(Unk13* a_out) = 0; // new, might be type reflection
virtual bool GetParamInfo(std::uint32_t a_idx, void* a_out) = 0; // param list stuff
virtual std::uint64_t* Unk_12(std::uint64_t* a_out) = 0; // new, might be type reflection
virtual Unk13* Unk_13(Unk13* a_out) = 0; // new, might be type reflection
virtual bool GetVarNameForStackIndex(std::uint32_t a_idx, BSFixedString& a_variableName) = 0;
virtual void* Unk_15(std::uint64_t a_arg0, std::uint64_t a_arg1) = 0; // param list stuff, loop
virtual bool GetUnk41(void) = 0;
virtual void SetUnk41(bool a_arg) = 0;

// members
BSIntrusiveRefCounted refCount; // 08
virtual bool CanBeCalledFromTasklets() = 0;
virtual void SetCallableFromTasklets(bool a_taskletCallable) = 0;
};
static_assert(sizeof(IFunction) == 0x10);
}
19 changes: 18 additions & 1 deletion include/RE/I/IStackCallbackSaveInterface.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
#pragma once

#include "RE/B/BSTSmartPointer.h"
#include "RE/B/BSIntrusiveRefCounted.h"

namespace RE
{
class BSStorage;

namespace BSScript
{
class IStackCallbackFunctor;
class Variable;

class __declspec(novtable) alignas(0x08) IStackCallbackFunctor :
public BSIntrusiveRefCounted
{
public:
virtual ~IStackCallbackFunctor(){}; // 00

// add
virtual void CallQueued() = 0; // 01
virtual void CallCanceled() = 0; // 02
virtual void StartMultiDispatch() = 0; // 03
virtual void EndMultiDispatch() = 0; // 04
virtual void operator()(Variable) = 0; // 05
virtual bool CanSave() { return false; }; // 06
};
static_assert(sizeof(IStackCallbackFunctor) == 0x10);

class __declspec(novtable) IStackCallbackSaveInterface
{
Expand Down
10 changes: 9 additions & 1 deletion include/RE/I/IVirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
namespace RE
{
template <class F>
using BSTThreadScrapFunction = msvc::function<F>;
using BSTThreadScrapFunction = std::function<F>;

namespace BSScript
{
Expand Down Expand Up @@ -133,6 +133,14 @@ namespace RE
virtual void PostCachedErrorToLogger(const ICachedErrorMessage& a_errorFunctor, ErrorLogger::Severity a_severity) const = 0; // 41
virtual void PostCachedErrorToLogger(const ICachedErrorMessage& a_errorFunctor, std::uint32_t a_stackID, ErrorLogger::Severity a_severity) const = 0; // 42

template <class F>
void BindNativeMethod(
stl::zstring a_object,
stl::zstring a_function,
F a_func,
std::optional<bool> a_taskletCallable,
bool a_isLatent);

void PostError(std::string_view a_msg, std::uint32_t a_stackID, ErrorLogger::Severity a_severity)
{
class ErrorImpl :
Expand Down
33 changes: 0 additions & 33 deletions include/RE/N/NativeFunction.h

This file was deleted.

Loading

0 comments on commit fd25015

Please sign in to comment.