diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9cc21420..6da53abe 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -12,6 +12,14 @@ body: - "Windows" validations: required: true + - type: input + id: commit + attributes: + label: SDK commit + description: The commit you used to compile your code with. If the bug is not related with your mod, you can leave this field empty. + placeholder: "Example: a674b97" + validations: + required: false - type: input id: version attributes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 133fa304..a5798580 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,12 @@ include(cmake/GeodeFile.cmake) include(cmake/Platform.cmake) include(cmake/CPM.cmake) +# this is needed for cross compilation on linux, +# since fmtlib will fail to compile otherwise +if (GEODE_DISABLE_FMT_CONSTEVAL) + target_compile_definitions(${PROJECT_NAME} INTERFACE -DFMT_CONSTEVAL=) +endif() + CPMAddPackage("gh:geode-sdk/json#2b76460") CPMAddPackage("gh:fmtlib/fmt#9.1.0") CPMAddPackage("gh:gulrak/filesystem#3e5b930") @@ -81,6 +87,7 @@ ExternalProject_Add(CodegenProject # this should hopefully fix generator cache mismatch between different projects, however # it causes a warning to be shown every time. if you know a better solution please tell us ok thx CONFIGURE_COMMAND ${CMAKE_COMMAND} ${GEODE_CODEGEN_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:STRING=${GEODE_CODEGEN_BINARY_OUT} -S -B + INSTALL_COMMAND ${CMAKE_COMMAND} --install --config $ ) diff --git a/bindings/GeometryDash.bro b/bindings/GeometryDash.bro index 3a42b3dc..e8a22fd2 100644 --- a/bindings/GeometryDash.bro +++ b/bindings/GeometryDash.bro @@ -186,15 +186,13 @@ class ButtonSprite : cocos2d::CCSprite { ) = win 0x134b0, mac 0x4f1d0; [[docs(" - /** - * Create a ButtonSprite with a top sprite and a texture. - * @param topSprite The top sprite to add on top of the sprite - * @param width Sprite width; ignored if `absolute` is false - * @param absolute Whether to use absolute width or not - * @param texture The name of the background sprite file (can't be in a spritesheet) - * @param height The height of the button, leave 0 for automatic - * @param scale Scale of top sprite - */ + Create a ButtonSprite with a top sprite and a texture. + @param topSprite The top sprite to add on top of the sprite + @param width Sprite width; ignored if `absolute` is false + @param absolute Whether to use absolute width or not + @param texture The name of the background sprite file (can't be in a spritesheet) + @param height The height of the button, leave 0 for automatic + @param scale Scale of top sprite ")]] static ButtonSprite* create( cocos2d::CCSprite* topSprite, @@ -208,26 +206,27 @@ class ButtonSprite : cocos2d::CCSprite { } [[docs(" - /** - * Create a ButtonSprite with text, a font and a texture. - * @param caption The text of the ButtonSprite - * @param width Sprite width; ignored if `absolute` is false - * @param absolute Whether to use absolute width or not - * @param font The name of the BM font file to use - * @param texture The name of the background sprite file (can't be in a spritesheet) - * @param height The height of the button, leave 0 for automatic - * @param scale Scale of text - * @returns Pointer to the created ButtonSprite, or nullptr on error - */ + Create a ButtonSprite with text, a font and a texture. + @param caption The text of the ButtonSprite + @param width Sprite width; ignored if `absolute` is false + @param absolute Whether to use absolute width or not + @param font The name of the BM font file to use + @param texture The name of the background sprite file (can't be in a spritesheet) + @param height The height of the button, leave 0 for automatic + @param scale Scale of text + @returns Pointer to the created ButtonSprite, or nullptr on error ")]] static ButtonSprite* create(const char* caption, int width, bool absolute, const char* font, const char* texture, float height, float scale) { return create(caption, width, 0, scale, absolute, font, texture, height); } - inline static ButtonSprite* create(char const* caption) { + static ButtonSprite* create(char const* caption) { return ButtonSprite::create(caption, 0, 0, "goldFont.fnt", "GJ_button_01.png", .0f, 1.f); } - inline static ButtonSprite* create(char const* caption, const char* font, const char* texture, float scale = 1.f) { + static ButtonSprite* create(char const* caption, const char* font, const char* texture) { + return ButtonSprite::create(caption, 0, 0, font, texture, .0f, 1.f); + } + static ButtonSprite* create(char const* caption, const char* font, const char* texture, float scale) { return ButtonSprite::create(caption, 0, 0, font, texture, .0f, scale); } static ButtonSprite* create(char const*, int, int, float, bool) = mac 0x4fa40; @@ -1268,6 +1267,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ void onUngroupSticky(cocos2d::CCObject* sender) = mac 0xc1d0, win 0x87ac0; void onGoToLayer(cocos2d::CCObject* sender) = win 0x886b0; void onGoToBaseLayer(cocos2d::CCObject* sender) = win 0x88790; + void onToggleGuide(cocos2d::CCObject* sender) = mac 0x19da0, win 0x79160; void editColor(cocos2d::CCObject* sender) = mac 0x19190, win 0x8d3c0; void alignObjects(cocos2d::CCArray* objs, bool alignY) = mac 0x2cea0, win 0x8f320; virtual void scrollWheel(float vertical, float horizontal) = win 0x921d0, mac 0x31370, ios 0x2c4884; diff --git a/codegen/CMakeLists.txt b/codegen/CMakeLists.txt index f0257203..960f382c 100644 --- a/codegen/CMakeLists.txt +++ b/codegen/CMakeLists.txt @@ -4,7 +4,7 @@ project(Codegen LANGUAGES C CXX) include(../cmake/CPM.cmake) CPMAddPackage("gh:fmtlib/fmt#9.1.0") -CPMAddPackage("gh:camila314/Broma#1.0.0") +CPMAddPackage("gh:geode-sdk/Broma#eb45fab") file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp @@ -22,4 +22,4 @@ target_precompile_headers(Codegen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Shared.hpp ) -install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) \ No newline at end of file +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/codegen/src/BindingGen.cpp b/codegen/src/BindingGen.cpp index 8ae7f092..c87fb974 100644 --- a/codegen/src/BindingGen.cpp +++ b/codegen/src/BindingGen.cpp @@ -40,13 +40,18 @@ public: char const* monostate_constructor_cutoff = R"GEN( GEODE_MONOSTATE_CONSTRUCTOR_CUTOFF({class_name}, {first_base}) )GEN"; - char const* function_definition = R"GEN({docs} {static}{virtual}{return_type} {function_name}({parameters}){const}; + char const* function_definition = R"GEN( + /** +{docs}{docs_addresses} */ + {static}{virtual}{return_type} {function_name}({parameters}){const}; )GEN"; char const* error_definition = R"GEN( #ifdef GEODE_WARN_INCORRECT_MEMBERS [[deprecated("Function is not implemented - will throw at runtime!!!")]] #endif + /** +{docs}{docs_addresses} */ {static}{return_type} {function_name}({parameters}){const}{{ throw std::runtime_error("Use of undefined function " + GEODE_PRETTY_FUNCTION); }} @@ -56,6 +61,8 @@ public: #ifdef GEODE_WARN_INCORRECT_MEMBERS [[deprecated("Use of undefined virtual function - will crash at runtime!!!")]] #endif + /** +{docs}{docs_addresses} */ {virtual}{return_type} {function_name}({parameters}){const}{{ #ifdef GEODE_NO_UNDEFINED_VIRTUALS static_assert(false, "Undefined virtual function - implement in GeometryDash.bro"); @@ -68,10 +75,13 @@ public: #ifdef GEODE_WARN_INCORRECT_MEMBERS [[deprecated("Member placed incorrectly - will crash at runtime!!!")]] #endif - )GEN"; +)GEN"; char const* structor_definition = R"GEN( - {function_name}({parameters});)GEN"; + /** +{docs}{docs_addresses} */ + {function_name}({parameters}); +)GEN"; // requires: type, member_name, array char const* member_definition = R"GEN( {type} {member_name}; @@ -84,6 +94,52 @@ public: )GEN"; }} +inline std::string nameForPlatform(Platform platform) { + switch (platform) { + case Platform::Mac: return "MacOS"; + case Platform::Windows: return "Windows"; + case Platform::iOS: return "iOS"; + case Platform::Android: return "Android"; + default: // unreachable + return "Windows"; + } +} + + +std::string generateAddressDocs(Field const& field, FunctionBindField* fn) { + std::string ret; + + for (auto platform : {Platform::Mac, Platform::Windows, Platform::iOS, Platform::Android}) { + auto status = codegen::getStatusWithPlatform(platform, field); + + if (status == BindStatus::NeedsBinding) { + ret += fmt::format(" * @note[short] {}: 0x{:x}\n", + nameForPlatform(platform), + codegen::platformNumberWithPlatform(platform, fn->binds) + ); + } + else if (status == BindStatus::Binded) { + ret += fmt::format(" * @note[short] {}\n", + nameForPlatform(platform) + ); + } + + } + + return ret; +} + +std::string generateDocs(std::string const& docs) { + if (docs.size() < 7) return ""; + auto ret = docs.substr(1, docs.size() - 6); // i hate this but idk how to generalize + + for (auto next = ret.find(" "); next != std::string::npos; next = ret.find(" ")) { + ret.replace(next, 8, " * "); + } + + return ret; +} + std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singleFolder) { std::string output; @@ -137,6 +193,8 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl FunctionBegin* fb; char const* used_format = format_strings::function_definition; + std::string addressDocs; + if (auto i = field.get_as()) { single_output += "\t" + i->inner + "\n"; continue; @@ -158,6 +216,8 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl continue; } else if (auto fn = field.get_as()) { fb = &fn->beginning; + addressDocs = " * @note[short] Out of line\n"; + } else if (auto fn = field.get_as()) { fb = &fn->beginning; @@ -170,8 +230,12 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl if (fb->type != FunctionType::Normal) continue; } + + addressDocs = generateAddressDocs(field, fn); } + std::string docs = generateDocs(fb->docs); + single_output += fmt::format(used_format, fmt::arg("virtual", str_if("virtual ", fb->is_virtual)), fmt::arg("static", str_if("static ", fb->is_static)), @@ -181,7 +245,8 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl fmt::arg("index", field.field_id), fmt::arg("parameters", codegen::getParameters(*fb)), fmt::arg("return_type", fb->ret.name), - fmt::arg("docs", fb->docs) + fmt::arg("docs_addresses", addressDocs), + fmt::arg("docs", docs) ); } diff --git a/codegen/src/Main.cpp b/codegen/src/Main.cpp index 5a504b37..53d2f941 100644 --- a/codegen/src/Main.cpp +++ b/codegen/src/Main.cpp @@ -34,7 +34,7 @@ int main(int argc, char** argv) try { writeFile(writeDir / "GeneratedAddress.cpp", generateAddressHeader(root)); writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify")); // writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root)); - writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root)); + // writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root)); writeFile(writeDir / "GeneratedBinding.hpp", generateBindingHeader(root, writeDir / "binding")); writeFile(writeDir / "GeneratedPredeclare.hpp", generatePredeclareHeader(root)); writeFile(writeDir / "GeneratedSource.cpp", generateBindingSource(root)); diff --git a/codegen/src/Shared.hpp b/codegen/src/Shared.hpp index 1ae45ceb..c93f398e 100644 --- a/codegen/src/Shared.hpp +++ b/codegen/src/Shared.hpp @@ -70,22 +70,26 @@ namespace codegen { inline Platform platform; - inline uintptr_t platformNumber(PlatformNumber const& p) { - switch (codegen::platform) { - case Platform::Mac: return p.mac; - case Platform::Windows: return p.win; - case Platform::iOS: return p.ios; - case Platform::Android: return p.android; + inline uintptr_t platformNumberWithPlatform(Platform p, PlatformNumber const& pn) { + switch (p) { + case Platform::Mac: return pn.mac; + case Platform::Windows: return pn.win; + case Platform::iOS: return pn.ios; + case Platform::Android: return pn.android; default: // unreachable - return p.win; + return pn.win; } } - inline BindStatus getStatus(Field const& field) { + inline uintptr_t platformNumber(PlatformNumber const& p) { + return platformNumberWithPlatform(codegen::platform, p); + } + + inline BindStatus getStatusWithPlatform(Platform p, Field const& field) { FunctionBegin const* fb; if (auto fn = field.get_as()) { - if (platformNumber(fn->binds)) return BindStatus::NeedsBinding; + if (platformNumberWithPlatform(p, fn->binds)) return BindStatus::NeedsBinding; fb = &fn->beginning; } @@ -96,7 +100,7 @@ namespace codegen { // if (field.parent.rfind("GDString", 0) == 0) return BindStatus::NeedsBinding; - if (platform == Platform::Android) { + if (p == Platform::Android) { for (auto& [type, name] : fb->args) { if (type.name.find("gd::") != std::string::npos) return BindStatus::NeedsBinding; } @@ -108,13 +112,17 @@ namespace codegen { if (fb->type == FunctionType::Normal) { if (field.parent.rfind("fmod::", 0) == 0) return BindStatus::Binded; - if (field.parent.rfind("cocos2d::", 0) == 0 && platform == Platform::Windows) + if (field.parent.rfind("cocos2d::", 0) == 0 && p == Platform::Windows) return BindStatus::Binded; } return BindStatus::Unbindable; } + inline BindStatus getStatus(Field const& field) { + return getStatusWithPlatform(codegen::platform, field); + } + inline std::string getParameters(FunctionBegin const& f) { // int p0, float p1 std::vector parameters; diff --git a/codegen/src/TypeGen.cpp b/codegen/src/TypeGen.cpp index f52143fc..e69de29b 100644 --- a/codegen/src/TypeGen.cpp +++ b/codegen/src/TypeGen.cpp @@ -1,96 +0,0 @@ -#include "Shared.hpp" -#include "TypeOpt.hpp" -#include - -std::string generateTypeHeader(Root& root) { - std::string output; - - TypeBank bank; - bank.loadFrom(root); - - std::map used_returns; - std::map used_funcs; - std::map used_pures; - - int i = 0; - for (auto& f : bank.typeList()) { - - char const* return_fmt = "using ret{index} = {return};"; - char const* func_fmt; - char const* pure_fmt = "ret{ret_index}({parameter_types});"; - char const* meta_fmt; - char const* member_fmt; - - switch (f.type) { - case FuncType::Member: - func_fmt = "ret{ret_index}(*)( {const}{class_name}*{parameter_type_comma}{parameter_types});"; - meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});"; - member_fmt = "ret{ret_index}({class_name}::*)({parameter_types}){const};"; - break; - case FuncType::Static: - func_fmt = "ret{ret_index}(*)({parameter_types});"; - meta_fmt = "ret{ret_index}({parameter_types});"; - member_fmt = "func{index};"; - break; - case FuncType::Structor: - func_fmt = "ret{ret_index}(*)({class_name}*{parameter_type_comma}{parameter_types});"; - meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});"; - member_fmt = "func{index};"; - break; - } - - if (used_returns.count(f.return_type) == 0) { - output += fmt::format(return_fmt, - fmt::arg("index", i), - fmt::arg("return", f.return_type) - ) + "\n"; - used_returns[f.return_type] = i; - } - int ret_index = used_returns[f.return_type]; - - std::string pure_val = fmt::format(pure_fmt, - fmt::arg("ret_index", ret_index), - fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")) - ); - if (used_pures.count(pure_val) == 0) { - output += fmt::format("using pure{} = {}\n", i, pure_val); - used_pures[pure_val] = i; - } - - std::string func_val = fmt::format(func_fmt, - fmt::arg("ret_index", ret_index), - fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")), - fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())), - fmt::arg("class_name", f.class_name), - fmt::arg("const", str_if(" const ", f.is_const)) - ); - std::string meta_val = fmt::format(meta_fmt, - fmt::arg("ret_index", ret_index), - fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")), - fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())), - fmt::arg("class_name", f.class_name), - fmt::arg("const", str_if(" const ", f.is_const)) - ); - std::string member_val = fmt::format(member_fmt, - fmt::arg("ret_index", ret_index), - fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")), - fmt::arg("class_name", f.class_name), - fmt::arg("const", str_if(" const ", f.is_const)), - fmt::arg("index", i) - ); - - if (used_funcs.count(func_val) == 0) { - output += fmt::format("using func{index} = {func}\nusing meta{index} = {meta}\nusing member{index} = {member}\n", - fmt::arg("index", i), - fmt::arg("func", func_val), - fmt::arg("meta", meta_val), - fmt::arg("member", member_val) - ); - used_funcs[func_val] = i; - } - - ++i; - } - - return output; -} diff --git a/loader/include/Geode/loader/Setting.hpp b/loader/include/Geode/loader/Setting.hpp index 0c77f265..cb3134a8 100644 --- a/loader/include/Geode/loader/Setting.hpp +++ b/loader/include/Geode/loader/Setting.hpp @@ -219,9 +219,9 @@ namespace geode { using ColorAlphaSettingValue = GeodeSettingValue; template - struct SettingValueSetter { - static GEODE_DLL T get(SettingValue* setting); - static GEODE_DLL void set(SettingValue* setting, T const& value); + struct GEODE_DLL SettingValueSetter { + static T get(SettingValue* setting); + static void set(SettingValue* setting, T const& value); }; } diff --git a/loader/include/Geode/modify/Modify.hpp b/loader/include/Geode/modify/Modify.hpp index 3b046c96..e2ed7c85 100644 --- a/loader/include/Geode/modify/Modify.hpp +++ b/loader/include/Geode/modify/Modify.hpp @@ -106,7 +106,7 @@ namespace geode::modifier { class ModifyDerive { public: ModifyDerive() { - static_assert(alwaysFalse, "Custom Modify not implemented."); + static_assert(alwaysFalse, "Modified class not recognized, please include to be able to use it."); } }; } diff --git a/loader/include/Geode/utils/addresser.hpp b/loader/include/Geode/utils/addresser.hpp index eb458ffa..7b4a5adb 100644 --- a/loader/include/Geode/utils/addresser.hpp +++ b/loader/include/Geode/utils/addresser.hpp @@ -163,18 +163,7 @@ namespace geode::addresser { return addressOfNonVirtual(reinterpret_cast(func)); } - static inline intptr_t followThunkFunction(intptr_t address) { -#ifdef GEODE_IS_WINDOWS - // check if first instruction is a jmp dword ptr [....], i.e. if the func is a thunk - if (*reinterpret_cast(address) == 0xFF && *reinterpret_cast(address + 1) == 0x25) { - // read where the jmp reads from - address = *reinterpret_cast(address + 2); - // that then contains the actual address of the func - address = *reinterpret_cast(address); - } -#endif - return address; - } + static intptr_t followThunkFunction(intptr_t address); template static intptr_t addressOfNonVirtual(R (T::*func)(Ps...)) { diff --git a/loader/src/loader/Setting.cpp b/loader/src/loader/Setting.cpp index 4b5d0b3e..c8754dff 100644 --- a/loader/src/loader/Setting.cpp +++ b/loader/src/loader/Setting.cpp @@ -275,18 +275,6 @@ std::string SettingValue::getKey() const { typename type_##Setting::ValueType const& value \ ) const -// instantiate value setters - -namespace geode { - template struct SettingValueSetter; - template struct SettingValueSetter; - template struct SettingValueSetter; - template struct SettingValueSetter; - template struct SettingValueSetter; - template struct SettingValueSetter; - template struct SettingValueSetter; -} - // instantiate values namespace geode { @@ -370,6 +358,18 @@ IMPL_NODE_AND_SETTERS(File); IMPL_NODE_AND_SETTERS(Color); IMPL_NODE_AND_SETTERS(ColorAlpha); +// instantiate value setters + +namespace geode { + template struct SettingValueSetter; + template struct SettingValueSetter; + template struct SettingValueSetter; + template struct SettingValueSetter; + template struct SettingValueSetter; + template struct SettingValueSetter; + template struct SettingValueSetter; +} + // SettingChangedEvent SettingChangedEvent::SettingChangedEvent(Mod* mod, SettingValue* value) diff --git a/loader/src/utils/addresser.cpp b/loader/src/utils/addresser.cpp index d9a4c9b8..9fd65c2f 100644 --- a/loader/src/utils/addresser.cpp +++ b/loader/src/utils/addresser.cpp @@ -65,3 +65,16 @@ namespace { Addresser::MultipleInheritance* Addresser::instance() { return reinterpret_cast(&TableTable::table); } + +intptr_t Addresser::followThunkFunction(intptr_t address) { +#ifdef GEODE_IS_WINDOWS + // check if first instruction is a jmp dword ptr [....], i.e. if the func is a thunk + if (*reinterpret_cast(address) == 0xFF && *reinterpret_cast(address + 1) == 0x25) { + // read where the jmp reads from + address = *reinterpret_cast(address + 2); + // that then contains the actual address of the func + address = *reinterpret_cast(address); + } +#endif + return address; +} \ No newline at end of file