Skip to content

Commit

Permalink
.NET: Implement API wrappers on the C++/CLI side
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 18, 2024
1 parent a46d541 commit 130ddf9
Show file tree
Hide file tree
Showing 18 changed files with 874 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12893,7 +12893,11 @@ if(REF_BUILD_FRAMEWORK AND CMAKE_SIZEOF_VOID_P EQUAL 8) # build-framework
list(APPEND csharp-api_SOURCES
"csharp-api/Plugin.cpp"
"csharp-api/REFrameworkNET/API.cpp"
"csharp-api/REFrameworkNET/ManagedObject.cpp"
"csharp-api/REFrameworkNET/Method.cpp"
"csharp-api/REFrameworkNET/PluginManager.cpp"
"csharp-api/REFrameworkNET/TDB.cpp"
"csharp-api/REFrameworkNET/TypeDefinition.cpp"
)

list(APPEND csharp-api_SOURCES
Expand Down
12 changes: 7 additions & 5 deletions csharp-api/REFrameworkNET/API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
#include "./API.hpp"


REFrameworkNET::API::API(const REFrameworkPluginInitializeParam* param)
{
Console::WriteLine("REFrameworkNET.API Constructor called.");
s_api = reframework::API::initialize(param).get();
}

REFrameworkNET::API::API(uintptr_t param)
: m_api{ reframework::API::initialize(param) }
{
Console::WriteLine("REFrameworkNET.API Constructor called.");
s_api = reframework::API::initialize((const REFrameworkPluginInitializeParam*)param).get();
}

REFrameworkNET::API::~API()
{
Console::WriteLine("REFrameworkNET.API Destructor called.");
}

reframework::API^ REFrameworkNET::API::Get() {
return m_api;
}
48 changes: 45 additions & 3 deletions csharp-api/REFrameworkNET/API.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#pragma once

#include <reframework/API.h>

#pragma managed

#include <msclr/marshal_cppstd.h>

#include "TypeInfo.hpp"
#include "ManagedObject.hpp"
#include "TDB.hpp"
#include "ManagedSingleton.hpp"

namespace reframework {
ref class API;
class API;
}

using namespace System;
Expand All @@ -11,12 +21,44 @@ namespace REFrameworkNET {
public ref class API
{
public:
API(const REFrameworkPluginInitializeParam* param);
API(uintptr_t param);
~API();

reframework::API^ Get();
inline REFrameworkNET::TDB^ GetTDB() {
return gcnew REFrameworkNET::TDB(s_api->tdb());
}

inline REFrameworkNET::ManagedObject^ GetManagedSingleton(System::String^ name) {
auto result = s_api->get_managed_singleton(msclr::interop::marshal_as<std::string>(name));

if (result == nullptr) {
return nullptr;
}

return gcnew REFrameworkNET::ManagedObject(result);
}

inline System::Collections::Generic::List<REFrameworkNET::ManagedSingleton^>^ GetManagedSingletons() {
auto singletons = s_api->get_managed_singletons();
auto result = gcnew System::Collections::Generic::List<REFrameworkNET::ManagedSingleton^>();

for (auto& singleton : singletons) {
if (singleton.instance == nullptr) {
continue;
}

result->Add(gcnew REFrameworkNET::ManagedSingleton(
gcnew REFrameworkNET::ManagedObject(singleton.instance),
gcnew REFrameworkNET::TypeDefinition(singleton.t),
gcnew REFrameworkNET::TypeInfo(singleton.type_info)
));
}

return result;
}

protected:
reframework::API^ m_api;
static reframework::API* s_api;
};
}
75 changes: 75 additions & 0 deletions csharp-api/REFrameworkNET/Field.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma once

#include <reframework/API.hpp>

#pragma managed

namespace REFrameworkNET {
ref class TypeDefinition;

public ref class Field {
public:
Field(const reframework::API::Field* field) : m_field(field) {}

System::String^ GetName() {
return gcnew System::String(m_field->get_name());
}

TypeDefinition^ GetDeclaringType() {
auto t = m_field->get_declaring_type();

if (t == nullptr) {
return nullptr;
}

return gcnew TypeDefinition(t);
}

TypeDefinition^ GetType() {
auto t = m_field->get_type();

if (t == nullptr) {
return nullptr;
}

return gcnew TypeDefinition(t);
}

uint32_t GetOffsetFromBase() {
return m_field->get_offset_from_base();
}

uint32_t GetOffsetFromFieldPtr() {
return m_field->get_offset_from_fieldptr();
}

uint32_t GetFlags() {
return m_field->get_flags();
}

bool IsStatic() {
return m_field->is_static();
}

bool IsLiteral() {
return m_field->is_literal();
}

uintptr_t GetInitDataPtr() {
return (uintptr_t)m_field->get_init_data();
}

uintptr_t GetDataRaw(uintptr_t obj, bool isValueType) {
return (uintptr_t)m_field->get_data_raw((void*)obj, isValueType);
}

// I have no idea if this will work correctly
template<typename T>
T GetData(uintptr_t obj, bool isValueType) {
return m_field->get_data<T>((void*)obj, isValueType);
}

private:
const reframework::API::Field* m_field;
};
}
35 changes: 35 additions & 0 deletions csharp-api/REFrameworkNET/InvokeRet.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <reframework/API.hpp>

#pragma managed

namespace REFrameworkNET {
public ref struct InvokeRet {
InvokeRet(const reframework::InvokeRet& ret) {
Bytes = gcnew array<uint8_t>(ret.bytes.size());
for (size_t i = 0; i < ret.bytes.size(); i++) {
Bytes[i] = ret.bytes[i];
}
Byte = ret.byte;
Word = ret.word;
DWord = ret.dword;
F = ret.f;
QWord = ret.qword;
D = ret.d;
Ptr = gcnew System::UIntPtr(ret.ptr);
ExceptionThrown = ret.exception_thrown;
}

// TODO: improve this? Does .NET have unions?
property array<uint8_t>^ Bytes;
property uint8_t Byte;
property uint16_t Word;
property uint32_t DWord;
property float F;
property uint64_t QWord;
property double D;
property System::Object^ Ptr;
property bool ExceptionThrown;
};
}
15 changes: 15 additions & 0 deletions csharp-api/REFrameworkNET/ManagedObject.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "TypeDefinition.hpp"

#include "ManagedObject.hpp"

namespace REFrameworkNET {
TypeDefinition^ ManagedObject::GetTypeDefinition() {
auto result = m_object->get_type_definition();

if (result == nullptr) {
return nullptr;
}

return gcnew TypeDefinition(result);
}
}
46 changes: 46 additions & 0 deletions csharp-api/REFrameworkNET/ManagedObject.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <reframework/API.hpp>

#pragma managed

namespace REFrameworkNET {
ref class TypeDefinition;

public ref class ManagedObject {
public:
ManagedObject(reframework::API::ManagedObject* obj) : m_object(obj) {
AddRef();
}
ManagedObject(::REFrameworkManagedObjectHandle handle) : m_object(reinterpret_cast<reframework::API::ManagedObject*>(handle)) {
AddRef();
}

~ManagedObject() {
if (m_object != nullptr) {
Release();
}
}

void AddRef() {
m_object->add_ref();
}

void Release() {
m_object->release();
}

void* Ptr() {
return (void*)m_object;
}

uintptr_t GetAddress() {
return (uintptr_t)m_object;
}

TypeDefinition^ GetTypeDefinition();

private:
reframework::API::ManagedObject* m_object;
};
}
24 changes: 24 additions & 0 deletions csharp-api/REFrameworkNET/ManagedSingleton.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <reframework/API.hpp>

#pragma managed

namespace REFrameworkNET {
ref class ManagedObject;
ref class TypeDefinition;
ref class TypeInfo;

public ref struct ManagedSingleton {
ManagedSingleton(ManagedObject^ instance, TypeDefinition^ type, TypeInfo^ typeInfo)
{
Instance = instance;
Type = type;
TypeInfo = typeInfo;
}

property ManagedObject^ Instance;
property TypeDefinition^ Type;
property TypeInfo^ TypeInfo;
};
}
57 changes: 57 additions & 0 deletions csharp-api/REFrameworkNET/Method.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "ManagedObject.hpp"

#include "Method.hpp"

namespace REFrameworkNET {
REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, array<System::Object^>^ args) {
// We need to convert the managed objects to 8 byte representations
std::vector<void*> args2{};
args2.resize(args->Length);

for (int i = 0; i < args->Length; ++i) {
//args2[i] = args[i]->ptr();
const auto t = args[i]->GetType();

if (t == System::Byte::typeid) {
uint8_t v = System::Convert::ToByte(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::UInt16::typeid) {
uint16_t v = System::Convert::ToUInt16(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::UInt32::typeid) {
uint32_t v = System::Convert::ToUInt32(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::Single::typeid) {
float v = System::Convert::ToSingle(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::UInt64::typeid) {
uint64_t v = System::Convert::ToUInt64(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::Double::typeid) {
double v = System::Convert::ToDouble(args[i]);
args2[i] = (void*)(uint64_t)v;
} else if (t == System::IntPtr::typeid) {
args2[i] = (void*)(uint64_t)System::Convert::ToInt64(args[i]);
} else {
//args2[i] = args[i]->ptr();
}
}

void* obj_ptr = nullptr;

if (obj != nullptr) {
const auto obj_t = obj->GetType();
if (obj_t == System::IntPtr::typeid || obj_t == System::UIntPtr::typeid) {
obj_ptr = (void*)(uint64_t)System::Convert::ToInt64(obj);
} else if (obj_t == REFrameworkNET::ManagedObject::typeid) {
obj_ptr = safe_cast<REFrameworkNET::ManagedObject^>(obj)->Ptr();
} else {
//obj_ptr = obj->ptr();
}
}

const auto native_result = m_method->invoke((reframework::API::ManagedObject*)obj_ptr, args2);

return gcnew REFrameworkNET::InvokeRet(native_result);
}
}
Loading

0 comments on commit 130ddf9

Please sign in to comment.