diff --git a/CMakeLists.txt b/CMakeLists.txt index e56be2fc..44174b90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/bindings/GeometryDash.bro b/bindings/GeometryDash.bro index 9718816f..8ae53eff 100644 --- a/bindings/GeometryDash.bro +++ b/bindings/GeometryDash.bro @@ -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; diff --git a/cmake/Platform.cmake b/cmake/Platform.cmake index 9e20c189..4a8c4129 100644 --- a/cmake/Platform.cmake +++ b/cmake/Platform.cmake @@ -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") diff --git a/codegen/src/AddressGen.cpp b/codegen/src/AddressGen.cpp index 8c3b0bbd..4921ab8e 100644 --- a/codegen/src/AddressGen.cpp +++ b/codegen/src/AddressGen.cpp @@ -3,8 +3,15 @@ namespace { namespace format_strings { + char const* address_begin = R"GEN( +#include +#include +#include +)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 ); diff --git a/codegen/src/BindingGen.cpp b/codegen/src/BindingGen.cpp index 2f4b4e22..8ae7f092 100644 --- a/codegen/src/BindingGen.cpp +++ b/codegen/src/BindingGen.cpp @@ -8,6 +8,7 @@ namespace { namespace format_strings { )GEN"; char const* class_includes = R"GEN(#pragma once +#include #include #include #include @@ -20,6 +21,7 @@ namespace { namespace format_strings { char const* class_no_includes = R"GEN(#pragma once #include +#include )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 + 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"; diff --git a/codegen/src/Main.cpp b/codegen/src/Main.cpp index 54a74b74..5a504b37 100644 --- a/codegen/src/Main.cpp +++ b/codegen/src/Main.cpp @@ -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)); diff --git a/codegen/src/ModifyGen.cpp b/codegen/src/ModifyGen.cpp index f3bb38f3..b1e2e7c4 100644 --- a/codegen/src/ModifyGen.cpp +++ b/codegen/src/ModifyGen.cpp @@ -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 #include #include +#include +{class_include} using namespace geode::modifier; - namespace geode::modifier {{ - {wrap} - template - struct ModifyDerive : ModifyBase> {{ - using ModifyBase>::ModifyBase; + {statics} + + template + struct ModifyDerive : ModifyBase> {{ + using BaseModify = ModifyBase>; + using ModifyBase>::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 "; + } + else if (c.name.find("cocos2d") != std::string::npos) { + class_include = "#include "; + } + else { + class_include = fmt::format( + "#include ", + fmt::arg("class_name", codegen::getUnqualifiedClassName(c.name)) + ); + } + + std::string statics; std::set 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, ", ")) ); } } diff --git a/codegen/src/Shared.hpp b/codegen/src/Shared.hpp index e9f81df6..0c2507e7 100644 --- a/codegen/src/Shared.hpp +++ b/codegen/src/Shared.hpp @@ -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; diff --git a/codegen/src/SourceGen.cpp b/codegen/src/SourceGen.cpp index 1db88533..9ed4089d 100644 --- a/codegen/src/SourceGen.cpp +++ b/codegen/src/SourceGen.cpp @@ -3,39 +3,72 @@ namespace { namespace format_strings { char const* source_start = R"CAC( +#include #include #include -#include -#include -#include -#include +#include +#include +#include + 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& CCDestructor::destructorLock() {{ + static auto ret = new std::unordered_map; + 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(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({{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(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({{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(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({{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(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({{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(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({{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(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), diff --git a/entry.cpp b/entry.cpp index a4679fe6..cf19cb70 100644 --- a/entry.cpp +++ b/entry.cpp @@ -1,7 +1,20 @@ -#include + #include +#include + +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(); -} \ No newline at end of file +} diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index a5d5651c..e97bcdfd 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -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" ) diff --git a/loader/include/Geode/Loader.hpp b/loader/include/Geode/Loader.hpp index 9fb66556..a1bb4c37 100644 --- a/loader/include/Geode/Loader.hpp +++ b/loader/include/Geode/Loader.hpp @@ -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 diff --git a/loader/include/Geode/cocos/cocoa/CCObject.h b/loader/include/Geode/cocos/cocoa/CCObject.h index e6bd1135..1f7adfae 100644 --- a/loader/include/Geode/cocos/cocoa/CCObject.h +++ b/loader/include/Geode/cocos/cocoa/CCObject.h @@ -79,21 +79,11 @@ public: */ class CCDestructor : public CCCopying { private: - static inline auto& destructorLock() { - static auto ret = new std::unordered_map; - return *ret; - } + static std::unordered_map& 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) diff --git a/loader/include/Geode/cocos/extensions/GUI/CCControlExtension/ColorPickerDelegate.h b/loader/include/Geode/cocos/extensions/GUI/CCControlExtension/ColorPickerDelegate.h index 98e78df9..63823c09 100644 --- a/loader/include/Geode/cocos/extensions/GUI/CCControlExtension/ColorPickerDelegate.h +++ b/loader/include/Geode/cocos/extensions/GUI/CCControlExtension/ColorPickerDelegate.h @@ -44,7 +44,8 @@ NS_CC_EXT_BEGIN * @{ */ -class GEODE_DLL ColorPickerDelegate { +class CC_DLL ColorPickerDelegate { +public: virtual void colorValueChanged(ccColor3B) {} }; diff --git a/loader/include/Geode/cocos/label_nodes/CCLabelTTF.h b/loader/include/Geode/cocos/label_nodes/CCLabelTTF.h index c366924a..721d8b60 100644 --- a/loader/include/Geode/cocos/label_nodes/CCLabelTTF.h +++ b/loader/include/Geode/cocos/label_nodes/CCLabelTTF.h @@ -57,7 +57,6 @@ NS_CC_BEGIN class CC_DLL CCLabelTTF : public CCSprite, public CCLabelProtocol { - GEODE_FRIEND_MODIFY GEODE_FRIEND_MODIFY public: /** diff --git a/loader/include/Geode/cocos/platform/CCPlatformMacros.h b/loader/include/Geode/cocos/platform/CCPlatformMacros.h index 3c2cb642..9ae219b9 100644 --- a/loader/include/Geode/cocos/platform/CCPlatformMacros.h +++ b/loader/include/Geode/cocos/platform/CCPlatformMacros.h @@ -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 + +namespace geode { + struct modify; + + namespace modifier { + struct types; class FieldContainer; - } + + template + class ModifyDerive; + + template + 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 \ + friend class ::geode::modifier::ModifyDerive; \ + friend struct ::geode::modifier::types; \ + friend class ::GeodeNodeMetadata; \ + template \ + friend uintptr_t geode::modifier::address(); #define GEODE_ADD(...) __VA_ARGS__ #ifdef __cplusplus diff --git a/loader/include/Geode/external/json/json_fwd.hpp b/loader/include/Geode/external/json/json_fwd.hpp new file mode 100644 index 00000000..613e7bce --- /dev/null +++ b/loader/include/Geode/external/json/json_fwd.hpp @@ -0,0 +1,79 @@ +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // 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 +struct adl_serializer; + +template class ObjectType = + std::map, + template 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 class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +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 +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 +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; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ \ No newline at end of file diff --git a/loader/include/Geode/loader/Event.hpp b/loader/include/Geode/loader/Event.hpp index e248db55..a95ecf57 100644 --- a/loader/include/Geode/loader/Event.hpp +++ b/loader/include/Geode/loader/Event.hpp @@ -1,7 +1,6 @@ #pragma once #include "../utils/casts.hpp" -#include "Mod.hpp" #include #include @@ -11,6 +10,8 @@ namespace geode { class Mod; class Event; + Mod* getMod(); + enum class ListenerResult { Propagate, Stop diff --git a/loader/include/Geode/loader/Hook.hpp b/loader/include/Geode/loader/Hook.hpp index a71cb9a2..c3e89a80 100644 --- a/loader/include/Geode/loader/Hook.hpp +++ b/loader/include/Geode/loader/Hook.hpp @@ -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 #include +#include 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 (*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 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(*)(void*)) & core::hook::add; - 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 m_impl; + Hook(std::shared_ptr&& impl); + ~Hook(); friend class Mod; friend class Loader; - static std::vector> 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 + 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(), + .m_abstract = tulip::hook::AbstractFunction::from(detour) + }; + return Hook::create( + owner, + address, + reinterpret_cast(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(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 { diff --git a/loader/include/Geode/loader/IPC.hpp b/loader/include/Geode/loader/IPC.hpp index 594ee043..5d818bf6 100644 --- a/loader/include/Geode/loader/IPC.hpp +++ b/loader/include/Geode/loader/IPC.hpp @@ -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 messageData; nlohmann::json& replyData; friend class IPCFilter; @@ -62,11 +63,5 @@ namespace geode { ); }; - template - 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*)); } diff --git a/loader/include/Geode/loader/Log.hpp b/loader/include/Geode/loader/Log.hpp index 8d48c0ed..938ae7a3 100644 --- a/loader/include/Geode/loader/Log.hpp +++ b/loader/include/Geode/loader/Log.hpp @@ -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; diff --git a/loader/include/Geode/loader/Mod.hpp b/loader/include/Geode/loader/Mod.hpp index dd49bae1..6fba9337 100644 --- a/loader/include/Geode/loader/Mod.hpp +++ b/loader/include/Geode/loader/Mod.hpp @@ -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 #include #include +#include namespace geode { template @@ -110,51 +111,16 @@ namespace geode { nlohmann::json& getSaveContainer(); template - T getSettingValue(std::string const& key) const { - if (auto sett = this->getSetting(key)) { - return SettingValueSetter::get(sett); - } - return T(); - } + T getSettingValue(std::string const& key) const; template - T setSettingValue(std::string const& key, T const& value) { - if (auto sett = this->getSetting(key)) { - auto old = this->getSettingValue(sett); - SettingValueSetter::set(sett, value); - return old; - } - return T(); - } + T setSettingValue(std::string const& key, T const& value); template - 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 - 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 - T setSavedValue(std::string const& key, T const& value) { - auto& saved = this->getSaveContainer(); - auto old = this->getSavedValue(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 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 class Convention> - Result addHook(void* address) { - return this->addHook("", 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 class Convention> - Result addHook(std::string const& displayName, void* address) { - auto hook = - Hook::create((decltype(Detour))address, displayName, this); + template + Result addHook( + void* address, DetourType detour, + std::string const& displayName = "", + tulip::hook::HookMetadata const& hookMetadata = tulip::hook::HookMetadata() + ) { + auto hook = Hook::create(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) { diff --git a/loader/include/Geode/loader/ModEvent.hpp b/loader/include/Geode/loader/ModEvent.hpp index 496a66e8..15c63d7c 100644 --- a/loader/include/Geode/loader/ModEvent.hpp +++ b/loader/include/Geode/loader/ModEvent.hpp @@ -6,7 +6,7 @@ namespace geode { class Mod; - inline Mod* getMod(); + Mod* getMod(); enum class ModEventType { Loaded, diff --git a/loader/include/Geode/loader/ModInfo.hpp b/loader/include/Geode/loader/ModInfo.hpp index a333f365..058c42ef 100644 --- a/loader/include/Geode/loader/ModInfo.hpp +++ b/loader/include/Geode/loader/ModInfo.hpp @@ -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 m_rawJSON; /** * Version is passed for backwards diff --git a/loader/include/Geode/loader/ModJsonTest.hpp b/loader/include/Geode/loader/ModJsonTest.hpp new file mode 100644 index 00000000..a6392949 --- /dev/null +++ b/loader/include/Geode/loader/ModJsonTest.hpp @@ -0,0 +1,59 @@ +#include "Mod.hpp" +#include "../external/json/json.hpp" + +namespace geode { + template + T Mod::getSettingValue(std::string const& key) const { + if (auto sett = this->getSetting(key)) { + return SettingValueSetter::get(sett); + } + return T(); + } + + template + T Mod::setSettingValue(std::string const& key, T const& value) { + if (auto sett = this->getSetting(key)) { + auto old = this->getSettingValue(sett); + SettingValueSetter::set(sett, value); + return old; + } + return T(); + } + + template + 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 + 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 + T Mod::setSavedValue(std::string const& key, T const& value) { + auto& saved = this->getSaveContainer(); + auto old = this->getSavedValue(key); + saved[key] = value; + return old; + } +} \ No newline at end of file diff --git a/loader/include/Geode/loader/Setting.hpp b/loader/include/Geode/loader/Setting.hpp index cc1915ba..a46b9bc5 100644 --- a/loader/include/Geode/loader/Setting.hpp +++ b/loader/include/Geode/loader/Setting.hpp @@ -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 #include @@ -16,6 +15,11 @@ namespace geode { class SettingNode; class SettingValue; + template + struct JsonMaybeObject; + template + 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 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(); - 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 { diff --git a/loader/include/Geode/loader/SettingJsonTest.hpp b/loader/include/Geode/loader/SettingJsonTest.hpp new file mode 100644 index 00000000..50b12b20 --- /dev/null +++ b/loader/include/Geode/loader/SettingJsonTest.hpp @@ -0,0 +1,21 @@ +#include "Setting.hpp" +#include "../external/json/json.hpp" + +namespace geode { + template + bool GeodeSettingValue::load(nlohmann::json const& json) { + try { + m_value = json.get(); + return true; + } + catch (...) { + return false; + } + } + + template + bool GeodeSettingValue::save(nlohmann::json& json) const { + json = m_value; + return true; + } +} \ No newline at end of file diff --git a/loader/include/Geode/loader/Types.hpp b/loader/include/Geode/loader/Types.hpp index a3edbbde..956584e5 100644 --- a/loader/include/Geode/loader/Types.hpp +++ b/loader/include/Geode/loader/Types.hpp @@ -2,7 +2,7 @@ #include "../DefaultInclude.hpp" #include "../platform/cplatform.h" -#include "../external/json/json.hpp" +#include "../external/json/json_fwd.hpp" #include diff --git a/loader/include/Geode/modify/Addresses.hpp b/loader/include/Geode/modify/Addresses.hpp index ef8bfee9..7f342e47 100644 --- a/loader/include/Geode/modify/Addresses.hpp +++ b/loader/include/Geode/modify/Addresses.hpp @@ -1,10 +1,6 @@ #pragma once -#include "Types.hpp" - -#include namespace geode::modifier { - struct addresses { -#include - }; + template + uintptr_t address(); } diff --git a/loader/include/Geode/modify/AsStaticFunction.hpp b/loader/include/Geode/modify/AsStaticFunction.hpp new file mode 100644 index 00000000..e9570492 --- /dev/null +++ b/loader/include/Geode/modify/AsStaticFunction.hpp @@ -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 \ + struct AsStaticFunction_##FunctionName_ { \ + template \ + struct Impl {}; \ + template \ + struct Impl { \ + static Return GEODE_CDECL_CALL function(Params... params) { \ + return Class2::FunctionName_(params...); \ + } \ + }; \ + template \ + struct Impl { \ + static Return GEODE_CDECL_CALL function(Class* self, Params... params) { \ + auto self2 = addresser::rthunkAdjust(Function, self); \ + return self2->Class2::FunctionName_(params...); \ + } \ + }; \ + template \ + struct Impl { \ + 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::function; \ + }; + + GEODE_AS_STATIC_FUNCTION(constructor) + GEODE_AS_STATIC_FUNCTION(destructor) +} diff --git a/loader/include/Geode/modify/Modify.hpp b/loader/include/Geode/modify/Modify.hpp index bffe5e6b..e3c43571 100644 --- a/loader/include/Geode/modify/Modify.hpp +++ b/loader/include/Geode/modify/Modify.hpp @@ -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 #include #include -#include "IDManager.hpp" +#include -#define GEODE_APPLY_MODIFY_FOR_FUNCTION( \ - addr_index, pure_index, convention, className, functionName \ -) \ - if constexpr (wrap::functionName::uuid != nullptr && (void*)wrap::functionName::uuid != (void*)wrap::functionName::uuid) { \ - (void)Mod::get() \ - ->addHook::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()) { \ + auto hook = Hook::create( \ + Mod::get(), \ + reinterpret_cast(address()), \ + AsStaticFunction_##FunctionName_::value, \ + #ClassName_ "::" #FunctionName_ \ + ); \ + this->m_hooks[#ClassName_ "::" #FunctionName_] = hook; \ + } \ + } while (0); + +#define GEODE_APPLY_MODIFY_FOR_CONSTRUCTOR(AddressIndex_, Convention_, ClassName_, ...) \ + do { \ + if constexpr (HasConstructor) { \ + constexpr auto d = Resolve<__VA_ARGS__>::func(&Derived::constructor); \ + auto hook = Hook::create( \ + Mod::get(), \ + reinterpret_cast(address()), \ + AsStaticFunction_##constructor::value, \ + #ClassName_ "::" #ClassName_ \ + ); \ + this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \ + } \ + } while (0); + +#define GEODE_APPLY_MODIFY_FOR_DESTRUCTOR(AddressIndex_, Convention_, ClassName_) \ + do { \ + if constexpr (HasDestructor) { \ + constexpr auto d = Resolve<>::func(&Derived::destructor); \ + auto hook = Hook::create( \ + Mod::get(), \ + reinterpret_cast(address()), \ + AsStaticFunction_##destructor::value, \ + #ClassName_ "::" #ClassName_ \ + ); \ + this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \ + } \ + } while (0); namespace geode::modifier { template class ModifyDerive; - template + template class ModifyBase { public: + std::map m_hooks; + + Result 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 idea ModifyBase() { - Derived::apply(); + // i really dont want to recompile codegen + auto test = static_cast(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 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 m_fields; + + static void onModify(auto& self) {} }; } diff --git a/loader/include/Geode/modify/Traits.hpp b/loader/include/Geode/modify/Traits.hpp index cefa006c..9431e585 100644 --- a/loader/include/Geode/modify/Traits.hpp +++ b/loader/include/Geode/modify/Traits.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include 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 - 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 + struct RemoveClass { + using type = Func; + }; + + template + struct RemoveClass { + using type = Return(Params...); + }; + + template + struct RemoveClass { + using type = Return(Params...); + }; + + template + using RemoveClassType = typename RemoveClass::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(nullptr); + + template + struct Impl { + static void unique(...) {}; + + static constexpr auto value = &unique; + }; + + template <> + struct Impl { + static constexpr auto value = nvalue; + }; + + template + static constexpr auto value = Impl::value; + + + /** + * Checks if two function pointers are the same. If their types are + * different, returns false. + */ + template + static constexpr auto same() { + if (!std::is_same_v, RemoveClassType>) return false; + auto v1 = value; + auto v2 = value; + 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 + static constexpr auto different() { + if (!std::is_same_v, RemoveClassType>) return false; + auto v1 = value; + auto v2 = value; + 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 + struct Resolve { + template + static constexpr auto func(Return(*ptr)(std::type_identity_t...)) { + return ptr; + } + + template + static constexpr auto func(Return(Class::*ptr)(std::type_identity_t...)) { + return ptr; + } + + template + static constexpr auto func(Return(Class::*ptr)(std::type_identity_t...) 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 + struct Resolve : Resolve { + using Resolve::func; + }; + + /** + * A type trait that checks if a class has a function called "constructor". + */ + template + concept HasConstructor = requires { + &Class::constructor; + }; + + /** + * A type trait that checks if a class has a function called "destructor". + */ + template + concept HasDestructor = requires { + &Class::destructor; + }; + + template + struct AsStaticType { + using type = FunctionType; + }; + + template + struct AsStaticType { + using type = Return(*)(Class*, Params...); + }; + + template + struct AsStaticType { + using type = Return(*)(Class const*, Params...); }; } diff --git a/loader/include/Geode/modify/Types.hpp b/loader/include/Geode/modify/Types.hpp index cdf15e07..a7b97392 100644 --- a/loader/include/Geode/modify/Types.hpp +++ b/loader/include/Geode/modify/Types.hpp @@ -2,7 +2,7 @@ #include namespace geode::modifier { - struct types { -#include - }; +// struct types { +// #include +// }; } diff --git a/loader/include/Geode/modify/Wrapper.hpp b/loader/include/Geode/modify/Wrapper.hpp deleted file mode 100644 index e5daa442..00000000 --- a/loader/include/Geode/modify/Wrapper.hpp +++ /dev/null @@ -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 \ - 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 \ - struct identifier< \ - Class, Return(Parameters...), \ - std::enable_if_t(&Class::identifier))>>> { \ - private: \ - static Return wrapperImpl(Class* self, Parameters... ps) { \ - self = addresser::rthunkAdjust( \ - substitute(&Class::identifier), self \ - ); \ - return self->Class::identifier(ps...); \ - } \ - \ - public: \ - constexpr static inline auto value = &wrapperImpl; \ - constexpr static inline auto uuid = \ - function_uuid(&Class::identifier)>::value; \ - }; \ - /* Specialization - function Return Class::identifier(Parameters...) is a static function */ \ - template \ - struct identifier< \ - Class, Return(Parameters...), \ - std::enable_if_t< \ - std::is_pointer_v(&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(&Class::identifier)>::value; \ - }; - -namespace geode::modifier { - namespace wrap { - GEODE_WRAPPER_FOR_IDENTIFIER(constructor) - GEODE_WRAPPER_FOR_IDENTIFIER(destructor) - }; - - // template class Identifier, class Base, class Derived, - // class ...Types> struct PotentiallyWrongIdentifier { - - // template - // static std::true_type existsImpl(...); - - // template ::uuid == nullptr || - // Identifier::uuid == Identifier::uuid - // )) - // >> - // static std::false_type existsImpl(char); - - // template ::uuid != nullptr && - // Identifier::uuid == Identifier::uuid - // )) - // >> - // static std::false_type existsImpl(int); - - // static constexpr bool value = decltype(existsImpl(0))::value; - - // }; - -} diff --git a/loader/include/Geode/platform/cplatform.h b/loader/include/Geode/platform/cplatform.h index b42e1b01..562b11d4 100644 --- a/loader/include/Geode/platform/cplatform.h +++ b/loader/include/Geode/platform/cplatform.h @@ -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 diff --git a/loader/include/Geode/utils/VersionInfo.hpp b/loader/include/Geode/utils/VersionInfo.hpp index 99183860..b21b46e6 100644 --- a/loader/include/Geode/utils/VersionInfo.hpp +++ b/loader/include/Geode/utils/VersionInfo.hpp @@ -2,7 +2,7 @@ #include "../DefaultInclude.hpp" #include -#include "../external/json/json.hpp" +#include "../external/json/json_fwd.hpp" #include #include "../utils/Result.hpp" diff --git a/loader/include/Geode/utils/addresser.hpp b/loader/include/Geode/utils/addresser.hpp index 93c2384b..bf4ac936 100644 --- a/loader/include/Geode/utils/addresser.hpp +++ b/loader/include/Geode/utils/addresser.hpp @@ -10,6 +10,7 @@ #include #include #include +#include "../utils/casts.hpp" namespace geode::addresser { @@ -198,11 +199,19 @@ namespace geode::addresser { template 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(&func); return (F)((intptr_t)self + Addresser::thunkOf(func)); } template 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(&func); return (F)((intptr_t)self - Addresser::thunkOf(func)); } #endif diff --git a/loader/include/Geode/utils/cocos.hpp b/loader/include/Geode/utils/cocos.hpp index db3a5aeb..4769ce6b 100644 --- a/loader/include/Geode/utils/cocos.hpp +++ b/loader/include/Geode/utils/cocos.hpp @@ -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" diff --git a/loader/include/Geode/utils/file.hpp b/loader/include/Geode/utils/file.hpp index ef6c5af5..84113f00 100644 --- a/loader/include/Geode/utils/file.hpp +++ b/loader/include/Geode/utils/file.hpp @@ -3,7 +3,7 @@ #include "Result.hpp" #include "general.hpp" -#include "../external/json/json.hpp" +#include "../external/json/json_fwd.hpp" #include #include #include diff --git a/loader/include/Geode/utils/general.hpp b/loader/include/Geode/utils/general.hpp index dffba00e..2013d493 100644 --- a/loader/include/Geode/utils/general.hpp +++ b/loader/include/Geode/utils/general.hpp @@ -23,7 +23,7 @@ namespace geode { using ByteVector = std::vector; template - 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)); diff --git a/loader/include/Geode/utils/ranges.hpp b/loader/include/Geode/utils/ranges.hpp index d75d8289..0481285b 100644 --- a/loader/include/Geode/utils/ranges.hpp +++ b/loader/include/Geode/utils/ranges.hpp @@ -17,6 +17,9 @@ namespace std { } #endif +#undef min +#undef max + namespace geode::utils::ranges { template concept ValidConstContainer = requires(C const& c) { diff --git a/loader/include/Geode/utils/web.hpp b/loader/include/Geode/utils/web.hpp index fcf74efb..ffa20b11 100644 --- a/loader/include/Geode/utils/web.hpp +++ b/loader/include/Geode/utils/web.hpp @@ -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 - Result 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 fetchJSON(std::string const& url); class SentAsyncWebRequest; template diff --git a/loader/launcher/mac/Bootstrapper.cpp b/loader/launcher/mac/Bootstrapper.cpp index ddbc8ce2..0fbf8642 100644 --- a/loader/launcher/mac/Bootstrapper.cpp +++ b/loader/launcher/mac/Bootstrapper.cpp @@ -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(trigger)(); return; } diff --git a/loader/src/hooks/MenuLayer.cpp b/loader/src/hooks/MenuLayer.cpp index 889568e5..74cc96da 100644 --- a/loader/src/hooks/MenuLayer.cpp +++ b/loader/src/hooks/MenuLayer.cpp @@ -45,6 +45,8 @@ struct CustomMenuLayer : Modify { 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 diff --git a/loader/src/hooks/MessageBoxFix.cpp b/loader/src/hooks/MessageBoxFix.cpp index 794f9639..fbe366af 100644 --- a/loader/src/hooks/MessageBoxFix.cpp +++ b/loader/src/hooks/MessageBoxFix.cpp @@ -7,14 +7,11 @@ #include 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(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_CON_ADDR)(self); - static auto p = Mod::get()->patch( - reinterpret_cast(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(geode::base::getCocos() + 0x19feec), + toByteArray(&fixedErrorHandler) ); } diff --git a/loader/src/ids/CreatorLayer.cpp b/loader/src/ids/CreatorLayer.cpp index 442f4276..9e161b83 100644 --- a/loader/src/ids/CreatorLayer.cpp +++ b/loader/src/ids/CreatorLayer.cpp @@ -1,7 +1,7 @@ #include "AddIDs.hpp" #include -#include +#include #include USE_GEODE_NAMESPACE(); diff --git a/loader/src/ids/LevelBrowserLayer.cpp b/loader/src/ids/LevelBrowserLayer.cpp index d3584613..de08965e 100644 --- a/loader/src/ids/LevelBrowserLayer.cpp +++ b/loader/src/ids/LevelBrowserLayer.cpp @@ -1,7 +1,7 @@ #include "AddIDs.hpp" #include -#include +#include #include USE_GEODE_NAMESPACE(); diff --git a/loader/src/ids/LevelSettingsLayer.cpp b/loader/src/ids/LevelSettingsLayer.cpp index 3b8bd810..9da13acc 100644 --- a/loader/src/ids/LevelSettingsLayer.cpp +++ b/loader/src/ids/LevelSettingsLayer.cpp @@ -1,7 +1,7 @@ #include "AddIDs.hpp" #include -#include +#include #include USE_GEODE_NAMESPACE(); @@ -205,3 +205,25 @@ struct LevelSettingsLayerIDs : Modify return true; } }; + + + template + struct Resolve2 { + template + static constexpr auto func(Return(*ptr)(float)) { + return ptr; + } + + template + static constexpr auto func(Return(Class::*ptr)(float)) { + return ptr; + } + + template + static constexpr auto func(Return(Class::*ptr)(float) const) { + return ptr; + } + }; + +#include +constexpr auto b = Resolve2::func(&PlayerObject::runBallRotation); \ No newline at end of file diff --git a/loader/src/loader/Hook.cpp b/loader/src/loader/Hook.cpp index 670f0db4..f3125191 100644 --- a/loader/src/loader/Hook.cpp +++ b/loader/src/loader/Hook.cpp @@ -1,50 +1,81 @@ -#include -#include -#include -#include -#include -#include -// #include -#include "ModImpl.hpp" - -#include - -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(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(m_address); - json["detour"] = reinterpret_cast(m_detour); - json["name"] = m_displayName; - json["enabled"] = m_enabled; - return json; -} +#include +#include +#include +#include +#include +#include +// #include +#include "ModImpl.hpp" + +#include +#include "HookImpl.hpp" + +USE_GEODE_NAMESPACE(); + +Hook::Hook(std::shared_ptr&& 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( + 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(); +} \ No newline at end of file diff --git a/loader/src/loader/HookImpl.cpp b/loader/src/loader/HookImpl.cpp new file mode 100644 index 00000000..c2945fce --- /dev/null +++ b/loader/src/loader/HookImpl.cpp @@ -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(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(m_address); + json["detour"] = reinterpret_cast(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; +} \ No newline at end of file diff --git a/loader/src/loader/HookImpl.hpp b/loader/src/loader/HookImpl.hpp new file mode 100644 index 00000000..551f38d5 --- /dev/null +++ b/loader/src/loader/HookImpl.hpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +// #include +#include "ModImpl.hpp" + +#include + +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); +}; \ No newline at end of file diff --git a/loader/src/loader/IPC.cpp b/loader/src/loader/IPC.cpp index dafb4e80..f882b477 100644 --- a/loader/src/loader/IPC.cpp +++ b/loader/src/loader/IPC.cpp @@ -1,7 +1,15 @@ #include +#include 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, diff --git a/loader/src/loader/Index.cpp b/loader/src/loader/Index.cpp index cbfa4916..9b95ef94 100644 --- a/loader/src/loader/Index.cpp +++ b/loader/src/loader/Index.cpp @@ -6,6 +6,8 @@ #include #include #include +#include + USE_GEODE_NAMESPACE(); diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp index f2d0f75c..ddb5604b 100644 --- a/loader/src/loader/LoaderImpl.cpp +++ b/loader/src/loader/LoaderImpl.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -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 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(); +} diff --git a/loader/src/loader/LoaderImpl.hpp b/loader/src/loader/LoaderImpl.hpp index f9957308..e0527fbe 100644 --- a/loader/src/loader/LoaderImpl.hpp +++ b/loader/src/loader/LoaderImpl.hpp @@ -18,6 +18,7 @@ #include #include #include +#include // TODO: Find a file convention for impl headers namespace geode { @@ -64,6 +65,13 @@ namespace geode { Mod* takeNextMod(); void releaseNextMod(); + std::unordered_map m_handlerHandles; + + Result<> createHandler(void* address, tulip::hook::HandlerMetadata const& metadata); + bool hasHandler(void* address); + Result 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 { diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index 749113e5..b1d542d1 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -1,188 +1,188 @@ -#include -#include "ModImpl.hpp" - -USE_GEODE_NAMESPACE(); - -Mod::Mod(ModInfo const& info) : m_impl(std::make_unique(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 Mod::getDescription() const { - return m_impl->getDescription(); -} - -std::optional 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 Mod::getSettingKeys() const { - return m_impl->getSettingKeys(); -} - -bool Mod::hasSetting(std::string const& key) const { - return m_impl->hasSetting(key); -} - -std::optional 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 value) { - return m_impl->registerCustomSetting(key, std::move(value)); -} - -std::vector Mod::getHooks() const { - return m_impl->getHooks(); -} - -Result 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 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 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 +#include "ModImpl.hpp" + +USE_GEODE_NAMESPACE(); + +Mod::Mod(ModInfo const& info) : m_impl(std::make_unique(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 Mod::getDescription() const { + return m_impl->getDescription(); +} + +std::optional 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 Mod::getSettingKeys() const { + return m_impl->getSettingKeys(); +} + +bool Mod::hasSetting(std::string const& key) const { + return m_impl->hasSetting(key); +} + +std::optional 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 value) { + return m_impl->registerCustomSetting(key, std::move(value)); +} + +std::vector Mod::getHooks() const { + return m_impl->getHooks(); +} + +Result 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 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 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(); } \ No newline at end of file diff --git a/loader/src/loader/ModImpl.cpp b/loader/src/loader/ModImpl.cpp index ffe05d87..855a6131 100644 --- a/loader/src/loader/ModImpl.cpp +++ b/loader/src/loader/ModImpl.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -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 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()); } -} \ No newline at end of file + return mod; +} + +Result<> Loader::Impl::setupInternalMod() { + return Mod::get()->m_impl->setup(); +} diff --git a/loader/src/loader/ModImpl.hpp b/loader/src/loader/ModImpl.hpp index 2d38d027..29437433 100644 --- a/loader/src/loader/ModImpl.hpp +++ b/loader/src/loader/ModImpl.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace geode { class Mod::Impl { public: diff --git a/loader/src/loader/ModInfo.cpp b/loader/src/loader/ModInfo.cpp index 644d1789..0da345e1 100644 --- a/loader/src/loader/ModInfo.cpp +++ b/loader/src/loader/ModInfo.cpp @@ -2,6 +2,9 @@ #include #include #include +#include +#include + #include USE_GEODE_NAMESPACE(); @@ -36,10 +39,9 @@ bool ModInfo::validateID(std::string const& id) { Result ModInfo::createFromSchemaV010(ModJson const& rawJson) { ModInfo info; - auto json = rawJson; - info.m_rawJSON = rawJson; + info.m_rawJSON = std::make_unique(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*>> 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 { diff --git a/loader/src/loader/Patch.cpp b/loader/src/loader/Patch.cpp index e5b971f3..9c0e4aa0 100644 --- a/loader/src/loader/Patch.cpp +++ b/loader/src/loader/Patch.cpp @@ -1,14 +1,14 @@ #include -#include +#include 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 { diff --git a/loader/src/loader/Setting.cpp b/loader/src/loader/Setting.cpp index 7d813206..06019a09 100644 --- a/loader/src/loader/Setting.cpp +++ b/loader/src/loader/Setting.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include USE_GEODE_NAMESPACE(); @@ -131,7 +133,7 @@ Result Setting::parse( case hash("custom"): { sett.m_kind = CustomSetting { - .json = obj.json() + .json = std::make_shared(obj.json()) }; // skip checking unknown keys return Ok(sett); diff --git a/loader/src/main.cpp b/loader/src/main.cpp index 04d8b051..bdbea13a 100644 --- a/loader/src/main.cpp +++ b/loader/src/main.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include 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 @@ -120,7 +129,7 @@ $execute { listenForIPC("list-mods", [](IPCEvent* event) -> nlohmann::json { std::vector 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; diff --git a/loader/src/platform/mac/Cocos2d.cpp b/loader/src/platform/mac/Cocos2d.cpp index 16af07d2..d0a366d8 100644 --- a/loader/src/platform/mac/Cocos2d.cpp +++ b/loader/src/platform/mac/Cocos2d.cpp @@ -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. { diff --git a/loader/src/utils/VersionInfo.cpp b/loader/src/utils/VersionInfo.cpp index c564a737..202ecd02 100644 --- a/loader/src/utils/VersionInfo.cpp +++ b/loader/src/utils/VersionInfo.cpp @@ -4,6 +4,7 @@ #include #include +#include USE_GEODE_NAMESPACE(); diff --git a/loader/src/utils/cocos.cpp b/loader/src/utils/cocos.cpp index 7f79ef30..0f30080c 100644 --- a/loader/src/utils/cocos.cpp +++ b/loader/src/utils/cocos.cpp @@ -1,5 +1,6 @@ #include #include +#include USE_GEODE_NAMESPACE(); diff --git a/loader/src/utils/file.cpp b/loader/src/utils/file.cpp index 9734e530..95c74343 100644 --- a/loader/src/utils/file.cpp +++ b/loader/src/utils/file.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include USE_GEODE_NAMESPACE(); diff --git a/loader/src/utils/web.cpp b/loader/src/utils/web.cpp index 32681852..26c6b1f6 100644 --- a/loader/src/utils/web.cpp +++ b/loader/src/utils/web.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include USE_GEODE_NAMESPACE(); @@ -97,6 +98,17 @@ Result web::fetchBytes(std::string const& url) { return Err("Error getting info: " + std::string(curl_easy_strerror(res))); } +Result 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 web::fetch(std::string const& url) { auto curl = curl_easy_init(); diff --git a/loader/test/dependency/main.cpp b/loader/test/dependency/main.cpp index 1c407338..ae2df000 100644 --- a/loader/test/dependency/main.cpp +++ b/loader/test/dependency/main.cpp @@ -4,6 +4,8 @@ USE_GEODE_NAMESPACE(); #include #include +#include + enum class Icon { Steve, diff --git a/loader/test/main/main.cpp b/loader/test/main/main.cpp index e5a2c31d..a94d3001 100644 --- a/loader/test/main/main.cpp +++ b/loader/test/main/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include USE_GEODE_NAMESPACE();