#pragma once #include #include #include // Virtual Function Information template class __Function; template class __VirtualFunctionInfo { __VirtualFunctionInfo(T *addr_, void *parent_); [[nodiscard]] bool can_overwrite() const; T *const addr; void *const parent; friend class __Function>; }; // Thunks typedef void *(*thunk_enabler_t)(void *target, void *thunk); extern thunk_enabler_t thunk_enabler; // Function Information template class __Function { public: // Types using ptr_type = Ret (*)(Args...); using type = std::function; using overwrite_type = std::function; // Normal Function __Function(const char *name_, ptr_type func_, ptr_type thunk_); // Virtual Function __Function(const char *name_, ptr_type *func_, void *parent, ptr_type thunk_); // Overwrite Function [[nodiscard]] bool overwrite(overwrite_type target) { // Check If Enabled if (!enabled) { return false; } // Enable Thunk enable_thunk(); // Overwrite type original = get_thunk_target(); thunk_target = [original, target](Args... args) { return target(original, std::forward(args)...); }; return true; } // Getters [[nodiscard]] ptr_type get(bool result_will_be_stored) { if (!enabled) { return nullptr; } else { if (result_will_be_stored) { enable_thunk(); } if (is_virtual) { return *get_vtable_addr(); } else { return std::get(func); } } } [[nodiscard]] ptr_type *get_vtable_addr() const { if (is_virtual) { return std::get<__VirtualFunctionInfo>(func).addr; } else { return nullptr; } } [[nodiscard]] type get_thunk_target() const { if (thunk_target) { return thunk_target; } else { return backup; } } private: // Current Function const bool is_virtual; std::variant> func; public: // State const bool enabled; const char *const name; // Backup Of Original Function Pointer const ptr_type backup; private: // Thunk const ptr_type thunk; type thunk_target; bool thunk_enabled = false; void enable_thunk() { if (!thunk_enabled && enabled) { ptr_type real_thunk = (ptr_type) thunk_enabler((void *) backup, (void *) thunk); if (!is_virtual) { func = real_thunk; } thunk_enabled = true; } } };