diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 8847d1ff..1c0a4994 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,7 +1,17 @@ name: Bug Report -description: Report a bug where something is not working as expected in Geode Loader (not specific mods), which does not crash the game. +description: Report a Geode bug (not mods themselves) where something is not working as expected in Geode Loader (not mods created by others), which does not crash the game. labels: [ "unverified", "bug" ] body: + - type: checkboxes + attributes: + label: Geode Issue + description: | + The Geode repository is for issues of *Geode Loader*, not individual mods created by other developers. + When submitting a bug report, please make sure that the bug is *actually* related to ***Geode Loader itself*** and not to a mod or mod combination. + Failing to do this will get your issue *closed without explanation*. + options: + - label: I confirm that this bug is NOT related to a mod but directly to Geode Loader itself. + required: true - type: dropdown id: platform attributes: diff --git a/.github/ISSUE_TEMPLATE/crash-report.yml b/.github/ISSUE_TEMPLATE/crash-report.yml index a0f69868..f4c372a4 100644 --- a/.github/ISSUE_TEMPLATE/crash-report.yml +++ b/.github/ISSUE_TEMPLATE/crash-report.yml @@ -1,7 +1,18 @@ name: Crash Report -description: Report a bug that crashes the game or prevents startup caused by Geode Loader (not individual mods). +description: Report a Geode bug (not mods themselves) that crashes the game or prevents startup caused by Geode Loader (not mods created by others). labels: [ "unverified", "crash" ] body: + - type: input + id: geode-confirmation + attributes: + label: Geode Issue + description: | + The Geode repository is for issues of *Geode Loader*, not individual mods created by other developers. + When submitting a crash report, please make sure that the crash is *actually* related to ***Geode Loader itself*** and not to a mod or mod combination, after you do that type in "confirm" in the input field above. + Failing to do this will get your issue *closed without explanation*. + placeholder: "Please, read the text below." + validations: + required: true - type: dropdown id: platform attributes: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8597aa34..a0cdd586 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -146,21 +146,13 @@ jobs: files: geode-win/XInput9_1_0.dll geode-win/Geode.dll geode-win/GeodeUpdater.exe geode-win/Geode.lib geode-win/Geode.pdb dest: geode-${{ steps.ref.outputs.hash }}-win.zip - # TODO change in 2.0.0 - - name: Zip Windows Resources - uses: vimtor/action-zip@v1.1 - with: - files: geode-win/resources - dest: resources-win.zip - - # This is basically a hack because of line endings. Blame windows. - - name: Zip MacOS Resources + - name: Zip Resources uses: vimtor/action-zip@v1.1 with: files: geode-mac/resources - dest: resources-mac.zip + dest: resources.zip - - name: Update Nightly Release + - name: Update Development Release uses: andelf/nightly-release@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -174,5 +166,4 @@ jobs: ./geode-installer-${{ steps.ref.outputs.hash }}-win.exe ./geode-${{ steps.ref.outputs.hash }}-mac.zip ./geode-${{ steps.ref.outputs.hash }}-win.zip - ./resources-win.zip - ./resources-mac.zip + ./resources.zip diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 34f757d2..d6051f79 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -28,8 +28,7 @@ jobs: mv dev/geode-installer-*-win.exe geode-installer-v${{ steps.ref.outputs.version }}-win.exe mv dev/geode-*-mac.zip geode-v${{ steps.ref.outputs.version }}-mac.zip mv dev/geode-*-win.zip geode-v${{ steps.ref.outputs.version }}-win.zip - mv dev/resources-win.zip resources-win.zip - mv dev/resources-mac.zip resources-mac.zip + mv dev/resources.zip resources.zip - name: Create Draft Release uses: softprops/action-gh-release@v1 @@ -49,5 +48,4 @@ jobs: ./geode-installer-v${{ steps.ref.outputs.version }}-win.exe ./geode-v${{ steps.ref.outputs.version }}-mac.zip ./geode-v${{ steps.ref.outputs.version }}-win.zip - ./resources-win.zip - ./resources-mac.zip + ./resources.zip diff --git a/.github/workflows/test-offsets.yml b/.github/workflows/test-offsets.yml index 66cfcf47..23403f67 100644 --- a/.github/workflows/test-offsets.yml +++ b/.github/workflows/test-offsets.yml @@ -3,6 +3,9 @@ name: Test Offsets on: workflow_dispatch: push: + paths: + - 'bindings/**' # only when adjusting bindings + - 'loader/test/members/**' branches: - '**' # every branch - '!no-build-**' # unless marked as no-build diff --git a/.gitignore b/.gitignore index eed1617f..119b6b2f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,20 +46,29 @@ build2 build-docs/ bin +# Ignore docs folders docs/** docs +# Ignore codegenned files loader/src/internal/about.hpp loader/src/internal/resources.hpp loader/resources/mod.json loader/resources/version loader/resources/blanks/rename.js +loader/resources/about.md loader/resources/changelog.md +loader/resources/support.md + +# Ignore generated files +installer/mac/*.pkg +installer/windows/*.exe + +# Ignore fod's include directories which are stored in this funny file fods-catgirl-hideout.txt + +# Ignore I don't even know what that is probably fod's flash testing script test-docs.bat -# krita files too because alk is funny +# Ignore krita files too because we don't want our project files shaking my head **/*.kra - -installer/mac/*.pkg -installer/windows/*.exe \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 180b2d85..094bce59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Geode Changelog +## v1.3.5 + * Follow redirect in web::utils functions (a942a45) + * Lots of bindings + * Make codegen symbols private visibility (696a2ca) + * Add deadstrip to macos (0d62940) + * Readd the nullptr check in InstallListPopup::createCells (499f256) + * Fix garagelayer ids on not logged in users (dd0179c) + +## v1.3.4 + * Implement string setting character filters (cf8fbba) + * Update bindings + +## v1.3.3 + * Reunify resources.zip (81de161) + +## v1.3.2 + * Fix alignment of some textures (8f39c38) + * Bring back unknown problems (0663569) + * Fix some Windows 7 incompatibility (2d2bdd1) + * Remove enabled from the crashlogs (5b7d318) + * Make index unzipping async (7c582f1) + * Fix mods by developer crashing when mod was toggled (a6a47bf) + * Fix nested lists in the markdown (2723588) + * Fix search paths (8f39c38, aa55ebe) + ## v1.3.1 * Fix TulipHook not relocating RIP relative operands on MacOS (6cad19d) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99b5201f..fd0548ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,7 +185,14 @@ target_include_directories(GeodeCodegenSources PRIVATE ${GEODE_LOADER_PATH}/include/Geode/cocos/extensions ${GEODE_LOADER_PATH}/include/Geode/fmod ) +set_target_properties(GeodeCodegenSources PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_features(GeodeCodegenSources PUBLIC cxx_std_20) + +if (APPLE) + target_compile_options(GeodeCodegenSources PUBLIC -ffunction-sections -fdata-sections) + target_link_options(GeodeCodegenSources PUBLIC -dead_strip) +endif() + if (NOT GEODE_DISABLE_PRECOMPILED_HEADERS) target_precompile_headers(GeodeCodegenSources INTERFACE "${GEODE_LOADER_PATH}/include/Geode/Bindings.hpp" @@ -218,6 +225,11 @@ else() set(GEODE_PLATFORM_BIN_PATH ${GEODE_BIN_PATH}/${PROJECT_VERSION}/${GEODE_PLATFORM_BINARY}) endif() +if (WIN32) + # This allows you to compile in debug mode + add_compile_definitions(_HAS_ITERATOR_DEBUGGING=0) +endif() + if (PROJECT_IS_TOP_LEVEL) add_subdirectory(loader) diff --git a/VERSION b/VERSION index e21e727f..88c5fb89 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.0 \ No newline at end of file +1.4.0 diff --git a/bindings/Cocos2d.bro b/bindings/Cocos2d.bro index 991a6988..93ff8fc6 100644 --- a/bindings/Cocos2d.bro +++ b/bindings/Cocos2d.bro @@ -121,6 +121,11 @@ class cocos2d::CCClippingNode { // void updateConnected() = win 0xc7fb0; //} +[[link(win)]] +class cocos2d::CCConfiguration { + void gatherGPUInfo() = mac 0x2a6e10; +} + [[link(win)]] class cocos2d::CCDelayTime { static cocos2d::CCDelayTime* create(float) = mac 0x1f4380; @@ -318,7 +323,7 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo { class cocos2d::CCGLProgram { auto setUniformsForBuiltins() = mac 0x232c70; auto use() = mac 0x231d70; - bool compileShader(unsigned int* shader, unsigned int type, const char* source); + bool compileShader(unsigned int* shader, unsigned int type, const char* source) = mac 0x231a30; } [[link(win)]] @@ -360,6 +365,7 @@ class cocos2d::CCImage { class cocos2d::CCKeyboardDispatcher { bool dispatchKeyboardMSG(cocos2d::enumKeyCodes, bool) = mac 0xe8190; const char* keyToString(cocos2d::enumKeyCodes) = mac 0xe8450; + void updateModifierKeys(bool shft, bool ctrl, bool alt, bool cmd) = mac 0xe8430; } [[link(win)]] @@ -380,6 +386,7 @@ class cocos2d::CCKeypadHandler { [[link(win)]] class cocos2d::CCLabelBMFont { + CCLabelBMFont() = mac 0x347b60; static cocos2d::CCLabelBMFont* create(char const*, char const*) = mac 0x347660; auto limitLabelWidth(float, float, float) = mac 0x34a6e0, ios 0x21b740; auto setFntFile(char const*) = mac 0x34a5f0; @@ -387,7 +394,7 @@ class cocos2d::CCLabelBMFont { static auto create() = mac 0x3473f0; virtual auto init() = mac 0x347b10, ios 0x2198e0; - bool initWithString(const char* str, const char* fnt, float width, cocos2d::CCTextAlignment align, cocos2d::CCPoint offset); + bool initWithString(const char* str, const char* fnt, float width, cocos2d::CCTextAlignment align, cocos2d::CCPoint offset) = mac 0x347710; virtual auto setScaleX(float) = mac 0x34a5b0, ios 0x21b6e8; virtual auto setScaleY(float) = mac 0x34a5d0, ios 0x21b714; virtual auto setScale(float) = mac 0x34a590, ios 0x21b6bc; @@ -631,6 +638,12 @@ class cocos2d::CCNode { virtual auto cleanup() = mac 0x123100, ios 0x15e3a4; auto convertToNodeSpace(cocos2d::CCPoint const&) = mac 0x124750, ios 0x15f55c; auto convertToWorldSpace(cocos2d::CCPoint const&) = mac 0x124790; + cocos2d::CCPoint convertToNodeSpaceAR(cocos2d::CCPoint const& worldPoint) { + return convertToNodeSpace(worldPoint) - getAnchorPointInPoints(); + } + cocos2d::CCPoint convertToWorldSpaceAR(cocos2d::CCPoint const& nodePoint) { + return convertToWorldSpace(nodePoint + getAnchorPointInPoints()); + } static cocos2d::CCNode* create() = mac 0x1230a0; virtual auto draw() = mac 0x123840, ios 0x15e974; auto getActionByTag(int) = mac 0x123ee0; @@ -1129,6 +1142,7 @@ class cocos2d::CCTouchHandler { [[link(win)]] class cocos2d::CCTransitionFade { static cocos2d::CCTransitionFade* create(float, cocos2d::CCScene*) = mac 0x8ea30, ios 0x12c244; + virtual bool initWithDuration(float t, cocos2d::CCScene* scene, cocos2d::ccColor3B const& color) = mac 0x8e930; } [[link(win)]] @@ -1251,16 +1265,30 @@ class cocos2d::extension::CCScrollView { [[link(win)]] class cocos2d { static auto FNTConfigLoadFile(char const*) = mac 0x344f10; + static auto ccGLUseProgram(GLuint) = mac 0x1ae540; static auto ccGLBlendFunc(GLenum, GLenum) = mac 0x1ae560; static auto ccDrawSolidRect(cocos2d::CCPoint, cocos2d::CCPoint, cocos2d::_ccColor4F) = mac 0xecf00; static auto ccGLEnableVertexAttribs(unsigned int) = mac 0x1ae740; static auto ccGLBindTexture2D(GLuint) = mac 0x1ae610; + static auto ccGLBindTexture2DN(GLuint, GLuint) = mac 0x1ae650; static float ccpDistance(cocos2d::CCPoint const&, cocos2d::CCPoint const&) = mac 0x1aaf90; + static auto ccDrawLine(cocos2d::CCPoint const&, cocos2d::CCPoint const&) = mac 0xeccc0; static void ccDrawPoly(cocos2d::CCPoint const*, unsigned int, bool) = mac 0xed0a0; static void ccDrawColor4B(GLubyte, GLubyte, GLubyte, GLubyte) = mac 0xeddd0; static void CCMessageBox(const char* msg, const char* title) = mac 0xbabc0; } +//uintptr_t macNumberOfDraws() { +// return geode::base::get() + 0x69ae90; +//} +//void ccIncrementGLDraws(int n) { +//#ifdef GEODE_IS_MACOS +// *reinterpret_cast(macNumberOfDraws()) += n; +//#else +// CC_INCREMENT_GL_DRAWS(n); +//#endif +//} + [[link(win)]] class DS_Dictionary { DS_Dictionary() = mac 0xbe9a0; diff --git a/bindings/GeometryDash.bro b/bindings/GeometryDash.bro index f31be6d1..9a685fae 100644 --- a/bindings/GeometryDash.bro +++ b/bindings/GeometryDash.bro @@ -280,6 +280,10 @@ class CCAnimatedSprite : cocos2d::CCSprite { class CCAnimateFrameCache : cocos2d::CCObject { static CCAnimateFrameCache* sharedSpriteFrameCache() = mac 0x2e4df0, win 0x158f0; void addSpriteFramesWithFile(const char* file) = win 0x159b0; + + cocos2d::CCDictionary* m_unknown1; + cocos2d::CCDictionary* m_unknown2; + cocos2d::CCDictionary* m_unknown3; } class CCBlockLayer : cocos2d::CCLayerColor { @@ -905,6 +909,8 @@ class CommentUploadDelegate { } class ConfigureHSVWidget : cocos2d::CCNode { + bool init(int abs, cocos2d::ccHSVValue val) = win 0x4a3f0, mac 0x237310; + void updateLabels() = win 0x4adf0, mac 0x237df0; cocos2d::CCLabelBMFont* m_hueLabel; cocos2d::CCLabelBMFont* m_saturationLabel; cocos2d::CCLabelBMFont* m_brightnessLabel; @@ -945,15 +951,15 @@ class CreateMenuItem : CCMenuItemSpriteExtra { class CreatorLayer : cocos2d::CCLayer, cocos2d::CCSceneTransitionDelegate, DialogDelegate { void onBack(cocos2d::CCObject*) = win 0x4fae0; void onChallenge(cocos2d::CCObject*) = mac 0x142960, win 0x4f1b0; - void onLeaderboards(cocos2d::CCObject*) = win 0x4ed20; + void onLeaderboards(cocos2d::CCObject*) = mac 0x142920, win 0x4ed20; void onMyLevels(cocos2d::CCObject*) = mac 0x142b70, win 0x4eaa0; void onSavedLevels(cocos2d::CCObject*) = mac 0x142860, win 0x4ebe0; - void onDailyLevel(cocos2d::CCObject*) = win 0x4f170; - void onWeeklyLevel(cocos2d::CCObject*) = win 0x4f190; - void onFeaturedLevels(cocos2d::CCObject*) = win 0x4edf0; - void onFameLevels(cocos2d::CCObject*) = win 0x4ee70; - void onMapPacks(cocos2d::CCObject*) = win 0x4efb0; - void onOnlineLevels(cocos2d::CCObject*) = win 0x4ef60; + void onDailyLevel(cocos2d::CCObject*) = mac 0x142980, win 0x4f170; + void onWeeklyLevel(cocos2d::CCObject*) = mac 0x1429a0, win 0x4f190; + void onFeaturedLevels(cocos2d::CCObject*) = mac 0x142a20, win 0x4edf0; + void onFameLevels(cocos2d::CCObject*) = mac 0x142a80, win 0x4ee70; + void onMapPacks(cocos2d::CCObject*) = mac 0x1429c0, win 0x4efb0; + void onOnlineLevels(cocos2d::CCObject*) = mac 0x142ae0, win 0x4ef60; void onGauntlets(cocos2d::CCObject*) = mac 0x142b20, win 0x4f0a0; void onSecretVault(cocos2d::CCObject*) = win 0x4f1d0; void onTreasureRoom(cocos2d::CCObject*) = win 0x4f540; @@ -1026,7 +1032,7 @@ class CustomizeObjectLayer : FLAlertLayer, TextInputDelegate, HSVWidgetPopupDele void onClose(cocos2d::CCObject*) = mac 0xdf660, win 0x57ac0; void updateSelected(int channelID) = mac 0xe0970, win 0x57850; bool init(GameObject* target, cocos2d::CCArray* targets) = mac 0xdd560, win 0x53e00; - void onHSV(cocos2d::CCObject* sender) = win 0x567c0; + void onHSV(cocos2d::CCObject* sender) = win 0x567c0, mac 0xdfa00; void toggleVisible() = mac 0xe1140, win 0x56fb0; void highlightSelected(ButtonSprite* target) = mac 0xe0aa0, win 0x579d0; void updateCustomColorLabels() = mac 0xdff40, win 0x576d0; @@ -1035,8 +1041,8 @@ class CustomizeObjectLayer : FLAlertLayer, TextInputDelegate, HSVWidgetPopupDele // inlined in most places void updateChannelLabel(int channel) = mac 0xe06e0, win 0x56f50; virtual void hsvPopupClosed(HSVWidgetPopup* popup, cocos2d::ccHSVValue value) = win 0x56990; - virtual void colorSelectClosed(cocos2d::CCNode*) = mac 0xe0c70, win 0x564a0; - virtual void textChanged(CCTextInputNode* input) = mac 0xe16a0, win 0x574d0; + virtual void colorSelectClosed(cocos2d::CCNode*) = mac 0xe0610, win 0x564a0; + virtual void textChanged(CCTextInputNode* input) = mac 0xe1470, win 0x574d0; inline CustomizeObjectLayer() {} ~CustomizeObjectLayer() = win 0x53c30; @@ -1073,7 +1079,7 @@ class CustomizeObjectLayer : FLAlertLayer, TextInputDelegate, HSVWidgetPopupDele } class DailyLevelPage : FLAlertLayer, FLAlertLayerProtocol, GJDailyLevelDelegate, LevelDownloadDelegate { - static DailyLevelPage* create(bool weekly) = win 0x6a860; + static DailyLevelPage* create(bool weekly) = mac 0x108ac0, win 0x6a860; bool init(bool weekly) = mac 0x108C90, win 0x6a900; virtual void updateTimers(float) = mac 0x109780, win 0x6bef0; virtual void show() = mac 0x10a4b0, win 0x3f360; @@ -1136,7 +1142,7 @@ class DrawGridLayer : cocos2d::CCLayer { } bool init(cocos2d::CCNode* grid, LevelEditorLayer* editor) = win 0x16c4d0; - void draw() = win 0x16ce90; + virtual void draw() = win 0x16ce90, mac 0xa3c40; virtual void update(float) = win 0x16cd80; void clearPlayerPoints() { m_playerNodePoints->removeAllObjects(); @@ -1272,7 +1278,7 @@ class EditorPauseLayer : CCBlockLayer, FLAlertLayerProtocol { bool init(LevelEditorLayer*) = mac 0x13c7a0, win 0x730e0, ios 0x280cb8; void onExitEditor(cocos2d::CCObject* sender) = mac 0x13f180, win 0x75660; void playStep2() = mac 0x13f040, win 0x75440; - void onResume(cocos2d::CCObject* sender) = win 0x74fe0; + void onResume(cocos2d::CCObject* sender) = mac 0x13e170, win 0x74fe0; void onSaveAndPlay(cocos2d::CCObject* sender) = mac 0x13e1b0, win 0x753d0; void onSaveAndExit(cocos2d::CCObject* sender) = mac 0x13e230, win 0x75620; void onSave(cocos2d::CCObject* sender) = mac 0x13e290, win 0x755a0; @@ -1303,6 +1309,8 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ void create(LevelEditorLayer*) = mac 0x8a80, win 0x76270; cocos2d::CCArray* createCustomItems() = mac 0x1ddf0, win 0x7a370; + void onDeleteCustomItem(cocos2d::CCObject* pSender) = win 0x7a280, mac 0x29860; + void onNewCustomItem(cocos2d::CCObject* pSender) = win 0x79fd0, mac 0x24480; void deselectAll() = mac 0x1f300, win 0x86af0; void onDeselectAll(cocos2d::CCObject*) = mac 0x19cd0, win 0x86ac0; void disableButton(CreateMenuItem*) = mac 0x1c0f0, win 0x78af0; @@ -1352,8 +1360,8 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ void onCreateButton(cocos2d::CCObject* sender) = mac 0x1fd70, win 0x854f0; CCMenuItemSpriteExtra* getSpriteButton(const char* sprite, cocos2d::SEL_MenuHandler callback, cocos2d::CCMenu* menu, float scale) = mac 0xb500, win 0x78bf0; cocos2d::CCPoint offsetForKey(int objID) = win 0x92310; - void updateDeleteMenu() = win 0x7c5d0; - void updateCreateMenu(bool updateTab) = mac 0x1e960, win 0x85530; + void updateDeleteMenu() = win 0x7c5d0, mac 0x1e960; + void updateCreateMenu(bool updateTab) = mac 0x1ba80, win 0x85530; void toggleMode(cocos2d::CCObject* sender) = mac 0x187b0, win 0x7ad20; void zoomIn(cocos2d::CCObject* sender) = mac 0xc0c0, win 0x877c0; void zoomOut(cocos2d::CCObject* sender) = mac 0xc120, win 0x87830; @@ -1421,6 +1429,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ void sliderChanged(cocos2d::CCObject* slider) = mac 0xaed0, win 0x78cc0; void repositionObjectsToCenter(cocos2d::CCArray* objs, cocos2d::CCPoint center, bool ignoreGroupParent) = mac 0x1fcd0, win 0x88410; virtual void draw() = win 0x8fbe0; + float valueFromXPos(float val) = win 0x78e30, mac 0x1c810; bool m_isPlayingMusic; EditButtonBar* m_buttonBar; @@ -1628,9 +1637,12 @@ class EffectGameObject : GameObject { PAD = mac 0x28, win 0x24; } -class EndLevelLayer { +class EndLevelLayer : cocos2d::CCLayer { static EndLevelLayer* create() = mac 0x2787d0, win 0x94b50; + void customSetup() = win 0x94cb0; + const char* getCoinString(void* p0) = win 0x96270; + const char* getEndText() = win 0x964A0; void onMenu(cocos2d::CCObject* sender) = mac 0x27a500, win 0x96c10; void onEdit(cocos2d::CCObject* sender) = mac 0x27a640, win 0x96d30; } @@ -1755,8 +1767,8 @@ class FMODSound : cocos2d::CCNode { class FriendRequestDelegate {} class FriendsProfilePage : FLAlertLayer, FLAlertLayerProtocol, UploadActionDelegate, UploadPopupDelegate, UserListDelegate { - static FriendsProfilePage* create(UserListType) = win 0x9ce80; - bool init(UserListType) = win 0x9cf30; + static FriendsProfilePage* create(UserListType) = win 0x9ce80, mac 0x3a9570; + bool init(UserListType) = win 0x9cf30, mac 0x3a9770; } class GJAccountBackupDelegate { @@ -1785,11 +1797,16 @@ class GJAccountLoginDelegate { class GJAccountManager : cocos2d::CCNode { static GJAccountManager* sharedState() = mac 0x85070, win 0x107d50; + static GJAccountManager* get() { + return GJAccountManager::sharedState(); + } + gd::string getGJP() = mac 0x89520, win 0x10abb0; PAD = mac 0x8, win 0x4; gd::string m_password; gd::string m_username; int m_accountID; + int m_playerID; } class GJAccountSyncDelegate { @@ -2281,7 +2298,7 @@ class GJGameLevel : cocos2d::CCNode { void getLengthKey(int) = mac 0x2dbba0; void getNormalPercent() = mac 0x2b8b20; void levelWasAltered() = mac 0x2db530, win 0xbd550; - void savePercentage(int, bool, int, int, bool) = mac 0x2db700; + void savePercentage(int, bool, int, int, bool) = mac 0x2db700, win 0xbd5c0; void dataLoaded(DS_Dictionary* dict) = mac 0x2dc0e0, win 0xbded0, ios 0x6fca4; GJDifficulty getAverageDifficulty() = win 0xbd9b0; gd::string getUnpackedLevelDescription() = mac 0x2DDB50, win 0xbf890; @@ -2581,21 +2598,21 @@ class GJScaleControl : cocos2d::CCLayer { virtual void ccTouchMoved(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x31e60, win 0x94840; virtual void ccTouchEnded(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x31fb0, win 0x94940; virtual void ccTouchCancelled(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x32060, win 0x2dea0; // shared with many others - void updateLabel(float value) = win 0x94990; + void updateLabel(float value) = win 0x94990, mac 0x31c90; void loadValues(GameObject* obj, cocos2d::CCArray* objs) = win 0x94590, mac 0x24f40; Slider* m_slider; unsigned int m_touchID; float m_value; - PAD = mac 0x8, win 0x4; + bool m_shouldSnapAt1; cocos2d::CCLabelBMFont* m_label; GJScaleControlDelegate* m_delegate; } class GJScaleControlDelegate { + virtual void scaleChanged(float) {} virtual void scaleChangeBegin() {} virtual void scaleChangeEnded() {} - virtual void scaleChanged(float) {} } class GJScoreCell : TableViewCell, FLAlertLayerProtocol { @@ -3547,7 +3564,8 @@ class GameStatsManager : cocos2d::CCNode { int getBaseCurrencyForLevel(GJGameLevel*) = mac 0x43470, win 0xf8530; GJChallengeItem* getChallenge(int id) = mac 0x451f0, win 0xa2fb0; void getSecretCoinKey(char const*) = mac 0x429f0; - int getStat(char const*) = mac 0x3d310, win 0xf3580; + int getStat(char const* type) = mac 0x3d310, win 0xf3580; + void setStat(char const* type, int amount) = win 0xf3690; void hasPendingUserCoin(char const*) = mac 0x42730, win 0xf7c50; void hasSecretCoin(char const*) = mac 0x40730, win 0xf7dc0; void hasUserCoin(char const*) = mac 0x427e0, win 0xf7ae0; @@ -3559,6 +3577,7 @@ class GameStatsManager : cocos2d::CCNode { void storeSecretCoin(char const*) = mac 0x42a10; void storeUserCoin(char const*) = mac 0x42890; bool isItemUnlocked(UnlockType type, int id) = win 0xfbb80; + void checkAchievement(char const* type) = win 0xf37c0; PAD = mac 0x50, win 0x28; cocos2d::CCDictionary* m_dailyChests; @@ -3595,7 +3614,7 @@ class GameToolbox { static cocos2d::CCDictionary* stringSetupToDict(gd::string text, char const* delimeter) = mac 0x28d700, win 0x272a0; static CCMenuItemToggler* createToggleButton(gd::string text, cocos2d::SEL_MenuHandler onToggled, bool isToggled, cocos2d::CCMenu* toggleMenu, cocos2d::CCPoint position, cocos2d::CCNode* callbackTarget, cocos2d::CCNode* labelParent, cocos2d::CCArray* toggleArray) = mac 0x28bc90, win 0x25fe0; static CCMenuItemToggler* createToggleButton(gd::string text, cocos2d::SEL_MenuHandler onToggled, bool isToggled, cocos2d::CCMenu* toggleMenu, cocos2d::CCPoint position, cocos2d::CCNode* callbackTarget, cocos2d::CCNode* labelParent, float checkboxScale, float labelSize, float maxWidth, cocos2d::CCPoint labelOffset, const char* unknown, bool anchorHorizontally, int toggleTag, cocos2d::CCArray* toggleArray) = mac 0x28bdd0, win 0x25fe0; - static cocos2d::ccColor3B transformColor(cocos2d::ccColor3B const& src, cocos2d::ccHSVValue hsv) = win 0x26a60; + static cocos2d::ccColor3B transformColor(cocos2d::ccColor3B const& src, cocos2d::ccHSVValue hsv) = win 0x26a60, mac 0x28c950; static void alignItemsHorisontally(cocos2d::CCArray* array, float pad, cocos2d::CCPoint start, bool idk) = win 0x25b20; static gd::map stringSetupToMap(gd::string, char const*) = mac 0x28d4c0; static cocos2d::ccColor3B multipliedColorValue(cocos2d::ccColor3B color1, cocos2d::ccColor3B color2, float factor) = win 0x26CE0; @@ -3634,7 +3653,7 @@ class GauntletSelectLayer { class GhostTrailEffect {} class HSVWidgetPopup : FLAlertLayer { - bool init(cocos2d::_ccHSVValue value, HSVWidgetPopupDelegate* delegate, gd::string title); + bool init(cocos2d::_ccHSVValue value, HSVWidgetPopupDelegate* delegate, gd::string title) = win 0x49f10, mac 0x236d30; void onClose(cocos2d::CCObject* sender) = win 0x4a280; ConfigureHSVWidget* m_configureWidget; @@ -3657,7 +3676,7 @@ class HardStreak : cocos2d::CCDrawNode { void reset() = mac 0x5c930; void resumeStroke() = mac 0x5c210; void stopStroke() = mac 0x5c8f0, win 0x14e460; - void updateStroke(float) = mac 0x5c240, win 0x14e530; + callback void updateStroke(float) = mac 0x5c240, win 0x14e530; cocos2d::CCArray* m_pointsArr; cocos2d::CCPoint m_currentPoint; @@ -3753,6 +3772,7 @@ class LevelBrowserLayer : cocos2d::CCLayer, LevelManagerDelegate, FLAlertLayerPr void updateLevelsLabel() = mac 0x255450, win 0x15c350; void onRefresh(cocos2d::CCObject* sender) = mac 0x253090; void onInfo(cocos2d::CCObject* sender) = mac 0x253170, win 0x15cb00; + void onNew(cocos2d::CCObject* sender) = win 0x15cbf0, mac 0x252ac0; static LevelBrowserLayer* create(GJSearchObject* search) = mac 0x251210, win 0x159fa0, ios 0x2d0a00; PAD = win 0x4, mac 0x8; @@ -4012,16 +4032,17 @@ class LevelEditorLayer : GJBaseGameLayer, LevelSettingsDelegate { class LevelInfoLayer : cocos2d::CCLayer, LevelDownloadDelegate, LevelUpdateDelegate, RateLevelDelegate, LikeItemDelegate, FLAlertLayerProtocol, LevelDeleteDelegate, NumberInputDelegate, SetIDPopupDelegate { static LevelInfoLayer* create(GJGameLevel* level) = mac 0x15f290, win 0x175d50; bool init(GJGameLevel* level) = win 0x175df0, mac 0x15f520; - void onGarage(cocos2d::CCObject* sender) = win 0x177c10; + void onGarage(cocos2d::CCObject* sender) = win 0x177c10, mac 0x163ac0; void onViewProfile(cocos2d::CCObject* sender) = mac 0x1617d0, win 0x17ac90; void onLevelInfo(cocos2d::CCObject* sender) = mac 0x163880, win 0x17acf0; - void setupProgressBars() = win 0x177fc0; + void setupProgressBars() = win 0x177fc0, mac 0x1627e0; void setupLevelInfo() = mac 0x161C80, win 0x178680; - void downloadLevel() = win 0x177d90; + void downloadLevel() = win 0x177d90, mac 0x161b90; void onPlay(cocos2d::CCObject* sender) = mac 0x161840, win 0x179730; virtual void levelDownloadFinished(GJGameLevel*) = mac 0x164C00, win 0x1790C0; virtual void levelUpdateFinished(GJGameLevel*, UpdateResponse) = mac 0x164E60, win 0x1792B0; void showUpdateAlert(UpdateResponse) = mac 0x164ED0, win 0x179300; + void updateLabelValues() = mac 0x164090, win 0x17b170; PAD = win 0x4, mac 0x8; cocos2d::CCMenu* m_playBtnMenu; @@ -4031,13 +4052,13 @@ class LevelInfoLayer : cocos2d::CCLayer, LevelDownloadDelegate, LevelUpdateDeleg CCMenuItemSpriteExtra* m_starRateBtn; CCMenuItemSpriteExtra* m_demonRateBtn; PAD = win 0x4, mac 0x8; - CCMenuItemToggler* m_toggler; - cocos2d::CCLabelBMFont* m_label0; - cocos2d::CCLabelBMFont* m_label1; - cocos2d::CCLabelBMFont* m_label2; - cocos2d::CCLabelBMFont* m_label3; - cocos2d::CCLabelBMFont* m_label4; - cocos2d::CCLabelBMFont* m_label5; + CCMenuItemToggler* m_ldmToggler; + cocos2d::CCLabelBMFont* m_ldmLabel; + cocos2d::CCLabelBMFont* m_lengthLabel; + cocos2d::CCLabelBMFont* m_downloadsLabel; + cocos2d::CCLabelBMFont* m_likesLabel; + cocos2d::CCLabelBMFont* m_orbsLabel; + cocos2d::CCLabelBMFont* m_folderLabel; CCMenuItemSpriteExtra* m_cloneBtn; PAD = win 0x4, mac 0x8; } @@ -4089,6 +4110,15 @@ class LevelSettingsDelegate { virtual void levelSettingsUpdated() {} } +class SecretLayer2 : cocos2d::CCLayer, TextInputDelegate, FLAlertLayerProtocol, DialogDelegate { + static SecretLayer2* create() = win 0x21FD70; + + bool init() = win 0x21FE10, mac 0x25fe70; + bool onSubmit(cocos2d::CCObject*) = win 0x221ac0, mac 0x2611a0; + void updateSearchLabel(const char* text) = win 0x222FC0, mac 0x260e10; + void showCompletedLevel() = win 0x220C10; +} + class SecretLayer4 : cocos2d::CCLayer, TextInputDelegate, FLAlertLayerProtocol, DialogDelegate { static SecretLayer4* create() = mac 0x1ed500; static cocos2d::CCScene* scene() = mac 0x1ed4c0; @@ -4258,7 +4288,6 @@ class LocalLevelManager : GManager { cocos2d::CCDictionary* getAllLevelsInDict() = mac 0x35e3d0, win 0x18d7c0; - PAD = mac 0x4, win 0x1C; cocos2d::CCDictionary* m_loadData; cocos2d::CCDictionary* m_levelData; cocos2d::CCArray* m_localLevels; @@ -4320,7 +4349,7 @@ class MoreOptionsLayer : FLAlertLayer, TextInputDelegate, GooglePlayDelegate { static MoreOptionsLayer* create() = win 0x1de850; virtual bool init() = mac 0x43f470, win 0x1DE8F0; void addToggle(const char* name, const char* key, const char* info) = mac 0x440430, win 0x1df6b0; - void onKeybindings(cocos2d::CCObject* sender) = win 0x749d0; + void onKeybindings(cocos2d::CCObject* sender) = mac 0x4410e0, win 0x749d0; void onToggle(cocos2d::CCObject* sender) = mac 0x441370; } @@ -4468,11 +4497,14 @@ class PauseLayer : CCBlockLayer { void createToggleButton(gd::string caption, cocos2d::SEL_MenuHandler callback, bool on, cocos2d::CCMenu* menu, cocos2d::CCPoint pos) = mac 0x20c890, win 0x1e5570; virtual void customSetup() = mac 0x20b300, win 0x1e4620; - void onRestart(cocos2d::CCObject* sender) = win 0x1e6040; + void onRestart(cocos2d::CCObject* sender) = mac 0x20c860, win 0x1e6040; + void onPracticeMode(cocos2d::CCObject* sender) = mac 0x20c6d0, win 0x1e5f30; + void onNormalMode(cocos2d::CCObject* sender) = mac 0x20c720, win 0x1e5f60; + void onResume(cocos2d::CCObject* sender) = mac 0x20c760, win 0x1e5fa0; virtual void keyDown(cocos2d::enumKeyCodes) = mac 0x20cc80, win 0x1E6580; - void musicSliderChanged(cocos2d::CCObject* sender) = win 0x1e5ce0; - void sfxSliderChanged(cocos2d::CCObject* sender) = win 0x1ddfa0; + void musicSliderChanged(cocos2d::CCObject* sender) = win 0x1e5ce0, mac 0x20cb00; + void sfxSliderChanged(cocos2d::CCObject* sender) = win 0x1ddfa0, mac 0x20cb40; bool m_unknown; bool m_unknown2; @@ -4625,7 +4657,7 @@ class PlayLayer : GJBaseGameLayer, CCCircleWaveDelegate, CurrencyRewardDelegate, void spawnFirework() = mac 0x74200; void spawnParticle(char const*, int, cocos2d::tCCPositionType, cocos2d::CCPoint) = mac 0x76330; void spawnPlayer2() = mac 0x7d170, win 0x2089e0; - void startGame() = mac 0x726b0; + void startGame() = mac 0x726b0, win 0x1fd390; void startMusic() = mac 0x72910, win 0x20C8F0; void startRecording() = mac 0x7fec0; void startRecordingDelayed() = mac 0x7fed0; @@ -4641,7 +4673,7 @@ class PlayLayer : GJBaseGameLayer, CCCircleWaveDelegate, CurrencyRewardDelegate, void toggleBGEffectVisibility(bool) = mac 0x7fe80; void toggleDualMode(GameObject*, bool, PlayerObject*, bool) = mac 0x7bf90, win 0x208880; void toggleFlipped(bool, bool) = mac 0x7bdc0, win 0x20ab20; - void toggleGhostEffect(int) = mac 0x7fe40; + void toggleGhostEffect(int) = mac 0x7fe40, win 0x1f8930; void toggleGlitter(bool) = mac 0x70e00, win 0x20a0d0; void togglePracticeMode(bool) = mac 0x7f9e0, win 0x20d0d0; void toggleProgressbar() = mac 0x6eeb0, win 0x208160; @@ -5240,6 +5272,9 @@ class SetGroupIDLayer : FLAlertLayer, TextInputDelegate { void updateZOrder() = win 0x22e3d0; void onAddGroup(cocos2d::CCObject* sender) = mac 0x1967d0, win 0x22de20; void onClose(cocos2d::CCObject* sender) = mac 0x1966a0, win 0x22e830; + void onEditorLayer(cocos2d::CCObject* sender) = win 0x22d690, mac 0x196800; + void onEditorLayer2(cocos2d::CCObject* sender) = win 0x22d710, mac 0x196a40; + void onZOrder(cocos2d::CCObject* sender) = win 0x22de80, mac 0x196920; GameObject* m_targetObject; cocos2d::CCArray* m_targetObjects; @@ -5350,12 +5385,14 @@ class SetupOpacityPopup : FLAlertLayer { class SetupPickupTriggerPopup : FLAlertLayer { static SetupPickupTriggerPopup* create(EffectGameObject*, cocos2d::CCArray*) = mac 0x35e70, win 0x23d4a0; + bool init(EffectGameObject* obj, cocos2d::CCArray* arr) = win 0x23d550, mac 0x36070; void onItemIDArrow(cocos2d::CCObject*) = mac 0x37100; void onNextItemID(cocos2d::CCObject*) = mac 0x37260; void textChanged(CCTextInputNode*) = mac 0x37ca0; void updateItemID() = mac 0x37ab0, win 0x23e4f0; - PAD = win 0xc; + PAD = win 0xc, mac 0x18; + CCTextInputNode* m_itemIDInput; CCTextInputNode* m_countInput; } @@ -5432,9 +5469,9 @@ class SimplePlayer : cocos2d::CCSprite { static SimplePlayer* create(int iconID) = mac 0x1b6140, win 0x12bd80; void updatePlayerFrame(int iconID, IconType iconType) = mac 0x1b62f0, win 0x12c650; void updateColors() = mac 0x1ba1f0, win 0x12c440, ios 0x224f2c; - void setFrames(const char* firstLayer, const char* secondLayer, const char* birdDome, const char* outlineSprite, const char* detailSprite) = win 0x12c9e0; + void setFrames(const char* firstLayer, const char* secondLayer, const char* birdDome, const char* outlineSprite, const char* detailSprite) = mac 0x1bca10, win 0x12c9e0; virtual void setColor(const cocos2d::ccColor3B& color) = mac 0x1bc9b0, win 0x12c410; - virtual void setOpacity(unsigned char opacity) = win 0x12cb90; + virtual void setOpacity(unsigned char opacity) = mac 0x135370, win 0x12cb90; cocos2d::CCSprite* m_firstLayer; cocos2d::CCSprite* m_secondLayer; @@ -5465,7 +5502,7 @@ class Slider : cocos2d::CCLayer { SliderTouchLogic* m_touchLogic; cocos2d::CCSprite* m_sliderBar; cocos2d::CCSprite* m_groove; - float m_unknown; + float m_width; float m_height; } @@ -5542,6 +5579,8 @@ class SpeedObject : cocos2d::CCNode { float m_somethingToCompare; float m_idk3; float m_idk4; + + static SpeedObject* create(GameObject*, int, float) = win 0x20DE70; } class SpritePartDelegate {} @@ -5741,7 +5780,7 @@ class UILayer : cocos2d::CCLayerColor { virtual void keyDown(cocos2d::enumKeyCodes key) = mac 0x280470, win 0x25f890; virtual void keyUp(cocos2d::enumKeyCodes key) = mac 0x280600, win 0x25fa10; UILayer() = win 0x25f230; - ~UILayer() = win 0x25fef0; + ~UILayer() = win 0x25fef0, mac 0x280c90; PAD = mac 0x16, win 0x8, android 0x8; cocos2d::CCMenu* m_checkPointMenu; diff --git a/cmake/GeodeFile.cmake b/cmake/GeodeFile.cmake index 4c7efaff..f44b7d26 100644 --- a/cmake/GeodeFile.cmake +++ b/cmake/GeodeFile.cmake @@ -307,6 +307,8 @@ function(package_geode_resources_now proname src dest header_dest) if (NOT FILE_NAME STREQUAL ".geode_cache" AND NOT FILE_SHOULD_HASH EQUAL -1) file(SHA256 ${file} COMPUTED_HASH) + file(SIZE ${file} FILE_SIZE) + message(STATUS "Hashed ${file} to ${COMPUTED_HASH} (${FILE_SIZE} bytes)") list(APPEND HEADER_FILE "\t{ \"${FILE_NAME}\", \"${COMPUTED_HASH}\" },\n") # list(APPEND HEADER_FILE "\t\"${FILE_NAME}\",\n") diff --git a/codegen/src/BindingGen.cpp b/codegen/src/BindingGen.cpp index eb4359c3..199e2555 100644 --- a/codegen/src/BindingGen.cpp +++ b/codegen/src/BindingGen.cpp @@ -185,7 +185,8 @@ std::string generateBindingHeader(Root const& root, ghc::filesystem::path const& single_output += fmt::format(::format_strings::class_start, fmt::arg("class_name", cls.name), - fmt::arg("base_classes", supers) + fmt::arg("base_classes", supers)//, + // fmt::arg("hidden", str_if("GEODE_HIDDEN ", (codegen::platform & (Platform::Mac | Platform::iOS)) != Platform::None)) ); // what. diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index 4258f77a..422bf8d5 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -27,10 +27,13 @@ execute_process( ) # Package info file for internal representation +set(GEODE_RESOURCES_PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources) configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json) file(READ resources/mod.json LOADER_MOD_JSON) -configure_file(${GEODE_ROOT_PATH}/VERSION ${CMAKE_CURRENT_SOURCE_DIR}/resources/version COPYONLY) -configure_file(${GEODE_ROOT_PATH}/CHANGELOG.md ${CMAKE_CURRENT_SOURCE_DIR}/resources/changelog.md COPYONLY) +configure_file(${GEODE_ROOT_PATH}/VERSION ${GEODE_RESOURCES_PATH}/version COPYONLY) +configure_file(${GEODE_RESOURCES_PATH}/about.md.in ${GEODE_RESOURCES_PATH}/about.md NEWLINE_STYLE LF) +configure_file(${GEODE_ROOT_PATH}/CHANGELOG.md ${GEODE_RESOURCES_PATH}/changelog.md NEWLINE_STYLE LF) +configure_file(${GEODE_RESOURCES_PATH}/support.md.in ${GEODE_RESOURCES_PATH}/support.md NEWLINE_STYLE LF) configure_file(src/internal/about.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/internal/about.hpp) # Source files diff --git a/loader/include/Geode/cocos/base_nodes/CCNode.h b/loader/include/Geode/cocos/base_nodes/CCNode.h index 51817e3c..944001e0 100644 --- a/loader/include/Geode/cocos/base_nodes/CCNode.h +++ b/loader/include/Geode/cocos/base_nodes/CCNode.h @@ -700,12 +700,6 @@ public: * @param cleanup true if all running actions and callbacks on the child node will be cleanup, false otherwise. */ virtual void removeChildByTag(int tag, bool cleanup); - /** - * Removes a child from the container by its ID. - * @param id The ID of the node - * @note Geode addition - */ - void removeChildByID(std::string const& id); /** * Removes all children from the container with a cleanup. * @@ -890,6 +884,13 @@ public: */ GEODE_DLL CCNode* getChildByIDRecursive(std::string const& id); + /** + * Removes a child from the container by its ID. + * @param id The ID of the node + * @note Geode addition + */ + GEODE_DLL void removeChildByID(std::string const& id); + /** * Add a child before a specified existing child * @param child The node to add. The node may not be a child of another diff --git a/loader/include/Geode/cocos/layers_scenes_transitions_nodes/CCTransition.h b/loader/include/Geode/cocos/layers_scenes_transitions_nodes/CCTransition.h index 8ebcda4e..d55ec10d 100644 --- a/loader/include/Geode/cocos/layers_scenes_transitions_nodes/CCTransition.h +++ b/loader/include/Geode/cocos/layers_scenes_transitions_nodes/CCTransition.h @@ -90,6 +90,7 @@ protected: bool m_bIsSendCleanupToScene; public: + GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCTransitionScene, CCScene) /** * @js ctor */ @@ -147,6 +148,7 @@ public: * @js ctor */ CCTransitionSceneOriented(); + GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCTransitionSceneOriented, CCTransitionScene) /** * @js NA * @lua NA @@ -171,6 +173,7 @@ public: * @js ctor */ CCTransitionRotoZoom(); + GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCTransitionRotoZoom, CCTransitionScene) /** * @js NA * @lua NA @@ -623,6 +626,7 @@ public: * @js ctor */ CCTransitionFade(); + GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCTransitionFade, CCTransitionScene) /** * @js NA * @lua NA diff --git a/loader/include/Geode/cocos/platform/android/CCPlatformDefine.h b/loader/include/Geode/cocos/platform/android/CCPlatformDefine.h index 93fe1c46..3f9a88d1 100644 --- a/loader/include/Geode/cocos/platform/android/CCPlatformDefine.h +++ b/loader/include/Geode/cocos/platform/android/CCPlatformDefine.h @@ -3,12 +3,8 @@ #include -#ifdef GEODE_EXPORTING - #define CC_DLL __attribute__((visibility("default"))) -#else - #define CC_DLL -#endif -#define ACTUAL_CC_DLL CC_DLL +#define CC_DLL +#define ACTUAL_CC_DLL #define CC_NO_MESSAGE_PSEUDOASSERT(cond) \ if (!(cond)) { \ diff --git a/loader/include/Geode/cocos/platform/ios/CCPlatformDefine.h b/loader/include/Geode/cocos/platform/ios/CCPlatformDefine.h index 74bc5d07..df4e4242 100644 --- a/loader/include/Geode/cocos/platform/ios/CCPlatformDefine.h +++ b/loader/include/Geode/cocos/platform/ios/CCPlatformDefine.h @@ -3,12 +3,8 @@ #include -#ifdef GEODE_EXPORTING - #define CC_DLL __attribute__((visibility("default"))) -#else - #define CC_DLL -#endif -#define ACTUAL_CC_DLL CC_DLL +#define CC_DLL //__attribute__((visibility("hidden"))) +#define ACTUAL_CC_DLL #define CC_ASSERT(cond) assert(cond) diff --git a/loader/include/Geode/cocos/platform/mac/CCPlatformDefine.h b/loader/include/Geode/cocos/platform/mac/CCPlatformDefine.h index e542eecb..8ccb4edd 100644 --- a/loader/include/Geode/cocos/platform/mac/CCPlatformDefine.h +++ b/loader/include/Geode/cocos/platform/mac/CCPlatformDefine.h @@ -3,12 +3,8 @@ #include -#ifdef GEODE_EXPORTING - #define CC_DLL __attribute__((visibility("default"))) -#else - #define CC_DLL -#endif -#define ACTUAL_CC_DLL CC_DLL +#define CC_DLL //__attribute__((visibility("hidden"))) +#define ACTUAL_CC_DLL #if CC_DISABLE_ASSERT > 0 diff --git a/loader/include/Geode/fmod/fmod.h b/loader/include/Geode/fmod/fmod.h index c12bc9e4..43f26439 100644 --- a/loader/include/Geode/fmod/fmod.h +++ b/loader/include/Geode/fmod/fmod.h @@ -31,7 +31,7 @@ FMOD_RESULT F_API FMOD_File_GetDiskBusy (int *busy); /* FMOD System factory functions. Use this to create an FMOD System Instance. below you will see FMOD_System_Init/Close to get started. */ -FMOD_RESULT F_API FMOD_System_Create (FMOD_SYSTEM **system); +FMOD_RESULT /*F_API*/ FMOD_System_Create (FMOD_SYSTEM **system); FMOD_RESULT F_API FMOD_System_Release (FMOD_SYSTEM *system); /* diff --git a/loader/include/Geode/loader/Mod.hpp b/loader/include/Geode/loader/Mod.hpp index bede828d..01f9380a 100644 --- a/loader/include/Geode/loader/Mod.hpp +++ b/loader/include/Geode/loader/Mod.hpp @@ -406,10 +406,12 @@ namespace geode { bool isLoggingEnabled() const; void setLoggingEnabled(bool enabled); + bool shouldLoad() const; + friend class ModImpl; }; } -inline char const* operator"" _spr(char const* str, size_t) { +GEODE_HIDDEN inline char const* operator"" _spr(char const* str, size_t) { return geode::Mod::get()->expandSpriteName(str); } diff --git a/loader/include/Geode/loader/Setting.hpp b/loader/include/Geode/loader/Setting.hpp index fca764c9..5425687a 100644 --- a/loader/include/Geode/loader/Setting.hpp +++ b/loader/include/Geode/loader/Setting.hpp @@ -96,6 +96,11 @@ namespace geode { */ std::optional match; + /** + * The CCTextInputNode's allowed character filter + */ + std::optional filter; + static Result parse(JsonMaybeObject& obj); }; diff --git a/loader/include/Geode/modify/Addresses.hpp b/loader/include/Geode/modify/Addresses.hpp index 4f0d9518..bea14fdf 100644 --- a/loader/include/Geode/modify/Addresses.hpp +++ b/loader/include/Geode/modify/Addresses.hpp @@ -5,7 +5,7 @@ namespace geode::modifier { template - uintptr_t address(); + GEODE_HIDDEN uintptr_t address(); Result handlerMetadataForAddress(uintptr_t address); } diff --git a/loader/include/Geode/ui/TextArea.hpp b/loader/include/Geode/ui/TextArea.hpp new file mode 100644 index 00000000..c923d4e4 --- /dev/null +++ b/loader/include/Geode/ui/TextArea.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +namespace geode { + enum WrappingMode { + NO_WRAP, + WORD_WRAP, + CUTOFF_WRAP + }; + + /** + * A class which provides a textarea with proper alignment and some extra features like: + * + * - Max lines + * - Changing all aspects after creation + * - Custom text alignment + * - Configurable and automatic word wrapping + * - Line padding + * + * Contact me on Discord (\@smjs) if you have any questions, suggestions or bugs. + */ + class GEODE_DLL SimpleTextArea : public cocos2d::CCNode { + static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", const float scale = 1); + static SimpleTextArea* create(const std::string& text, const std::string& font, const float scale, const float width); + + cocos2d::CCMenu* m_container; + std::string m_font; + std::string m_text; + std::vector m_lines; + cocos2d::ccColor4B m_color; + cocos2d::CCTextAlignment m_alignment; + WrappingMode m_wrappingMode; + size_t m_maxLines; + float m_scale; + float m_lineHeight; + float m_linePadding; + + void setFont(const std::string& font); + std::string getFont(); + void setColor(const cocos2d::ccColor4B& color); + cocos2d::ccColor4B getColor(); + void setAlignment(const cocos2d::CCTextAlignment alignment); + cocos2d::CCTextAlignment getAlignment(); + void setWrappingMode(const WrappingMode mode); + WrappingMode getWrappingMode(); + void setText(const std::string& text); + std::string getText(); + void setMaxLines(const size_t maxLines); + size_t getMaxLines(); + void setWidth(const float width); + float getWidth(); + void setScale(const float scale); + float getScale(); + void setLinePadding(const float padding); + float getLinePadding(); + std::vector getLines(); + float getHeight(); + float getLineHeight(); + private: + static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth); + + bool m_shouldUpdate; + bool m_artificialWidth; + + SimpleTextArea(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth); + cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top); + float calculateOffset(cocos2d::CCLabelBMFont* label); + void charIteration(const std::function& overflowHandling); + void updateLinesNoWrap(); + void updateLinesWordWrap(); + void updateLinesCutoffWrap(); + void updateContainer(); + virtual void draw() override; + }; +} \ No newline at end of file diff --git a/loader/include/Geode/utils/cocos.hpp b/loader/include/Geode/utils/cocos.hpp index d8a66905..edc4fd2e 100644 --- a/loader/include/Geode/utils/cocos.hpp +++ b/loader/include/Geode/utils/cocos.hpp @@ -953,7 +953,7 @@ namespace geode::cocos { return m_arr ? m_arr->count() : 0; } - T operator[](size_t index) { + T* operator[](size_t index) { return static_cast(m_arr->objectAtIndex(index)); } diff --git a/loader/resources/about.md b/loader/resources/about.md.in similarity index 100% rename from loader/resources/about.md rename to loader/resources/about.md.in diff --git a/loader/resources/support.md b/loader/resources/support.md.in similarity index 100% rename from loader/resources/support.md rename to loader/resources/support.md.in diff --git a/loader/src/hooks/LoadingLayer.cpp b/loader/src/hooks/LoadingLayer.cpp index b3a24bd0..93e82f07 100644 --- a/loader/src/hooks/LoadingLayer.cpp +++ b/loader/src/hooks/LoadingLayer.cpp @@ -8,68 +8,77 @@ using namespace geode::prelude; struct CustomLoadingLayer : Modify { - CCLabelBMFont* m_loadedModsLabel; - bool m_updatingResources; - - CustomLoadingLayer() : m_loadedModsLabel(nullptr), m_updatingResources(false) {} + CCLabelBMFont* m_smallLabel = nullptr; + int m_geodeLoadStep = 0; void updateLoadedModsLabel() { auto allMods = Loader::get()->getAllMods(); auto count = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) { return item->isEnabled(); }); - auto str = fmt::format("Geode: Loaded {}/{} mods", count, allMods.size()); - m_fields->m_loadedModsLabel->setCString(str.c_str()); + auto totalCount = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) { + return item->shouldLoad(); + }); + auto str = fmt::format("Geode: Loaded {}/{} mods", count, totalCount); + this->setSmallText(str); } + void setSmallText(std::string const& text) { + m_fields->m_smallLabel->setString(text.c_str()); + } + + // hook bool init(bool fromReload) { CCFileUtils::get()->updatePaths(); if (!LoadingLayer::init(fromReload)) return false; - if (fromReload) return true; - auto winSize = CCDirector::sharedDirector()->getWinSize(); - m_fields->m_loadedModsLabel = CCLabelBMFont::create("Geode: Loaded 0/0 mods", "goldFont.fnt"); - m_fields->m_loadedModsLabel->setPosition(winSize.width / 2, 30.f); - m_fields->m_loadedModsLabel->setScale(.45f); - m_fields->m_loadedModsLabel->setID("geode-loaded-info"); - this->addChild(m_fields->m_loadedModsLabel); - this->updateLoadedModsLabel(); - - // fields have unpredictable destructors - this->addChild(EventListenerNode::create( - this, &CustomLoadingLayer::updateResourcesProgress - )); - - // verify loader resources - if (!LoaderImpl::get()->verifyLoaderResources()) { - m_fields->m_updatingResources = true; - this->setUpdateText("Downloading Resources"); - } - else { - LoaderImpl::get()->updateSpecialFiles(); - } + m_fields->m_smallLabel = CCLabelBMFont::create("", "goldFont.fnt"); + m_fields->m_smallLabel->setPosition(winSize.width / 2, 30.f); + m_fields->m_smallLabel->setScale(.45f); + m_fields->m_smallLabel->setID("geode-small-label"); + this->addChild(m_fields->m_smallLabel); return true; } - void setUpdateText(std::string const& text) { - m_textArea->setString(text.c_str()); + void setupLoadingMods() { + if (Loader::get()->getLoadingState() != Loader::LoadingState::Done) { + this->updateLoadedModsLabel(); + this->waitLoadAssets(); + } + else { + this->continueLoadAssets(); + } + } + + void setupLoaderResources() { + // verify loader resources + if (!LoaderImpl::get()->verifyLoaderResources()) { + this->setSmallText("Downloading Loader Resources"); + this->addChild(EventListenerNode::create( + this, &CustomLoadingLayer::updateResourcesProgress + )); + } + else { + this->setSmallText("Loading Loader Resources"); + LoaderImpl::get()->updateSpecialFiles(); + this->continueLoadAssets(); + } } void updateResourcesProgress(ResourceDownloadEvent* event) { std::visit(makeVisitor { [&](UpdateProgress const& progress) { - this->setUpdateText(fmt::format( - "Downloading Resources: {}%", progress.first + this->setSmallText(fmt::format( + "Downloading Loader Resources: {}%", progress.first )); }, [&](UpdateFinished) { - this->setUpdateText("Resources Downloaded"); - m_fields->m_updatingResources = false; - this->loadAssets(); + this->setSmallText("Downloaded Loader Resources"); + this->continueLoadAssets(); }, [&](UpdateFailed const& error) { LoaderImpl::get()->platformMessageBox( @@ -81,24 +90,70 @@ struct CustomLoadingLayer : Modify { "The game will be loaded as normal, but please be aware " "that it is very likely to crash. " ); - this->setUpdateText("Resource Download Failed"); - m_fields->m_updatingResources = false; - this->loadAssets(); + this->setSmallText("Failed Loader Resources"); + this->continueLoadAssets(); } }, event->status); } - void loadAssets() { - if (Loader::get()->getLoadingState() != Loader::LoadingState::Done) { - this->updateLoadedModsLabel(); - Loader::get()->queueInMainThread([this]() { - this->loadAssets(); - }); - return; + void setupModResources() { + log::debug("Loading mod resources"); + this->setSmallText("Loading mod resources"); + Loader::get()->updateResources(true); + this->continueLoadAssets(); + } + + int getCurrentStep() { + return m_fields->m_geodeLoadStep + m_loadStep + 1; + } + + int getTotalStep() { + return 18; + } + + void updateLoadingBar() { + auto length = m_sliderGrooveXPos * this->getCurrentStep() / this->getTotalStep(); + m_sliderBar->setTextureRect({0, 0, length, m_sliderGrooveHeight}); + } + + void waitLoadAssets() { + Loader::get()->queueInMainThread([this]() { + this->loadAssets(); + }); + } + + void continueLoadAssets() { + ++m_fields->m_geodeLoadStep; + Loader::get()->queueInMainThread([this]() { + this->loadAssets(); + }); + } + + bool skipOnRefresh() { + if (m_fromRefresh) { + this->continueLoadAssets(); } - if (m_fields->m_updatingResources) { - return; + return !m_fromRefresh; + } + + // hook + void loadAssets() { + switch (m_fields->m_geodeLoadStep) { + case 0: + if (this->skipOnRefresh()) this->setupLoadingMods(); + break; + case 1: + if (this->skipOnRefresh()) this->setupLoaderResources(); + break; + case 2: + this->setupModResources(); + break; + case 3: + default: + this->setSmallText("Loading game resources"); + LoadingLayer::loadAssets(); + break; } - LoadingLayer::loadAssets(); + this->updateLoadingBar(); } }; diff --git a/loader/src/hooks/updateResources.cpp b/loader/src/hooks/updateResources.cpp deleted file mode 100644 index 85817635..00000000 --- a/loader/src/hooks/updateResources.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -using namespace geode::prelude; - -struct ResourcesUpdate : Modify { - void loadAssets() { - LoadingLayer::loadAssets(); - // this is in case the user refreshes texture quality at runtime - if (m_loadStep == 10) { - Loader::get()->updateResources(true); - } - } -}; diff --git a/loader/src/ids/GJGarageLayer.cpp b/loader/src/ids/GJGarageLayer.cpp index 57d644c5..b4ea8425 100644 --- a/loader/src/ids/GJGarageLayer.cpp +++ b/loader/src/ids/GJGarageLayer.cpp @@ -7,8 +7,11 @@ using namespace geode::prelude; $register_ids(GJGarageLayer) { + // the lock does not exist for not logged in users + auto loggedInOffset = GJAccountManager::get()->m_accountID == GJAccountManager::get()->m_playerID ? -1 : 0; + setIDSafe(this, 2, "username-label"); - setIDSafe(this, 6, "player-icon"); + setIDSafe(this, 6 + loggedInOffset, "player-icon"); auto winSize = CCDirector::get()->getWinSize(); @@ -39,7 +42,7 @@ $register_ids(GJGarageLayer) { setIDs( this, - 10, + 10 + loggedInOffset, "cube-selection-menu", "ship-selection-menu", "ball-selection-menu", diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp index 197676f5..26ccc097 100644 --- a/loader/src/loader/LoaderImpl.cpp +++ b/loader/src/loader/LoaderImpl.cpp @@ -408,7 +408,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { return; } - if (Mod::get()->getSavedValue("should-load-" + node->getID(), true)) { + if (node->shouldLoad()) { log::debug("Load"); auto res = node->m_impl->loadBinary(); if (!res) { @@ -432,6 +432,10 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { void Loader::Impl::findProblems() { for (auto const& [id, mod] : m_mods) { + if (!mod->shouldLoad()) { + log::debug("{} is not enabled", id); + continue; + } log::debug(id); log::pushNest(); @@ -492,7 +496,7 @@ void Loader::Impl::findProblems() { Mod* myEpicMod = mod; // clang fix // if the mod is not loaded but there are no problems related to it if (!mod->isEnabled() && - Mod::get()->getSavedValue("should-load-" + mod->getID(), true) && + mod->shouldLoad() && !std::any_of(m_problems.begin(), m_problems.end(), [myEpicMod](auto& item) { return std::holds_alternative(item.cause) && std::get(item.cause).getID() == myEpicMod->getID() || @@ -777,7 +781,7 @@ void Loader::Impl::downloadLoaderResources(bool useLatestRelease) { .json() .then([this](json::Value const& json) { this->tryDownloadLoaderResources(fmt::format( - "https://github.com/geode-sdk/geode/releases/download/{}/resources-" GEODE_PLATFORM_SHORT_IDENTIFIER ".zip", + "https://github.com/geode-sdk/geode/releases/download/{}/resources.zip", this->getVersion().toString() ), true); }) @@ -805,7 +809,7 @@ void Loader::Impl::downloadLoaderResources(bool useLatestRelease) { // find release asset for (auto asset : root.needs("assets").iterate()) { auto obj = asset.obj(); - if (obj.needs("name").template get() == "resources-" GEODE_PLATFORM_SHORT_IDENTIFIER ".zip") { + if (obj.needs("name").template get() == "resources.zip") { this->tryDownloadLoaderResources( obj.needs("browser_download_url").template get(), false diff --git a/loader/src/loader/LoaderImpl.hpp b/loader/src/loader/LoaderImpl.hpp index 24920413..ab6f4b1a 100644 --- a/loader/src/loader/LoaderImpl.hpp +++ b/loader/src/loader/LoaderImpl.hpp @@ -13,7 +13,6 @@ #include #include #include "ModImpl.hpp" -#include #include #include #include diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index 77b551a4..0408ef10 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -245,3 +245,7 @@ void Mod::setLoggingEnabled(bool enabled) { bool Mod::hasSavedValue(std::string const& key) { return this->getSaveContainer().contains(key); } + +bool Mod::shouldLoad() const { + return m_impl->shouldLoad(); +} \ No newline at end of file diff --git a/loader/src/loader/ModImpl.cpp b/loader/src/loader/ModImpl.cpp index 510693d1..bc21fc9f 100644 --- a/loader/src/loader/ModImpl.cpp +++ b/loader/src/loader/ModImpl.cpp @@ -43,7 +43,9 @@ Result<> Mod::Impl::setup() { log::warn("Unable to load data for \"{}\": {}", m_metadata.getID(), loadRes.unwrapErr()); } if (!m_resourcesLoaded) { - LoaderImpl::get()->updateModResources(m_self); + auto searchPathRoot = dirs::getModRuntimeDir() / m_metadata.getID() / "resources"; + CCFileUtils::get()->addSearchPath(searchPathRoot.string().c_str()); + m_resourcesLoaded = true; } @@ -590,14 +592,13 @@ ghc::filesystem::path Mod::Impl::getConfigDir(bool create) const { } char const* Mod::Impl::expandSpriteName(char const* name) { - static std::unordered_map expanded = {}; - if (expanded.count(name)) return expanded[name]; + if (m_expandedSprites.count(name)) return m_expandedSprites[name]; auto exp = new char[strlen(name) + 2 + m_metadata.getID().size()]; auto exps = m_metadata.getID() + "/" + name; memcpy(exp, exps.c_str(), exps.size() + 1); - expanded[name] = exp; + m_expandedSprites[name] = exp; return exp; } @@ -633,6 +634,10 @@ void Mod::Impl::setLoggingEnabled(bool enabled) { m_loggingEnabled = enabled; } +bool Mod::Impl::shouldLoad() const { + return Mod::get()->getSavedValue("should-load-" + m_metadata.getID(), true); +} + static Result getModImplInfo() { std::string err; json::Value json; diff --git a/loader/src/loader/ModImpl.hpp b/loader/src/loader/ModImpl.hpp index c7a82dfe..55e5c88d 100644 --- a/loader/src/loader/ModImpl.hpp +++ b/loader/src/loader/ModImpl.hpp @@ -61,6 +61,8 @@ namespace geode { */ bool m_loggingEnabled = true; + std::unordered_map m_expandedSprites; + ModRequestedAction m_requestedAction = ModRequestedAction::None; @@ -137,6 +139,8 @@ namespace geode { bool isLoggingEnabled() const; void setLoggingEnabled(bool enabled); + + bool shouldLoad() const; }; class ModImpl : public Mod::Impl { diff --git a/loader/src/loader/Setting.cpp b/loader/src/loader/Setting.cpp index ccce4622..e5dcda72 100644 --- a/loader/src/loader/Setting.cpp +++ b/loader/src/loader/Setting.cpp @@ -62,6 +62,7 @@ Result StringSetting::parse(JsonMaybeObject& obj) { StringSetting sett; parseCommon(sett, obj); obj.has("match").into(sett.match); + obj.has("filter").into(sett.filter); return Ok(sett); } diff --git a/loader/src/ui/internal/GeodeUI.cpp b/loader/src/ui/internal/GeodeUI.cpp index 5bc6705a..99d936b2 100644 --- a/loader/src/ui/internal/GeodeUI.cpp +++ b/loader/src/ui/internal/GeodeUI.cpp @@ -68,7 +68,7 @@ CCNode* geode::createDefaultLogo(CCSize const& size) { if (!spr) { spr = CCLabelBMFont::create("OwO", "goldFont.fnt"); } - limitNodeSize(spr, size, 1.f, .1f); + limitNodeSize(spr, size, 1.f, .01f); return spr; } @@ -78,7 +78,7 @@ CCNode* geode::createModLogo(Mod* mod, CCSize const& size) { CCSprite::create(fmt::format("{}/logo.png", mod->getID()).c_str()); if (!spr) spr = CCSprite::createWithSpriteFrameName("no-logo.png"_spr); if (!spr) spr = CCLabelBMFont::create("N/A", "goldFont.fnt"); - limitNodeSize(spr, size, 1.f, .1f); + limitNodeSize(spr, size, 1.f, .01f); spr->setPosition(size/2); spr->setAnchorPoint({.5f, .5f}); @@ -116,7 +116,7 @@ CCNode* geode::createIndexItemLogo(IndexItemHandle item, CCSize const& size) { spr = logoGlow; } else { - limitNodeSize(spr, size, 1.f, .1f); + limitNodeSize(spr, size, 1.f, .01f); } spr->setPosition(size/2); spr->setAnchorPoint({.5f, .5f}); diff --git a/loader/src/ui/internal/info/DevProfilePopup.cpp b/loader/src/ui/internal/info/DevProfilePopup.cpp index f5146e84..6571b078 100644 --- a/loader/src/ui/internal/info/DevProfilePopup.cpp +++ b/loader/src/ui/internal/info/DevProfilePopup.cpp @@ -5,8 +5,9 @@ #include "../list/ModListCell.hpp" #include "../list/ModListLayer.hpp" -bool DevProfilePopup::setup(std::string const& developer) { +bool DevProfilePopup::setup(std::string const& developer, ModListLayer* list) { m_noElasticity = true; + m_layer = list; this->setTitle("Mods by " + developer); @@ -18,7 +19,7 @@ bool DevProfilePopup::setup(std::string const& developer) { for (auto& mod : Loader::get()->getAllMods()) { if (mod->getDeveloper() == developer) { auto cell = ModCell::create( - mod, nullptr, ModListDisplay::Concise, { 358.f, 40.f } + mod, m_layer, ModListDisplay::Concise, { 358.f, 40.f } ); cell->disableDeveloperButton(); items->addObject(cell); @@ -31,7 +32,7 @@ bool DevProfilePopup::setup(std::string const& developer) { continue; } auto cell = IndexItemCell::create( - item, nullptr, ModListDisplay::Concise, { 358.f, 40.f } + item, m_layer, ModListDisplay::Concise, { 358.f, 40.f } ); cell->disableDeveloperButton(); items->addObject(cell); @@ -39,18 +40,18 @@ bool DevProfilePopup::setup(std::string const& developer) { // mods list auto listSize = CCSize { 358.f, 160.f }; - auto list = ListView::create(items, 40.f, listSize.width, listSize.height); - list->setPosition(winSize / 2 - listSize / 2); - m_mainLayer->addChild(list); + auto cellList = ListView::create(items, 40.f, listSize.width, listSize.height); + cellList->setPosition(winSize / 2 - listSize / 2); + m_mainLayer->addChild(cellList); addListBorders(m_mainLayer, winSize / 2, listSize); return true; } -DevProfilePopup* DevProfilePopup::create(std::string const& developer) { +DevProfilePopup* DevProfilePopup::create(std::string const& developer, ModListLayer* list) { auto ret = new DevProfilePopup(); - if (ret && ret->init(420.f, 260.f, developer)) { + if (ret && ret->init(420.f, 260.f, developer, list)) { ret->autorelease(); return ret; } diff --git a/loader/src/ui/internal/info/DevProfilePopup.hpp b/loader/src/ui/internal/info/DevProfilePopup.hpp index af47a572..5ff3b631 100644 --- a/loader/src/ui/internal/info/DevProfilePopup.hpp +++ b/loader/src/ui/internal/info/DevProfilePopup.hpp @@ -4,10 +4,14 @@ using namespace geode::prelude; -class DevProfilePopup : public Popup { +class ModListLayer; + +class DevProfilePopup : public Popup { protected: - bool setup(std::string const& developer) override; + ModListLayer* m_layer; + + bool setup(std::string const& developer, ModListLayer* list) override; public: - static DevProfilePopup* create(std::string const& developer); + static DevProfilePopup* create(std::string const& developer, ModListLayer* list); }; diff --git a/loader/src/ui/internal/info/ModInfoPopup.cpp b/loader/src/ui/internal/info/ModInfoPopup.cpp index ebe9ac9b..945e5ec4 100644 --- a/loader/src/ui/internal/info/ModInfoPopup.cpp +++ b/loader/src/ui/internal/info/ModInfoPopup.cpp @@ -4,6 +4,7 @@ #include "../list/ModListLayer.hpp" #include "../settings/ModSettingsPopup.hpp" #include +#include #include #include @@ -49,45 +50,51 @@ bool ModInfoPopup::init(ModMetadata const& metadata, ModListLayer* list) { constexpr float logoSize = 40.f; constexpr float logoOffset = 10.f; - auto nameLabel = CCLabelBMFont::create(metadata.getName().c_str(), "bigFont.fnt"); - nameLabel->setAnchorPoint({ .0f, .5f }); - nameLabel->limitLabelWidth(200.f, .7f, .1f); - m_mainLayer->addChild(nameLabel, 2); + auto topNode = CCNode::create(); + topNode->setContentSize({350.f, 80.f}); + topNode->setLayout( + RowLayout::create() + ->setAxisAlignment(AxisAlignment::Center) + ->setAutoScale(false) + ->setCrossAxisOverflow(true) + ); + m_mainLayer->addChild(topNode); + topNode->setAnchorPoint({.5f, .5f}); + topNode->setPosition(winSize.width / 2, winSize.height / 2 + 115.f); auto logoSpr = this->createLogo({logoSize, logoSize}); - m_mainLayer->addChild(logoSpr); + topNode->addChild(logoSpr); + + auto labelNode = CCNode::create(); + labelNode->setLayout( + ColumnLayout::create() + ->setAxisAlignment(AxisAlignment::Center) + ->setCrossAxisLineAlignment(AxisAlignment::Start) + ->setGap(0.f) + ->setAutoScale(false) + ->setCrossAxisOverflow(true) + ); + labelNode->setContentSize({200.f, 80.f}); + topNode->addChild(labelNode); + + auto nameLabel = CCLabelBMFont::create(metadata.getName().c_str(), "bigFont.fnt"); + nameLabel->limitLabelWidth(200.f, .7f, .1f); + labelNode->addChild(nameLabel, 2); auto developerStr = "by " + metadata.getDeveloper(); auto developerLabel = CCLabelBMFont::create(developerStr.c_str(), "goldFont.fnt"); developerLabel->setScale(.5f); - developerLabel->setAnchorPoint({.0f, .5f}); - m_mainLayer->addChild(developerLabel); - - auto logoTitleWidth = - std::max(nameLabel->getScaledContentSize().width, developerLabel->getScaledContentSize().width) + - logoSize + logoOffset; - - nameLabel->setPosition( - winSize.width / 2 - logoTitleWidth / 2 + logoSize + logoOffset, winSize.height / 2 + 125.f - ); - logoSpr->setPosition( - {winSize.width / 2 - logoTitleWidth / 2 + logoSize / 2, winSize.height / 2 + 115.f} - ); - developerLabel->setPosition( - winSize.width / 2 - logoTitleWidth / 2 + logoSize + logoOffset, winSize.height / 2 + 105.f - ); + labelNode->addChild(developerLabel); auto versionLabel = CCLabelBMFont::create(metadata.getVersion().toString().c_str(), "bigFont.fnt" ); - versionLabel->setAnchorPoint({ .0f, .5f }); versionLabel->setScale(.4f); - versionLabel->setPosition( - nameLabel->getPositionX() + nameLabel->getScaledContentSize().width + 5.f, - winSize.height / 2 + 125.f - ); versionLabel->setColor({0, 255, 0}); - m_mainLayer->addChild(versionLabel); + topNode->addChild(versionLabel); + + labelNode->updateLayout(); + topNode->updateLayout(); this->setTouchEnabled(true); @@ -425,7 +432,7 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) { disableBtnSpr, enableBtnSpr, this, menu_selector(LocalModInfoPopup::onEnableMod) ); enableBtn->setPosition(-155.f, 75.f); - enableBtn->toggle(!mod->isEnabled()); + enableBtn->toggle(!mod->shouldLoad()); m_buttonMenu->addChild(enableBtn); if (!mod->supportsDisabling()) { @@ -629,9 +636,9 @@ void LocalModInfoPopup::onEnableMod(CCObject* sender) { } } if (m_layer) { - m_layer->updateAllStates(); + m_layer->reloadList(); } - as(sender)->toggle(m_mod->isEnabled()); + as(sender)->toggle(m_mod->shouldLoad()); } void LocalModInfoPopup::onOpenConfigDir(CCObject*) { @@ -640,7 +647,7 @@ void LocalModInfoPopup::onOpenConfigDir(CCObject*) { void LocalModInfoPopup::onDisablingNotSupported(CCObject* pSender) { FLAlertLayer::create("Unsupported", "Disabling is not supported for this mod.", "OK")->show(); - as(pSender)->toggle(m_mod->isEnabled()); + as(pSender)->toggle(m_mod->shouldLoad()); } void LocalModInfoPopup::onSettings(CCObject*) { diff --git a/loader/src/ui/internal/list/InstallListCell.cpp b/loader/src/ui/internal/list/InstallListCell.cpp index 07daca20..352d8aaf 100644 --- a/loader/src/ui/internal/list/InstallListCell.cpp +++ b/loader/src/ui/internal/list/InstallListCell.cpp @@ -53,24 +53,21 @@ void InstallListCell::setupInfo( } this->addChild(m_titleLabel); - m_developerBtn = nullptr; + m_creatorLabel = nullptr; if (developer) { auto creatorStr = "by " + *developer; - auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); - creatorLabel->setScale(.34f); + m_creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); + m_creatorLabel->setScale(.34f); if (inactive) { - creatorLabel->setColor({ 163, 163, 163 }); + m_creatorLabel->setColor({ 163, 163, 163 }); } - m_developerBtn = CCMenuItemSpriteExtra::create( - creatorLabel, this, menu_selector(InstallListCell::onViewDev) - ); - m_developerBtn->setPosition( + m_creatorLabel->setPosition( m_titleLabel->getPositionX() + m_titleLabel->getScaledContentSize().width + 3.f + - creatorLabel->getScaledContentSize().width / 2, + m_creatorLabel->getScaledContentSize().width / 2, m_height / 2 ); - m_menu->addChild(m_developerBtn); + m_menu->addChild(m_creatorLabel); } this->setupVersion(version); @@ -96,7 +93,7 @@ void InstallListCell::setupVersion(std::variantsetScale(.2f); m_versionLabel->setPosition( m_titleLabel->getPositionX() + m_titleLabel->getScaledContentSize().width + 3.f + - (m_developerBtn ? m_developerBtn->getScaledContentSize().width + 3.f : 0.f), + (m_creatorLabel ? m_creatorLabel->getScaledContentSize().width + 3.f : 0.f), m_titleLabel->getPositionY() - 1.f ); m_versionLabel->setColor({ 0, 255, 0 }); @@ -123,7 +120,7 @@ void InstallListCell::setupInfo(ModMetadata const& metadata, bool inactive) { } void InstallListCell::onViewDev(CCObject*) { - DevProfilePopup::create(getDeveloper())->show(); + // DevProfilePopup::create(getDeveloper(), m_layer)->show(); } bool InstallListCell::init(InstallListPopup* list, CCSize const& size) { diff --git a/loader/src/ui/internal/list/InstallListCell.hpp b/loader/src/ui/internal/list/InstallListCell.hpp index f2fd677c..ce5927ea 100644 --- a/loader/src/ui/internal/list/InstallListCell.hpp +++ b/loader/src/ui/internal/list/InstallListCell.hpp @@ -21,7 +21,7 @@ protected: float m_height; InstallListPopup* m_layer = nullptr; CCMenu* m_menu = nullptr; - CCMenuItemSpriteExtra* m_developerBtn = nullptr; + CCLabelBMFont* m_creatorLabel = nullptr; CCLabelBMFont* m_titleLabel = nullptr; CCLabelBMFont* m_versionLabel = nullptr; TagNode* m_tagLabel = nullptr; diff --git a/loader/src/ui/internal/list/InstallListPopup.cpp b/loader/src/ui/internal/list/InstallListPopup.cpp index e561ca9b..42beb812 100644 --- a/loader/src/ui/internal/list/InstallListPopup.cpp +++ b/loader/src/ui/internal/list/InstallListPopup.cpp @@ -113,7 +113,7 @@ CCArray* InstallListPopup::createCells(std::unordered_mapisUninstalled()*/item.mod->getMetadata().getID() == "geode.loader") { + if (item.mod && /*!item.mod->isUninstalled()*/item.mod->getMetadata().getID() == "geode.loader") { bottom.push_back(ModInstallListCell::create(item.mod, this, this->getCellSize())); for (auto const& dep : item.mod->getMetadata().getDependencies()) { queue.push(dep); diff --git a/loader/src/ui/internal/list/ModListCell.cpp b/loader/src/ui/internal/list/ModListCell.cpp index 5b883959..061e6dcb 100644 --- a/loader/src/ui/internal/list/ModListCell.cpp +++ b/loader/src/ui/internal/list/ModListCell.cpp @@ -186,7 +186,7 @@ void ModListCell::updateCellLayout() { } void ModListCell::onViewDev(CCObject*) { - DevProfilePopup::create(this->getDeveloper())->show(); + DevProfilePopup::create(this->getDeveloper(), m_layer)->show(); } bool ModListCell::init(ModListLayer* list, CCSize const& size) { @@ -233,7 +233,9 @@ void ModCell::onEnable(CCObject* sender) { else { tryOrAlert(m_mod->disable(), "Error disabling mod"); } - m_layer->reloadList(); + if (m_layer) { + m_layer->reloadList(); + } } void ModCell::onUnresolvedInfo(CCObject*) { @@ -250,13 +252,15 @@ void ModCell::onRestart(CCObject*) { void ModCell::updateState() { bool unresolved = m_mod->hasUnresolvedDependencies(); + bool shouldLoad = m_mod->shouldLoad(); + auto toggleable = !unresolved || !shouldLoad; if (m_enableToggle) { m_enableToggle->toggle(m_mod->isEnabled()); - m_enableToggle->setEnabled(!unresolved); - m_enableToggle->m_offButton->setOpacity(unresolved ? 100 : 255); - m_enableToggle->m_offButton->setColor(unresolved ? cc3x(155) : cc3x(255)); - m_enableToggle->m_onButton->setOpacity(unresolved ? 100 : 255); - m_enableToggle->m_onButton->setColor(unresolved ? cc3x(155) : cc3x(255)); + m_enableToggle->setEnabled(toggleable); + m_enableToggle->m_offButton->setOpacity(!toggleable ? 100 : 255); + m_enableToggle->m_offButton->setColor(!toggleable ? cc3x(155) : cc3x(255)); + m_enableToggle->m_onButton->setOpacity(!toggleable ? 100 : 255); + m_enableToggle->m_onButton->setColor(!toggleable ? cc3x(155) : cc3x(255)); } bool hasProblems = false; for (auto const& item : Loader::get()->getProblems()) { @@ -309,9 +313,6 @@ bool ModCell::init( auto viewSpr = ButtonSprite::create("View", "bigFont.fnt", "GJ_button_01.png", .8f); viewSpr->setScale(.65f); - auto viewBtn = CCMenuItemSpriteExtra::create(viewSpr, this, menu_selector(ModCell::onInfo)); - m_menu->addChild(viewBtn); - if (m_mod->isEnabled()) { auto latestIndexItem = Index::get()->getMajorItem( mod->getMetadata().getID() @@ -333,6 +334,9 @@ bool ModCell::init( } } } + + auto viewBtn = CCMenuItemSpriteExtra::create(viewSpr, this, menu_selector(ModCell::onInfo)); + m_menu->addChild(viewBtn); } this->updateState(); diff --git a/loader/src/ui/internal/settings/GeodeSettingNode.cpp b/loader/src/ui/internal/settings/GeodeSettingNode.cpp index 70b14542..28a37e1e 100644 --- a/loader/src/ui/internal/settings/GeodeSettingNode.cpp +++ b/loader/src/ui/internal/settings/GeodeSettingNode.cpp @@ -324,6 +324,11 @@ bool StringSettingNode::setup(StringSettingValue* setting, float width) { m_input = InputNode::create(width / 2 - 10.f, "Text", "chatFont.fnt"); m_input->setPosition({ -(width / 2 - 70.f) / 2, .0f }); m_input->setScale(.65f); + + if (setting->castDefinition().filter.has_value()) { + m_input->getInput()->setAllowedChars(setting->castDefinition().filter.value()); + } + m_input->getInput()->setDelegate(this); m_menu->addChild(m_input); diff --git a/loader/src/ui/nodes/MDTextArea.cpp b/loader/src/ui/nodes/MDTextArea.cpp index 051d5bcd..fb5e2046 100644 --- a/loader/src/ui/nodes/MDTextArea.cpp +++ b/loader/src/ui/nodes/MDTextArea.cpp @@ -109,8 +109,8 @@ bool MDTextArea::init(std::string const& str, CCSize const& size) { if (!CCLayer::init()) return false; m_text = str; - m_size = size; - this->setContentSize(size); + m_size = size - CCSize { 15.f, 0.f }; + this->setContentSize(m_size); m_renderer = TextRenderer::create(); CC_SAFE_RETAIN(m_renderer); @@ -118,8 +118,8 @@ bool MDTextArea::init(std::string const& str, CCSize const& size) { m_bgSprite->setScale(.5f); m_bgSprite->setColor({ 0, 0, 0 }); m_bgSprite->setOpacity(75); - m_bgSprite->setContentSize(size * 2 + CCSize { 25.f, 25.f }); - m_bgSprite->setPosition(size / 2); + m_bgSprite->setContentSize(size * 2); + m_bgSprite->setPosition(m_size / 2); this->addChild(m_bgSprite); m_scrollLayer = ScrollLayer::create({ 0, 0, m_size.width, m_size.height }, true); @@ -209,6 +209,7 @@ struct MDParser { static float s_codeStart; static size_t s_orderedListNum; static std::vector s_codeSpans; + static bool s_breakListLine; static int parseText(MD_TEXTTYPE type, MD_CHAR const* rawText, MD_SIZE size, void* mdtextarea) { auto textarea = static_cast(mdtextarea); @@ -364,6 +365,10 @@ struct MDParser { renderer->pushIndent(g_indent); s_isOrderedList = type == MD_BLOCKTYPE::MD_BLOCK_OL; s_orderedListNum = 0; + if (s_breakListLine) { + renderer->breakLine(); + s_breakListLine = false; + } } break; @@ -377,6 +382,10 @@ struct MDParser { case MD_BLOCKTYPE::MD_BLOCK_LI: { + if (s_breakListLine) { + renderer->breakLine(); + s_breakListLine = false; + } renderer->pushOpacity(renderer->getCurrentOpacity() / 2); auto lidetail = static_cast(detail); if (s_isOrderedList) { @@ -387,6 +396,7 @@ struct MDParser { renderer->renderString("• "); } renderer->popOpacity(); + s_breakListLine = true; } break; @@ -446,7 +456,13 @@ struct MDParser { case MD_BLOCKTYPE::MD_BLOCK_UL: { renderer->popIndent(); - renderer->breakLine(); + if (s_breakListLine) { + renderer->breakLine(); + s_breakListLine = false; + } + if (renderer->getCurrentIndent() == 0) { + renderer->breakLine(); + } } break; @@ -489,7 +505,6 @@ struct MDParser { case MD_BLOCKTYPE::MD_BLOCK_LI: { - renderer->breakLine(); } break; @@ -626,6 +641,7 @@ size_t MDParser::s_orderedListNum = 0; bool MDParser::s_isCodeBlock = false; float MDParser::s_codeStart = 0; decltype(MDParser::s_codeSpans) MDParser::s_codeSpans = {}; +bool MDParser::s_breakListLine = false; void MDTextArea::updateLabel() { m_renderer->begin(m_content, CCPointZero, m_size); @@ -679,7 +695,16 @@ void MDTextArea::updateLabel() { m_renderer->end(); - m_scrollLayer->m_contentLayer->setContentSize(m_content->getContentSize()); + if (m_content->getContentSize().height > m_size.height) { + // Generate bottom padding + m_scrollLayer->m_contentLayer->setContentSize(m_content->getContentSize() + CCSize { 0.f, 12.5 }); + m_content->setPositionY(10.f); + } else { + m_scrollLayer->m_contentLayer->setContentSize(m_content->getContentSize()); + m_content->setPositionY(-2.5f); + } + + m_scrollLayer->moveToTop(); } diff --git a/loader/src/ui/nodes/TextArea.cpp b/loader/src/ui/nodes/TextArea.cpp new file mode 100644 index 00000000..a2b74717 --- /dev/null +++ b/loader/src/ui/nodes/TextArea.cpp @@ -0,0 +1,300 @@ +#include + +using namespace geode::prelude; + +SimpleTextArea* SimpleTextArea::create(const std::string& text, const std::string& font, const float scale) { + return SimpleTextArea::create(font, text, scale, CCDirector::sharedDirector()->getWinSize().width / 2, false); +} + +SimpleTextArea* SimpleTextArea::create(const std::string& text, const std::string& font, const float scale, const float width) { + return SimpleTextArea::create(font, text, scale, width, true); +} + +SimpleTextArea* SimpleTextArea::create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth) { + SimpleTextArea* instance = new SimpleTextArea(font, text, scale, width, artificialWidth); + + if (instance && instance->init()) { + instance->autorelease(); + + return instance; + } else { + CC_SAFE_DELETE(instance); + + return nullptr; + } +} + +SimpleTextArea::SimpleTextArea(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth) { + m_font = font; + m_text = text; + m_maxLines = 0; + m_scale = scale; + m_linePadding = 0; + m_color = { 0xFF, 0xFF, 0xFF, 0xFF }; + m_alignment = kCCTextAlignmentLeft; + m_wrappingMode = WORD_WRAP; + m_artificialWidth = artificialWidth; + m_container = CCMenu::create(); + m_shouldUpdate = true; + + this->setAnchorPoint({ 0.5f, 0.5f }); + m_container->setPosition({ 0, 0 }); + m_container->setAnchorPoint({ 0, 1 }); + m_container->setContentSize({ width, 0 }); + + this->addChild(m_container); +} + +void SimpleTextArea::setFont(const std::string& font) { + m_font = font; + m_shouldUpdate = true; +} + +std::string SimpleTextArea::getFont() { + return m_font; +} + +void SimpleTextArea::setColor(const ccColor4B& color) { + m_color = color; + m_shouldUpdate = true; +} + +ccColor4B SimpleTextArea::getColor() { + return m_color; +} + +void SimpleTextArea::setAlignment(const CCTextAlignment alignment) { + m_alignment = alignment; + m_shouldUpdate = true; +} + +CCTextAlignment SimpleTextArea::getAlignment() { + return m_alignment; +} + +void SimpleTextArea::setWrappingMode(const WrappingMode mode) { + m_wrappingMode = mode; + m_shouldUpdate = true; +} + +WrappingMode SimpleTextArea::getWrappingMode() { + return m_wrappingMode; +} + +void SimpleTextArea::setText(const std::string& text) { + m_text = text; + m_shouldUpdate = true; +} + +std::string SimpleTextArea::getText() { + return m_text; +} + +void SimpleTextArea::setMaxLines(const size_t maxLines) { + m_maxLines = maxLines; + m_shouldUpdate = true; +} + +size_t SimpleTextArea::getMaxLines() { + return m_maxLines; +} + +void SimpleTextArea::setWidth(const float width) { + m_artificialWidth = true; + m_shouldUpdate = true; + + this->setContentSize({ width, this->getContentSize().height }); + m_container->setContentSize(this->getContentSize()); +} + +float SimpleTextArea::getWidth() { + return m_container->getContentSize().width; +} + +void SimpleTextArea::setScale(const float scale) { + m_scale = scale; + m_shouldUpdate = true; +} + +float SimpleTextArea::getScale() { + return m_scale; +} + +void SimpleTextArea::setLinePadding(const float padding) { + m_linePadding = padding; + m_shouldUpdate = true; +} + +float SimpleTextArea::getLinePadding() { + return m_linePadding; +} + +std::vector SimpleTextArea::getLines() { + return m_lines; +} + +float SimpleTextArea::getHeight() { + return m_container->getContentSize().height; +} + +float SimpleTextArea::getLineHeight() { + return m_lineHeight; +} + +CCLabelBMFont* SimpleTextArea::createLabel(const std::string& text, const float top) { + CCLabelBMFont* label = CCLabelBMFont::create(text.c_str(), m_font.c_str()); + + label->setScale(m_scale); + label->setPosition({ 0, top }); + label->setColor({ m_color.r, m_color.g, m_color.b }); + label->setOpacity(m_color.a); + + return label; +} + +float SimpleTextArea::calculateOffset(CCLabelBMFont* label) { + return m_linePadding + label->getContentSize().height * m_scale; +} + +void SimpleTextArea::charIteration(const std::function& overflowHandling) { + float top = 0; + CCLabelBMFont* line = this->createLabel("", top); + m_lines = { line }; + + for (const char c : m_text) { + if (m_maxLines && m_lines.size() > m_maxLines) { + CCLabelBMFont* last = m_lines.at(m_maxLines - 1); + const std::string text = last->getString(); + + m_lines.pop_back(); + last->setString(text.substr(0, text.size() - 3).append("...").c_str()); + + break; + } else if (c == '\n') { + m_lines.push_back(line = this->createLabel("", top -= this->calculateOffset(line))); + } else if (m_artificialWidth && line->getContentSize().width * m_scale >= this->getWidth()) { + m_lines.push_back(line = overflowHandling(line, c, top -= this->calculateOffset(line))); + } else { + line->setString((std::string(line->getString()) + c).c_str()); + } + } +} + +void SimpleTextArea::updateLinesNoWrap() { + std::stringstream stream(m_text); + std::string part; + float top = 0; + + while (std::getline(stream, part)) { + if (m_maxLines && m_lines.size() >= m_maxLines) { + CCLabelBMFont* last = m_lines.at(m_maxLines - 1); + const std::string text = last->getString(); + + last->setString(text.substr(0, text.size() - 3).append("...").c_str()); + + break; + } else { + CCLabelBMFont* line = this->createLabel(part, 0); + + top -= this->calculateOffset(line); + + m_lines.push_back(line); + } + } +} + +void SimpleTextArea::updateLinesWordWrap() { + this->charIteration([this](CCLabelBMFont* line, const char c, const float top) { + static std::string delimiters(" `~!@#$%^&*()-_=+[{}];:'\",<.>/?\\|"); + + if (delimiters.find(c) == std::string_view::npos) { + const std::string text = line->getString(); + const size_t position = text.find_last_of(delimiters) + 1; + + line->setString(text.substr(0, position).c_str()); + + return this->createLabel(text.substr(position) + c, top); + } else { + return this->createLabel(std::string(c, c != ' '), top); + } + }); +} + +void SimpleTextArea::updateLinesCutoffWrap() { + this->charIteration([this](CCLabelBMFont* line, const char c, const float top) { + const std::string text = line->getString(); + const char back = text.back(); + const bool lastIsSpace = back == ' '; + CCLabelBMFont* newLine = this->createLabel(std::string(!lastIsSpace, back).append(std::string(c != ' ', c)), top); + + if (!lastIsSpace) { + if (text[text.size() - 2] == ' ') { + line->setString(text.substr(0, text.size() - 1).c_str()); + } else { + line->setString((text.substr(0, text.size() - 1) + '-').c_str()); + } + } + + return newLine; + }); +} + +void SimpleTextArea::updateContainer() { + switch (m_wrappingMode) { + case NO_WRAP: { + this->updateLinesNoWrap(); + } break; + case WORD_WRAP: { + this->updateLinesWordWrap(); + } break; + case CUTOFF_WRAP: { + this->updateLinesCutoffWrap(); + } break; + } + + const size_t lineCount = m_lines.size(); + const float width = this->getWidth(); + + if (lineCount > 0) { + m_lineHeight = m_lines.back()->getContentSize().height * m_scale; + } else { + m_lineHeight = 0; + } + + float height = m_lineHeight * lineCount + m_linePadding * (lineCount - 1); + + this->setContentSize({ width, height }); + m_container->setContentSize(this->getContentSize()); + m_container->removeAllChildren(); + + for (CCLabelBMFont* line : m_lines) { + const float y = height + line->getPositionY(); + + switch (m_alignment) { + case kCCTextAlignmentLeft: { + line->setAnchorPoint({ 0, 1 }); + line->setPosition({ 0, y }); + } break; + case kCCTextAlignmentCenter: { + line->setAnchorPoint({ 0, 1 }); + line->setPosition({ width / 2, y }); + } break; + case kCCTextAlignmentRight: { + line->setAnchorPoint({ 1, 1 }); + line->setPosition({ width, y }); + } break; + } + + m_container->addChild(line); + } +} + +void SimpleTextArea::draw() { + CCNode::draw(); + + if (m_shouldUpdate) { + this->updateContainer(); + + m_shouldUpdate = false; + } +} \ No newline at end of file diff --git a/loader/src/ui/nodes/TextRenderer.cpp b/loader/src/ui/nodes/TextRenderer.cpp index fe2c712d..ca7396b3 100644 --- a/loader/src/ui/nodes/TextRenderer.cpp +++ b/loader/src/ui/nodes/TextRenderer.cpp @@ -341,8 +341,8 @@ std::vector TextRenderer::renderStringEx( auto lastIndent = m_indentationStack.size() > 1 ? m_indentationStack.at(m_indentationStack.size() - 1) : .0f; - if (m_cursor.x == m_origin.x + lastIndent && this->getCurrentIndent() > .0f) { - m_cursor.x += this->getCurrentIndent(); + if (m_cursor.x < m_origin.x + this->getCurrentIndent()) { + m_cursor.x = this->getCurrentIndent(); } auto createLabel = [&]() -> bool { @@ -487,7 +487,7 @@ void TextRenderer::breakLine(float incY) { } if (h > y) y = h; m_cursor.y -= y; - m_cursor.x = m_origin.x + getCurrentIndent(); + m_cursor.x = m_origin.x; } float TextRenderer::adjustLineAlignment() { diff --git a/loader/src/utils/file.cpp b/loader/src/utils/file.cpp index 8df76c0d..1f8a7ad6 100644 --- a/loader/src/utils/file.cpp +++ b/loader/src/utils/file.cpp @@ -21,48 +21,51 @@ using namespace geode::prelude; using namespace geode::utils::file; Result utils::file::readString(ghc::filesystem::path const& path) { + if (!ghc::filesystem::exists(path)) + return Err("File does not exist"); + #if _WIN32 std::ifstream in(path.wstring(), std::ios::in | std::ios::binary); #else std::ifstream in(path.string(), std::ios::in | std::ios::binary); #endif - if (in) { - std::string contents; - in.seekg(0, std::ios::end); - contents.resize((const size_t)in.tellg()); - in.seekg(0, std::ios::beg); - in.read(&contents[0], contents.size()); - in.close(); - return Ok(contents); - } - return Err("Unable to open file"); + if (!in) + return Err("Unable to open file"); + + std::string contents; + in.seekg(0, std::ios::end); + contents.resize((const size_t)in.tellg()); + in.seekg(0, std::ios::beg); + in.read(&contents[0], contents.size()); + in.close(); + return Ok(contents); } Result utils::file::readJson(ghc::filesystem::path const& path) { - auto str = utils::file::readString(path); - - if (str) { - try { - return Ok(json::parse(str.value())); - } catch(std::exception const& e) { - return Err("Unable to parse JSON: " + std::string(e.what())); - } - } else { - return Err("Unable to open file"); + if (!str) + return Err(str.unwrapErr()); + try { + return Ok(json::parse(str.value())); + } + catch(std::exception const& e) { + return Err("Unable to parse JSON: " + std::string(e.what())); } } Result utils::file::readBinary(ghc::filesystem::path const& path) { + if (!ghc::filesystem::exists(path)) + return Err("File does not exist"); + #if _WIN32 std::ifstream in(path.wstring(), std::ios::in | std::ios::binary); #else std::ifstream in(path.string(), std::ios::in | std::ios::binary); #endif - if (in) { - return Ok(ByteVector(std::istreambuf_iterator(in), {})); - } - return Err("Unable to open file"); + if (!in) + return Err("Unable to open file"); + + return Ok(ByteVector(std::istreambuf_iterator(in), {})); } Result<> utils::file::writeString(ghc::filesystem::path const& path, std::string const& data) { @@ -72,14 +75,14 @@ Result<> utils::file::writeString(ghc::filesystem::path const& path, std::string #else file.open(path.string()); #endif - if (file.is_open()) { - file << data; + if (!file.is_open()) { file.close(); - - return Ok(); + return Err("Unable to open file"); } + + file << data; file.close(); - return Err("Unable to open file"); + return Ok(); } Result<> utils::file::writeBinary(ghc::filesystem::path const& path, ByteVector const& data) { @@ -89,14 +92,14 @@ Result<> utils::file::writeBinary(ghc::filesystem::path const& path, ByteVector #else file.open(path.string(), std::ios::out | std::ios::binary); #endif - if (file.is_open()) { - file.write(reinterpret_cast(data.data()), data.size()); + if (!file.is_open()) { file.close(); - - return Ok(); + return Err("Unable to open file"); } + + file.write(reinterpret_cast(data.data()), data.size()); file.close(); - return Err("Unable to open file"); + return Ok(); } Result<> utils::file::createDirectory(ghc::filesystem::path const& path) { diff --git a/loader/src/utils/web.cpp b/loader/src/utils/web.cpp index 9bc7cd99..8dccd048 100644 --- a/loader/src/utils/web.cpp +++ b/loader/src/utils/web.cpp @@ -79,6 +79,7 @@ Result web::fetchBytes(std::string const& url) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBytes); auto res = curl_easy_perform(curl); if (res != CURLE_OK) { @@ -118,6 +119,7 @@ Result web::fetch(std::string const& url) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeString); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); auto res = curl_easy_perform(curl); if (res != CURLE_OK) { curl_easy_cleanup(curl);