Merge pull request #112 from geode-sdk/tulip-hook

Tulip hook
This commit is contained in:
mat 2023-01-23 15:25:02 -03:00 committed by GitHub
commit e170fbdf99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 1440 additions and 689 deletions

View file

@ -21,6 +21,10 @@ project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C)
add_library(${PROJECT_NAME} INTERFACE)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(GEODE_ALWAYS_BUILD_CODEGEN ON)
endif()
if (NOT DEFINED GEODE_DEBUG AND (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo))
set(GEODE_DEBUG ON)
endif()
@ -68,7 +72,7 @@ add_custom_command(
COMMAND echo codegen > ${GEODE_CODEGEN_PATH}/.stamp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Run Codegen"
OUTPUT ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/.stamp
OUTPUT ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp ${GEODE_CODEGEN_PATH}/.stamp
)
add_custom_target(CodegenRun
@ -84,7 +88,26 @@ if (NOT EXISTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
file(TOUCH ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
endif()
target_sources(${PROJECT_NAME} INTERFACE ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
if (NOT EXISTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp)
make_directory(${GEODE_CODEGEN_PATH})
make_directory(${GEODE_CODEGEN_PATH}/Geode)
file(TOUCH ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp)
endif()
add_library(GeodeCodegenSources ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp)
target_link_directories(GeodeCodegenSources PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/loader/include/link)
target_link_libraries(GeodeCodegenSources PRIVATE ghc_filesystem fmt TulipHookInclude)
target_include_directories(GeodeCodegenSources PRIVATE
${GEODE_CODEGEN_PATH}
${GEODE_LOADER_PATH}/include
${GEODE_LOADER_PATH}/include/Geode/cocos/include
${GEODE_LOADER_PATH}/include/Geode/cocos/extensions
${GEODE_LOADER_PATH}/include/Geode/fmod
)
target_compile_features(GeodeCodegenSources PUBLIC cxx_std_20)
target_precompile_headers(GeodeCodegenSources INTERFACE
"${GEODE_LOADER_PATH}/include/Geode/Bindings.hpp"
)
target_include_directories(${PROJECT_NAME} INTERFACE
${GEODE_CODEGEN_PATH}
@ -99,7 +122,10 @@ target_link_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lo
CPMAddPackage("gh:fmtlib/fmt#9.1.0")
CPMAddPackage("gh:gulrak/filesystem#3e5b930")
target_link_libraries(${PROJECT_NAME} INTERFACE ghc_filesystem fmt)
# Tulip hook (hooking)
CPMAddPackage("gh:geode-sdk/TulipHook#9980bcf")
target_link_libraries(${PROJECT_NAME} INTERFACE ghc_filesystem fmt TulipHookInclude GeodeCodegenSources)
if (NOT EXISTS ${GEODE_BIN_PATH})
@ -124,11 +150,8 @@ elseif(EXISTS ${GEODE_PLATFORM_BIN_PATH})
target_link_libraries(${PROJECT_NAME} INTERFACE "${GEODE_PLATFORM_BIN_PATH}")
target_precompile_headers(${PROJECT_NAME} INTERFACE
"${GEODE_LOADER_PATH}/include/Geode/DefaultInclude.hpp"
"${GEODE_LOADER_PATH}/include/Geode/Loader.hpp"
"${GEODE_LOADER_PATH}/include/Geode/Geode.hpp"
# please stop adding modify here its not here because it makes windows compilation take longer than geode 1.0 release date
"${GEODE_LOADER_PATH}/include/Geode/UI.hpp"
"${GEODE_LOADER_PATH}/include/Geode/cocos/include/cocos2d.h"
"${GEODE_LOADER_PATH}/include/Geode/cocos/extensions/cocos-ext.h"
)
else()
message(FATAL_ERROR

View file

@ -3801,6 +3801,7 @@ class LevelSettingsObject : cocos2d::CCNode {
int m_groundIndex;
int m_fontIndex;
bool m_startsWithStartPos;
bool m_isFlipped;
GJGameLevel* m_level;
gd::string m_guidelineString;
int m_unknown;

View file

@ -32,7 +32,7 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "MacOS")
)
target_link_libraries(${PROJECT_NAME} INTERFACE curl "-framework Cocoa")
target_compile_options(${PROJECT_NAME} INTERFACE -fms-extensions -Wno-deprecated -Wno-ignored-attributes -Os #[[-flto]] -fvisibility=internal)
target_compile_options(${PROJECT_NAME} INTERFACE -fms-extensions #[[-Wno-deprecated]] -Wno-ignored-attributes -Os #[[-flto]] #[[-fvisibility=internal]])
set(GEODE_PLATFORM_BINARY "Geode.dylib")

View file

@ -3,8 +3,15 @@
namespace { namespace format_strings {
char const* address_begin = R"GEN(
#include <Geode/Bindings.hpp>
#include <Geode/modify/Addresses.hpp>
#include <Geode/modify/Traits.hpp>
)GEN";
char const* declare_address = R"GEN(
GEODE_INLINE GEODE_HIDDEN static uintptr_t address{index}() {{
template <>
uintptr_t geode::modifier::address<{index}>() {{
static uintptr_t ret = {address};
return ret;
}}
@ -17,6 +24,7 @@ std::string generateAddressHeader(Root& root) {
TypeBank bank;
bank.loadFrom(root);
output += format_strings::address_begin;
for (auto& c : root.classes) {
@ -32,9 +40,9 @@ std::string generateAddressHeader(Root& root) {
if (codegen::getStatus(field) == BindStatus::Binded) {
const auto ids = bank.getIDs(fn->beginning, c.name);
address_str = fmt::format("addresser::get{}Virtual((types::member{})(&{}::{}))",
address_str = fmt::format("addresser::get{}Virtual(Resolve<{}>::func(&{}::{}))",
str_if("Non", !fn->beginning.is_virtual),
ids.member,
codegen::getParameterTypes(fn->beginning),
field.parent,
fn->beginning.name
);

View file

@ -8,6 +8,7 @@ namespace { namespace format_strings {
)GEN";
char const* class_includes = R"GEN(#pragma once
#include <stdexcept>
#include <Geode/platform/platform.hpp>
#include <Geode/c++stl/gdstdlib.hpp>
#include <cocos2d.h>
@ -20,6 +21,7 @@ namespace { namespace format_strings {
char const* class_no_includes = R"GEN(#pragma once
#include <Geode/platform/platform.hpp>
#include <stdexcept>
)GEN";
@ -41,14 +43,17 @@ public:
char const* function_definition = R"GEN({docs} {static}{virtual}{return_type} {function_name}({parameters}){const};
)GEN";
char const* error_definition = R"GEN( template <bool T=false>
char const* error_definition = R"GEN(
#ifdef GEODE_WARN_INCORRECT_MEMBERS
[[deprecated("Function is not implemented - will throw at runtime!!!")]]
#endif
{static}{return_type} {function_name}({parameters}){const}{{
static_assert(T, "Implement {class_name}::{function_name}");
throw std::runtime_error("Use of undefined function " + GEODE_PRETTY_FUNCTION);
}}
)GEN";
char const* error_definition_virtual = R"GEN(
#ifndef GEODE_DONT_WARN_INCORRECT_MEMBERS
#ifdef GEODE_WARN_INCORRECT_MEMBERS
[[deprecated("Use of undefined virtual function - will crash at runtime!!!")]]
#endif
{virtual}{return_type} {function_name}({parameters}){const}{{
@ -60,7 +65,7 @@ public:
)GEN";
char const* warn_offset_member = R"GEN(
#ifndef GEODE_DONT_WARN_INCORRECT_MEMBERS
#ifdef GEODE_WARN_INCORRECT_MEMBERS
[[deprecated("Member placed incorrectly - will crash at runtime!!!")]]
#endif
)GEN";

View file

@ -31,9 +31,9 @@ int main(int argc, char** argv) try {
}
}
writeFile(writeDir / "GeneratedAddress.hpp", generateAddressHeader(root));
writeFile(writeDir / "GeneratedAddress.cpp", generateAddressHeader(root));
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify"));
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root));
// writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root));
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root));
writeFile(writeDir / "GeneratedBinding.hpp", generateBindingHeader(root, writeDir / "binding"));
writeFile(writeDir / "GeneratedPredeclare.hpp", generatePredeclareHeader(root));

View file

@ -6,41 +6,44 @@
namespace {
namespace format_strings {
char const* wrap_start = R"GEN(
namespace wrap {
)GEN";
char const* wrap_declare_identifier = R"GEN(
#ifndef GEODE_WRAP_{function_name}
#define GEODE_WRAP_{function_name}
GEODE_WRAPPER_FOR_IDENTIFIER({function_name})
#endif
)GEN";
char const* wrap_end = R"GEN(
}
)GEN";
// requires: class_name
// requires: class_name, class_include
char const* modify_start = R"GEN(#pragma once
#include <Geode/modify/Modify.hpp>
#include <Geode/modify/Field.hpp>
#include <Geode/modify/InternalMacros.hpp>
#include <Geode/modify/Addresses.hpp>
{class_include}
using namespace geode::modifier;
namespace geode::modifier {{
{wrap}
template<class Derived>
struct ModifyDerive<Derived, {class_name}> : ModifyBase<ModifyDerive<Derived, {class_name}>> {{
using ModifyBase<ModifyDerive<Derived, {class_name}>>::ModifyBase;
{statics}
template<class Der>
struct ModifyDerive<Der, {class_name}> : ModifyBase<ModifyDerive<Der, {class_name}>> {{
using BaseModify = ModifyBase<ModifyDerive<Der, {class_name}>>;
using ModifyBase<ModifyDerive<Der, {class_name}>>::ModifyBase;
using Base = {class_name};
static void apply() {{
using Derived = Der;
void apply() override {{
using namespace geode::core::meta;
)GEN";
char const* statics_declare_identifier = R"GEN(
#ifndef GEODE_STATICS_{function_name}
#define GEODE_STATICS_{function_name}
GEODE_AS_STATIC_FUNCTION({function_name})
#endif
)GEN";
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
char const* apply_function = R"GEN(
GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {pure_index}, {function_convention}, {class_name}, {function_name}))GEN";
GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {function_convention}, {class_name}, {function_name}, {parameter_types}))GEN";
char const* apply_constructor = R"GEN(
GEODE_APPLY_MODIFY_FOR_CONSTRUCTOR({addr_index}, {function_convention}, {class_name}, {parameter_types}))GEN";
char const* apply_destructor = R"GEN(
GEODE_APPLY_MODIFY_FOR_DESTRUCTOR({addr_index}, {function_convention}, {class_name}))GEN";
char const* modify_end = R"GEN(
}
@ -66,45 +69,71 @@ std::string generateModifyHeader(Root& root, ghc::filesystem::path const& single
output += fmt::format(format_strings::modify_include, fmt::arg("file_name", filename));
std::string single_output;
std::string wrap;
// wrap
wrap += format_strings::wrap_start;
std::string class_include;
if (c.name.find("cocos2d::extension") != std::string::npos) {
class_include = "#include <cocos-ext.h>";
}
else if (c.name.find("cocos2d") != std::string::npos) {
class_include = "#include <cocos2d.h>";
}
else {
class_include = fmt::format(
"#include <Geode/binding/{class_name}.hpp>",
fmt::arg("class_name", codegen::getUnqualifiedClassName(c.name))
);
}
std::string statics;
std::set<std::string> used;
for (auto& f : c.fields) {
if (auto fn = f.get_fn()) {
if (fn->type == FunctionType::Normal && !used.count(fn->name)) {
used.insert(fn->name);
wrap += fmt::format(
format_strings::wrap_declare_identifier, fmt::arg("function_name", fn->name)
statics += fmt::format(
format_strings::statics_declare_identifier,
fmt::arg("function_name", fn->name)
);
}
}
}
wrap += format_strings::wrap_end;
single_output += fmt::format(
format_strings::modify_start, fmt::arg("class_name", c.name), fmt::arg("wrap", wrap)
format_strings::modify_start,
fmt::arg("statics", statics),
fmt::arg("class_name", c.name),
fmt::arg("class_include", class_include)
);
// modify
for (auto& f : c.fields) {
if (codegen::getStatus(f) != BindStatus::Unbindable) {
auto begin = f.get_fn();
auto func = TypeBank::makeFunc(*begin, c.name);
std::string function_name;
std::string format_string;
switch (begin->type) {
case FunctionType::Normal: function_name = begin->name; break;
case FunctionType::Ctor: function_name = "constructor"; break;
case FunctionType::Dtor: function_name = "destructor"; break;
case FunctionType::Normal:
format_string = format_strings::apply_function;
break;
case FunctionType::Ctor:
format_string = format_strings::apply_constructor;
break;
case FunctionType::Dtor:
format_string = format_strings::apply_destructor;
break;
}
single_output += fmt::format(
format_strings::apply_function, fmt::arg("addr_index", f.field_id),
format_string,
fmt::arg("addr_index", f.field_id),
fmt::arg("pure_index", bank.getPure(*begin, c.name)),
fmt::arg("class_name", c.name), fmt::arg("function_name", function_name),
fmt::arg("function_convention", codegen::getConvention(f))
fmt::arg("class_name", c.name),
fmt::arg("function_name", begin->name),
fmt::arg("function_convention", codegen::getModifyConvention(f)),
fmt::arg("parameter_types", fmt::join(func.parameter_types, ", "))
);
}
}

View file

@ -166,6 +166,27 @@ namespace codegen {
else throw codegen::error("Tried to get convention of non-function");
}
inline std::string getModifyConvention(Field& f) {
if (codegen::platform != Platform::Windows) return "tulip::hook::DefaultConvention";
if (auto fn = f.get_fn()) {
auto status = getStatus(f);
if (fn->is_static) {
if (status == BindStatus::Binded) return "tulip::hook::CdeclConvention";
else return "tulip::hook::OptcallConvention";
}
else if (fn->is_virtual) {
return "tulip::hook::ThiscallConvention";
}
else {
if (status == BindStatus::Binded) return "tulip::hook::ThiscallConvention";
else return "tulip::hook::MembercallConvention";
}
}
else throw codegen::error("Tried to get convention of non-function");
}
inline std::string getUnqualifiedClassName(std::string const& s) {
auto index = s.rfind("::");
if (index == std::string::npos) return s;

View file

@ -3,39 +3,72 @@
namespace { namespace format_strings {
char const* source_start = R"CAC(
#include <stdexcept>
#include <Geode/Bindings.hpp>
#include <Geode/utils/addresser.hpp>
#include <Geode/utils/casts.hpp>
#include <Geode/meta/meta.hpp>
#include <Geode/modify/Addresses.hpp>
#include <Geode/modify/Types.hpp>
#include <Geode/modify/Addresses.hpp>
#include <Geode/modify/Traits.hpp>
#include <tulip/TulipHook.hpp>
using namespace geode;
using namespace geode::cast;
using namespace geode::modifier;
using cocos2d::CCDestructor;
using namespace geode::core::meta; // Default convention
using namespace geode::core::meta::x86; // Windows x86 conventions, Function
using namespace geode::modifier; // types
std::unordered_map<void*, bool>& CCDestructor::destructorLock() {{
static auto ret = new std::unordered_map<void*, bool>;
return *ret;
}}
bool& CCDestructor::globalLock() {{
static thread_local bool ret = false;
return ret;
}}
bool& CCDestructor::lock(void* self) {
return destructorLock()[self];
}
CCDestructor::~CCDestructor() {{
destructorLock().erase(this);
}}
auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadata) {
auto wrapped = tulip::hook::createWrapper(reinterpret_cast<void*>(address), metadata);
if (wrapped.isErr()) {{
throw std::runtime_error(wrapped.unwrapErr());
}}
return wrapped.unwrap();
}
)CAC";
char const* declare_member = R"GEN(
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(this{parameter_comma}{arguments});
auto {class_name}::{function_name}({parameters}){const} -> decltype({function_name}({arguments})) {{
using FunctionType = decltype({function_name}({arguments}))(*)({class_name}{const}*{parameter_comma}{parameter_types});
static auto func = wrapFunction(address<{addr_index}>(), tulip::hook::WrapperMetadata{{
.m_convention = std::make_shared<{convention}>(),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
return reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
}}
)GEN";
char const* declare_virtual = R"GEN(
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto self = addresser::thunkAdjust((types::member{member_index})(&{class_name}::{function_name}), this);
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(self{parameter_comma}{arguments});
auto {class_name}::{function_name}({parameters}){const} -> decltype({function_name}({arguments})) {{
auto self = addresser::thunkAdjust(Resolve<{parameter_types}>::func(&{class_name}::{function_name}), this);
using FunctionType = decltype({function_name}({arguments}))(*)({class_name}{const}*{parameter_comma}{parameter_types});
static auto func = wrapFunction(address<{addr_index}>(), tulip::hook::WrapperMetadata{{
.m_convention = std::make_shared<{convention}>(),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
return reinterpret_cast<FunctionType>(func)(self{parameter_comma}{arguments});
}}
)GEN";
char const* declare_static = R"GEN(
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func({arguments});
auto {class_name}::{function_name}({parameters}){const} -> decltype({function_name}({arguments})) {{
using FunctionType = decltype({function_name}({arguments}))(*)({parameter_types});
static auto func = wrapFunction(address<{addr_index}>(), tulip::hook::WrapperMetadata{{
.m_convention = std::make_shared<{convention}>(),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
return reinterpret_cast<FunctionType>(func)({arguments});
}}
)GEN";
@ -44,8 +77,12 @@ types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
// basically we destruct it once by calling the gd function,
// then lock it, so that other gd destructors dont get called
if (CCDestructor::lock(this)) return;
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction(address<{addr_index}>(), tulip::hook::WrapperMetadata{{
.m_convention = std::make_shared<{convention}>(),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
// we need to construct it back so that it uhhh ummm doesnt crash
// while going to the child destructors
auto thing = new (this) {class_name}(std::monostate(), sizeof({class_name}));
@ -60,8 +97,12 @@ types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
// no crashes :pray:
CCDestructor::lock(this) = true;
{class_name}::~{unqualified_class_name}();
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction(address<{addr_index}>(), tulip::hook::WrapperMetadata{{
.m_convention = std::make_shared<{convention}>(),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
}}
)GEN";
@ -149,7 +190,7 @@ std::string generateBindingSource(Root& root) {
fmt::arg("class_name", c.name),
fmt::arg("unqualified_class_name", codegen::getUnqualifiedClassName(c.name)),
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("convention", codegen::getConvention(f)),
fmt::arg("convention", codegen::getModifyConvention(f)),
fmt::arg("function_name", fn->beginning.name),
fmt::arg("meta_index", ids.meta),
fmt::arg("member_index", ids.member),

View file

@ -1,7 +1,20 @@
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Mod.hpp>
namespace geode {
/**
* To bypass the need for cyclic dependencies,
* this function does the exact same as Mod::get()
* However, it can be externed, unlike Mod::get()
* @returns Same thing Mod::get() returns
*/
Mod* getMod() {
return Mod::get();
}
}
namespace {
// to make sure the instance is set into the sharedMod<> in load time
static auto mod = geode::getMod();
}
}

View file

@ -121,6 +121,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
if (APPLE)
# For profiling
target_compile_options(${PROJECT_NAME} PUBLIC "-ftime-trace")
# target_link_options(${PROJECT_NAME} PRIVATE "-Wl,-e,_dynamicInit")
#set_property(TARGET ${PROJECT_NAME} PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time")
endif()
@ -134,9 +135,8 @@ CPMAddPackage("gh:google/re2#954656f")
target_include_directories(${PROJECT_NAME} PRIVATE ${md4c_SOURCE_DIR}/src)
# Lilac (hooking)
add_subdirectory(lilac)
target_link_libraries(${PROJECT_NAME} md4c z lilac_hook geode-sdk re2)
target_link_libraries(${PROJECT_NAME} md4c z TulipHook geode-sdk re2)
# Use precompiled headers for faster builds
if (NOT GEODE_DISABLE_PRECOMPILED_HEADERS)
@ -146,7 +146,6 @@ if (NOT GEODE_DISABLE_PRECOMPILED_HEADERS)
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Loader.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/UI.hpp"
# "${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Bindings.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Modify.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/include/cocos2d.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/extensions/cocos-ext.h"
)

View file

@ -6,7 +6,6 @@
#include "loader/Mod.hpp"
#include "loader/ModEvent.hpp"
#include "loader/Setting.hpp"
#include "loader/SettingEvent.hpp"
#include "loader/Dirs.hpp"
#include <Geode/DefaultInclude.hpp>

View file

@ -79,21 +79,11 @@ public:
*/
class CCDestructor : public CCCopying {
private:
static inline auto& destructorLock() {
static auto ret = new std::unordered_map<void*, bool>;
return *ret;
}
static std::unordered_map<void*, bool>& destructorLock();
public:
static inline bool& globalLock() {
static thread_local bool ret = false;
return ret;
}
static inline bool& lock(void* self) {
return destructorLock()[self];
}
inline ~CCDestructor() {
destructorLock().erase(this);
}
static bool& globalLock();
static bool& lock(void* self);
~CCDestructor();
};
#pragma warning(push)

View file

@ -44,7 +44,8 @@ NS_CC_EXT_BEGIN
* @{
*/
class GEODE_DLL ColorPickerDelegate {
class CC_DLL ColorPickerDelegate {
public:
virtual void colorValueChanged(ccColor3B) {}
};

View file

@ -57,7 +57,6 @@ NS_CC_BEGIN
class CC_DLL CCLabelTTF : public CCSprite, public CCLabelProtocol
{
GEODE_FRIEND_MODIFY
GEODE_FRIEND_MODIFY
public:
/**

View file

@ -113,20 +113,32 @@ It's new in cocos2d-x since v0.99.5
* macro.
*/
class GeodeNodeMetadata;
namespace geode {
struct modify;
namespace modifier {
struct addresses;
struct types;
#include <stdint.h>
namespace geode {
struct modify;
namespace modifier {
struct types;
class FieldContainer;
}
template <class Derived, class Base>
class ModifyDerive;
template <uint32_t>
uintptr_t address();
}
}
#define GEODE_FRIEND_MODIFY GEODE_ADD(\
friend struct ::geode::modify;\
friend struct ::geode::modifier::addresses;\
friend struct ::geode::modifier::types;\
friend class ::GeodeNodeMetadata;\
)
#define GEODE_FRIEND_MODIFY \
friend struct ::geode::modify; \
template <class Derived, class Base> \
friend class ::geode::modifier::ModifyDerive; \
friend struct ::geode::modifier::types; \
friend class ::GeodeNodeMetadata; \
template <uint32_t> \
friend uintptr_t geode::modifier::address();
#define GEODE_ADD(...) __VA_ARGS__
#ifdef __cplusplus

View file

@ -0,0 +1,79 @@
// #include <nlohmann/json_fwd.hpp>
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>>
class basic_json;
/*!
@brief JSON Pointer
A JSON pointer defines a string syntax for identifying a specific value
within a JSON document. It can be used with functions `at` and
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
@since version 2.0.0
*/
template<typename BasicJsonType>
class json_pointer;
/*!
@brief default JSON class
This type is the default specialization of the @ref basic_json class which
uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
template<class Key, class T, class IgnoredLess, class Allocator>
struct ordered_map;
/*!
@brief ordered JSON class
This type preserves the insertion order of object keys.
@since version 3.9.0
*/
using ordered_json = basic_json<nlohmann::ordered_map>;
} // namespace nlohmann
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_

View file

@ -1,7 +1,6 @@
#pragma once
#include "../utils/casts.hpp"
#include "Mod.hpp"
#include <Geode/DefaultInclude.hpp>
#include <type_traits>
@ -11,6 +10,8 @@ namespace geode {
class Mod;
class Event;
Mod* getMod();
enum class ListenerResult {
Propagate,
Stop

View file

@ -3,9 +3,10 @@
#include "../DefaultInclude.hpp"
#include "../hook-core/Hook.hpp"
#include "../utils/general.hpp"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include <inttypes.h>
#include <string_view>
#include <tulip/TulipHook.hpp>
namespace geode {
class Mod;
@ -13,84 +14,132 @@ namespace geode {
class GEODE_DLL Hook {
private:
Mod* m_owner;
std::string m_displayName;
void* m_address;
void* m_detour;
core::HookHandle m_handle;
bool m_enabled;
Result<core::HookHandle> (*m_addFunction)(void*);
// Only allow friend classes to create
// hooks. Whatever method created the
// hook should take care of populating
// m_owner and m_handle.
Hook() : m_enabled(false) {}
template <auto Detour, template <class, class...> class Conv, class Ret, class... Args>
static Hook* create(Ret (*address)(Args...), std::string const& displayName, Mod* owner) {
auto ret = new Hook;
ret->m_address = (void*)address;
ret->m_detour = (void*)Detour;
ret->m_owner = owner;
ret->m_displayName = displayName;
ret->m_addFunction =
(Result<core::HookHandle>(*)(void*)) & core::hook::add<Detour, Conv, Ret, Args...>;
return ret;
}
// no copying
Hook(Hook const&) = delete;
Hook operator=(Hook const&) = delete;
// Used by Mod
Result<> enable();
Result<> disable();
class Impl;
std::shared_ptr<Impl> m_impl;
Hook(std::shared_ptr<Impl>&& impl);
~Hook();
friend class Mod;
friend class Loader;
static std::vector<std::pair<Hook*, Mod*>> internalHooks;
static bool readyToHook;
Result<> enable();
Result<> disable();
public:
/**
* 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,
* 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 detour The detour to run when the hook is hit. The detour's
* calling convention should be cdecl
* @param displayName A human-readable name describing the hook,
* usually the fully qualified name of the function being hooked
* @param handlerMetadata Metadata for the hook handler
* @param hookMetadata Metadata for the hook itself
* @returns The created hook, or an error. Make sure to add the created
* hook to the mod that owns it using mod->addHook!
*/
static Hook* create(
Mod* owner,
void* address,
void* detour,
std::string const& displayName,
tulip::hook::HandlerMetadata const& handlerMetadata,
tulip::hook::HookMetadata const& hookMetadata
);
template <class Convention, class DetourType>
static Hook* create(
Mod* owner,
void* address,
DetourType detour,
std::string const& displayName,
tulip::hook::HookMetadata const& hookMetadata = tulip::hook::HookMetadata()
) {
auto handlerMetadata = tulip::hook::HandlerMetadata{
.m_convention = std::make_shared<Convention>(),
.m_abstract = tulip::hook::AbstractFunction::from(detour)
};
return Hook::create(
owner,
address,
reinterpret_cast<void*>(detour),
displayName,
handlerMetadata,
hookMetadata
);
}
Hook(Hook const&) = delete;
Hook operator=(Hook const&) = delete;
/**
* Get the address of the function hooked.
* @returns Address
*/
uintptr_t getAddress() const {
return reinterpret_cast<uintptr_t>(m_address);
}
uintptr_t getAddress() const;
/**
* Get the display name of the function hooked.
* @returns Display name
*/
std::string_view getDisplayName() const {
return m_displayName;
}
std::string_view getDisplayName() const;
/**
* Get whether the hook is enabled or not.
* @returns True if enabled, false if not.
*/
bool isEnabled() const {
return m_enabled;
}
bool isEnabled() const;
/**
* Get the owner of this hook.
* @returns Pointer to the owner's Mod handle.
*/
Mod* getOwner() const {
return m_owner;
}
Mod* getOwner() const;
/**
* Get info about the hook as JSON
* @note For IPC
*/
nlohmann::json getRuntimeInfo() const;
/**
* Get the metadata of the hook.
* @returns Hook metadata
*/
tulip::hook::HookMetadata getHookMetadata() const;
/**
* Set the metadata of the hook.
* @param metadata Hook metadata
*/
void setHookMetadata(tulip::hook::HookMetadata const& metadata);
/**
* Get the priority of the hook.
* @returns Priority
*/
int32_t getPriority() const;
/**
* Set the priority of the hook.
* @param priority 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 {

View file

@ -2,6 +2,7 @@
#include "Event.hpp"
#include "Loader.hpp"
#include "../external/json/json_fwd.hpp"
namespace geode {
#ifdef GEODE_IS_WINDOWS
@ -31,7 +32,7 @@ namespace geode {
public:
std::string targetModID;
std::string messageID;
nlohmann::json messageData;
std::unique_ptr<nlohmann::json> messageData;
nlohmann::json& replyData;
friend class IPCFilter;
@ -62,11 +63,5 @@ namespace geode {
);
};
template<class = void>
std::monostate listenForIPC(std::string const& messageID, nlohmann::json(*callback)(IPCEvent*)) {
(void) new EventListener(
callback, IPCFilter(getMod()->getID(), messageID)
);
return std::monostate();
}
std::monostate listenForIPC(std::string const& messageID, nlohmann::json(*callback)(IPCEvent*));
}

View file

@ -15,7 +15,7 @@ namespace geode {
#pragma warning(disable : 4251)
class Mod;
inline Mod* getMod();
Mod* getMod();
namespace log {
using log_clock = std::chrono::system_clock;

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include "../cocos/support/zip_support/ZipUtils.h"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include "../utils/Result.hpp"
#include "../utils/VersionInfo.hpp"
#include "../utils/general.hpp"
@ -16,6 +16,7 @@
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <tulip/TulipHook.hpp>
namespace geode {
template <class T>
@ -110,51 +111,16 @@ namespace geode {
nlohmann::json& getSaveContainer();
template <class T>
T getSettingValue(std::string const& key) const {
if (auto sett = this->getSetting(key)) {
return SettingValueSetter<T>::get(sett);
}
return T();
}
T getSettingValue(std::string const& key) const;
template <class T>
T setSettingValue(std::string const& key, T const& value) {
if (auto sett = this->getSetting(key)) {
auto old = this->getSettingValue<T>(sett);
SettingValueSetter<T>::set(sett, value);
return old;
}
return T();
}
T setSettingValue(std::string const& key, T const& value);
template <class T>
T getSavedValue(std::string const& key) {
auto& saved = this->getSaveContainer();
if (saved.count(key)) {
try {
// json -> T may fail
return saved.at(key);
}
catch (...) {
}
}
return T();
}
T getSavedValue(std::string const& key);
template <class T>
T getSavedValue(std::string const& key, T const& defaultValue) {
auto& saved = this->getSaveContainer();
if (saved.count(key)) {
try {
// json -> T may fail
return saved.at(key);
}
catch (...) {
}
}
saved[key] = defaultValue;
return defaultValue;
}
T getSavedValue(std::string const& key, T const& defaultValue);
/**
* Set the value of an automatically saved variable. When the game is
@ -164,17 +130,11 @@ namespace geode {
* @returns The old value
*/
template<class T>
T setSavedValue(std::string const& key, T const& value) {
auto& saved = this->getSaveContainer();
auto old = this->getSavedValue<T>(key);
saved[key] = value;
return old;
}
T setSavedValue(std::string const& key, T const& value);
/**
* Get the mod container stored in the Interface
* @returns nullptr if Interface is not initialized,
* the mod pointer if it is initialized
* Get the Mod of the current mod being developed
* @returns The current mod
*/
template <class = void>
static inline GEODE_HIDDEN Mod* get() {
@ -197,31 +157,20 @@ namespace geode {
* @param address The absolute address of
* the function to hook, i.e. gd_base + 0xXXXX
* @param detour Pointer to your detour function
* @param displayName Name of the hook that will be
* displayed in the hook list
* @param hookMetadata Metadata of the hook
* @returns Successful result containing the
* Hook handle, errorful result with info on
* Hook pointer, errorful result with info on
* error
*/
template <auto Detour, template <class, class...> class Convention>
Result<Hook*> addHook(void* address) {
return this->addHook<Detour, Convention>("", address);
}
/**
* Create a hook at an address. Call the original
* function by calling the original function
* no trampoline needed. Also takes a displayName
* parameter to use for when visualizing the hook.
* @param address The absolute address of
* the function to hook, i.e. gd_base + 0xXXXX
* @param detour Pointer to your detour function
* @returns Successful result containing the
* Hook handle, errorful result with info on
* error
*/
template <auto Detour, template <class, class...> class Convention>
Result<Hook*> addHook(std::string const& displayName, void* address) {
auto hook =
Hook::create<Detour, Convention>((decltype(Detour))address, displayName, this);
template <class Convention, class DetourType>
Result<Hook*> addHook(
void* address, DetourType detour,
std::string const& displayName = "",
tulip::hook::HookMetadata const& hookMetadata = tulip::hook::HookMetadata()
) {
auto hook = Hook::create<Convention>(this, address, detour, displayName, hookMetadata);
return this->addHook(hook);
}
@ -346,16 +295,6 @@ namespace geode {
friend class ModImpl;
};
/**
* To bypass the need for cyclic dependencies,
* this function does the exact same as Mod::get()
* However, it can be externed, unlike Mod::get()
* @returns Same thing Mod::get() returns
*/
inline GEODE_HIDDEN Mod* getMod() {
return Mod::get();
}
}
inline char const* operator"" _spr(char const* str, size_t) {

View file

@ -6,7 +6,7 @@
namespace geode {
class Mod;
inline Mod* getMod();
Mod* getMod();
enum class ModEventType {
Loaded,

View file

@ -1,7 +1,7 @@
#pragma once
#include "Types.hpp"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include "../utils/VersionInfo.hpp"
#include "../utils/Result.hpp"
#include "Setting.hpp"
@ -157,7 +157,7 @@ namespace geode {
static bool validateID(std::string const& id);
private:
ModJson m_rawJSON;
std::shared_ptr<ModJson> m_rawJSON;
/**
* Version is passed for backwards

View file

@ -0,0 +1,59 @@
#include "Mod.hpp"
#include "../external/json/json.hpp"
namespace geode {
template <class T>
T Mod::getSettingValue(std::string const& key) const {
if (auto sett = this->getSetting(key)) {
return SettingValueSetter<T>::get(sett);
}
return T();
}
template <class T>
T Mod::setSettingValue(std::string const& key, T const& value) {
if (auto sett = this->getSetting(key)) {
auto old = this->getSettingValue<T>(sett);
SettingValueSetter<T>::set(sett, value);
return old;
}
return T();
}
template <class T>
T Mod::getSavedValue(std::string const& key) {
auto& saved = this->getSaveContainer();
if (saved.count(key)) {
try {
// json -> T may fail
return saved.at(key);
}
catch (...) {
}
}
return T();
}
template <class T>
T Mod::getSavedValue(std::string const& key, T const& defaultValue) {
auto& saved = this->getSaveContainer();
if (saved.count(key)) {
try {
// json -> T may fail
return saved.at(key);
}
catch (...) {
}
}
saved[key] = defaultValue;
return defaultValue;
}
template<class T>
T Mod::setSavedValue(std::string const& key, T const& value) {
auto& saved = this->getSaveContainer();
auto old = this->getSavedValue<T>(key);
saved[key] = value;
return old;
}
}

View file

@ -2,10 +2,9 @@
#include "Types.hpp"
#include "../DefaultInclude.hpp"
#include "../utils/JsonValidation.hpp"
#include "../utils/Result.hpp"
#include "../utils/file.hpp"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include <optional>
#include <unordered_set>
@ -16,6 +15,11 @@ namespace geode {
class SettingNode;
class SettingValue;
template <class Json>
struct JsonMaybeObject;
template <class Json>
struct JsonMaybeValue;
struct GEODE_DLL BoolSetting final {
using ValueType = bool;
@ -114,7 +118,7 @@ namespace geode {
};
struct GEODE_DLL CustomSetting final {
ModJson json;
std::shared_ptr<ModJson> json;
};
using SettingKind = std::variant<
@ -190,18 +194,8 @@ namespace geode {
m_definition(definition),
m_value(definition.defaultValue) {}
bool load(nlohmann::json const& json) override {
try {
m_value = json.get<ValueType>();
return true;
} catch(...) {
return false;
}
}
bool save(nlohmann::json& json) const {
json = m_value;
return true;
}
bool load(nlohmann::json const& json) override;
bool save(nlohmann::json& json) const;
GEODE_DLL SettingNode* createNode(float width) override;
T castDefinition() const {

View file

@ -0,0 +1,21 @@
#include "Setting.hpp"
#include "../external/json/json.hpp"
namespace geode {
template<class T>
bool GeodeSettingValue<T>::load(nlohmann::json const& json) {
try {
m_value = json.get<ValueType>();
return true;
}
catch (...) {
return false;
}
}
template<class T>
bool GeodeSettingValue<T>::save(nlohmann::json& json) const {
json = m_value;
return true;
}
}

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include "../platform/cplatform.h"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include <string>

View file

@ -1,10 +1,6 @@
#pragma once
#include "Types.hpp"
#include <Geode/utils/addresser.hpp>
namespace geode::modifier {
struct addresses {
#include <Geode/GeneratedAddress.hpp>
};
template <uint32_t Id>
uintptr_t address();
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "../utils/addresser.hpp"
#include "Traits.hpp"
#include "../loader/Log.hpp"
namespace geode::modifier {
/**
* A helper struct that generates a static function that calls the given function.
*/
#define GEODE_AS_STATIC_FUNCTION(FunctionName_) \
template <class Class2, auto Function> \
struct AsStaticFunction_##FunctionName_ { \
template <class FunctionType2> \
struct Impl {}; \
template <class Return, class... Params> \
struct Impl<Return (*)(Params...)> { \
static Return GEODE_CDECL_CALL function(Params... params) { \
return Class2::FunctionName_(params...); \
} \
}; \
template <class Return, class Class, class... Params> \
struct Impl<Return (Class::*)(Params...)> { \
static Return GEODE_CDECL_CALL function(Class* self, Params... params) { \
auto self2 = addresser::rthunkAdjust(Function, self); \
return self2->Class2::FunctionName_(params...); \
} \
}; \
template <class Return, class Class, class... Params> \
struct Impl<Return (Class::*)(Params...) const> { \
static Return GEODE_CDECL_CALL function(Class const* self, Params... params) { \
auto self2 = addresser::rthunkAdjust(Function, self); \
return self2->Class2::FunctionName_(params...); \
} \
}; \
static constexpr auto value = &Impl<decltype(Function)>::function; \
};
GEODE_AS_STATIC_FUNCTION(constructor)
GEODE_AS_STATIC_FUNCTION(destructor)
}

View file

@ -1,37 +1,89 @@
#pragma once
#include "../meta/meta.hpp"
#include "Addresses.hpp"
#include "AsStaticFunction.hpp"
#include "Field.hpp"
#include "Types.hpp"
#include "Wrapper.hpp"
#include "IDManager.hpp"
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Mod.hpp>
#include <iostream>
#include "IDManager.hpp"
#include <tulip/TulipHook.hpp>
#define GEODE_APPLY_MODIFY_FOR_FUNCTION( \
addr_index, pure_index, convention, className, functionName \
) \
if constexpr (wrap::functionName<Derived, types::pure##pure_index>::uuid != nullptr && (void*)wrap::functionName<Base, types::pure##pure_index>::uuid != (void*)wrap::functionName<Derived, types::pure##pure_index>::uuid) { \
(void)Mod::get() \
->addHook<wrap::functionName<Derived, types::pure##pure_index>::value, convention>( \
#className "::" #functionName, (void*)addresses::address##addr_index() \
); \
}
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(AddressIndex_, Convention_, ClassName_, FunctionName_, ...) \
do { \
constexpr auto b = Resolve<__VA_ARGS__>::func(&Base::FunctionName_); \
constexpr auto d = Resolve<__VA_ARGS__>::func(&Derived::FunctionName_); \
if constexpr (Unique::different<b, d>()) { \
auto hook = Hook::create<Convention_>( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##FunctionName_<Derived, d>::value, \
#ClassName_ "::" #FunctionName_ \
); \
this->m_hooks[#ClassName_ "::" #FunctionName_] = hook; \
} \
} while (0);
#define GEODE_APPLY_MODIFY_FOR_CONSTRUCTOR(AddressIndex_, Convention_, ClassName_, ...) \
do { \
if constexpr (HasConstructor<Derived>) { \
constexpr auto d = Resolve<__VA_ARGS__>::func(&Derived::constructor); \
auto hook = Hook::create<Convention_>( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##constructor<Derived, d>::value, \
#ClassName_ "::" #ClassName_ \
); \
this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \
} \
} while (0);
#define GEODE_APPLY_MODIFY_FOR_DESTRUCTOR(AddressIndex_, Convention_, ClassName_) \
do { \
if constexpr (HasDestructor<Derived>) { \
constexpr auto d = Resolve<>::func(&Derived::destructor); \
auto hook = Hook::create<Convention_>( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##destructor<Derived, d>::value, \
#ClassName_ "::" #ClassName_ \
); \
this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \
} \
} while (0);
namespace geode::modifier {
template <class Derived, class Base>
class ModifyDerive;
template <class Derived>
template <class ModifyDerived>
class ModifyBase {
public:
std::map<std::string, Hook*> m_hooks;
Result<Hook*> getHook(std::string const& name) {
if (m_hooks.find(name) == m_hooks.end()) {
return Err("Hook not in this modify");
}
return Ok(m_hooks[name]);
}
// unordered_map<handles> idea
ModifyBase() {
Derived::apply();
// i really dont want to recompile codegen
auto test = static_cast<ModifyDerived*>(this);
test->ModifyDerived::apply();
ModifyDerived::Derived::onModify(*this);
for (auto& [uuid, hook] : m_hooks) {
auto res = Mod::get()->addHook(hook);
if (!res) {
log::error("Failed to add hook {}: {}", hook->getDisplayName(), res.error());
}
}
}
virtual void apply() {}
template <class, class>
friend class ModifyDerive;
// explicit Modify(Property property) idea
@ -61,12 +113,18 @@ namespace geode {
// we already have utilities for these, which are ccdestructor
// and the monostate constructor
Modify() : Base(std::monostate(), sizeof(Base)) {}
~Modify() {
cocos2d::CCDestructor::lock(this) = true;
}
Modify(Modify const&) = delete;
Modify(Modify&&) = delete;
Modify& operator=(Modify const&) = delete;
Modify& operator=(Modify&&) = delete;
modifier::FieldIntermediate<Derived, Base> m_fields;
static void onModify(auto& self) {}
};
}

View file

@ -1,5 +1,4 @@
#pragma once
#include <Geode/meta/common.hpp>
#include <type_traits>
namespace geode::modifier {
@ -28,8 +27,6 @@ namespace geode::modifier {
using type = FunctionType*;
};
using ::geode::core::meta::always_false;
/**
* The ~unevaluated~ function that gets the appropriate
* version of a function type from its return, parameters, and classes.
@ -58,11 +55,151 @@ namespace geode::modifier {
* function pointers.
*/
template <auto a>
struct function_uuid {
struct FunctionUUID {
private:
constexpr static void function() {}
public:
constexpr static inline void (*value)() = &function_uuid::function;
constexpr static inline void (*value)() = &FunctionUUID::function;
};
/**
* A type trait that removes the class from a member function pointer.
*/
template <class Func>
struct RemoveClass {
using type = Func;
};
template <class Return, class Class, class... Params>
struct RemoveClass<Return(Class::*)(Params...)> {
using type = Return(Params...);
};
template <class Return, class Class, class... Params>
struct RemoveClass<Return(Class::*)(Params...) const> {
using type = Return(Params...);
};
template <class Func>
using RemoveClassType = typename RemoveClass<Func>::type;
/**
* A helper struct that allows for checking if two function pointers
* are the same or different.
*/
struct Unique {
using ValueType = void(*)(...);
static constexpr auto nvalue = static_cast<void(*)(...)>(nullptr);
template <auto Value>
struct Impl {
static void unique(...) {};
static constexpr auto value = &unique;
};
template <>
struct Impl<nvalue> {
static constexpr auto value = nvalue;
};
template <auto Value>
static constexpr auto value = Impl<Value>::value;
/**
* Checks if two function pointers are the same. If their types are
* different, returns false.
*/
template <auto p1, auto p2>
static constexpr auto same() {
if (!std::is_same_v<RemoveClassType<decltype(p1)>, RemoveClassType<decltype(p2)>>) return false;
auto v1 = value<p1>;
auto v2 = value<p2>;
if (v1 == nvalue) return false;
if (v2 == nvalue) return false;
return v1 == v2;
}
/**
* Checks if two function pointers are different. If their types are
* different, returns false.
*/
template <auto p1, auto p2>
static constexpr auto different() {
if (!std::is_same_v<RemoveClassType<decltype(p1)>, RemoveClassType<decltype(p2)>>) return false;
auto v1 = value<p1>;
auto v2 = value<p2>;
if (v1 == nvalue) return false;
if (v2 == nvalue) return false;
return v1 != v2;
}
};
/**
* Helps resolving an overloaded function pointer to a specific function using
* its parameter types as the hint.
*/
template <class... Params>
struct Resolve {
template <class Return>
static constexpr auto func(Return(*ptr)(std::type_identity_t<Params>...)) {
return ptr;
}
template <class Return, class Class>
static constexpr auto func(Return(Class::*ptr)(std::type_identity_t<Params>...)) {
return ptr;
}
template <class Return, class Class>
static constexpr auto func(Return(Class::*ptr)(std::type_identity_t<Params>...) const) {
return ptr;
}
static constexpr auto func(...) {
return Unique::nvalue;
}
};
/**
* A specialization for giving the variadic types as a single type with the
* function type. The return type is ignored.
*/
template <class... Params>
struct Resolve<void(Params...)> : Resolve<Params...> {
using Resolve<Params...>::func;
};
/**
* A type trait that checks if a class has a function called "constructor".
*/
template <class Class>
concept HasConstructor = requires {
&Class::constructor;
};
/**
* A type trait that checks if a class has a function called "destructor".
*/
template <class Class>
concept HasDestructor = requires {
&Class::destructor;
};
template <class FunctionType>
struct AsStaticType {
using type = FunctionType;
};
template <class Return, class Class, class... Params>
struct AsStaticType<Return(Class::*)(Params...)> {
using type = Return(*)(Class*, Params...);
};
template <class Return, class Class, class... Params>
struct AsStaticType<Return(Class::*)(Params...) const> {
using type = Return(*)(Class const*, Params...);
};
}

View file

@ -2,7 +2,7 @@
#include <Geode/Bindings.hpp>
namespace geode::modifier {
struct types {
#include <Geode/GeneratedType.hpp>
};
// struct types {
// #include <Geode/GeneratedType.hpp>
// };
}

View file

@ -1,81 +0,0 @@
#pragma once
#include "../utils/addresser.hpp"
#include "Traits.hpp"
#define GEODE_WRAPPER_FOR_IDENTIFIER(identifier) \
/* Default - function Return Class::identifier(Parameters...) does not exist */ \
template <class Class, class FunctionType, class = void> \
struct identifier { \
public: \
constexpr static inline auto value = nullptr; \
constexpr static inline auto uuid = nullptr; \
}; \
/* Specialization - function Return Class::identifier(Parameters...) is a member function */ \
template <class Class, class Return, class... Parameters> \
struct identifier< \
Class, Return(Parameters...), \
std::enable_if_t<std::is_member_function_pointer_v< \
decltype(substitute<Return(Parameters...)>(&Class::identifier))>>> { \
private: \
static Return wrapperImpl(Class* self, Parameters... ps) { \
self = addresser::rthunkAdjust( \
substitute<Return(Parameters...)>(&Class::identifier), self \
); \
return self->Class::identifier(ps...); \
} \
\
public: \
constexpr static inline auto value = &wrapperImpl; \
constexpr static inline auto uuid = \
function_uuid<substitute<Return(Parameters...)>(&Class::identifier)>::value; \
}; \
/* Specialization - function Return Class::identifier(Parameters...) is a static function */ \
template <class Class, class Return, class... Parameters> \
struct identifier< \
Class, Return(Parameters...), \
std::enable_if_t< \
std::is_pointer_v<decltype(substitute<Return(Parameters...)>(&Class::identifier))>>> { \
private: \
static Return wrapperImpl(Parameters... ps) { \
return Class::identifier(ps...); \
} \
\
public: \
constexpr static inline auto value = &wrapperImpl; \
constexpr static inline auto uuid = \
function_uuid<substitute<Return(Parameters...)>(&Class::identifier)>::value; \
};
namespace geode::modifier {
namespace wrap {
GEODE_WRAPPER_FOR_IDENTIFIER(constructor)
GEODE_WRAPPER_FOR_IDENTIFIER(destructor)
};
// template <template<class, class, class=void> class Identifier, class Base, class Derived,
// class ...Types> struct PotentiallyWrongIdentifier {
// template <typename D>
// static std::true_type existsImpl(...);
// template <typename D, typename = std::enable_if_t<
// (... && (
// Identifier<D, Types>::uuid == nullptr ||
// Identifier<Base, Types>::uuid == Identifier<D, Types>::uuid
// ))
// >>
// static std::false_type existsImpl(char);
// template <typename D, typename = std::enable_if_t<
// (... || (
// Identifier<D, Types>::uuid != nullptr &&
// Identifier<Base, Types>::uuid == Identifier<D, Types>::uuid
// ))
// >>
// static std::false_type existsImpl(int);
// static constexpr bool value = decltype(existsImpl<Derived>(0))::value;
// };
}

View file

@ -15,6 +15,7 @@
#define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_NAME "Windows"
#define GEODE_CALL __stdcall
#define GEODE_CDECL_CALL __cdecl
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#define CC_TARGET_OS_WIN32
@ -44,6 +45,7 @@
#define CC_TARGET_OS_MAC
#endif
#define GEODE_CALL
#define GEODE_CDECL_CALL
#else
#define GEODE_MACOS(...)
#define GEODE_IOS(...)
@ -56,6 +58,7 @@
#define GEODE_IS_MOBILE
#define GEODE_PLATFORM_NAME "Android"
#define GEODE_CALL
#define GEODE_CDECL_CALL
#define GEODE_PLATFORM_EXTENSION ".so"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "android"
#define CC_TARGET_OS_ANDROID

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp"
#include <string_view>
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include <tuple>
#include "../utils/Result.hpp"

View file

@ -10,6 +10,7 @@
#include <cstdlib>
#include <stddef.h>
#include <type_traits>
#include "../utils/casts.hpp"
namespace geode::addresser {
@ -198,11 +199,19 @@ namespace geode::addresser {
template <typename T, typename F>
inline F thunkAdjust(T func, F self) {
// do NOT delete the line below.
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
return (F)((intptr_t)self + Addresser::thunkOf(func));
}
template <typename T, typename F>
inline F rthunkAdjust(T func, F self) {
// do NOT delete the line below.
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
return (F)((intptr_t)self - Addresser::thunkOf(func));
}
#endif

View file

@ -1,6 +1,6 @@
#pragma once
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include "casts.hpp"
#include "general.hpp"
#include "../DefaultInclude.hpp"

View file

@ -3,7 +3,7 @@
#include "Result.hpp"
#include "general.hpp"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include <Geode/DefaultInclude.hpp>
#include <ghc/filesystem.hpp>
#include <string>

View file

@ -23,7 +23,7 @@ namespace geode {
using ByteVector = std::vector<uint8_t>;
template <typename T>
ByteVector to_byte_array(T const& a) {
ByteVector toByteArray(T const& a) {
ByteVector out;
out.resize(sizeof(T));
std::memcpy(out.data(), &a, sizeof(T));

View file

@ -17,6 +17,9 @@ namespace std {
}
#endif
#undef min
#undef max
namespace geode::utils::ranges {
template <class C>
concept ValidConstContainer = requires(C const& c) {

View file

@ -1,7 +1,7 @@
#pragma once
#include "../DefaultInclude.hpp"
#include "../external/json/json.hpp"
#include "../external/json/json_fwd.hpp"
#include "Result.hpp"
#include "general.hpp"
@ -46,17 +46,7 @@ namespace geode::utils::web {
* @param url URL to fetch
* @returns Returned data as JSON, or error on error
*/
template <class Json = nlohmann::json>
Result<Json> fetchJSON(std::string const& url) {
std::string res;
GEODE_UNWRAP_INTO(res, fetch(url));
try {
return Ok(Json::parse(res));
}
catch (std::exception& e) {
return Err(e.what());
}
}
Result<nlohmann::json> fetchJSON(std::string const& url);
class SentAsyncWebRequest;
template <class T>

View file

@ -14,11 +14,17 @@ void displayError(std::string alertMessage) {
}
void loadGeode() {
auto dylib = dlopen("Geode.dylib", RTLD_LAZY);
if (dylib) return;
displayError(std::string("Couldn't open Geode: ") + dlerror());
auto dylib = dlopen("Geode.dylib", RTLD_NOW);
if (!dylib) {
displayError(std::string("Couldn't load Geode: ") + dlerror());
return;
}
auto trigger = dlsym(dylib, "dynamicTrigger");
if (!trigger) {
displayError(std::string("Couldn't start Geode: ") + dlerror());
return;
}
reinterpret_cast<void(*)()>(trigger)();
return;
}

View file

@ -45,6 +45,8 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
CCSprite* m_geodeButton;
bool init() {
log::debug("this: {}", this);
if (!MenuLayer::init()) return false;
// make sure to add the string IDs for nodes (Geode has no manual

View file

@ -7,14 +7,11 @@
#include <Geode/loader/Mod.hpp>
USE_GEODE_NAMESPACE();
using geode::core::meta::x86::Thiscall;
// for some reason RobTop uses MessageBoxW in his GLFW error handler.
// no one knows how this is possible (he passes char* to wchar_t*).
// so anyway, here's a fix for it
static auto CCEGLVIEW_CON_ADDR = reinterpret_cast<void*>(base::getCocos() + 0xc2860);
static void __cdecl fixedErrorHandler(int code, char const* description) {
log::error("GLFW Error {}: {}", code, description);
MessageBoxA(
@ -28,20 +25,10 @@ static void __cdecl fixedErrorHandler(int code, char const* description) {
);
}
static CCEGLView* CCEGLView_CCEGLView(CCEGLView* self) {
// you will never have to make a manual hook with Geode again, they said
// it will be fun, they said
reinterpret_cast<CCEGLView*(__thiscall*)(CCEGLView*)>(CCEGLVIEW_CON_ADDR)(self);
static auto p = Mod::get()->patch(
reinterpret_cast<void*>(geode::base::getCocos() + 0x19feec),
to_byte_array(&fixedErrorHandler)
);
return self;
}
$execute {
(void)Mod::get()->addHook<&CCEGLView_CCEGLView, Thiscall>(
"CCEGLView::CCEGLView", CCEGLVIEW_CON_ADDR
(void)Mod::get()->patch(
reinterpret_cast<void*>(geode::base::getCocos() + 0x19feec),
toByteArray(&fixedErrorHandler)
);
}

View file

@ -1,7 +1,7 @@
#include "AddIDs.hpp"
#include <Geode/Bindings.hpp>
#include <Geode/Modify.hpp>
#include <Geode/modify/CreatorLayer.hpp>
#include <Geode/utils/cocos.hpp>
USE_GEODE_NAMESPACE();

View file

@ -1,7 +1,7 @@
#include "AddIDs.hpp"
#include <Geode/Bindings.hpp>
#include <Geode/Modify.hpp>
#include <Geode/modify/LevelBrowserLayer.hpp>
#include <Geode/utils/cocos.hpp>
USE_GEODE_NAMESPACE();

View file

@ -1,7 +1,7 @@
#include "AddIDs.hpp"
#include <Geode/Bindings.hpp>
#include <Geode/Modify.hpp>
#include <Geode/modify/LevelSettingsLayer.hpp>
#include <Geode/utils/cocos.hpp>
USE_GEODE_NAMESPACE();
@ -205,3 +205,25 @@ struct LevelSettingsLayerIDs : Modify<LevelSettingsLayerIDs, LevelSettingsLayer>
return true;
}
};
template <class... Params>
struct Resolve2 {
template <class Return>
static constexpr auto func(Return(*ptr)(float)) {
return ptr;
}
template <class Return, class Class>
static constexpr auto func(Return(Class::*ptr)(float)) {
return ptr;
}
template <class Return, class Class>
static constexpr auto func(Return(Class::*ptr)(float) const) {
return ptr;
}
};
#include <Geode/modify/PlayerObject.hpp>
constexpr auto b = Resolve2<float>::func(&PlayerObject::runBallRotation);

View file

@ -1,50 +1,81 @@
#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 <hook/hook.hpp>
#include "ModImpl.hpp"
#include <Geode/hook-core/Hook.hpp>
USE_GEODE_NAMESPACE();
Result<> Hook::enable() {
if (!m_enabled) {
auto res = std::invoke(m_addFunction, m_address);
if (res) {
log::debug("Enabling hook at function {}", m_displayName);
m_enabled = true;
m_handle = res.unwrap();
return Ok();
}
else {
return Err(
"Unable to create hook at " + std::to_string(reinterpret_cast<uintptr_t>(m_address))
);
}
return Err("Hook already has a handle");
}
return Ok();
}
Result<> Hook::disable() {
if (m_enabled) {
if (!geode::core::hook::remove(m_handle)) return Err("Unable to remove hook");
log::debug("Disabling hook at function {}", m_displayName);
m_enabled = false;
}
return Ok();
}
nlohmann::json Hook::getRuntimeInfo() const {
auto json = nlohmann::json::object();
json["address"] = reinterpret_cast<uintptr_t>(m_address);
json["detour"] = reinterpret_cast<uintptr_t>(m_detour);
json["name"] = m_displayName;
json["enabled"] = m_enabled;
return json;
}
#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 <hook/hook.hpp>
#include "ModImpl.hpp"
#include <Geode/hook-core/Hook.hpp>
#include "HookImpl.hpp"
USE_GEODE_NAMESPACE();
Hook::Hook(std::shared_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
Hook::~Hook() {}
Hook* Hook::create(
Mod* owner,
void* address,
void* detour,
std::string const& displayName,
tulip::hook::HandlerMetadata const& handlerMetadata,
tulip::hook::HookMetadata const& hookMetadata
) {
auto impl = std::make_shared<Hook::Impl>(
address, detour, displayName, handlerMetadata, hookMetadata, owner
);
return new Hook(std::move(impl));
}
uintptr_t Hook::getAddress() const {
return m_impl->getAddress();
}
std::string_view Hook::getDisplayName() const {
return m_impl->getDisplayName();
}
bool Hook::isEnabled() const {
return m_impl->isEnabled();
}
Mod* Hook::getOwner() const {
return m_impl->getOwner();
}
nlohmann::json Hook::getRuntimeInfo() const {
return m_impl->getRuntimeInfo();
}
tulip::hook::HookMetadata Hook::getHookMetadata() const {
return m_impl->getHookMetadata();
}
void Hook::setHookMetadata(tulip::hook::HookMetadata const& metadata) {
return m_impl->setHookMetadata(metadata);
}
int32_t Hook::getPriority() const {
return m_impl->getPriority();
}
void Hook::setPriority(int32_t 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();
}

View file

@ -0,0 +1,104 @@
#include "HookImpl.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) :
m_address(address),
m_detour(detour),
m_displayName(displayName),
m_handlerMetadata(handlerMetadata),
m_hookMetadata(hookMetadata),
m_owner(owner),
m_enabled(false),
m_autoEnable(true) {}
Hook::Impl::~Impl() {
if (m_enabled) {
auto res = this->disable();
if (!res) {
log::error("Failed to disable hook: {}", res.unwrapErr());
}
}
}
uintptr_t Hook::Impl::getAddress() const {
return reinterpret_cast<uintptr_t>(m_address);
}
std::string_view Hook::Impl::getDisplayName() const {
return m_displayName;
}
bool Hook::Impl::isEnabled() const {
return m_enabled;
}
Mod* Hook::Impl::getOwner() const {
return m_owner;
}
nlohmann::json Hook::Impl::getRuntimeInfo() const {
auto json = nlohmann::json::object();
json["address"] = reinterpret_cast<uintptr_t>(m_address);
json["detour"] = 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() {
if (!m_enabled) {
if (!LoaderImpl::get()->hasHandler(m_address)) {
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);
log::debug("Enabling hook at function {} with address {}", m_displayName, m_address);
m_enabled = true;
}
return Ok();
}
Result<> Hook::Impl::disable() {
if (m_enabled) {
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
tulip::hook::removeHook(handler, m_handle);
log::debug("Disabling hook at function {}", m_displayName);
m_enabled = false;
}
return Ok();
}
Result<> Hook::Impl::updateMetadata() {
if (m_enabled) {
GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
tulip::hook::updateHookMetadata(handler, m_handle, m_hookMetadata);
}
return Ok();
}
void Hook::Impl::setHookMetadata(tulip::hook::HookMetadata const& metadata) {
m_hookMetadata = metadata;
auto res = this->updateMetadata();
if (!res) {
log::error("Failed to update hook metadata: {}", res.unwrapErr());
}
}
int32_t Hook::Impl::getPriority() const {
return m_hookMetadata.m_priority;
}
void Hook::Impl::setPriority(int32_t priority) {
m_hookMetadata.m_priority = priority;
auto res = this->updateMetadata();
if (!res) {
log::error("Failed to update hook priority: {}", res.unwrapErr());
}
}
bool Hook::Impl::getAutoEnable() const {
return m_autoEnable;
}
void Hook::Impl::setAutoEnable(bool autoEnable) {
m_autoEnable = autoEnable;
}

View file

@ -0,0 +1,54 @@
#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 <hook/hook.hpp>
#include "ModImpl.hpp"
#include <Geode/hook-core/Hook.hpp>
USE_GEODE_NAMESPACE();
class Hook::Impl {
public:
Impl(
void* address,
void* detour,
std::string const& displayName,
tulip::hook::HandlerMetadata const& handlerMetadata,
tulip::hook::HookMetadata const& hookMetadata,
Mod* owner
);
~Impl();
void* m_address;
void* m_detour;
std::string m_displayName;
tulip::hook::HandlerMetadata m_handlerMetadata;
tulip::hook::HookMetadata m_hookMetadata;
Mod* m_owner;
tulip::hook::HookHandle m_handle;
bool m_enabled = false;
bool m_autoEnable = true;
// Used by Mod
Result<> enable();
Result<> disable();
Result<> updateMetadata();
uintptr_t getAddress() const;
std::string_view getDisplayName() const;
bool isEnabled() const;
Mod* getOwner() const;
nlohmann::json getRuntimeInfo() const;
tulip::hook::HookMetadata getHookMetadata() const;
void setHookMetadata(tulip::hook::HookMetadata const& metadata);
int32_t getPriority() const;
void setPriority(int32_t priority);
bool getAutoEnable() const;
void setAutoEnable(bool autoEnable);
};

View file

@ -1,7 +1,15 @@
#include <Geode/loader/IPC.hpp>
#include <Geode/external/json/json.hpp>
USE_GEODE_NAMESPACE();
std::monostate geode::listenForIPC(std::string const& messageID, nlohmann::json(*callback)(IPCEvent*)) {
(void) new EventListener(
callback, IPCFilter(getMod()->getID(), messageID)
);
return std::monostate();
}
IPCEvent::IPCEvent(
void* rawPipeHandle,
std::string const& targetModID,

View file

@ -6,6 +6,8 @@
#include <Geode/utils/string.hpp>
#include <Geode/utils/map.hpp>
#include <hash/hash.hpp>
#include <Geode/utils/JsonValidation.hpp>
USE_GEODE_NAMESPACE();

View file

@ -6,6 +6,7 @@
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/ModJsonTest.hpp>
#include <Geode/utils/file.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/utils/ranges.hpp>
@ -590,13 +591,14 @@ ResourceDownloadFilter::ResourceDownloadFilter() {}
void Loader::Impl::provideNextMod(Mod* mod) {
m_nextModLock.lock();
m_nextMod = mod;
if (mod) {
m_nextMod = mod;
}
}
Mod* Loader::Impl::takeNextMod() {
if (!m_nextMod) {
this->setupInternalMod();
m_nextMod = Mod::sharedMod<>;
m_nextMod = this->createInternalMod();
}
auto ret = m_nextMod;
return ret;
@ -607,3 +609,37 @@ void Loader::Impl::releaseNextMod() {
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) {
if (!m_handlerHandles.count(address)) {
return Err("Handler does not exist at address");
}
return Ok(m_handlerHandles[address]);
}
Result<> Loader::Impl::removeHandler(void* address) {
if (!m_handlerHandles.count(address)) {
return Err("Handler does not exist at address");
}
auto handle = m_handlerHandles[address];
GEODE_UNWRAP(tulip::hook::removeHandler(handle));
m_handlerHandles.erase(address);
return Ok();
}

View file

@ -18,6 +18,7 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <tulip/TulipHook.hpp>
// TODO: Find a file convention for impl headers
namespace geode {
@ -64,6 +65,13 @@ namespace geode {
Mod* takeNextMod();
void releaseNextMod();
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<> removeHandler(void* address);
void downloadLoaderResources();
bool loadHooks();
@ -126,7 +134,8 @@ namespace geode {
bool isReadyToHook() const;
void addInternalHook(Hook* hook, Mod* mod);
void setupInternalMod();
Mod* createInternalMod();
Result<> setupInternalMod();
};
class LoaderImpl {

View file

@ -1,188 +1,188 @@
#include <Geode/loader/Mod.hpp>
#include "ModImpl.hpp"
USE_GEODE_NAMESPACE();
Mod::Mod(ModInfo const& info) : m_impl(std::make_unique<Impl>(this, info)) {}
Mod::~Mod() {}
std::string Mod::getID() const {
return m_impl->getID();
}
std::string Mod::getName() const {
return m_impl->getName();
}
std::string Mod::getDeveloper() const {
return m_impl->getDeveloper();
}
std::optional<std::string> Mod::getDescription() const {
return m_impl->getDescription();
}
std::optional<std::string> Mod::getDetails() const {
return m_impl->getDetails();
}
ghc::filesystem::path Mod::getPackagePath() const {
return m_impl->getPackagePath();
}
VersionInfo Mod::getVersion() const {
return m_impl->getVersion();
}
nlohmann::json& Mod::getSaveContainer() {
return m_impl->getSaveContainer();
}
bool Mod::isEnabled() const {
return m_impl->isEnabled();
}
bool Mod::isLoaded() const {
return m_impl->isLoaded();
}
bool Mod::supportsDisabling() const {
return m_impl->supportsDisabling();
}
bool Mod::supportsUnloading() const {
return m_impl->supportsUnloading();
}
bool Mod::wasSuccesfullyLoaded() const {
return m_impl->wasSuccesfullyLoaded();
}
ModInfo Mod::getModInfo() const {
return m_impl->getModInfo();
}
ghc::filesystem::path Mod::getTempDir() const {
return m_impl->getTempDir();
}
ghc::filesystem::path Mod::getBinaryPath() const {
return m_impl->getBinaryPath();
}
Result<> Mod::saveData() {
return m_impl->saveData();
}
Result<> Mod::loadData() {
return m_impl->loadData();
}
ghc::filesystem::path Mod::getSaveDir() const {
return m_impl->getSaveDir();
}
ghc::filesystem::path Mod::getConfigDir(bool create) const {
return m_impl->getConfigDir(create);
}
bool Mod::hasSettings() const {
return m_impl->hasSettings();
}
std::vector<std::string> Mod::getSettingKeys() const {
return m_impl->getSettingKeys();
}
bool Mod::hasSetting(std::string const& key) const {
return m_impl->hasSetting(key);
}
std::optional<Setting> Mod::getSettingDefinition(std::string const& key) const {
return m_impl->getSettingDefinition(key);
}
SettingValue* Mod::getSetting(std::string const& key) const {
return m_impl->getSetting(key);
}
void Mod::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
return m_impl->registerCustomSetting(key, std::move(value));
}
std::vector<Hook*> Mod::getHooks() const {
return m_impl->getHooks();
}
Result<Hook*> Mod::addHook(Hook* hook) {
return m_impl->addHook(hook);
}
Result<> Mod::enableHook(Hook* hook) {
return m_impl->enableHook(hook);
}
Result<> Mod::disableHook(Hook* hook) {
return m_impl->disableHook(hook);
}
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::loadBinary() {
return m_impl->loadBinary();
}
Result<> Mod::unloadBinary() {
return m_impl->unloadBinary();
}
Result<> Mod::enable() {
return m_impl->enable();
}
Result<> Mod::disable() {
return m_impl->disable();
}
Result<> Mod::uninstall() {
return m_impl->uninstall();
}
bool Mod::isUninstalled() const {
return m_impl->isUninstalled();
}
bool Mod::depends(std::string const& id) const {
return m_impl->depends(id);
}
bool Mod::hasUnresolvedDependencies() const {
return m_impl->hasUnresolvedDependencies();
}
Result<> Mod::updateDependencies() {
return m_impl->updateDependencies();
}
std::vector<Dependency> Mod::getUnresolvedDependencies() {
return m_impl->getUnresolvedDependencies();
}
char const* Mod::expandSpriteName(char const* name) {
return m_impl->expandSpriteName(name);
}
ModJson Mod::getRuntimeInfo() const {
return m_impl->getRuntimeInfo();
#include <Geode/loader/Mod.hpp>
#include "ModImpl.hpp"
USE_GEODE_NAMESPACE();
Mod::Mod(ModInfo const& info) : m_impl(std::make_unique<Impl>(this, info)) {}
Mod::~Mod() {}
std::string Mod::getID() const {
return m_impl->getID();
}
std::string Mod::getName() const {
return m_impl->getName();
}
std::string Mod::getDeveloper() const {
return m_impl->getDeveloper();
}
std::optional<std::string> Mod::getDescription() const {
return m_impl->getDescription();
}
std::optional<std::string> Mod::getDetails() const {
return m_impl->getDetails();
}
ghc::filesystem::path Mod::getPackagePath() const {
return m_impl->getPackagePath();
}
VersionInfo Mod::getVersion() const {
return m_impl->getVersion();
}
nlohmann::json& Mod::getSaveContainer() {
return m_impl->getSaveContainer();
}
bool Mod::isEnabled() const {
return m_impl->isEnabled();
}
bool Mod::isLoaded() const {
return m_impl->isLoaded();
}
bool Mod::supportsDisabling() const {
return m_impl->supportsDisabling();
}
bool Mod::supportsUnloading() const {
return m_impl->supportsUnloading();
}
bool Mod::wasSuccesfullyLoaded() const {
return m_impl->wasSuccesfullyLoaded();
}
ModInfo Mod::getModInfo() const {
return m_impl->getModInfo();
}
ghc::filesystem::path Mod::getTempDir() const {
return m_impl->getTempDir();
}
ghc::filesystem::path Mod::getBinaryPath() const {
return m_impl->getBinaryPath();
}
Result<> Mod::saveData() {
return m_impl->saveData();
}
Result<> Mod::loadData() {
return m_impl->loadData();
}
ghc::filesystem::path Mod::getSaveDir() const {
return m_impl->getSaveDir();
}
ghc::filesystem::path Mod::getConfigDir(bool create) const {
return m_impl->getConfigDir(create);
}
bool Mod::hasSettings() const {
return m_impl->hasSettings();
}
std::vector<std::string> Mod::getSettingKeys() const {
return m_impl->getSettingKeys();
}
bool Mod::hasSetting(std::string const& key) const {
return m_impl->hasSetting(key);
}
std::optional<Setting> Mod::getSettingDefinition(std::string const& key) const {
return m_impl->getSettingDefinition(key);
}
SettingValue* Mod::getSetting(std::string const& key) const {
return m_impl->getSetting(key);
}
void Mod::registerCustomSetting(std::string const& key, std::unique_ptr<SettingValue> value) {
return m_impl->registerCustomSetting(key, std::move(value));
}
std::vector<Hook*> Mod::getHooks() const {
return m_impl->getHooks();
}
Result<Hook*> Mod::addHook(Hook* hook) {
return m_impl->addHook(hook);
}
Result<> Mod::enableHook(Hook* hook) {
return m_impl->enableHook(hook);
}
Result<> Mod::disableHook(Hook* hook) {
return m_impl->disableHook(hook);
}
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::loadBinary() {
return m_impl->loadBinary();
}
Result<> Mod::unloadBinary() {
return m_impl->unloadBinary();
}
Result<> Mod::enable() {
return m_impl->enable();
}
Result<> Mod::disable() {
return m_impl->disable();
}
Result<> Mod::uninstall() {
return m_impl->uninstall();
}
bool Mod::isUninstalled() const {
return m_impl->isUninstalled();
}
bool Mod::depends(std::string const& id) const {
return m_impl->depends(id);
}
bool Mod::hasUnresolvedDependencies() const {
return m_impl->hasUnresolvedDependencies();
}
Result<> Mod::updateDependencies() {
return m_impl->updateDependencies();
}
std::vector<Dependency> Mod::getUnresolvedDependencies() {
return m_impl->getUnresolvedDependencies();
}
char const* Mod::expandSpriteName(char const* name) {
return m_impl->expandSpriteName(name);
}
ModJson Mod::getRuntimeInfo() const {
return m_impl->getRuntimeInfo();
}

View file

@ -7,7 +7,9 @@
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/ModEvent.hpp>
#include <Geode/utils/file.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <optional>
#include <string>
#include <vector>
@ -19,8 +21,6 @@ Mod::Impl* ModImpl::getImpl(Mod* mod) {
}
Mod::Impl::Impl(Mod* self, ModInfo const& info) : m_self(self), m_info(info) {
m_saveDirPath = dirs::getModsSaveDir() / info.id;
ghc::filesystem::create_directories(m_saveDirPath);
}
Mod::Impl::~Impl() {
@ -28,6 +28,9 @@ Mod::Impl::~Impl() {
}
Result<> Mod::Impl::setup() {
m_saveDirPath = dirs::getModsSaveDir() / m_info.id;
ghc::filesystem::create_directories(m_saveDirPath);
this->setupSettings();
auto loadRes = this->loadData();
if (!loadRes) {
@ -481,6 +484,9 @@ bool Mod::Impl::depends(std::string const& id) const {
Result<> Mod::Impl::enableHook(Hook* hook) {
auto res = hook->enable();
if (res) m_hooks.push_back(hook);
else {
log::error("Can't enable hook {} for mod {}: {}", m_info.id, res.unwrapErr());
}
return res;
}
@ -491,10 +497,12 @@ Result<> Mod::Impl::disableHook(Hook* hook) {
Result<Hook*> Mod::Impl::addHook(Hook* hook) {
if (LoaderImpl::get()->isReadyToHook()) {
auto res = this->enableHook(hook);
if (!res) {
delete hook;
return Err("Can't create hook");
if (hook->getAutoEnable()) {
auto res = this->enableHook(hook);
if (!res) {
delete hook;
return Err("Can't create hook: "+ res.unwrapErr());
}
}
}
else {
@ -662,13 +670,14 @@ static ModInfo getModImplInfo() {
}
}
void Loader::Impl::setupInternalMod() {
Mod* Loader::Impl::createInternalMod() {
auto& mod = Mod::sharedMod<>;
if (mod) return;
mod = new Mod(getModImplInfo());
auto setupRes = mod->m_impl->setup();
if (!setupRes) {
log::error("Failed to setup internal mod! ({})", setupRes.unwrapErr());
if (!mod) {
mod = new Mod(getModImplInfo());
}
}
return mod;
}
Result<> Loader::Impl::setupInternalMod() {
return Mod::get()->m_impl->setup();
}

View file

@ -1,5 +1,7 @@
#pragma once
#include <Geode/external/json/json.hpp>
namespace geode {
class Mod::Impl {
public:

View file

@ -2,6 +2,9 @@
#include <Geode/loader/Mod.hpp>
#include <Geode/utils/file.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/external/json/json.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <about.hpp>
USE_GEODE_NAMESPACE();
@ -36,10 +39,9 @@ bool ModInfo::validateID(std::string const& id) {
Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
ModInfo info;
auto json = rawJson;
info.m_rawJSON = rawJson;
info.m_rawJSON = std::make_unique<ModJson>(rawJson);
JsonChecker checker(json);
JsonChecker checker(*info.m_rawJSON);
auto root = checker.root("[mod.json]").obj();
root.addKnownKey("geode");
@ -242,14 +244,14 @@ std::vector<std::pair<std::string, std::optional<std::string>*>> ModInfo::getSpe
}
ModJson ModInfo::toJSON() const {
auto json = m_rawJSON;
auto json = *m_rawJSON;
json["path"] = this->path;
json["binary"] = this->binaryName;
return json;
}
ModJson ModInfo::getRawJSON() const {
return m_rawJSON;
return *m_rawJSON;
}
bool ModInfo::operator==(ModInfo const& other) const {

View file

@ -1,14 +1,14 @@
#include <Geode/loader/Hook.hpp>
#include <lilac/include/geode/core/hook/hook.hpp>
#include <Geode/external/json/json.hpp>
USE_GEODE_NAMESPACE();
bool Patch::apply() {
return lilac::hook::write_memory(m_address, m_patch.data(), m_patch.size());
return bool(tulip::hook::writeMemory(m_address, m_patch.data(), m_patch.size()));
}
bool Patch::restore() {
return lilac::hook::write_memory(m_address, m_original.data(), m_original.size());
return bool(tulip::hook::writeMemory(m_address, m_original.data(), m_original.size()));
}
nlohmann::json Patch::getRuntimeInfo() const {

View file

@ -3,7 +3,9 @@
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/loader/SettingJsonTest.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <re2/re2.h>
USE_GEODE_NAMESPACE();
@ -131,7 +133,7 @@ Result<Setting> Setting::parse(
case hash("custom"): {
sett.m_kind = CustomSetting {
.json = obj.json()
.json = std::make_shared<ModJson>(obj.json())
};
// skip checking unknown keys
return Ok(sett);

View file

@ -8,6 +8,8 @@
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <loader/ModImpl.hpp>
#include <Geode/loader/ModJsonTest.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <array>
USE_GEODE_NAMESPACE();
@ -26,7 +28,7 @@ std::length_error::~length_error() _NOEXCEPT {} // do not ask...
// from dynamic to static thats why she needs to define it
// this is what old versions does to a silly girl
__attribute__((constructor)) void _entry() {
void dynamicEntry() {
auto dylib = dlopen("GeodeBootstrapper.dylib", RTLD_NOLOAD);
dlclose(dylib);
@ -54,6 +56,13 @@ __attribute__((constructor)) void _entry() {
geodeEntry(nullptr);
}
extern "C" __attribute__((visibility("default"))) void dynamicTrigger() {
std::thread(&dynamicEntry).detach();
}
// remove when we can figure out how to not remove it
auto dynamicTriggerRef = &dynamicTrigger;
#elif defined(GEODE_IS_WINDOWS)
#include <Windows.h>
@ -120,7 +129,7 @@ $execute {
listenForIPC("list-mods", [](IPCEvent* event) -> nlohmann::json {
std::vector<nlohmann::json> res;
auto args = event->messageData;
auto args = *event->messageData;
JsonChecker checker(args);
auto root = checker.root("").obj();
@ -155,12 +164,26 @@ int geodeEntry(void* platformData) {
return 1;
}
// set up loader, load mods, etc.
if (!LoaderImpl::get()->setup()) {
// set up internal mod, settings and data
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
if (!internalSetupRes) {
LoaderImpl::get()->platformMessageBox(
"Unable to Load Geode!",
"There was an unknown fatal error setting up "
"the loader and Geode can not be loaded."
"the internal mod and Geode can not be loaded." + internalSetupRes.unwrapErr()
);
LoaderImpl::get()->reset();
return 1;
}
// set up loader, load mods, etc.
auto setupRes = LoaderImpl::get()->setup();
if (!setupRes) {
LoaderImpl::get()->platformMessageBox(
"Unable to Load Geode!",
"There was an unknown fatal error setting up "
"the loader and Geode can not be loaded. "
"(" + setupRes.unwrapErr() + ")"
);
LoaderImpl::get()->reset();
return 1;

View file

@ -662,7 +662,7 @@ CCObject* CCArray::randomObject()
return NULL;
}
float r = CCRANDOM_0_1();
float r = ((float)rand()/(float)RAND_MAX);
if (r == 1) // to prevent from accessing data-arr[data->num], out of range.
{

View file

@ -4,6 +4,7 @@
#include <Geode/utils/VersionInfo.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/external/json/json.hpp>
USE_GEODE_NAMESPACE();

View file

@ -1,5 +1,6 @@
#include <Geode/modify/LoadingLayer.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/external/json/json.hpp>
USE_GEODE_NAMESPACE();

View file

@ -6,6 +6,7 @@
#include <Geode/utils/file.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/external/json/json.hpp>
#include <fstream>
USE_GEODE_NAMESPACE();

View file

@ -2,6 +2,7 @@
#include <Geode/loader/Loader.hpp>
#include <Geode/utils/casts.hpp>
#include <Geode/utils/web.hpp>
#include <Geode/external/json/json.hpp>
#include <thread>
USE_GEODE_NAMESPACE();
@ -97,6 +98,17 @@ Result<ByteVector> web::fetchBytes(std::string const& url) {
return Err("Error getting info: " + std::string(curl_easy_strerror(res)));
}
Result<nlohmann::json> web::fetchJSON(std::string const& url) {
std::string res;
GEODE_UNWRAP_INTO(res, fetch(url));
try {
return Ok(nlohmann::json::parse(res));
}
catch (std::exception& e) {
return Err(e.what());
}
}
Result<std::string> web::fetch(std::string const& url) {
auto curl = curl_easy_init();

View file

@ -4,6 +4,8 @@ USE_GEODE_NAMESPACE();
#include <Geode/modify/MenuLayer.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/loader/ModJsonTest.hpp>
enum class Icon {
Steve,

View file

@ -1,4 +1,6 @@
#include <Geode/Loader.hpp>
#include <Geode/loader/ModJsonTest.hpp>
#include <Geode/loader/ModEvent.hpp>
USE_GEODE_NAMESPACE();