mod loader

This commit is contained in:
madeline
2026-04-20 04:17:42 -07:00
parent c43f33a5bc
commit 22d906a248
30 changed files with 1170 additions and 51 deletions
+5 -5
View File
@@ -1049,11 +1049,11 @@ public:
STATIC_ASSERT(122384 == sizeof(dComIfG_inf_c));
extern dComIfG_inf_c g_dComIfG_gameInfo;
extern GXColor g_blackColor;
extern GXColor g_clearColor;
extern GXColor g_whiteColor;
extern GXColor g_saftyWhiteColor;
DUSK_GAME_EXTERN dComIfG_inf_c g_dComIfG_gameInfo;
DUSK_GAME_EXTERN GXColor g_blackColor;
DUSK_GAME_EXTERN GXColor g_clearColor;
DUSK_GAME_EXTERN GXColor g_whiteColor;
DUSK_GAME_EXTERN GXColor g_saftyWhiteColor;
int dComLbG_PhaseHandler(request_of_phase_process_class*, request_of_phase_process_fn*,
void*);
+4 -9
View File
@@ -18,9 +18,8 @@
class GXTexObjRAII : public GXTexObj {
public:
GXTexObjRAII() : GXTexObj() {}
~GXTexObjRAII() { GXDestroyTexObj(this); }
void reset() { GXDestroyTexObj(this); }
~GXTexObjRAII();
void reset();
GXTexObjRAII(const GXTexObjRAII&) = delete;
GXTexObjRAII& operator=(const GXTexObjRAII&) = delete;
@@ -44,12 +43,8 @@ typedef GXTexObj TGXTexObj;
#endif
struct GXScopedDebugGroup {
explicit GXScopedDebugGroup(const char* text) {
GXPushDebugGroup(text);
}
~GXScopedDebugGroup() {
GXPopDebugGroup();
}
explicit GXScopedDebugGroup(const char* text);
~GXScopedDebugGroup();
};
#define GX_AND_TRACY_SCOPED(name) GXScopedDebugGroup scope(name); ZoneScopedN(name);
+84
View File
@@ -0,0 +1,84 @@
#pragma once
#include <cstring>
#include <memory>
#include <type_traits>
#include "dusk/mod_api.h"
namespace dusk {
inline DuskModAPI* g_api = nullptr;
inline void init(DuskModAPI* api) { g_api = api; }
template <class T>
T arg(void* args_raw, int n) noexcept {
void** a = static_cast<void**>(args_raw);
return *static_cast<std::add_pointer_t<std::remove_reference_t<T>>>(a[n]);
}
template <class T>
std::remove_reference_t<T>& argRef(void* args_raw, int n) noexcept {
void** a = static_cast<void**>(args_raw);
return *static_cast<std::add_pointer_t<std::remove_reference_t<T>>>(a[n]);
}
template <class F>
void* mfpAddr(F fn) noexcept {
void* p = nullptr;
static_assert(sizeof(fn) >= sizeof(void*), "unexpected MFP size");
std::memcpy(&p, &fn, sizeof(void*));
return p;
}
template <auto MFP, class R, class Self, class Orig, class... A>
struct HookEntryBase {
static inline Orig g_orig = nullptr;
static R trampoline(Self self, A... args) {
void* ptrs[] = {static_cast<void*>(std::addressof(self)), static_cast<void*>(std::addressof(args))...};
const bool cancel = g_api->hook_dispatch_pre(mfpAddr(MFP), static_cast<void*>(ptrs));
if constexpr (std::is_void_v<R>) {
if (!cancel) g_orig(self, args...);
g_api->hook_dispatch_post(mfpAddr(MFP), static_cast<void*>(ptrs));
} else {
R result{};
if (!cancel) result = g_orig(self, args...);
g_api->hook_dispatch_post(mfpAddr(MFP), static_cast<void*>(ptrs));
return result;
}
}
};
template <auto MFP>
struct HookEntry;
template <class C, class R, class... A, R (C::*MFP)(A...)>
struct HookEntry<MFP> : HookEntryBase<MFP, R, C*, R(*)(C*, A...), A...> {};
template <class C, class R, class... A, R (C::*MFP)(A...) const>
struct HookEntry<MFP> : HookEntryBase<MFP, R, const C*, R(*)(const C*, A...), A...> {};
template <auto MFP>
void hookAddPre(int32_t (*fn)(void* args)) {
using E = HookEntry<MFP>;
g_api->hook_install(mfpAddr(MFP), reinterpret_cast<void*>(E::trampoline),
reinterpret_cast<void**>(&E::g_orig));
g_api->hook_pre(mfpAddr(MFP), fn);
}
template <auto MFP>
void hookAddPost(void (*fn)(void* args)) {
using E = HookEntry<MFP>;
g_api->hook_install(mfpAddr(MFP), reinterpret_cast<void*>(E::trampoline),
reinterpret_cast<void**>(&E::g_orig));
g_api->hook_post(mfpAddr(MFP), fn);
}
template <auto MFP>
void hookSetReplace(void (*fn)(void* args)) {
using E = HookEntry<MFP>;
g_api->hook_install(mfpAddr(MFP), reinterpret_cast<void*>(E::trampoline),
reinterpret_cast<void**>(&E::g_orig));
g_api->hook_replace(mfpAddr(MFP), fn);
}
} // namespace dusk
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
namespace dusk {
void hookInstallByAddr(void* fn_addr, void* tramp_fn, void** orig_store);
void hookRegisterPre (void* fn_addr, void* mod, int32_t (*fn)(void* args));
void hookRegisterPost(void* fn_addr, void* mod, void (*fn)(void* args));
void hookSetReplace (void* fn_addr, void* mod, void (*fn)(void* args));
bool hookDispatchPre (void* fn_addr, void* args);
void hookDispatchPost(void* fn_addr, void* args);
void hookClearMod(void* mod);
} // namespace dusk
+50
View File
@@ -0,0 +1,50 @@
#ifndef DUSK_MOD_API_H
#define DUSK_MOD_API_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DUSK_MOD_API_VERSION 1
#if defined(_WIN32)
# define DUSK_MOD_EXPORT __declspec(dllexport)
#else
# define DUSK_MOD_EXPORT __attribute__((visibility("default")))
#endif
typedef struct DuskModAPI {
uint32_t api_version;
const char* mod_dir;
void (*log_info) (const char* fmt, ...);
void (*log_warn) (const char* fmt, ...);
void (*log_error)(const char* fmt, ...);
void* (*load_resource)(const char* relative_path, size_t* out_size);
void (*free_resource)(void* data);
void (*register_tab_content)(void (*draw_fn)(void* userdata), void* userdata);
void (*register_menu_item) (void (*draw_fn)(void* userdata), void* userdata);
void (*hook_install)(void* fn_addr, void* tramp_fn, void** orig_store);
void (*hook_pre) (void* fn_addr, int32_t (*fn)(void* args));
void (*hook_post) (void* fn_addr, void (*fn)(void* args));
void (*hook_replace)(void* fn_addr, void (*fn)(void* args));
bool (*hook_dispatch_pre) (void* fn_addr, void* args);
void (*hook_dispatch_post)(void* fn_addr, void* args);
} DuskModAPI;
void mod_init(DuskModAPI* api);
void mod_tick(DuskModAPI* api);
#ifdef __cplusplus
}
#endif
#endif
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include "dusk/mod_api.h"
namespace dusk {
struct ModDrawCallback {
void (*draw_fn)(void* userdata);
void* userdata;
};
struct LoadedMod {
std::string name;
std::string version;
std::string author;
std::string description;
std::string mod_path;
std::string dir;
void* handle = nullptr;
bool active = false;
using FnInit = void (*)(DuskModAPI*);
using FnTick = void (*)(DuskModAPI*);
using FnCleanup = void (*)(DuskModAPI*);
using FnSetImguiCtx = void (*)(void*);
FnInit fn_init = nullptr;
FnTick fn_tick = nullptr;
FnCleanup fn_cleanup = nullptr;
FnSetImguiCtx fn_set_imgui_ctx = nullptr;
DuskModAPI api{};
std::vector<ModDrawCallback> tab_content;
std::vector<ModDrawCallback> menu_items;
};
class ModLoader {
public:
static ModLoader& instance();
void setModsDir(std::filesystem::path dir) { m_modsDir = std::move(dir); }
void init();
void tick();
void shutdown();
const std::vector<LoadedMod>& mods() const { return m_mods; }
static void callDrawCallback(const LoadedMod& mod, const ModDrawCallback& cb);
private:
std::vector<LoadedMod> m_mods;
std::filesystem::path m_modsDir = "mods";
bool m_initialized = false;
void tryLoadDusk(const std::filesystem::path& modPath);
void buildAPI(LoadedMod& mod);
};
} // namespace dusk
+13
View File
@@ -104,6 +104,19 @@ inline int __builtin_clz(unsigned int v) {
#endif
// Data symbols in dusk.dll need dllimport on the mod side
// DUSK_BUILDING_GAME is defined for the game build so the same headers work in both.
#if defined(TARGET_PC) && defined(_WIN32) && !defined(DUSK_BUILDING_GAME)
# define DUSK_GAME_EXTERN extern __declspec(dllimport)
# define DUSK_GAME_DATA __declspec(dllimport)
#elif defined(TARGET_PC) && defined(_WIN32) && defined(DUSK_BUILDING_GAME)
# define DUSK_GAME_EXTERN extern
# define DUSK_GAME_DATA __declspec(dllexport)
#else
# define DUSK_GAME_EXTERN extern
# define DUSK_GAME_DATA
#endif
#define FAST_DIV(x, n) (x >> (n / 2))
#define SQUARE(x) ((x) * (x))
+2 -1
View File
@@ -4,6 +4,7 @@
#include "JSystem/JUtility/JUTGamePad.h"
#include "SSystem/SComponent/c_API_controller_pad.h"
#include "dusk/settings.h"
#include "global.h"
// Controller Ports 1 - 4
enum { PAD_1, PAD_2, PAD_3, PAD_4 };
@@ -94,7 +95,7 @@ public:
static void stopMotorWaveHard(u32 pad) { return m_gamePad[pad]->stopMotorWaveHard(); }
static JUTGamePad* m_gamePad[4];
static interface_of_controller_pad m_cpadInfo[4];
static DUSK_GAME_DATA interface_of_controller_pad m_cpadInfo[4];
static interface_of_controller_pad m_debugCpadInfo[4];
};
+1 -1
View File
@@ -371,6 +371,7 @@ public:
static int m_height;
static f32 m_heightF;
static f32 m_widthF;
#endif
#if TARGET_PC
static f32 m_safeMinXF;
@@ -380,7 +381,6 @@ public:
static f32 m_safeWidthF;
static f32 m_safeHeightF;
#endif
#endif
};
#endif /* M_DO_M_DO_GRAPHIC_H */