diff --git a/csharp-api/CMakeLists.txt b/csharp-api/CMakeLists.txt index 45053f047..eebfce4b1 100644 --- a/csharp-api/CMakeLists.txt +++ b/csharp-api/CMakeLists.txt @@ -142,6 +142,7 @@ set(csharp-api_SOURCES "REFrameworkNET/SystemString.cpp" "REFrameworkNET/TDB.cpp" "REFrameworkNET/TypeDefinition.cpp" + "REFrameworkNET/UnifiedObject.cpp" "REFrameworkNET/VM.cpp" "REFrameworkNET/API.hpp" "REFrameworkNET/Attributes/Plugin.hpp" @@ -156,6 +157,7 @@ set(csharp-api_SOURCES "REFrameworkNET/MethodHook.hpp" "REFrameworkNET/MethodParameter.hpp" "REFrameworkNET/NativeObject.hpp" + "REFrameworkNET/ObjectEnumerator.hpp" "REFrameworkNET/Plugin.hpp" "REFrameworkNET/PluginLoadContext.hpp" "REFrameworkNET/PluginManager.hpp" @@ -166,6 +168,7 @@ set(csharp-api_SOURCES "REFrameworkNET/TDB.hpp" "REFrameworkNET/TypeDefinition.hpp" "REFrameworkNET/TypeInfo.hpp" + "REFrameworkNET/UnifiedObject.hpp" "REFrameworkNET/Utility.hpp" "REFrameworkNET/VM.hpp" cmake.toml diff --git a/csharp-api/REFrameworkNET/IObject.hpp b/csharp-api/REFrameworkNET/IObject.hpp index b659a86c0..a2e6de48e 100644 --- a/csharp-api/REFrameworkNET/IObject.hpp +++ b/csharp-api/REFrameworkNET/IObject.hpp @@ -9,11 +9,12 @@ ref class TypeDefinition; ref struct InvokeRet; // Base interface of ManagedObject and NativeObject -public interface class IObject : public IProxyable { +public interface class IObject : public IProxyable, public System::IEquatable { InvokeRet^ Invoke(System::String^ methodName, array^ args); + System::Object^ Call(System::String^ methodName, ... array^ args); + System::Object^ GetField(System::String^ fieldName); TypeDefinition^ GetTypeDefinition(); - bool IsProperObject(); // For interface types generic diff --git a/csharp-api/REFrameworkNET/IProxyable.hpp b/csharp-api/REFrameworkNET/IProxyable.hpp index 5bb44d1a2..80ecc22cb 100644 --- a/csharp-api/REFrameworkNET/IProxyable.hpp +++ b/csharp-api/REFrameworkNET/IProxyable.hpp @@ -11,5 +11,6 @@ public interface class IProxyable { virtual bool IsProxy(); bool HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result); + bool HandleTryGetMember_Internal(System::String^ fieldName, System::Object^% result); }; } \ No newline at end of file diff --git a/csharp-api/REFrameworkNET/ManagedObject.cpp b/csharp-api/REFrameworkNET/ManagedObject.cpp index 30ce79b94..d88f5c887 100644 --- a/csharp-api/REFrameworkNET/ManagedObject.cpp +++ b/csharp-api/REFrameworkNET/ManagedObject.cpp @@ -33,244 +33,6 @@ namespace REFrameworkNET { return gcnew TypeInfo(result); } - REFrameworkNET::InvokeRet^ ManagedObject::Invoke(System::String^ methodName, array^ args) { - // Get method - auto t = this->GetTypeDefinition(); - if (t == nullptr) { - return nullptr; - } - - auto m = t->GetMethod(methodName); - - if (m == nullptr) { - return nullptr; - } - - return m->Invoke(this, args); - } - - bool ManagedObject::HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result) { - auto t = this->GetTypeDefinition(); - - if (t == nullptr) { - return false; - } - - auto method = t->FindMethod(methodName); - - if (method != nullptr) - { - // Re-used with ManagedObject::TryInvokeMember - return method->HandleInvokeMember_Internal(this, methodName, args, result); - } - - REFrameworkNET::API::LogInfo("Method not found: " + methodName); - - result = nullptr; - return false; - } - - bool ManagedObject::TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) - { - auto methodName = binder->Name; - return HandleInvokeMember_Internal(methodName, args, result); - } - - bool ManagedObject::TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result) - { - auto memberName = binder->Name; - auto t = this->GetTypeDefinition(); - - if (t == nullptr) { - return false; - } - - auto field = t->FindField(memberName); - - if (field != nullptr) - { - const auto field_type = field->GetType(); - - if (field_type == nullptr) { - return false; - } - - const auto raw_ft = (reframework::API::TypeDefinition*)field_type; - const uintptr_t addr = field->IsStatic() ? 0 : this->GetAddress(); - const auto vm_obj_type = field_type->GetVMObjType(); - - #define MAKE_TYPE_HANDLER(X, Y) \ - case ##X##_fnv: \ - result = gcnew Y(field->GetData<##Y##>(addr, field_type->IsValueType())); \ - break; - - switch (REFrameworkNET::hash(raw_ft->get_full_name())) { - MAKE_TYPE_HANDLER("System.Boolean", bool) - MAKE_TYPE_HANDLER("System.Byte", uint8_t) - MAKE_TYPE_HANDLER("System.SByte", int8_t) - MAKE_TYPE_HANDLER("System.Int16", int16_t) - MAKE_TYPE_HANDLER("System.UInt16", uint16_t) - MAKE_TYPE_HANDLER("System.Int32", int32_t) - MAKE_TYPE_HANDLER("System.UInt32", uint32_t) - MAKE_TYPE_HANDLER("System.Int64", int64_t) - MAKE_TYPE_HANDLER("System.UInt64", uint64_t) - MAKE_TYPE_HANDLER("System.Single", float) - MAKE_TYPE_HANDLER("System.Double", double) - MAKE_TYPE_HANDLER("System.Char", wchar_t) - MAKE_TYPE_HANDLER("System.IntPtr", intptr_t) - MAKE_TYPE_HANDLER("System.UIntPtr", uintptr_t) - case "System.String"_fnv: - { - if (field->IsLiteral()) { - result = gcnew System::String((const char*)field->GetInitDataPtr()); - break; - } - - // TODO: Check if this half of it works - auto strObject = field->GetData(addr, field_type->IsValueType()); - - if (strObject == nullptr) { - result = nullptr; - break; - } - - const auto firstCharField = field_type->GetField("_firstChar"); - uint32_t offset = 0; - - if (firstCharField != nullptr) { - offset = field_type->IsValueType() ? firstCharField->GetOffsetFromFieldPtr() : firstCharField->GetOffsetFromBase(); - } else { - const auto fieldOffset = *(uint32_t*)(*(uintptr_t*)strObject - sizeof(void*)); - offset = fieldOffset + 4; - } - - wchar_t* chars = (wchar_t*)((uintptr_t)strObject + offset); - result = gcnew System::String(chars); - break; - } - default: - if (vm_obj_type > VMObjType::NULL_ && vm_obj_type < VMObjType::ValType) { - switch (vm_obj_type) { - case VMObjType::Array: - //return sol::make_object(l, *(::sdk::SystemArray**)data); - result = nullptr; - break; // TODO: Implement array - default: { - //const auto td = utility::re_managed_object::get_type_definition(*(::REManagedObject**)data); - auto& obj = field->GetData(addr, field_type->IsValueType()); - - if (obj == nullptr) { - result = nullptr; - break; - } - - auto td = gcnew REFrameworkNET::TypeDefinition(obj->get_type_definition()); - - // another fallback incase the method returns an object which is an array - if (td != nullptr && td->GetVMObjType() == VMObjType::Array) { - //return sol::make_object(l, *(::sdk::SystemArray**)data); - result = nullptr; - break; - } - - result = gcnew ManagedObject(obj); - break; - } - } - } else { - switch (field_type->GetSize()) { - case 8: - result = gcnew System::UInt64(field->GetData(addr, field_type->IsValueType())); - break; - case 4: - result = gcnew System::UInt32(field->GetData(addr, field_type->IsValueType())); - break; - case 2: - result = gcnew System::UInt16(field->GetData(addr, field_type->IsValueType())); - break; - case 1: - result = gcnew System::Byte(field->GetData(addr, field_type->IsValueType())); - break; - default: - result = nullptr; - break; - } - - break; - } - }; - - return true; - } - - /*auto property = t->FindProperty(memberName); - - if (property != nullptr) - { - result = property->GetValue(this); - return true; - }*/ - - REFrameworkNET::API::LogInfo("Member not found: " + memberName); - - result = nullptr; - return false; - } - - bool ManagedObject::TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value) - { - auto memberName = binder->Name; - auto t = this->GetTypeDefinition(); - - if (t == nullptr) { - return false; - } - - auto field = t->FindField(memberName); - - if (field != nullptr) - { - const auto field_type = field->GetType(); - - if (field_type == nullptr) { - return false; - } - - const auto raw_ft = (reframework::API::TypeDefinition*)field_type; - const uintptr_t addr = field->IsStatic() ? 0 : this->GetAddress(); - const auto vm_obj_type = field_type->GetVMObjType(); - - #define MAKE_TYPE_HANDLER_SET(X, Y) \ - case ##X##_fnv: \ - field->GetData<##Y##>(addr, field_type->IsValueType()) = (Y)value; \ - break; - - switch (REFrameworkNET::hash(raw_ft->get_full_name())) { - MAKE_TYPE_HANDLER_SET("System.Boolean", bool) - MAKE_TYPE_HANDLER_SET("System.Byte", uint8_t) - MAKE_TYPE_HANDLER_SET("System.SByte", int8_t) - MAKE_TYPE_HANDLER_SET("System.Int16", int16_t) - MAKE_TYPE_HANDLER_SET("System.UInt16", uint16_t) - MAKE_TYPE_HANDLER_SET("System.Int32", int32_t) - MAKE_TYPE_HANDLER_SET("System.UInt32", uint32_t) - MAKE_TYPE_HANDLER_SET("System.Int64", int64_t) - MAKE_TYPE_HANDLER_SET("System.UInt64", uint64_t) - MAKE_TYPE_HANDLER_SET("System.Single", float) - MAKE_TYPE_HANDLER_SET("System.Double", double) - MAKE_TYPE_HANDLER_SET("System.Char", wchar_t) - MAKE_TYPE_HANDLER_SET("System.IntPtr", intptr_t) - MAKE_TYPE_HANDLER_SET("System.UIntPtr", uintptr_t) - - default: - break; - } - - return true; - } - - return false; - } - generic T ManagedObject::As() { return ManagedProxy::Create(this); diff --git a/csharp-api/REFrameworkNET/ManagedObject.hpp b/csharp-api/REFrameworkNET/ManagedObject.hpp index 5b620c682..6bbc39dd7 100644 --- a/csharp-api/REFrameworkNET/ManagedObject.hpp +++ b/csharp-api/REFrameworkNET/ManagedObject.hpp @@ -3,6 +3,9 @@ #include #include "IObject.hpp" +#include "ObjectEnumerator.hpp" +#include "UnifiedObject.hpp" + #pragma managed namespace REFrameworkNET { @@ -11,7 +14,7 @@ ref class TypeInfo; ref class InvokeRet; ref class ManagedObject; -public ref class ManagedObject : public System::Dynamic::DynamicObject, public System::IEquatable, public REFrameworkNET::IObject +public ref class ManagedObject : public REFrameworkNET::UnifiedObject { public: ManagedObject(reframework::API::ManagedObject* obj) : m_object(obj) { @@ -48,67 +51,6 @@ public ref class ManagedObject : public System::Dynamic::DynamicObject, public S m_object->release(); } - virtual void* Ptr() { - return (void*)m_object; - } - - virtual uintptr_t GetAddress() { - return (uintptr_t)m_object; - } - - virtual bool IsProxy() { - return false; - } - - virtual bool IsProperObject() { - return true; - } - - virtual bool Equals(System::Object^ other) override { - if (System::Object::ReferenceEquals(this, other)) { - return true; - } - - if (System::Object::ReferenceEquals(other, nullptr)) { - return false; - } - - if (other->GetType() != ManagedObject::typeid) { - return false; - } - - return Ptr() == safe_cast(other)->Ptr(); - } - - // Override equality operator - virtual bool Equals(ManagedObject^ other) { - if (System::Object::ReferenceEquals(this, other)) { - return true; - } - - if (System::Object::ReferenceEquals(other, nullptr)) { - return false; - } - - return Ptr() == other->Ptr(); - } - - static bool operator ==(ManagedObject^ left, ManagedObject^ right) { - if (System::Object::ReferenceEquals(left, right)) { - return true; - } - - if (System::Object::ReferenceEquals(left, nullptr) || System::Object::ReferenceEquals(right, nullptr)) { - return false; - } - - return left->Ptr() == right->Ptr(); - } - - static bool operator !=(ManagedObject^ left, ManagedObject^ right) { - return !(left == right); - } - static bool IsManagedObject(uintptr_t ptr) { if (ptr == 0) { return false; @@ -134,18 +76,21 @@ public ref class ManagedObject : public System::Dynamic::DynamicObject, public S return ToManagedObject(ptr); } - virtual TypeDefinition^ GetTypeDefinition(); TypeInfo^ GetTypeInfo(); - virtual bool HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result); +public: // IObject + virtual void* Ptr() override { + return (void*)m_object; + } + + virtual uintptr_t GetAddress() override { + return (uintptr_t)m_object; + } - virtual REFrameworkNET::InvokeRet^ Invoke(System::String^ methodName, array^ args); - virtual bool TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) override; - virtual bool TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result) override; - virtual bool TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value) override; + virtual TypeDefinition^ GetTypeDefinition() override; generic - virtual T As(); + virtual T As() override; // TODO methods: /*public Void* GetReflectionProperties() { diff --git a/csharp-api/REFrameworkNET/Method.cpp b/csharp-api/REFrameworkNET/Method.cpp index abd12b158..a76d34eea 100644 --- a/csharp-api/REFrameworkNET/Method.cpp +++ b/csharp-api/REFrameworkNET/Method.cpp @@ -44,23 +44,17 @@ REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, arrayIsSubclassOf(REFrameworkNET::IObject::typeid)) { auto iobj = safe_cast(args[i]); - - if (iobj->IsProperObject()) { - args2[i] = iobj->Ptr(); - } else if (t == REFrameworkNET::TypeDefinition::typeid) { - // TypeDefinitions are wrappers for System.RuntimeTypeHandle - // However there's basically no functions that actually take a System.RuntimeTypeHandle - // so we will just convert it to a System.Type. - if (auto td = iobj->GetTypeDefinition(); td != nullptr) { - if (auto rt = td->GetRuntimeType(); rt != nullptr) { - args2[i] = rt->Ptr(); - } else { - System::Console::WriteLine("TypeDefinition has no runtime type @ arg " + i); - } - } + args2[i] = iobj->Ptr(); + } else if (t == REFrameworkNET::TypeDefinition::typeid) { + // TypeDefinitions are wrappers for System.RuntimeTypeHandle + // However there's basically no functions that actually take a System.RuntimeTypeHandle + // so we will just convert it to a System.Type. + auto td = safe_cast(args[i]); + + if (auto rt = td->GetRuntimeType(); rt != nullptr) { + args2[i] = rt->Ptr(); } else { - args2[i] = nullptr; - System::Console::WriteLine("Unknown IObject type passed to method invocation @ arg " + i); + System::Console::WriteLine("TypeDefinition has no runtime type @ arg " + i); } } else if (t == System::String::typeid) { auto createdStr = VM::CreateString(safe_cast(args[i])); diff --git a/csharp-api/REFrameworkNET/NativeObject.cpp b/csharp-api/REFrameworkNET/NativeObject.cpp index 038ce3b92..6f63a9390 100644 --- a/csharp-api/REFrameworkNET/NativeObject.cpp +++ b/csharp-api/REFrameworkNET/NativeObject.cpp @@ -6,44 +6,6 @@ #include "API.hpp" namespace REFrameworkNET { -InvokeRet^ NativeObject::Invoke(System::String^ methodName, array^ args) { - auto t = this->GetTypeDefinition(); - - if (t == nullptr) { - return nullptr; - } - - auto m = t->GetMethod(methodName); - - if (m == nullptr) { - return nullptr; - } - - return m->Invoke(this, args); -} - -bool NativeObject::HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result) { - auto t = this->GetTypeDefinition(); - - if (t == nullptr) { - return false; - } - - auto m = t->GetMethod(methodName); - - if (m == nullptr) { - REFrameworkNET::API::LogInfo("Method not found: " + methodName); - return false; - } - - return m->HandleInvokeMember_Internal(this, methodName, args, result); -} - -bool NativeObject::TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) -{ - return HandleInvokeMember_Internal(binder->Name, args, result); -} - generic T NativeObject::As() { return NativeProxy::Create(this); diff --git a/csharp-api/REFrameworkNET/NativeObject.hpp b/csharp-api/REFrameworkNET/NativeObject.hpp index c2863aded..5ec990ea5 100644 --- a/csharp-api/REFrameworkNET/NativeObject.hpp +++ b/csharp-api/REFrameworkNET/NativeObject.hpp @@ -4,7 +4,9 @@ #include "TypeDefinition.hpp" #include "InvokeRet.hpp" -#include "IObject.hpp" +#include "UnifiedObject.hpp" + +#include "ObjectEnumerator.hpp" namespace REFrameworkNET { ref class InvokeRet; @@ -13,7 +15,7 @@ ref class InvokeRet; // However, they still have reflection information associated with them // So this intends to be the "ManagedObject" class for native objects // So we can easily interact with them in C# -public ref class NativeObject : public System::Dynamic::DynamicObject, public System::Collections::IEnumerable, public REFrameworkNET::IObject +public ref class NativeObject : public REFrameworkNET::UnifiedObject { public: NativeObject(uintptr_t obj, TypeDefinition^ t){ @@ -46,96 +48,20 @@ public ref class NativeObject : public System::Dynamic::DynamicObject, public Sy m_object = nullptr; } - virtual TypeDefinition^ GetTypeDefinition() { + virtual TypeDefinition^ GetTypeDefinition() override { return m_type; } - virtual void* Ptr() { + virtual void* Ptr() override { return m_object; } - virtual uintptr_t GetAddress() { + virtual uintptr_t GetAddress() override { return (uintptr_t)m_object; } - virtual bool IsProxy() { - return false; - } - - virtual bool IsProperObject() { - return true; - } - - virtual InvokeRet^ Invoke(System::String^ methodName, array^ args); - - virtual bool HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result); - virtual bool TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) override; - generic - virtual T As(); - -public: - // IEnumerable implementation - virtual System::Collections::IEnumerator^ GetEnumerator() { - return gcnew NativeObjectEnumerator(this); - } - -private: - ref class NativeObjectEnumerator : public System::Collections::IEnumerator - { - int position = -1; - NativeObject^ nativeObject; - - public: - NativeObjectEnumerator(NativeObject^ nativeObject) { - this->nativeObject = nativeObject; - } - - // IEnumerator implementation - virtual bool MoveNext() { - int itemCount = GetItemCount(); - if (position < itemCount - 1) { - position++; - return true; - } - return false; - } - - virtual void Reset() { - position = -1; - } - - virtual property System::Object^ Current { - System::Object^ get() { - if (position == -1 || position >= GetItemCount()) { - throw gcnew System::InvalidOperationException(); - } - - System::Object^ result = nullptr; - if (nativeObject->HandleInvokeMember_Internal("get_Item", gcnew array{ position }, result)) { - return result; - } - - return nullptr; - } - } - - private: - int GetItemCount() { - //return nativeObject->Invoke("get_Count", gcnew array{})->DWord; - System::Object^ result = nullptr; - - if (nativeObject->HandleInvokeMember_Internal("get_Count", gcnew array{}, result)) { - return (int)result; - } - - if (nativeObject->HandleInvokeMember_Internal("get_Length", gcnew array{}, result)) { - return (int)result; - } - - return 0; - } - }; + virtual T As() override; private: void* m_object{}; diff --git a/csharp-api/REFrameworkNET/ObjectEnumerator.hpp b/csharp-api/REFrameworkNET/ObjectEnumerator.hpp new file mode 100644 index 000000000..536323a8a --- /dev/null +++ b/csharp-api/REFrameworkNET/ObjectEnumerator.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "IObject.hpp" + +namespace REFrameworkNET { +ref class ObjectEnumerator : public System::Collections::IEnumerator { +private: + int position = -1; + IObject^ nativeObject; + +public: + ObjectEnumerator(IObject^ nativeObject) { + this->nativeObject = nativeObject; + } + + // IEnumerator implementation + virtual bool MoveNext() { + int itemCount = GetItemCount(); + if (position < itemCount - 1) { + position++; + return true; + } + return false; + } + + virtual void Reset() { + position = -1; + } + + virtual property System::Object^ Current { + System::Object^ get() { + if (position == -1 || position >= GetItemCount()) { + throw gcnew System::InvalidOperationException(); + } + + System::Object^ result = nullptr; + if (nativeObject->HandleInvokeMember_Internal("get_Item", gcnew array{ position }, result)) { + return result; + } + + return nullptr; + } + } + +private: + int GetItemCount() { + //return nativeObject->Invoke("get_Count", gcnew array{})->DWord; + System::Object^ result = nullptr; + + if (nativeObject->HandleInvokeMember_Internal("get_Count", gcnew array{}, result)) { + return (int)result; + } + + if (nativeObject->HandleInvokeMember_Internal("get_Length", gcnew array{}, result)) { + return (int)result; + } + + return 0;} +}; +} \ No newline at end of file diff --git a/csharp-api/REFrameworkNET/Proxy.hpp b/csharp-api/REFrameworkNET/Proxy.hpp index df65116de..0a05538e4 100644 --- a/csharp-api/REFrameworkNET/Proxy.hpp +++ b/csharp-api/REFrameworkNET/Proxy.hpp @@ -33,10 +33,6 @@ public ref class Proxy : public Reflection::DispatchProxy, public IProxy, public virtual uintptr_t GetAddress() { return Instance->GetAddress(); } - - virtual bool IsProperObject() { - return Instance->IsProperObject(); - } virtual bool IsProxy() { return true; @@ -50,6 +46,19 @@ public ref class Proxy : public Reflection::DispatchProxy, public IProxy, public return Instance->HandleInvokeMember_Internal(methodName, args, result); } + virtual bool HandleTryGetMember_Internal(System::String^ fieldName, System::Object^% result) { + return Instance->HandleTryGetMember_Internal(fieldName, result); + } + + virtual System::Object^ GetField(System::String^ fieldName) { + return Instance->GetField(fieldName); + } + + virtual System::Object^ Call(System::String^ methodName, ... array^ args) { + return Instance->Call(methodName, args); + } + + // For interface types generic virtual T As() { @@ -88,6 +97,18 @@ public ref class Proxy : public Reflection::DispatchProxy, public IProxy, public return Equals(other); } + virtual bool Equals(IObject^ other) { + if (System::Object::ReferenceEquals(this, other)) { + return true; + } + + if (System::Object::ReferenceEquals(other, nullptr)) { + return false; + } + + return Ptr() == other->Ptr(); + } + int GetHashCode() override { return Instance->GetHashCode(); } diff --git a/csharp-api/REFrameworkNET/TypeDefinition.cpp b/csharp-api/REFrameworkNET/TypeDefinition.cpp index 89df7d449..2f9c8bf73 100644 --- a/csharp-api/REFrameworkNET/TypeDefinition.cpp +++ b/csharp-api/REFrameworkNET/TypeDefinition.cpp @@ -147,6 +147,6 @@ namespace REFrameworkNET { generic T TypeDefinition::As() { - return NativeProxy::Create(this); + return NativeProxy::Create(gcnew NativeObject(this)); } } \ No newline at end of file diff --git a/csharp-api/REFrameworkNET/TypeDefinition.hpp b/csharp-api/REFrameworkNET/TypeDefinition.hpp index 7177d4e87..0d763e3bc 100644 --- a/csharp-api/REFrameworkNET/TypeDefinition.hpp +++ b/csharp-api/REFrameworkNET/TypeDefinition.hpp @@ -27,8 +27,7 @@ public enum VMObjType { public ref class TypeDefinition : public System::Dynamic::DynamicObject, - public System::IEquatable, - public REFrameworkNET::IObject + public System::IEquatable { public: TypeDefinition(reframework::API::TypeDefinition* td) : m_type(td) {} @@ -324,18 +323,6 @@ public return (uintptr_t)m_type; } - virtual TypeDefinition^ GetTypeDefinition() { - return this; - } - - virtual bool IsProperObject() { - return false; - } - - virtual bool IsProxy() { - return false; - } - virtual REFrameworkNET::InvokeRet^ Invoke(System::String^ methodName, array^ args); virtual bool HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result); diff --git a/csharp-api/REFrameworkNET/UnifiedObject.cpp b/csharp-api/REFrameworkNET/UnifiedObject.cpp new file mode 100644 index 000000000..845fe6983 --- /dev/null +++ b/csharp-api/REFrameworkNET/UnifiedObject.cpp @@ -0,0 +1,249 @@ +#include "API.hpp" + +#include "TypeDefinition.hpp" +#include "UnifiedObject.hpp" + +#include "./Utility.hpp" + +namespace REFrameworkNET { + REFrameworkNET::InvokeRet^ UnifiedObject::Invoke(System::String^ methodName, array^ args) { + // Get method + auto t = this->GetTypeDefinition(); + if (t == nullptr) { + return nullptr; + } + + auto m = t->GetMethod(methodName); + + if (m == nullptr) { + return nullptr; + } + + return m->Invoke(this, args); + } + + bool UnifiedObject::HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result) { + auto t = this->GetTypeDefinition(); + + if (t == nullptr) { + return false; + } + + auto method = t->FindMethod(methodName); + + if (method != nullptr) + { + // Re-used with UnifiedObject::TryInvokeMember + return method->HandleInvokeMember_Internal(this, methodName, args, result); + } + + REFrameworkNET::API::LogInfo("Method not found: " + methodName); + + result = nullptr; + return false; + } + + bool UnifiedObject::TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) + { + auto methodName = binder->Name; + return HandleInvokeMember_Internal(methodName, args, result); + } + + bool UnifiedObject::TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result) { + auto memberName = binder->Name; + return HandleTryGetMember_Internal(memberName, result); + } + + bool UnifiedObject::HandleTryGetMember_Internal(System::String^ memberName, System::Object^% result) { + auto t = this->GetTypeDefinition(); + + if (t == nullptr) { + return false; + } + + auto field = t->FindField(memberName); + + if (field != nullptr) + { + const auto field_type = field->GetType(); + + if (field_type == nullptr) { + return false; + } + + const auto raw_ft = (reframework::API::TypeDefinition*)field_type; + const uintptr_t addr = field->IsStatic() ? 0 : this->GetAddress(); + const auto vm_obj_type = field_type->GetVMObjType(); + + #define MAKE_TYPE_HANDLER(X, Y) \ + case ##X##_fnv: \ + result = gcnew Y(field->GetData<##Y##>(addr, field_type->IsValueType())); \ + break; + + switch (REFrameworkNET::hash(raw_ft->get_full_name())) { + MAKE_TYPE_HANDLER("System.Boolean", bool) + MAKE_TYPE_HANDLER("System.Byte", uint8_t) + MAKE_TYPE_HANDLER("System.SByte", int8_t) + MAKE_TYPE_HANDLER("System.Int16", int16_t) + MAKE_TYPE_HANDLER("System.UInt16", uint16_t) + MAKE_TYPE_HANDLER("System.Int32", int32_t) + MAKE_TYPE_HANDLER("System.UInt32", uint32_t) + MAKE_TYPE_HANDLER("System.Int64", int64_t) + MAKE_TYPE_HANDLER("System.UInt64", uint64_t) + MAKE_TYPE_HANDLER("System.Single", float) + MAKE_TYPE_HANDLER("System.Double", double) + MAKE_TYPE_HANDLER("System.Char", wchar_t) + MAKE_TYPE_HANDLER("System.IntPtr", intptr_t) + MAKE_TYPE_HANDLER("System.UIntPtr", uintptr_t) + case "System.String"_fnv: + { + if (field->IsLiteral()) { + result = gcnew System::String((const char*)field->GetInitDataPtr()); + break; + } + + // TODO: Check if this half of it works + auto strObject = field->GetData(addr, field_type->IsValueType()); + + if (strObject == nullptr) { + result = nullptr; + break; + } + + const auto firstCharField = field_type->GetField("_firstChar"); + uint32_t offset = 0; + + if (firstCharField != nullptr) { + offset = field_type->IsValueType() ? firstCharField->GetOffsetFromFieldPtr() : firstCharField->GetOffsetFromBase(); + } else { + const auto fieldOffset = *(uint32_t*)(*(uintptr_t*)strObject - sizeof(void*)); + offset = fieldOffset + 4; + } + + wchar_t* chars = (wchar_t*)((uintptr_t)strObject + offset); + result = gcnew System::String(chars); + break; + } + default: + if (vm_obj_type > VMObjType::NULL_ && vm_obj_type < VMObjType::ValType) { + switch (vm_obj_type) { + case VMObjType::Array: + //return sol::make_object(l, *(::sdk::SystemArray**)data); + result = nullptr; + break; // TODO: Implement array + default: { + //const auto td = utility::re_managed_object::get_type_definition(*(::REManagedObject**)data); + auto& obj = field->GetData(addr, field_type->IsValueType()); + + if (obj == nullptr) { + result = nullptr; + break; + } + + auto td = gcnew REFrameworkNET::TypeDefinition(obj->get_type_definition()); + + // another fallback incase the method returns an object which is an array + if (td != nullptr && td->GetVMObjType() == VMObjType::Array) { + //return sol::make_object(l, *(::sdk::SystemArray**)data); + result = nullptr; + break; + } + + result = gcnew ManagedObject(obj); + break; + } + } + } else { + switch (field_type->GetSize()) { + case 8: + result = gcnew System::UInt64(field->GetData(addr, field_type->IsValueType())); + break; + case 4: + result = gcnew System::UInt32(field->GetData(addr, field_type->IsValueType())); + break; + case 2: + result = gcnew System::UInt16(field->GetData(addr, field_type->IsValueType())); + break; + case 1: + result = gcnew System::Byte(field->GetData(addr, field_type->IsValueType())); + break; + default: + result = nullptr; + break; + } + + break; + } + }; + + return true; + } + + /*auto property = t->FindProperty(memberName); + + if (property != nullptr) + { + result = property->GetValue(this); + return true; + }*/ + + REFrameworkNET::API::LogInfo("Member not found: " + memberName); + + result = nullptr; + return false; + } + + bool UnifiedObject::TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value) + { + auto memberName = binder->Name; + auto t = this->GetTypeDefinition(); + + if (t == nullptr) { + return false; + } + + auto field = t->FindField(memberName); + + if (field != nullptr) + { + const auto field_type = field->GetType(); + + if (field_type == nullptr) { + return false; + } + + const auto raw_ft = (reframework::API::TypeDefinition*)field_type; + const uintptr_t addr = field->IsStatic() ? 0 : this->GetAddress(); + const auto vm_obj_type = field_type->GetVMObjType(); + + #define MAKE_TYPE_HANDLER_SET(X, Y) \ + case ##X##_fnv: \ + field->GetData<##Y##>(addr, field_type->IsValueType()) = (Y)value; \ + break; + + switch (REFrameworkNET::hash(raw_ft->get_full_name())) { + MAKE_TYPE_HANDLER_SET("System.Boolean", bool) + MAKE_TYPE_HANDLER_SET("System.Byte", uint8_t) + MAKE_TYPE_HANDLER_SET("System.SByte", int8_t) + MAKE_TYPE_HANDLER_SET("System.Int16", int16_t) + MAKE_TYPE_HANDLER_SET("System.UInt16", uint16_t) + MAKE_TYPE_HANDLER_SET("System.Int32", int32_t) + MAKE_TYPE_HANDLER_SET("System.UInt32", uint32_t) + MAKE_TYPE_HANDLER_SET("System.Int64", int64_t) + MAKE_TYPE_HANDLER_SET("System.UInt64", uint64_t) + MAKE_TYPE_HANDLER_SET("System.Single", float) + MAKE_TYPE_HANDLER_SET("System.Double", double) + MAKE_TYPE_HANDLER_SET("System.Char", wchar_t) + MAKE_TYPE_HANDLER_SET("System.IntPtr", intptr_t) + MAKE_TYPE_HANDLER_SET("System.UIntPtr", uintptr_t) + + default: + break; + } + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/csharp-api/REFrameworkNET/UnifiedObject.hpp b/csharp-api/REFrameworkNET/UnifiedObject.hpp new file mode 100644 index 000000000..c6a0fd534 --- /dev/null +++ b/csharp-api/REFrameworkNET/UnifiedObject.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include "IObject.hpp" +#include "ObjectEnumerator.hpp" + +namespace REFrameworkNET { +ref class TypeDefinition; +ref struct InvokeRet; + +// UnifiedObject is the base class that ManagedObject and NativeObject will derive from +// It will have several shared methods but some unimplemented methods that will be implemented in the derived classes +public ref class UnifiedObject abstract : public System::Dynamic::DynamicObject, public System::Collections::IEnumerable, public REFrameworkNET::IObject { +public: + virtual bool IsProxy() { + return false; + } + + // These methods will be implemented in the derived classes + virtual void* Ptr() abstract = 0; + virtual uintptr_t GetAddress() abstract = 0; + virtual REFrameworkNET::TypeDefinition^ GetTypeDefinition() abstract = 0; + + generic + virtual T As() abstract = 0; + + // Shared methods + virtual bool HandleInvokeMember_Internal(System::String^ methodName, array^ args, System::Object^% result); + virtual bool HandleTryGetMember_Internal(System::String^ fieldName, System::Object^% result); + + virtual REFrameworkNET::InvokeRet^ Invoke(System::String^ methodName, array^ args); + virtual bool TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array^ args, System::Object^% result) override; + virtual bool TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result) override; + virtual bool TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value) override; + + virtual System::Object^ Call(System::String^ methodName, ... array^ args) { + System::Object^ result = nullptr; + HandleInvokeMember_Internal(methodName, args, result); + + return result; + } + + virtual System::Object^ GetField(System::String^ fieldName) { + System::Object^ result = nullptr; + if (!HandleTryGetMember_Internal(fieldName, result)) { + if (!HandleInvokeMember_Internal("get_" + fieldName, gcnew array{}, result)) { + // TODO? what else can we do here? + } + } + + return result; + } + +public: + virtual bool Equals(System::Object^ other) override { + if (System::Object::ReferenceEquals(this, other)) { + return true; + } + + if (System::Object::ReferenceEquals(other, nullptr)) { + return false; + } + + if (!other->GetType()->IsSubclassOf(IObject::typeid)) { + return false; + } + + return Ptr() == safe_cast(other)->Ptr(); + } + + // Override equality operator + virtual bool Equals(IObject^ other) { + if (System::Object::ReferenceEquals(this, other)) { + return true; + } + + if (System::Object::ReferenceEquals(other, nullptr)) { + return false; + } + + return Ptr() == other->Ptr(); + } + + static bool operator ==(UnifiedObject^ left, UnifiedObject^ right) { + if (System::Object::ReferenceEquals(left, right)) { + return true; + } + + if (System::Object::ReferenceEquals(left, nullptr) || System::Object::ReferenceEquals(right, nullptr)) { + return false; + } + + return left->Ptr() == right->Ptr(); + } + + static bool operator !=(UnifiedObject^ left, UnifiedObject^ right) { + return !(left == right); + } + +public: + // IEnumerable implementation + virtual System::Collections::IEnumerator^ GetEnumerator() { + return gcnew REFrameworkNET::ObjectEnumerator(this); + } +}; +} \ No newline at end of file diff --git a/csharp-api/test/Test/Test.cs b/csharp-api/test/Test/Test.cs index 2cd8ba80e..a2c7e8e08 100644 --- a/csharp-api/test/Test/Test.cs +++ b/csharp-api/test/Test/Test.cs @@ -18,17 +18,24 @@ public static void isInsidePostHook(ref System.Object retval) { public static void Entry() { var tdb = REFrameworkNET.API.GetTDB(); - tdb.GetType("app.CameraManager")?. + /*tdb.GetType("app.CameraManager")?. GetMethod("isInside")?. AddHook(false). AddPre(isInsidePreHook). - AddPost(isInsidePostHook); + AddPost(isInsidePostHook);*/ // These via.SceneManager and via.Scene are // loaded from an external reference assembly // the classes are all interfaces that correspond to real in-game classes var sceneManager = REFrameworkNET.API.GetNativeSingletonT(); var scene = sceneManager.get_CurrentScene(); + var scene2 = sceneManager.get_CurrentScene(); + + if (scene == scene2) { + REFrameworkNET.API.LogInfo("Test success: Scene is the same"); + } else { + REFrameworkNET.API.LogError("Test failure: Scene is not the same"); + } //scene.set_Pause(true); var view = sceneManager.get_MainView();