mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-30 03:15:38 -05:00
refactor Hook/Patch
This commit is contained in:
parent
5ff74e849a
commit
28c91f762e
17 changed files with 546 additions and 441 deletions
|
@ -4,7 +4,7 @@
|
||||||
#include "../utils/general.hpp"
|
#include "../utils/general.hpp"
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
#include "Tulip.hpp"
|
#include "Tulip.hpp"
|
||||||
#include <inttypes.h>
|
#include <cinttypes>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tulip/TulipHook.hpp>
|
#include <tulip/TulipHook.hpp>
|
||||||
|
|
||||||
|
@ -16,21 +16,17 @@ namespace geode {
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
std::shared_ptr<Impl> m_impl;
|
std::shared_ptr<Impl> m_impl;
|
||||||
Hook(std::shared_ptr<Impl>&& impl);
|
explicit Hook(std::shared_ptr<Impl>&& impl);
|
||||||
~Hook();
|
~Hook();
|
||||||
|
|
||||||
friend class Mod;
|
friend class Mod;
|
||||||
friend class Loader;
|
friend class Loader;
|
||||||
|
|
||||||
Result<> enable();
|
|
||||||
Result<> disable();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Create a hook at an address. The hook is enabled immediately. By
|
* Create a hook at an address. The hook is enabled immediately. By
|
||||||
* default, the hook is placed at the end of the detour list; however,
|
* default, the hook is placed at the end of the detour list; however,
|
||||||
* this can be controlled using metadata settings.
|
* this can be controlled using metadata settings.
|
||||||
* @param owner The mod that owns this hook; must be provided
|
|
||||||
* @param address The address to hook
|
* @param address The address to hook
|
||||||
* @param detour The detour to run when the hook is hit. The detour's
|
* @param detour The detour to run when the hook is hit. The detour's
|
||||||
* calling convention should be cdecl
|
* calling convention should be cdecl
|
||||||
|
@ -39,10 +35,9 @@ namespace geode {
|
||||||
* @param handlerMetadata Metadata for the hook handler
|
* @param handlerMetadata Metadata for the hook handler
|
||||||
* @param hookMetadata Metadata for the hook itself
|
* @param hookMetadata Metadata for the hook itself
|
||||||
* @returns The created hook, or an error. Make sure to add the created
|
* @returns The created hook, or an error. Make sure to add the created
|
||||||
* hook to the mod that owns it using mod->addHook!
|
* hook to the mod that owns it using mod->claimHook!
|
||||||
*/
|
*/
|
||||||
static Hook* create(
|
static Hook* create(
|
||||||
Mod* owner,
|
|
||||||
void* address,
|
void* address,
|
||||||
void* detour,
|
void* detour,
|
||||||
std::string const& displayName,
|
std::string const& displayName,
|
||||||
|
@ -52,7 +47,6 @@ namespace geode {
|
||||||
|
|
||||||
template<class DetourType>
|
template<class DetourType>
|
||||||
static Hook* create(
|
static Hook* create(
|
||||||
Mod* owner,
|
|
||||||
void* address,
|
void* address,
|
||||||
DetourType detour,
|
DetourType detour,
|
||||||
std::string const& displayName,
|
std::string const& displayName,
|
||||||
|
@ -64,7 +58,6 @@ namespace geode {
|
||||||
.m_abstract = tulip::hook::AbstractFunction::from(detour)
|
.m_abstract = tulip::hook::AbstractFunction::from(detour)
|
||||||
};
|
};
|
||||||
return Hook::create(
|
return Hook::create(
|
||||||
owner,
|
|
||||||
address,
|
address,
|
||||||
reinterpret_cast<void*>(detour),
|
reinterpret_cast<void*>(detour),
|
||||||
displayName,
|
displayName,
|
||||||
|
@ -77,40 +70,56 @@ namespace geode {
|
||||||
Hook operator=(Hook const&) = delete;
|
Hook operator=(Hook const&) = delete;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the address of the function hooked.
|
* Get the owner of this hook.
|
||||||
* @returns Address
|
* @returns Pointer to the owner's Mod handle.
|
||||||
*/
|
*/
|
||||||
uintptr_t getAddress() const;
|
[[nodiscard]] Mod* getOwner() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the display name of the function hooked.
|
|
||||||
* @returns Display name
|
|
||||||
*/
|
|
||||||
std::string_view getDisplayName() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether the hook is enabled or not.
|
* Get whether the hook is enabled or not.
|
||||||
* @returns True if enabled, false if not.
|
* @returns True if enabled, false if not.
|
||||||
*/
|
*/
|
||||||
bool isEnabled() const;
|
[[nodiscard]] bool isEnabled() const;
|
||||||
|
|
||||||
|
Result<> enable();
|
||||||
|
|
||||||
|
Result<> disable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the owner of this hook.
|
* Get whether the hook should be auto enabled or not.
|
||||||
* @returns Pointer to the owner's Mod handle.
|
* @returns Auto enable
|
||||||
*/
|
*/
|
||||||
Mod* getOwner() const;
|
[[nodiscard]] bool getAutoEnable() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether the hook should be auto enabled or not.
|
||||||
|
* @param autoEnable Auto enable
|
||||||
|
*/
|
||||||
|
void setAutoEnable(bool autoEnable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the address of the function hooked.
|
||||||
|
* @returns Address
|
||||||
|
*/
|
||||||
|
[[nodiscard]] uintptr_t getAddress() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the display name of the function hooked.
|
||||||
|
* @returns Display name
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string_view getDisplayName() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get info about the hook as JSON
|
* Get info about the hook as JSON
|
||||||
* @note For IPC
|
* @note For IPC
|
||||||
*/
|
*/
|
||||||
matjson::Value getRuntimeInfo() const;
|
[[nodiscard]] matjson::Value getRuntimeInfo() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the metadata of the hook.
|
* Get the metadata of the hook.
|
||||||
* @returns Hook metadata
|
* @returns Hook metadata
|
||||||
*/
|
*/
|
||||||
tulip::hook::HookMetadata getHookMetadata() const;
|
[[nodiscard]] tulip::hook::HookMetadata getHookMetadata() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the metadata of the hook.
|
* Set the metadata of the hook.
|
||||||
|
@ -122,90 +131,69 @@ namespace geode {
|
||||||
* Get the priority of the hook.
|
* Get the priority of the hook.
|
||||||
* @returns Priority
|
* @returns Priority
|
||||||
*/
|
*/
|
||||||
int32_t getPriority() const;
|
[[nodiscard]] int32_t getPriority() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the priority of the hook.
|
* Set the priority of the hook.
|
||||||
* @param priority Priority
|
* @param priority Priority
|
||||||
*/
|
*/
|
||||||
void setPriority(int32_t priority);
|
void setPriority(int32_t priority);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get whether the hook should be auto enabled or not.
|
|
||||||
* @returns Auto enable
|
|
||||||
*/
|
|
||||||
bool getAutoEnable() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether the hook should be auto enabled or not.
|
|
||||||
* @param autoEnable Auto enable
|
|
||||||
*/
|
|
||||||
void setAutoEnable(bool autoEnable);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL Patch final {
|
class GEODE_DLL Patch final {
|
||||||
// Change to private in 2.0.0
|
private:
|
||||||
protected:
|
class Impl;
|
||||||
Mod* m_owner;
|
std::shared_ptr<Impl> m_impl;
|
||||||
void* m_address;
|
explicit Patch(std::shared_ptr<Impl>&& impl);
|
||||||
ByteVector m_original;
|
|
||||||
ByteVector m_patch;
|
|
||||||
bool m_applied;
|
|
||||||
bool m_autoEnable;
|
|
||||||
|
|
||||||
// Only allow friend classes to create
|
|
||||||
// patches. Whatever method created the
|
|
||||||
// patches should take care of populating
|
|
||||||
// m_owner, m_address, m_original and
|
|
||||||
// m_patch.
|
|
||||||
Patch();
|
|
||||||
~Patch();
|
~Patch();
|
||||||
|
|
||||||
// no copying
|
|
||||||
Patch(Patch const&) = delete;
|
|
||||||
Patch operator=(Patch const&) = delete;
|
|
||||||
|
|
||||||
friend class Mod;
|
friend class Mod;
|
||||||
friend class Loader;
|
friend class Loader;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
static Patch* create(void* address, const ByteVector& patch);
|
||||||
* Get the address of the patch.
|
|
||||||
* @returns Address
|
|
||||||
*/
|
|
||||||
uintptr_t getAddress() const;
|
|
||||||
|
|
||||||
/**
|
Patch(Patch const&) = delete;
|
||||||
* Get whether the patch is applied or not.
|
Patch operator=(Patch const&) = delete;
|
||||||
* @returns True if applied, false if not.
|
|
||||||
*/
|
|
||||||
bool isApplied() const;
|
|
||||||
|
|
||||||
bool apply();
|
|
||||||
bool restore();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the owner of this patch.
|
* Get the owner of this patch.
|
||||||
* @returns Pointer to the owner's Mod handle.
|
* @returns Pointer to the owner's Mod handle.
|
||||||
*/
|
*/
|
||||||
Mod* getOwner() const;
|
[[nodiscard]] Mod* getOwner() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get info about the patch as JSON
|
* Get whether the patch is enabled or not.
|
||||||
* @note For IPC
|
* @returns True if enabled, false if not.
|
||||||
*/
|
*/
|
||||||
matjson::Value getRuntimeInfo() const;
|
[[nodiscard]] bool isEnabled() const;
|
||||||
|
|
||||||
|
Result<> enable();
|
||||||
|
|
||||||
|
Result<> disable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether the patch should be auto enabled or not.
|
* Get whether the patch should be auto enabled or not.
|
||||||
* @returns Auto enable
|
* @returns Auto enable
|
||||||
*/
|
*/
|
||||||
bool getAutoEnable() const;
|
[[nodiscard]] bool getAutoEnable() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether the patch should be auto enabled or not.
|
* Set whether the patch should be auto enabled or not.
|
||||||
* @param autoEnable Auto enable
|
* @param autoEnable Auto enable
|
||||||
*/
|
*/
|
||||||
void setAutoEnable(bool autoEnable);
|
void setAutoEnable(bool autoEnable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the address of the patch.
|
||||||
|
* @returns Address
|
||||||
|
*/
|
||||||
|
[[nodiscard]] uintptr_t getAddress() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info about the patch as JSON
|
||||||
|
* @note For IPC
|
||||||
|
*/
|
||||||
|
[[nodiscard]] matjson::Value getRuntimeInfo() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,12 +229,6 @@ namespace geode {
|
||||||
return sharedMod<>;
|
return sharedMod<>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all hooks owned by this Mod
|
|
||||||
* @returns Vector of hooks
|
|
||||||
*/
|
|
||||||
std::vector<Hook*> getHooks() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a hook at an address. Call the original
|
* Create a hook at an address. Call the original
|
||||||
* function by calling the original function –
|
* function by calling the original function –
|
||||||
|
@ -251,37 +245,35 @@ namespace geode {
|
||||||
* error
|
* error
|
||||||
*/
|
*/
|
||||||
template<class DetourType>
|
template<class DetourType>
|
||||||
Result<Hook*> addHook(
|
Result<Hook*> hook(
|
||||||
void* address, DetourType detour, std::string const& displayName = "",
|
void* address, DetourType detour, std::string const& displayName = "",
|
||||||
tulip::hook::TulipConvention convention = tulip::hook::TulipConvention::Default,
|
tulip::hook::TulipConvention convention = tulip::hook::TulipConvention::Default,
|
||||||
tulip::hook::HookMetadata const& hookMetadata = tulip::hook::HookMetadata()
|
tulip::hook::HookMetadata const& hookMetadata = tulip::hook::HookMetadata()
|
||||||
) {
|
) {
|
||||||
auto hook = Hook::create(this, address, detour, displayName, convention, hookMetadata);
|
auto hook = Hook::create(address, detour, displayName, convention, hookMetadata);
|
||||||
return this->addHook(hook);
|
GEODE_UNWRAP(this->claimHook(hook));
|
||||||
|
return Ok(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Hook*> addHook(Hook* hook);
|
Result<Hook*> hook(
|
||||||
|
void* address, void* detour, std::string const& displayName,
|
||||||
|
tulip::hook::HandlerMetadata const& handlerMetadata,
|
||||||
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
|
) {
|
||||||
|
auto hook = Hook::create(address, detour, displayName, handlerMetadata, hookMetadata);
|
||||||
|
GEODE_UNWRAP(this->claimHook(hook));
|
||||||
|
return Ok(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> claimHook(Hook* hook);
|
||||||
|
|
||||||
|
Result<> disownHook(Hook* hook);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable a hook owned by this Mod
|
* Get all hooks owned by this Mod
|
||||||
* @returns Successful result on success,
|
* @returns Vector of hooks
|
||||||
* errorful result with info on error
|
|
||||||
*/
|
*/
|
||||||
Result<> enableHook(Hook* hook);
|
[[nodiscard]] std::vector<Hook*> getHooks() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable a hook owned by this Mod
|
|
||||||
* @returns Successful result on success,
|
|
||||||
* errorful result with info on error
|
|
||||||
*/
|
|
||||||
Result<> disableHook(Hook* hook);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a hook owned by this Mod
|
|
||||||
* @returns Successful result on success,
|
|
||||||
* errorful result with info on error
|
|
||||||
*/
|
|
||||||
Result<> removeHook(Hook* hook);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a patch at an address
|
* Write a patch at an address
|
||||||
|
@ -290,14 +282,21 @@ namespace geode {
|
||||||
* @returns Successful result on success,
|
* @returns Successful result on success,
|
||||||
* errorful result with info on error
|
* errorful result with info on error
|
||||||
*/
|
*/
|
||||||
Result<Patch*> patch(void* address, ByteVector const& data);
|
Result<Patch*> patch(void* address, ByteVector const& data) {
|
||||||
|
auto patch = Patch::create(address, data);
|
||||||
|
GEODE_UNWRAP(this->claimPatch(patch));
|
||||||
|
return Ok(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> claimPatch(Patch* patch);
|
||||||
|
|
||||||
|
Result<> disownPatch(Patch* patch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a patch owned by this Mod
|
* Get all patches owned by this Mod
|
||||||
* @returns Successful result on success,
|
* @returns Vector of patches
|
||||||
* errorful result with info on error
|
|
||||||
*/
|
*/
|
||||||
Result<> unpatch(Patch* patch);
|
[[nodiscard]] std::vector<Patch*> getPatches() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable this mod
|
* Enable this mod
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
auto hook = Hook::create( \
|
auto hook = Hook::create( \
|
||||||
Mod::get(), \
|
|
||||||
reinterpret_cast<void*>(address), \
|
reinterpret_cast<void*>(address), \
|
||||||
AsStaticFunction_##FunctionName_< \
|
AsStaticFunction_##FunctionName_< \
|
||||||
Derived, \
|
Derived, \
|
||||||
|
@ -38,7 +37,6 @@
|
||||||
if constexpr (HasConstructor<Derived>) { \
|
if constexpr (HasConstructor<Derived>) { \
|
||||||
static auto address = AddressInline_; \
|
static auto address = AddressInline_; \
|
||||||
auto hook = Hook::create( \
|
auto hook = Hook::create( \
|
||||||
Mod::get(), \
|
|
||||||
reinterpret_cast<void*>(address), \
|
reinterpret_cast<void*>(address), \
|
||||||
AsStaticFunction_##constructor< \
|
AsStaticFunction_##constructor< \
|
||||||
Derived, \
|
Derived, \
|
||||||
|
@ -55,7 +53,6 @@
|
||||||
if constexpr (HasDestructor<Derived>) { \
|
if constexpr (HasDestructor<Derived>) { \
|
||||||
static auto address = AddressInline_; \
|
static auto address = AddressInline_; \
|
||||||
auto hook = Hook::create( \
|
auto hook = Hook::create( \
|
||||||
Mod::get(), \
|
|
||||||
reinterpret_cast<void*>(address), \
|
reinterpret_cast<void*>(address), \
|
||||||
AsStaticFunction_##destructor<Derived, decltype(Resolve<>::func(&Derived::destructor))>::value, \
|
AsStaticFunction_##destructor<Derived, decltype(Resolve<>::func(&Derived::destructor))>::value, \
|
||||||
#ClassName_ "::" #ClassName_, \
|
#ClassName_ "::" #ClassName_, \
|
||||||
|
@ -98,9 +95,9 @@ namespace geode::modifier {
|
||||||
test->ModifyDerived::apply();
|
test->ModifyDerived::apply();
|
||||||
ModifyDerived::Derived::onModify(*this);
|
ModifyDerived::Derived::onModify(*this);
|
||||||
for (auto& [uuid, hook] : m_hooks) {
|
for (auto& [uuid, hook] : m_hooks) {
|
||||||
auto res = Mod::get()->addHook(hook);
|
auto res = Mod::get()->claimHook(hook);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::error("Failed to add hook {}: {}", hook->getDisplayName(), res.error());
|
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,13 @@ namespace geode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct matjson::Serialize<geode::ByteVector> {
|
||||||
|
static matjson::Value to_json(geode::ByteVector const& bytes) {
|
||||||
|
return matjson::Array(bytes.begin(), bytes.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
namespace geode::utils::clipboard {
|
namespace geode::utils::clipboard {
|
||||||
GEODE_DLL bool write(std::string const& data);
|
GEODE_DLL bool write(std::string const& data);
|
||||||
GEODE_DLL std::string read();
|
GEODE_DLL std::string read();
|
||||||
|
|
|
@ -1,30 +1,43 @@
|
||||||
#include <Geode/loader/Hook.hpp>
|
#include <Geode/loader/Hook.hpp>
|
||||||
#include <Geode/loader/Loader.hpp>
|
|
||||||
#include <Geode/loader/Mod.hpp>
|
|
||||||
#include <Geode/utils/casts.hpp>
|
|
||||||
#include <Geode/utils/ranges.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include "ModImpl.hpp"
|
|
||||||
#include "HookImpl.hpp"
|
#include "HookImpl.hpp"
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
Hook::Hook(std::shared_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
Hook::Hook(std::shared_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
||||||
Hook::~Hook() {}
|
Hook::~Hook() = default;
|
||||||
|
|
||||||
// These classes (Hook and Patch) are nasty using new and delete, change them in 2.0.0
|
|
||||||
Hook* Hook::create(
|
Hook* Hook::create(
|
||||||
Mod* owner,
|
|
||||||
void* address,
|
void* address,
|
||||||
void* detour,
|
void* detour,
|
||||||
std::string const& displayName,
|
std::string const& displayName,
|
||||||
tulip::hook::HandlerMetadata const& handlerMetadata,
|
tulip::hook::HandlerMetadata const& handlerMetadata,
|
||||||
tulip::hook::HookMetadata const& hookMetadata
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
) {
|
) {
|
||||||
auto impl = std::make_shared<Hook::Impl>(
|
return Impl::create(address, detour, displayName, handlerMetadata, hookMetadata);
|
||||||
address, detour, displayName, handlerMetadata, hookMetadata, owner
|
}
|
||||||
);
|
|
||||||
return new Hook(std::move(impl));
|
Mod* Hook::getOwner() const {
|
||||||
|
return m_impl->getOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Hook::isEnabled() const {
|
||||||
|
return m_impl->isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Hook::enable() {
|
||||||
|
return m_impl->enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Hook::disable() {
|
||||||
|
return m_impl->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Hook::getAutoEnable() const {
|
||||||
|
return m_impl->getAutoEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hook::setAutoEnable(bool autoEnable) {
|
||||||
|
return m_impl->setAutoEnable(autoEnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t Hook::getAddress() const {
|
uintptr_t Hook::getAddress() const {
|
||||||
|
@ -35,14 +48,6 @@ std::string_view Hook::getDisplayName() const {
|
||||||
return m_impl->getDisplayName();
|
return m_impl->getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Hook::isEnabled() const {
|
|
||||||
return m_impl->isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mod* Hook::getOwner() const {
|
|
||||||
return m_impl->getOwner();
|
|
||||||
}
|
|
||||||
|
|
||||||
matjson::Value Hook::getRuntimeInfo() const {
|
matjson::Value Hook::getRuntimeInfo() const {
|
||||||
return m_impl->getRuntimeInfo();
|
return m_impl->getRuntimeInfo();
|
||||||
}
|
}
|
||||||
|
@ -62,18 +67,3 @@ int32_t Hook::getPriority() const {
|
||||||
void Hook::setPriority(int32_t priority) {
|
void Hook::setPriority(int32_t priority) {
|
||||||
return m_impl->setPriority(priority);
|
return m_impl->setPriority(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Hook::getAutoEnable() const {
|
|
||||||
return m_impl->getAutoEnable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Hook::setAutoEnable(bool autoEnable) {
|
|
||||||
return m_impl->setAutoEnable(autoEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<> Hook::enable() {
|
|
||||||
return m_impl->enable();
|
|
||||||
}
|
|
||||||
Result<> Hook::disable() {
|
|
||||||
return m_impl->disable();
|
|
||||||
}
|
|
|
@ -1,15 +1,19 @@
|
||||||
#include "HookImpl.hpp"
|
#include "HookImpl.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include "LoaderImpl.hpp"
|
#include "LoaderImpl.hpp"
|
||||||
|
|
||||||
Hook::Impl::Impl(void* address, void* detour, std::string const& displayName, tulip::hook::HandlerMetadata const& handlerMetadata, tulip::hook::HookMetadata const& hookMetadata, Mod* owner) :
|
Hook::Impl::Impl(
|
||||||
|
void* address,
|
||||||
|
void* detour,
|
||||||
|
std::string displayName,
|
||||||
|
tulip::hook::HandlerMetadata handlerMetadata,
|
||||||
|
tulip::hook::HookMetadata const& hookMetadata) :
|
||||||
m_address(address),
|
m_address(address),
|
||||||
m_detour(detour),
|
m_detour(detour),
|
||||||
m_displayName(displayName),
|
m_displayName(std::move(displayName)),
|
||||||
m_handlerMetadata(handlerMetadata),
|
m_handlerMetadata(std::move(handlerMetadata)),
|
||||||
m_hookMetadata(hookMetadata),
|
m_hookMetadata(hookMetadata) {}
|
||||||
m_owner(owner),
|
|
||||||
m_enabled(false),
|
|
||||||
m_autoEnable(true) {}
|
|
||||||
Hook::Impl::~Impl() {
|
Hook::Impl::~Impl() {
|
||||||
if (m_enabled) {
|
if (m_enabled) {
|
||||||
auto res = this->disable();
|
auto res = this->disable();
|
||||||
|
@ -17,30 +21,27 @@ Hook::Impl::~Impl() {
|
||||||
log::error("Failed to disable hook: {}", res.unwrapErr());
|
log::error("Failed to disable hook: {}", res.unwrapErr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m_owner) {
|
||||||
|
auto res = m_owner->disownHook(m_self);
|
||||||
|
if (!res) {
|
||||||
|
log::error("Failed to disown hook: {}", res.unwrapErr());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t Hook::Impl::getAddress() const {
|
Hook* Hook::Impl::create(
|
||||||
return reinterpret_cast<uintptr_t>(m_address);
|
void* address,
|
||||||
}
|
void* detour,
|
||||||
std::string_view Hook::Impl::getDisplayName() const {
|
std::string const& displayName,
|
||||||
return m_displayName;
|
tulip::hook::HandlerMetadata const& handlerMetadata,
|
||||||
}
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
bool Hook::Impl::isEnabled() const {
|
) {
|
||||||
return m_enabled;
|
auto impl = std::make_shared<Impl>(
|
||||||
}
|
address, detour, displayName, handlerMetadata, hookMetadata
|
||||||
Mod* Hook::Impl::getOwner() const {
|
);
|
||||||
return m_owner;
|
auto hook = new Hook(std::move(impl));
|
||||||
}
|
impl->m_self = hook;
|
||||||
matjson::Value Hook::Impl::getRuntimeInfo() const {
|
return hook;
|
||||||
auto json = matjson::Object();
|
|
||||||
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
|
||||||
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
|
||||||
json["name"] = m_displayName;
|
|
||||||
json["enabled"] = m_enabled;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
tulip::hook::HookMetadata Hook::Impl::getHookMetadata() const {
|
|
||||||
return m_hookMetadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Hook::Impl::enable() {
|
Result<> Hook::Impl::enable() {
|
||||||
|
@ -53,23 +54,21 @@ Result<> Hook::Impl::enable() {
|
||||||
// functions not yet RE'd but that would prevent compilation
|
// functions not yet RE'd but that would prevent compilation
|
||||||
if ((uintptr_t)m_address == (geode::base::get() + 0x9999999)) {
|
if ((uintptr_t)m_address == (geode::base::get() + 0x9999999)) {
|
||||||
if (m_owner) {
|
if (m_owner) {
|
||||||
log::debug(
|
log::warn(
|
||||||
"Hook {} for {} uses placeholder address, refusing to hook",
|
"Hook {} for {} uses placeholder address, refusing to hook",
|
||||||
m_displayName, m_owner->getID()
|
m_displayName, m_owner->getID()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log::debug("Hook {} uses placeholder address, refusing to hook", m_displayName);
|
log::warn("Hook {} uses placeholder address, refusing to hook", m_displayName);
|
||||||
}
|
}
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LoaderImpl::get()->hasHandler(m_address)) {
|
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getOrCreateHandler(m_address, m_handlerMetadata));
|
||||||
GEODE_UNWRAP(LoaderImpl::get()->createHandler(m_address, m_handlerMetadata));
|
|
||||||
}
|
|
||||||
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
|
|
||||||
|
|
||||||
m_handle = tulip::hook::createHook(handler, m_detour, m_hookMetadata);
|
m_handle = tulip::hook::createHook(handler, m_detour, m_hookMetadata);
|
||||||
|
m_enabled = true;
|
||||||
|
|
||||||
if (m_owner) {
|
if (m_owner) {
|
||||||
log::debug("Enabled {} hook at {} for {}", m_displayName, m_address, m_owner->getID());
|
log::debug("Enabled {} hook at {} for {}", m_displayName, m_address, m_owner->getID());
|
||||||
}
|
}
|
||||||
|
@ -77,54 +76,63 @@ Result<> Hook::Impl::enable() {
|
||||||
log::debug("Enabled {} hook at {}", m_displayName, m_address);
|
log::debug("Enabled {} hook at {}", m_displayName, m_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_enabled = true;
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Hook::Impl::disable() {
|
Result<> Hook::Impl::disable() {
|
||||||
if (!m_enabled)
|
if (!m_enabled)
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
|
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
|
||||||
|
|
||||||
tulip::hook::removeHook(handler, m_handle);
|
tulip::hook::removeHook(handler, m_handle);
|
||||||
|
|
||||||
log::debug("Disabled {} hook", m_displayName);
|
|
||||||
|
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
|
log::debug("Disabled {} hook", m_displayName);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Hook::Impl::updateMetadata() {
|
uintptr_t Hook::Impl::getAddress() const {
|
||||||
if (m_enabled) {
|
return reinterpret_cast<uintptr_t>(m_address);
|
||||||
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
|
}
|
||||||
|
|
||||||
tulip::hook::updateHookMetadata(handler, m_handle, m_hookMetadata);
|
std::string_view Hook::Impl::getDisplayName() const {
|
||||||
|
return m_displayName;
|
||||||
}
|
}
|
||||||
return Ok();
|
|
||||||
|
matjson::Value Hook::Impl::getRuntimeInfo() const {
|
||||||
|
auto json = matjson::Object();
|
||||||
|
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
||||||
|
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
||||||
|
json["name"] = m_displayName;
|
||||||
|
json["enabled"] = m_enabled;
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tulip::hook::HookMetadata Hook::Impl::getHookMetadata() const {
|
||||||
|
return m_hookMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
void Hook::Impl::setHookMetadata(tulip::hook::HookMetadata const& metadata) {
|
void Hook::Impl::setHookMetadata(tulip::hook::HookMetadata const& metadata) {
|
||||||
m_hookMetadata = metadata;
|
m_hookMetadata = metadata;
|
||||||
auto res = this->updateMetadata();
|
auto res = this->updateHookMetadata();
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::error("Failed to update hook metadata: {}", res.unwrapErr());
|
log::error("Failed to update hook metadata: {}", res.unwrapErr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t Hook::Impl::getPriority() const {
|
int32_t Hook::Impl::getPriority() const {
|
||||||
return m_hookMetadata.m_priority;
|
return m_hookMetadata.m_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hook::Impl::setPriority(int32_t priority) {
|
void Hook::Impl::setPriority(int32_t priority) {
|
||||||
m_hookMetadata.m_priority = priority;
|
m_hookMetadata.m_priority = priority;
|
||||||
auto res = this->updateMetadata();
|
auto res = this->updateHookMetadata();
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::error("Failed to update hook priority: {}", res.unwrapErr());
|
log::error("Failed to update hook priority: {}", res.unwrapErr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Hook::Impl::getAutoEnable() const {
|
Result<> Hook::Impl::updateHookMetadata() {
|
||||||
return m_autoEnable;
|
if (!m_enabled) return Ok();
|
||||||
}
|
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
|
||||||
|
tulip::hook::updateHookMetadata(handler, m_handle, m_hookMetadata);
|
||||||
void Hook::Impl::setAutoEnable(bool autoEnable) {
|
return Ok();
|
||||||
m_autoEnable = autoEnable;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <Geode/loader/Hook.hpp>
|
#include <Geode/loader/Hook.hpp>
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <Geode/loader/Loader.hpp>
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <Geode/loader/Mod.hpp>
|
||||||
|
@ -5,47 +7,59 @@
|
||||||
#include <Geode/utils/ranges.hpp>
|
#include <Geode/utils/ranges.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "ModImpl.hpp"
|
#include "ModImpl.hpp"
|
||||||
|
#include "ModPatch.hpp"
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
class Hook::Impl {
|
class Hook::Impl final : ModPatch {
|
||||||
public:
|
public:
|
||||||
Impl(
|
Impl(
|
||||||
void* address,
|
void* address,
|
||||||
void* detour,
|
void* detour,
|
||||||
std::string const& displayName,
|
std::string displayName,
|
||||||
tulip::hook::HandlerMetadata const& handlerMetadata,
|
tulip::hook::HandlerMetadata handlerMetadata,
|
||||||
tulip::hook::HookMetadata const& hookMetadata,
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
Mod* owner
|
|
||||||
);
|
);
|
||||||
~Impl();
|
~Impl();
|
||||||
|
|
||||||
|
static Hook* create(
|
||||||
|
void* address,
|
||||||
|
void* detour,
|
||||||
|
std::string const& displayName,
|
||||||
|
tulip::hook::HandlerMetadata const& handlerMetadata,
|
||||||
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
|
);
|
||||||
|
|
||||||
|
template<class DetourType>
|
||||||
|
static Hook* create(
|
||||||
|
void* address,
|
||||||
|
DetourType detour,
|
||||||
|
std::string const& displayName,
|
||||||
|
tulip::hook::TulipConvention convention,
|
||||||
|
tulip::hook::HookMetadata const& hookMetadata
|
||||||
|
);
|
||||||
|
|
||||||
|
Hook* m_self = nullptr;
|
||||||
void* m_address;
|
void* m_address;
|
||||||
void* m_detour;
|
void* m_detour;
|
||||||
std::string m_displayName;
|
std::string m_displayName;
|
||||||
tulip::hook::HandlerMetadata m_handlerMetadata;
|
tulip::hook::HandlerMetadata m_handlerMetadata;
|
||||||
tulip::hook::HookMetadata m_hookMetadata;
|
tulip::hook::HookMetadata m_hookMetadata;
|
||||||
Mod* m_owner;
|
tulip::hook::HookHandle m_handle = 0;
|
||||||
tulip::hook::HookHandle m_handle;
|
|
||||||
bool m_enabled = false;
|
|
||||||
bool m_autoEnable = true;
|
|
||||||
|
|
||||||
|
|
||||||
// Used by Mod
|
|
||||||
Result<> enable();
|
Result<> enable();
|
||||||
Result<> disable();
|
Result<> disable();
|
||||||
Result<> updateMetadata();
|
|
||||||
|
|
||||||
uintptr_t getAddress() const;
|
uintptr_t getAddress() const;
|
||||||
std::string_view getDisplayName() const;
|
std::string_view getDisplayName() const;
|
||||||
bool isEnabled() const;
|
|
||||||
Mod* getOwner() const;
|
|
||||||
matjson::Value getRuntimeInfo() const;
|
matjson::Value getRuntimeInfo() const;
|
||||||
tulip::hook::HookMetadata getHookMetadata() const;
|
tulip::hook::HookMetadata getHookMetadata() const;
|
||||||
void setHookMetadata(tulip::hook::HookMetadata const& metadata);
|
void setHookMetadata(tulip::hook::HookMetadata const& metadata);
|
||||||
int32_t getPriority() const;
|
int32_t getPriority() const;
|
||||||
void setPriority(int32_t priority);
|
void setPriority(int32_t priority);
|
||||||
bool getAutoEnable() const;
|
|
||||||
void setAutoEnable(bool autoEnable);
|
Result<> updateHookMetadata();
|
||||||
|
|
||||||
|
friend class Hook;
|
||||||
|
friend class Mod;
|
||||||
};
|
};
|
|
@ -681,7 +681,7 @@ bool Loader::Impl::loadHooks() {
|
||||||
m_readyToHook = true;
|
m_readyToHook = true;
|
||||||
bool hadErrors = false;
|
bool hadErrors = false;
|
||||||
for (auto const& [hook, mod] : m_uninitializedHooks) {
|
for (auto const& [hook, mod] : m_uninitializedHooks) {
|
||||||
auto res = mod->addHook(hook);
|
auto res = hook->enable();
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::logImpl(Severity::Error, mod, "{}", res.unwrapErr());
|
log::logImpl(Severity::Error, mod, "{}", res.unwrapErr());
|
||||||
hadErrors = true;
|
hadErrors = true;
|
||||||
|
@ -728,35 +728,18 @@ void Loader::Impl::releaseNextMod() {
|
||||||
m_nextModLock.unlock();
|
m_nextModLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Loader::Impl::createHandler(void* address, tulip::hook::HandlerMetadata const& metadata) {
|
|
||||||
if (m_handlerHandles.count(address)) {
|
|
||||||
return Err("Handler already exists at address");
|
|
||||||
}
|
|
||||||
|
|
||||||
GEODE_UNWRAP_INTO(auto handle, tulip::hook::createHandler(address, metadata));
|
|
||||||
m_handlerHandles[address] = handle;
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::hasHandler(void* address) {
|
|
||||||
return m_handlerHandles.count(address) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<tulip::hook::HandlerHandle> Loader::Impl::getHandler(void* address) {
|
Result<tulip::hook::HandlerHandle> Loader::Impl::getHandler(void* address) {
|
||||||
if (!m_handlerHandles.count(address)) {
|
if (!m_handlerHandles.count(address)) {
|
||||||
return Err("Handler does not exist at address");
|
return Err("Handler does not exist at address");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(m_handlerHandles[address]);
|
return Ok(m_handlerHandles[address]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Loader::Impl::removeHandler(void* address) {
|
Result<tulip::hook::HandlerHandle> Loader::Impl::getOrCreateHandler(void* address, tulip::hook::HandlerMetadata const& metadata) {
|
||||||
if (!m_handlerHandles.count(address)) {
|
if (m_handlerHandles.count(address)) {
|
||||||
return Err("Handler does not exist at address");
|
return Ok(m_handlerHandles[address]);
|
||||||
}
|
}
|
||||||
|
GEODE_UNWRAP_INTO(auto handle, tulip::hook::createHandler(address, metadata));
|
||||||
auto handle = m_handlerHandles[address];
|
m_handlerHandles[address] = handle;
|
||||||
GEODE_UNWRAP(tulip::hook::removeHandler(handle));
|
return Ok(handle);
|
||||||
m_handlerHandles.erase(address);
|
|
||||||
return Ok();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,10 +69,8 @@ namespace geode {
|
||||||
|
|
||||||
std::unordered_map<void*, tulip::hook::HandlerHandle> m_handlerHandles;
|
std::unordered_map<void*, tulip::hook::HandlerHandle> m_handlerHandles;
|
||||||
|
|
||||||
Result<> createHandler(void* address, tulip::hook::HandlerMetadata const& metadata);
|
|
||||||
bool hasHandler(void* address);
|
|
||||||
Result<tulip::hook::HandlerHandle> getHandler(void* address);
|
Result<tulip::hook::HandlerHandle> getHandler(void* address);
|
||||||
Result<> removeHandler(void* address);
|
Result<tulip::hook::HandlerHandle> getOrCreateHandler(void* address, tulip::hook::HandlerMetadata const& metadata);
|
||||||
|
|
||||||
bool loadHooks();
|
bool loadHooks();
|
||||||
|
|
||||||
|
|
|
@ -121,32 +121,28 @@ void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<Sett
|
||||||
return m_impl->registerCustomSetting(key, std::move(value));
|
return m_impl->registerCustomSetting(key, std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<> Mod::claimHook(Hook* hook) {
|
||||||
|
return m_impl->claimHook(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Mod::disownHook(Hook* hook) {
|
||||||
|
return m_impl->disownHook(hook);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Hook*> Mod::getHooks() const {
|
std::vector<Hook*> Mod::getHooks() const {
|
||||||
return m_impl->getHooks();
|
return m_impl->getHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Hook*> Mod::addHook(Hook* hook) {
|
Result<> Mod::claimPatch(Patch* patch) {
|
||||||
return m_impl->addHook(hook);
|
return m_impl->claimPatch(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Mod::enableHook(Hook* hook) {
|
Result<> Mod::disownPatch(Patch* patch) {
|
||||||
return m_impl->enableHook(hook);
|
return m_impl->disownPatch(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Mod::disableHook(Hook* hook) {
|
std::vector<Patch*> Mod::getPatches() const {
|
||||||
return m_impl->disableHook(hook);
|
return m_impl->getPatches();
|
||||||
}
|
|
||||||
|
|
||||||
Result<> Mod::removeHook(Hook* hook) {
|
|
||||||
return m_impl->removeHook(hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Patch*> Mod::patch(void* address, ByteVector const& data) {
|
|
||||||
return m_impl->patch(address, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<> Mod::unpatch(Patch* patch) {
|
|
||||||
return m_impl->unpatch(patch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Mod::enable() {
|
Result<> Mod::enable() {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "ModImpl.hpp"
|
#include "ModImpl.hpp"
|
||||||
#include "LoaderImpl.hpp"
|
#include "LoaderImpl.hpp"
|
||||||
#include "ModMetadataImpl.hpp"
|
#include "ModMetadataImpl.hpp"
|
||||||
|
#include "HookImpl.hpp"
|
||||||
|
#include "PatchImpl.hpp"
|
||||||
#include "about.hpp"
|
#include "about.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
|
||||||
|
@ -133,6 +135,10 @@ std::vector<Hook*> Mod::Impl::getHooks() const {
|
||||||
return m_hooks;
|
return m_hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Patch*> Mod::Impl::getPatches() const {
|
||||||
|
return m_patches;
|
||||||
|
}
|
||||||
|
|
||||||
// Settings and saved values
|
// Settings and saved values
|
||||||
|
|
||||||
Result<> Mod::Impl::loadData() {
|
Result<> Mod::Impl::loadData() {
|
||||||
|
@ -328,32 +334,6 @@ Result<> Mod::Impl::loadBinary() {
|
||||||
|
|
||||||
LoaderImpl::get()->releaseNextMod();
|
LoaderImpl::get()->releaseNextMod();
|
||||||
|
|
||||||
for (auto const& hook : m_hooks) {
|
|
||||||
if (!hook) {
|
|
||||||
log::warn("Hook is null in mod \"{}\"", m_metadata.getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (hook->getAutoEnable()) {
|
|
||||||
auto res = this->enableHook(hook);
|
|
||||||
if (!res) {
|
|
||||||
log::error("Can't enable hook {} for mod {}: {}", hook->getDisplayName(), m_metadata.getID(), res.unwrapErr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& patch : m_patches) {
|
|
||||||
if (!patch) {
|
|
||||||
log::warn("Patch is null in mod \"{}\"", m_metadata.getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (patch->getAutoEnable()) {
|
|
||||||
if (!patch->apply()) {
|
|
||||||
log::warn("Unable to apply patch at {}", patch->getAddress());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_enabled = true;
|
m_enabled = true;
|
||||||
|
|
||||||
ModStateEvent(m_self, ModEventType::Loaded).post();
|
ModStateEvent(m_self, ModEventType::Loaded).post();
|
||||||
|
@ -461,79 +441,90 @@ bool Mod::Impl::depends(std::string_view const id) const {
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
|
|
||||||
Result<> Mod::Impl::enableHook(Hook* hook) {
|
Result<> Mod::Impl::claimHook(Hook* hook) {
|
||||||
auto res = hook->enable();
|
auto res1 = hook->m_impl->setOwner(m_self);
|
||||||
if (!res) {
|
if (!res1) {
|
||||||
log::error("Can't enable hook {} for mod {}: {}", hook->getDisplayName(), m_metadata.getID(), res.unwrapErr());
|
return Err("Cannot claim hook: {}", res1.unwrapErr());
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<> Mod::Impl::disableHook(Hook* hook) {
|
|
||||||
return hook->disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Hook*> Mod::Impl::addHook(Hook* hook) {
|
|
||||||
if (!ranges::contains(m_hooks, [&](auto const& h) { return h == hook; }))
|
|
||||||
m_hooks.push_back(hook);
|
m_hooks.push_back(hook);
|
||||||
|
|
||||||
|
if (!this->isEnabled() || !hook->getAutoEnable())
|
||||||
|
return Ok();
|
||||||
|
|
||||||
if (!LoaderImpl::get()->isReadyToHook()) {
|
if (!LoaderImpl::get()->isReadyToHook()) {
|
||||||
LoaderImpl::get()->addUninitializedHook(hook, m_self);
|
LoaderImpl::get()->addUninitializedHook(hook, m_self);
|
||||||
return Ok(hook);
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto res2 = hook->enable();
|
||||||
|
if (!res2) {
|
||||||
|
return Err("Cannot enable hook: {}", res2.unwrapErr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Mod::Impl::disownHook(Hook* hook) {
|
||||||
|
if (hook->getOwner() != m_self) {
|
||||||
|
return Err("Cannot disown hook not owned by this mod");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res1 = hook->m_impl->setOwner(nullptr);
|
||||||
|
if (!res1) {
|
||||||
|
return Err("Cannot disown hook: {}", res1.unwrapErr());
|
||||||
|
}
|
||||||
|
m_hooks.erase(std::find(m_hooks.begin(), m_hooks.end(), hook));
|
||||||
|
|
||||||
if (!this->isEnabled() || !hook->getAutoEnable())
|
if (!this->isEnabled() || !hook->getAutoEnable())
|
||||||
return Ok(hook);
|
return Ok();
|
||||||
|
|
||||||
auto res = this->enableHook(hook);
|
auto res2 = hook->disable();
|
||||||
if (!res) {
|
if (!res2) {
|
||||||
delete hook;
|
return Err("Cannot disable hook: {}", res2.unwrapErr());
|
||||||
return Err("Can't create hook: " + res.unwrapErr());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(hook);
|
return Ok();
|
||||||
}
|
|
||||||
|
|
||||||
Result<> Mod::Impl::removeHook(Hook* hook) {
|
|
||||||
auto res = this->disableHook(hook);
|
|
||||||
if (res) {
|
|
||||||
ranges::remove(m_hooks, hook);
|
|
||||||
delete hook;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patches
|
// Patches
|
||||||
|
|
||||||
// TODO: replace this with a safe one
|
Result<> Mod::Impl::claimPatch(Patch* patch) {
|
||||||
static ByteVector readMemory(void* address, size_t amount) {
|
auto res1 = patch->m_impl->setOwner(m_self);
|
||||||
ByteVector ret;
|
if (!res1) {
|
||||||
for (size_t i = 0; i < amount; i++) {
|
return Err("Cannot claim patch: {}", res1.unwrapErr());
|
||||||
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
|
|
||||||
}
|
}
|
||||||
return ret;
|
m_patches.push_back(patch);
|
||||||
|
|
||||||
|
if (!this->isEnabled() || !patch->getAutoEnable())
|
||||||
|
return Ok();
|
||||||
|
|
||||||
|
auto res2 = patch->enable();
|
||||||
|
if (!res2) {
|
||||||
|
return Err("Cannot enable patch: {}", res2.unwrapErr());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Patch*> Mod::Impl::patch(void* address, ByteVector const& data) {
|
return Ok();
|
||||||
auto p = new Patch;
|
}
|
||||||
p->m_address = address;
|
|
||||||
p->m_original = readMemory(address, data.size());
|
Result<> Mod::Impl::disownPatch(Patch* patch) {
|
||||||
p->m_owner = m_self;
|
if (patch->getOwner() != m_self) {
|
||||||
p->m_patch = data;
|
return Err("Cannot disown patch not owned by this mod");
|
||||||
if (this->isEnabled() && !p->apply()) {
|
}
|
||||||
delete p;
|
|
||||||
return Err("Unable to enable patch at " + std::to_string(reinterpret_cast<uintptr_t>(address)));
|
auto res1 = patch->m_impl->setOwner(nullptr);
|
||||||
}
|
if (!res1) {
|
||||||
m_patches.push_back(p);
|
return Err("Cannot disown patch: {}", res1.unwrapErr());
|
||||||
return Ok(p);
|
}
|
||||||
|
m_patches.erase(std::find(m_patches.begin(), m_patches.end(), patch));
|
||||||
|
|
||||||
|
if (!this->isEnabled() || !patch->getAutoEnable())
|
||||||
|
return Ok();
|
||||||
|
|
||||||
|
auto res2 = patch->disable();
|
||||||
|
if (!res2) {
|
||||||
|
return Err("Cannot disable patch: {}", res2.unwrapErr());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> Mod::Impl::unpatch(Patch* patch) {
|
|
||||||
if (!patch->restore())
|
|
||||||
return Err("Unable to restore patch at " + std::to_string(patch->getAddress()));
|
|
||||||
ranges::remove(m_patches, patch);
|
|
||||||
delete patch;
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
|
#include "ModPatch.hpp"
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
class Mod::Impl {
|
class Mod::Impl {
|
||||||
|
@ -112,13 +113,14 @@ namespace geode {
|
||||||
SettingValue* getSetting(std::string_view const key) const;
|
SettingValue* getSetting(std::string_view const key) const;
|
||||||
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
|
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
|
||||||
|
|
||||||
std::vector<Hook*> getHooks() const;
|
Result<> claimHook(Hook* hook);
|
||||||
Result<Hook*> addHook(Hook* hook);
|
Result<> disownHook(Hook* hook);
|
||||||
Result<> enableHook(Hook* hook);
|
[[nodiscard]] std::vector<Hook*> getHooks() const;
|
||||||
Result<> disableHook(Hook* hook);
|
|
||||||
Result<> removeHook(Hook* hook);
|
Result<> claimPatch(Patch* patch);
|
||||||
Result<Patch*> patch(void* address, ByteVector const& data);
|
Result<> disownPatch(Patch* patch);
|
||||||
Result<> unpatch(Patch* patch);
|
[[nodiscard]] std::vector<Patch*> getPatches() const;
|
||||||
|
|
||||||
Result<> enable();
|
Result<> enable();
|
||||||
Result<> disable();
|
Result<> disable();
|
||||||
Result<> uninstall(bool deleteSaveData = false);
|
Result<> uninstall(bool deleteSaveData = false);
|
||||||
|
|
25
loader/src/loader/ModPatch.cpp
Normal file
25
loader/src/loader/ModPatch.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include "ModPatch.hpp"
|
||||||
|
|
||||||
|
Mod* ModPatch::getOwner() const {
|
||||||
|
return m_owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> ModPatch::setOwner(geode::Mod* mod) {
|
||||||
|
if (mod && m_owner) {
|
||||||
|
return Err("Cannot directly replace owner of an already owned mod");
|
||||||
|
}
|
||||||
|
m_owner = mod;
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModPatch::isEnabled() const {
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModPatch::getAutoEnable() const {
|
||||||
|
return m_autoEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModPatch::setAutoEnable(bool autoEnable) {
|
||||||
|
m_autoEnable = autoEnable;
|
||||||
|
}
|
22
loader/src/loader/ModPatch.hpp
Normal file
22
loader/src/loader/ModPatch.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Geode/loader/Mod.hpp>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
class ModPatch {
|
||||||
|
public:
|
||||||
|
Mod* m_owner = nullptr;
|
||||||
|
bool m_enabled = false;
|
||||||
|
bool m_autoEnable = true;
|
||||||
|
|
||||||
|
[[nodiscard]] Mod* getOwner() const;
|
||||||
|
Result<> setOwner(Mod* mod);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isEnabled() const;
|
||||||
|
virtual Result<> enable() = 0;
|
||||||
|
virtual Result<> disable() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] bool getAutoEnable() const;
|
||||||
|
void setAutoEnable(bool autoEnable);
|
||||||
|
};
|
|
@ -1,58 +1,43 @@
|
||||||
#include <Geode/loader/Hook.hpp>
|
#include <Geode/loader/Hook.hpp>
|
||||||
#include <matjson.hpp>
|
#include "PatchImpl.hpp"
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
bool Patch::apply() {
|
Patch::Patch(std::shared_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
||||||
bool res = bool(tulip::hook::writeMemory(m_address, m_patch.data(), m_patch.size()));
|
Patch::~Patch() = default;
|
||||||
if (res)
|
|
||||||
m_applied = true;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Patch::restore() {
|
Patch* Patch::create(void* address, const ByteVector& patch) {
|
||||||
bool res = bool(tulip::hook::writeMemory(m_address, m_original.data(), m_original.size()));
|
return Impl::create(address, patch);
|
||||||
if (res)
|
|
||||||
m_applied = false;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Patch::Patch() : m_owner(nullptr), m_address(nullptr), m_applied(false), m_autoEnable(true) {}
|
|
||||||
|
|
||||||
void Patch::setAutoEnable(bool autoEnable) {
|
|
||||||
m_autoEnable = autoEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Patch::getAutoEnable() const {
|
|
||||||
return m_autoEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t Patch::getAddress() const {
|
|
||||||
return reinterpret_cast<uintptr_t>(m_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Patch::isApplied() const {
|
|
||||||
return m_applied;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mod* Patch::getOwner() const {
|
Mod* Patch::getOwner() const {
|
||||||
return m_owner;
|
return m_impl->getOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
Patch::~Patch() {}
|
bool Patch::isEnabled() const {
|
||||||
|
return m_impl->isEnabled();
|
||||||
template <>
|
}
|
||||||
struct matjson::Serialize<ByteVector> {
|
|
||||||
static matjson::Value to_json(ByteVector const& bytes) {
|
Result<> Patch::enable() {
|
||||||
return matjson::Array(bytes.begin(), bytes.end());
|
return m_impl->enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Patch::disable() {
|
||||||
|
return m_impl->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Patch::getAutoEnable() const {
|
||||||
|
return m_impl->getAutoEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch::setAutoEnable(bool autoEnable) {
|
||||||
|
return m_impl->setAutoEnable(autoEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t Patch::getAddress() const {
|
||||||
|
return m_impl->getAddress();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
matjson::Value Patch::getRuntimeInfo() const {
|
matjson::Value Patch::getRuntimeInfo() const {
|
||||||
auto json = matjson::Object();
|
return m_impl->getRuntimeInfo();
|
||||||
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
|
||||||
json["original"] = m_original;
|
|
||||||
json["patch"] = m_patch;
|
|
||||||
json["applied"] = m_applied;
|
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
|
|
69
loader/src/loader/PatchImpl.cpp
Normal file
69
loader/src/loader/PatchImpl.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include "PatchImpl.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include "LoaderImpl.hpp"
|
||||||
|
|
||||||
|
Patch::Impl::Impl(void* address, ByteVector original, ByteVector patch) :
|
||||||
|
m_address(address),
|
||||||
|
m_original(std::move(original)),
|
||||||
|
m_patch(std::move(patch)) {}
|
||||||
|
Patch::Impl::~Impl() {
|
||||||
|
if (m_enabled) {
|
||||||
|
auto res = this->disable();
|
||||||
|
if (!res) {
|
||||||
|
log::error("Failed to disable patch: {}", res.unwrapErr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_owner) {
|
||||||
|
auto res = m_owner->disownPatch(m_self);
|
||||||
|
if (!res) {
|
||||||
|
log::error("Failed to disown patch: {}", res.unwrapErr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace this with a safe one
|
||||||
|
static ByteVector readMemory(void* address, size_t amount) {
|
||||||
|
ByteVector ret;
|
||||||
|
for (size_t i = 0; i < amount; i++) {
|
||||||
|
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Patch* Patch::Impl::create(void* address, const geode::ByteVector& patch) {
|
||||||
|
auto impl = std::make_shared<Impl>(
|
||||||
|
address, readMemory(address, patch.size()), patch
|
||||||
|
);
|
||||||
|
auto p = new Patch(std::move(impl));
|
||||||
|
impl->m_self = p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Patch::Impl::enable() {
|
||||||
|
// TODO: add overlap checking
|
||||||
|
auto res = tulip::hook::writeMemory(m_address, m_patch.data(), m_patch.size());
|
||||||
|
if (!res) return Err(res.unwrapErr());
|
||||||
|
m_enabled = true;
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<> Patch::Impl::disable() {
|
||||||
|
auto res = tulip::hook::writeMemory(m_address, m_original.data(), m_original.size());
|
||||||
|
if (!res) return Err(res.unwrapErr());
|
||||||
|
m_enabled = false;
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t Patch::Impl::getAddress() const {
|
||||||
|
return reinterpret_cast<uintptr_t>(m_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
matjson::Value Patch::Impl::getRuntimeInfo() const {
|
||||||
|
auto json = matjson::Object();
|
||||||
|
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
||||||
|
json["original"] = m_original;
|
||||||
|
json["patch"] = m_patch;
|
||||||
|
json["enabled"] = m_enabled;
|
||||||
|
return json;
|
||||||
|
}
|
31
loader/src/loader/PatchImpl.hpp
Normal file
31
loader/src/loader/PatchImpl.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Geode/loader/Hook.hpp>
|
||||||
|
#include <Geode/loader/Loader.hpp>
|
||||||
|
#include <Geode/loader/Mod.hpp>
|
||||||
|
#include "ModImpl.hpp"
|
||||||
|
#include "ModPatch.hpp"
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
class Patch::Impl final : ModPatch {
|
||||||
|
public:
|
||||||
|
Impl(void* address, ByteVector original, ByteVector patch);
|
||||||
|
~Impl();
|
||||||
|
|
||||||
|
static Patch* create(void* address, const ByteVector& patch);
|
||||||
|
|
||||||
|
Patch* m_self = nullptr;
|
||||||
|
void* m_address;
|
||||||
|
ByteVector m_original;
|
||||||
|
ByteVector m_patch;
|
||||||
|
|
||||||
|
Result<> enable();
|
||||||
|
Result<> disable();
|
||||||
|
|
||||||
|
uintptr_t getAddress() const;
|
||||||
|
matjson::Value getRuntimeInfo() const;
|
||||||
|
|
||||||
|
friend class Patch;
|
||||||
|
friend class Mod;
|
||||||
|
};
|
Loading…
Reference in a new issue