mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-24 03:39:56 -04:00
Merge remote-tracking branch 'refs/remotes/origin/main'
This commit is contained in:
commit
509503e0eb
35 changed files with 1130 additions and 432 deletions
.github/workflows
CHANGELOG.mdEULAVERSIONbindings
cmake
installer/windows
loader
include/Geode
src
cocos2d-ext
hooks
ids
internal
loader
platform
ui/internal/list
utils
test/members
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -44,7 +44,7 @@ jobs:
|
|||
package_cmd: './installer/mac/package.sh ./bin/nightly ./installer/mac/geode-installer-mac.pkg'
|
||||
installer_path: './installer/mac/geode-installer-mac.pkg'
|
||||
|
||||
name: ${{ matrix.config.name }}
|
||||
name: Build ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
steps:
|
||||
|
|
2
.github/workflows/test-offsets.yml
vendored
2
.github/workflows/test-offsets.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
|||
prefixes: PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
extra_flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DGEODE_DEBUG=On
|
||||
|
||||
name: ${{ matrix.config.name }}
|
||||
name: Test Offsets ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
steps:
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -1,5 +1,23 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v1.2.1
|
||||
* Mods now target macOS 10.13 instead of 10.14 (7cc1cd4)
|
||||
* Fix CustomizeObjectLayer ids moving around when multiple objects are selected (9ee0994, 87749d4)
|
||||
* Fix Windows uninstaller trying to uninstall from parent directory instead of current (bcb8a6f)
|
||||
* Fix search paths on reload (65b4364)
|
||||
* Fix search paths missing for mods without spritesheets (e78eaf0)
|
||||
* Fix dependencies getting installed again when they're already installed (4f2835a)
|
||||
|
||||
## v1.2.0
|
||||
* Add Objective-C hooking utilities (a31d072)
|
||||
* Rename queueInGDThread to queueInMainThread (277f68b)
|
||||
* Fix build on apple silicon (dd16797)
|
||||
* Move windows uninstaller to gd folder (bdf11a2)
|
||||
* Option to pick features on windows installer (4cc9164)
|
||||
* Improve lag in some places due to search paths (9875f5d)
|
||||
* Some missing methods for macOS gd::map (db02c3d)
|
||||
* Some missing methods for windows gd::string (7139ac4)
|
||||
|
||||
## v1.1.1
|
||||
* Improve installation confirmation popup (9192769)
|
||||
* Remove unnecessary main thread queues for mod events (38cc38c)
|
||||
|
|
24
EULA
24
EULA
|
@ -4,9 +4,9 @@
|
|||
|
||||
This is the End User License Agreement for the Geode Software. By using this Software, you agree to the terms of this EULA.
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.0. Definitions
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
The Software is all of the source code within the Geode (geode-sdk) Organization on GitHub that when put in a funky little blender and mashed together form the compiled Geode libraries and runtime binaries along with any additional side dishes such as the documentation website.
|
||||
|
||||
|
@ -18,9 +18,9 @@ A Modder is someone who is using Geode and its functions in order to develope th
|
|||
|
||||
Geometry Dash is a game by RobTop Games. If you're using Geode, you probably know what it is. If you're an actual laywer or something alike looking at this text document, why? How did we end up in this situation? Can you let us off the hook if we just give you some cute photos of catgirls?
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.1. Your rights as an End User
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
You are not allowed to not do anything you want to without your own written permission. You are required to stop before every step of using this Software and ponder to yourself: "Is a car with a trailer driving on railway tracks a train?" Failing to do this will result in absolutely no punishments whatsoever. Not because it isn't wrong, but because we simply do not care.
|
||||
|
||||
|
@ -48,9 +48,9 @@ There are also things you are permitted to use this Software for. These include:
|
|||
|
||||
Any act you might use this Software for that isn't explicitly listed in any of the above lists you must consult at least yourself to decide if it's okay to do. Do note that the Developers of this Software, i.e. The Core Geode Team, may, at any time, publicly announce other cases that you may / may not use this Software for, or exceptions to previous cases. In the case that that happens, you are required to - wow did I just use 'that' twice in a row? That's so cool - comply with whatever The Core Geode Team says. Unless it's Pie telling you not to use any libraries. Don't listen to Pie.
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.2. Your rights as a Modder
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
This section mostly lists types of mods that you are allowed to and not allowed to make. Creating a mod that has features which are explicitly disallowed within this document will result in your mod being publicly shamed and humiliated. Be warned.
|
||||
|
||||
|
@ -76,23 +76,23 @@ You are not allowed to create mods that:
|
|||
|
||||
Anything not listed in the above lists may be okay to make, or it may not. Consult The Core Geode Team in case you aren't sure if a given mod is okay to make. It is also advised to communicate and discuss with other modders in the Geometry Dash modding community about any mods you are planning on creating which you are unsure about. It might be that your mod does more harm than good.
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.3 Our rights
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
We reserve the right to publicly shame and humiliate you for failing to understand our perfectly reasonable and unquestionable choices in the matters of design, code, public speaking, advertising, private speaking, behaviour, protected speaking, and anything else.
|
||||
|
||||
Our rights are your lefts. Because we are standing menacingly in front of you. Metaphorically speaking.
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.4 Software
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
I am tired of writing and I don't know what to write here. It's a mod loader and a modding SDK. You didn't download it not knowing what it is, did you?
|
||||
|
||||
===========================================
|
||||
============================================
|
||||
1.5 Too Long; Didn't Read
|
||||
===========================================
|
||||
============================================
|
||||
|
||||
* Geode is a mod loader & modding SDK for the game Geometry Dash.
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.1.1
|
||||
1.2.1
|
|
@ -310,7 +310,7 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo {
|
|||
static void purgeFileUtils();
|
||||
virtual void addSearchPath(const char* path);
|
||||
virtual void removeSearchPath(const char *path);
|
||||
virtual std::string fullPathForFilename(const char* filename, bool unk);
|
||||
virtual gd::string fullPathForFilename(const char* filename, bool unk) = mac 0x23f940;
|
||||
void removeAllPaths() = mac 0x241600;
|
||||
}
|
||||
|
||||
|
@ -1267,6 +1267,8 @@ class DS_Dictionary {
|
|||
bool stepIntoSubDictWithKey(char const*) = mac 0xc0cd0;
|
||||
int getIntegerForKey(char const*) = mac 0xc1610;
|
||||
void setIntegerForKey(char const*, int) = mac 0xc26b0;
|
||||
void setDictForKey(char const*, cocos2d::CCDictionary*) = mac 0xC4EA0;
|
||||
auto getObjectForKey(char const*) = mac 0xC4BB0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
|
|
|
@ -887,10 +887,10 @@ class ColorSetupDelegate {
|
|||
|
||||
class CommentCell : TableViewCell, LikeItemDelegate, FLAlertLayerProtocol {
|
||||
void loadFromComment(GJComment*) = mac 0x111c70, win 0x5f3d0;
|
||||
void onConfirmDelete(cocos2d::CCObject* sender) = mac 0x25ec80, win 0x61140;
|
||||
void onConfirmDelete(cocos2d::CCObject* sender) = mac 0x11d100, win 0x61140;
|
||||
void onLike(cocos2d::CCObject* sender) = mac 0x11d000, win 0x60F90;
|
||||
virtual void FLAlert_Clicked(FLAlertLayer* layer, bool btn) = win 0x61260;
|
||||
virtual void likedItem(LikeItemType type, int id, bool special) = win 0x61070;
|
||||
virtual void FLAlert_Clicked(FLAlertLayer* layer, bool btn) = mac 0x11d540, win 0x61260;
|
||||
virtual void likedItem(LikeItemType type, int id, bool special) = mac 0x11d340, win 0x61070;
|
||||
|
||||
cocos2d::CCSprite* m_iconSprite;
|
||||
cocos2d::CCLabelBMFont* m_likeLabel;
|
||||
|
@ -957,7 +957,7 @@ class CreatorLayer : cocos2d::CCLayer, cocos2d::CCSceneTransitionDelegate, Dialo
|
|||
void onGauntlets(cocos2d::CCObject*) = mac 0x142b20, win 0x4f0a0;
|
||||
void onSecretVault(cocos2d::CCObject*) = win 0x4f1d0;
|
||||
void onTreasureRoom(cocos2d::CCObject*) = win 0x4f540;
|
||||
virtual void sceneWillResume() = win 0x4fb50;
|
||||
virtual void sceneWillResume() = mac 0x1438F0, win 0x4fb50;
|
||||
virtual bool init() = mac 0x141c10, win 0x4de40;
|
||||
static CreatorLayer* create() = win 0x4dda0;
|
||||
}
|
||||
|
@ -1072,13 +1072,13 @@ class CustomizeObjectLayer : FLAlertLayer, TextInputDelegate, HSVWidgetPopupDele
|
|||
bool m_customColorSelected;
|
||||
}
|
||||
|
||||
class DailyLevelPage : FLAlertLayer {
|
||||
class DailyLevelPage : FLAlertLayer, FLAlertLayerProtocol, GJDailyLevelDelegate, LevelDownloadDelegate {
|
||||
static DailyLevelPage* create(bool weekly) = win 0x6a860;
|
||||
bool init(bool weekly) = mac 0x10abb0, win 0x6a900;
|
||||
virtual void updateTimers(float) = win 0x6bef0;
|
||||
bool init(bool weekly) = mac 0x108C90, win 0x6a900;
|
||||
virtual void updateTimers(float) = mac 0x109780, win 0x6bef0;
|
||||
virtual void show() = mac 0x10a4b0, win 0x3f360;
|
||||
|
||||
PAD = win 0x21;
|
||||
PAD = mac 0x29, win 0x15;
|
||||
bool m_weekly;
|
||||
}
|
||||
|
||||
|
@ -1226,6 +1226,9 @@ class EditLevelLayer : cocos2d::CCLayer, FLAlertLayerProtocol, TextInputDelegate
|
|||
static EditLevelLayer* create(GJGameLevel* level) = mac 0xe1e50, win 0x6f530, ios 0x82420;
|
||||
bool init(GJGameLevel* level) = mac 0xe1fd0, win 0x6f5d0;
|
||||
void onLevelInfo(cocos2d::CCObject*) = mac 0xe4f60, win 0x70660;
|
||||
void onPlay(cocos2d::CCObject*) = mac 0xe3ae0, win 0x71700;
|
||||
void onEdit(cocos2d::CCObject*) = mac 0xe3970, win 0x71ac0;
|
||||
void onShare(cocos2d::CCObject*) = mac 0xe3c60, win 0x71be0;
|
||||
|
||||
cocos2d::CCMenu* m_buttonMenu;
|
||||
GJGameLevel* m_level;
|
||||
|
@ -1447,8 +1450,9 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
|
|||
PAD = mac 0x2, win 0x2, android 0x2;
|
||||
bool m_freeMoveEnabled;
|
||||
bool m_unkSwipeRelated;
|
||||
PAD = mac 0xa, win 0xa, android 0x9;
|
||||
PAD = mac 0x2, win 0x2, android 0x2;
|
||||
bool m_updateTimeMarkers;
|
||||
PAD = mac 0x8, win 0x8, android 0x2;
|
||||
cocos2d::CCArray* m_unknownArray2;
|
||||
PAD = mac 0x8, win 0x8, android 0x8;
|
||||
cocos2d::CCArray* m_selectedObjects;
|
||||
|
@ -1687,7 +1691,6 @@ class FLAlertLayer : cocos2d::CCLayerColor {
|
|||
static FLAlertLayer* create(FLAlertLayerProtocol* protocol, char const* title, gd::string content, char const* btn1, char const* btn2, float width, bool scrollable, float height) = mac 0x25dec0, win 0x227e0;
|
||||
void onBtn1(cocos2d::CCObject*) = mac 0x25ec20, win 0x23340;
|
||||
void onBtn2(cocos2d::CCObject*) = mac 0x25ec80, win 0x23380;
|
||||
void onClose(cocos2d::CCObject*) = win 0x49C60;
|
||||
|
||||
cocos2d::CCMenu* m_buttonMenu;
|
||||
int m_controlConnected;
|
||||
|
@ -2032,7 +2035,10 @@ class GJCommentListLayer : cocos2d::CCLayerColor {
|
|||
BoomListView* m_list;
|
||||
}
|
||||
|
||||
class GJDailyLevelDelegate {}
|
||||
class GJDailyLevelDelegate {
|
||||
virtual void dailyStatusFinished(bool) {}
|
||||
virtual void dailyStatusFailed(bool) {}
|
||||
}
|
||||
|
||||
class GJDropDownLayer : cocos2d::CCLayerColor {
|
||||
virtual void customSetup() {}
|
||||
|
@ -2278,7 +2284,7 @@ class GJGameLevel : cocos2d::CCNode {
|
|||
void savePercentage(int, bool, int, int, bool) = mac 0x2db700;
|
||||
void dataLoaded(DS_Dictionary* dict) = mac 0x2dc0e0, win 0xbded0, ios 0x6fca4;
|
||||
GJDifficulty getAverageDifficulty() = win 0xbd9b0;
|
||||
gd::string getUnpackedLevelDescription() = win 0xbf890;
|
||||
gd::string getUnpackedLevelDescription() = mac 0x2DDB50, win 0xbf890;
|
||||
gd::string lengthKeyToString(int key) = win 0xbd910;
|
||||
|
||||
static GJGameLevel* getCurrent() {
|
||||
|
@ -2592,12 +2598,14 @@ class GJScaleControlDelegate {
|
|||
virtual void scaleChanged(float) {}
|
||||
}
|
||||
|
||||
class GJScoreCell : TableViewCell {
|
||||
virtual void FLAlert_Clicked(FLAlertLayer*, bool) = win 0x624a0;
|
||||
void loadFromScore(GJUserScore* score) = win 0x61440;
|
||||
class GJScoreCell : TableViewCell, FLAlertLayerProtocol {
|
||||
virtual void FLAlert_Clicked(FLAlertLayer*, bool) = mac 0x11D8E0, win 0x624a0;
|
||||
void loadFromScore(GJUserScore* score) = mac 0x113AA0, win 0x61440;
|
||||
void onViewProfile(cocos2d::CCObject* sender) = win 0x62380;
|
||||
void updateBGColor(int index) = win 0x5c6b0;
|
||||
GJScoreCell(char const* key, float width, float height) = win 0x613C0;
|
||||
void updateBGColor(int index) = mac 0x113A40, win 0x5c6b0;
|
||||
inline GJScoreCell(char const* identifier, float parentHeight, float height) : TableViewCell(identifier, parentHeight, height) {}
|
||||
|
||||
GJUserScore* m_score;
|
||||
}
|
||||
|
||||
class GJSearchObject : cocos2d::CCNode {
|
||||
|
@ -2607,8 +2615,8 @@ class GJSearchObject : cocos2d::CCNode {
|
|||
|
||||
static GJSearchObject* create(SearchType nID) = win 0xc2b90, mac 0x2df120;
|
||||
static GJSearchObject* create(SearchType nID, gd::string str) = win 0xc2c80, mac 0x2df310;
|
||||
static GJSearchObject* createFromKey(const char* key) = win 0xC2760;
|
||||
const char* getKey() = win 0xC30A0;
|
||||
static GJSearchObject* createFromKey(const char* key) = mac 0x2C0620, win 0xC2760;
|
||||
const char* getKey() = mac 0x2C6A40, win 0xC30A0;
|
||||
const char* getNextPageKey() = win 0xC31F0;
|
||||
|
||||
SearchType m_searchType;
|
||||
|
@ -2710,8 +2718,19 @@ class GJUserScore : cocos2d::CCNode {
|
|||
gd::string getPlayerName() const {
|
||||
return m_userName;
|
||||
}
|
||||
static GJUserScore* create() = win 0xc1660;
|
||||
static GJUserScore* create(cocos2d::CCDictionary*) = win 0xc0750;
|
||||
static GJUserScore* create() {
|
||||
auto pRet = new GJUserScore();
|
||||
|
||||
if (pRet) {
|
||||
pRet->autorelease();
|
||||
return pRet;
|
||||
}
|
||||
|
||||
CC_SAFE_DELETE(pRet);
|
||||
return nullptr;
|
||||
}
|
||||
inline GJUserScore() : CCNode() {}
|
||||
static GJUserScore* create(cocos2d::CCDictionary*) = mac 0x2BD020, win 0xc0750;
|
||||
|
||||
gd::string m_userName;
|
||||
gd::string m_userUDID;
|
||||
|
@ -2756,7 +2775,7 @@ class GJUserScore : cocos2d::CCNode {
|
|||
}
|
||||
|
||||
class GManager : cocos2d::CCNode {
|
||||
virtual void setup() = win 0x28F60;
|
||||
virtual void setup() = mac 0x26EE20, win 0x28F60;
|
||||
virtual void encodeDataTo(DS_Dictionary* data) {}
|
||||
virtual void dataLoaded(DS_Dictionary* data) {}
|
||||
virtual void firstLoad() {}
|
||||
|
@ -2789,13 +2808,13 @@ class GameLevelManager : cocos2d::CCNode {
|
|||
cocos2d::CCArray* createAndGetScores(gd::string, GJScoreType) = win 0xa2780;
|
||||
GJGameLevel* createNewLevel() = mac 0x2b8180, win 0xa0db0;
|
||||
static GameLevelManager* sharedState() = mac 0x2a8340, win 0x9f860;
|
||||
void limitSavedLevels() = win 0xA43B0;
|
||||
cocos2d::CCArray* getCompletedLevels(bool newFilter) = win 0xa2d20;
|
||||
void getGJUserInfo(int) = win 0xb00b0;
|
||||
void getLevelLeaderboard(GJGameLevel* level, LevelLeaderboardType leaderboardType) = win 0xAED70;
|
||||
void getOnlineLevels(GJSearchObject*) = win 0xa7bc0;
|
||||
void limitSavedLevels() = mac 0x2C0C30, win 0xA43B0;
|
||||
cocos2d::CCArray* getCompletedLevels(bool newFilter) = mac 0x2BEEE0, win 0xa2d20;
|
||||
void getGJUserInfo(int) = mac 0x2CEBB0, win 0xb00b0;
|
||||
void getLevelLeaderboard(GJGameLevel* level, LevelLeaderboardType leaderboardType) = mac 0x2CD6F0, win 0xAED70;
|
||||
void getOnlineLevels(GJSearchObject*) = mac 0x2C5920, win 0xa7bc0;
|
||||
void getPageInfo(char const*) = mac 0x2c0050;
|
||||
cocos2d::CCArray* getSavedLevels(bool favorite, int levelFolder) = win 0xa2960;
|
||||
cocos2d::CCArray* getSavedLevels(bool favorite, int levelFolder) = mac 0x2BE910, win 0xa2960;
|
||||
cocos2d::CCArray* getStoredOnlineLevels(char const*) = mac 0x2bfe80, win 0xa3a90;
|
||||
void getTopArtists(int, int) = mac 0x2ce3d0;
|
||||
void getTopArtistsKey(int) = mac 0x2ce7a0;
|
||||
|
@ -2806,15 +2825,15 @@ class GameLevelManager : cocos2d::CCNode {
|
|||
callback void ProcessHttpRequest(gd::string, gd::string, gd::string, GJHttpType) = mac 0x2a8670, win 0x9f8e0;
|
||||
cocos2d::CCDictionary* responseToDict(gd::string response, bool comment) = win 0xbba50;
|
||||
void storeUserNames(gd::string) = win 0xa1840;
|
||||
void storeUserName(int userID, int accountID, gd::string str) = win 0xa1a70;
|
||||
gd::string userNameForUserID(int id) = win 0xa1c20;
|
||||
void updateUserScore() = win 0xada60;
|
||||
void storeUserName(int userID, int accountID, gd::string str) = mac 0x2B9020, win 0xa1a70;
|
||||
gd::string userNameForUserID(int id) = mac 0x2B91D0, win 0xa1c20;
|
||||
void updateUserScore() = mac 0x2CB6A0, win 0xada60;
|
||||
void downloadLevel(int id, bool downloadData) = win 0xaa730;
|
||||
bool hasDownloadedLevel(int id) = win 0xab830;
|
||||
GJGameLevel* getSavedLevel(int id) = win 0xa2ee0;
|
||||
void saveLevel(GJGameLevel* level) = win 0xa31c0;
|
||||
void deleteLevel(GJGameLevel* level) = mac 0x2b88d0, win 0xa1640;
|
||||
void resetCommentTimersForLevelID(int id, bool commentHistory) = win 0xB3F10;
|
||||
void resetCommentTimersForLevelID(int id, bool commentHistory) = mac 0x2D43D0, win 0xB3F10;
|
||||
void resetStoredUserInfo(int id) {
|
||||
m_storedUserInfo->removeObjectForKey(id);
|
||||
}
|
||||
|
@ -2838,12 +2857,11 @@ class GameLevelManager : cocos2d::CCNode {
|
|||
return GameLevelManager::sharedState();
|
||||
}
|
||||
|
||||
|
||||
//cocos2d::CCDictionary* timerDict = mac 0x1e8;
|
||||
cocos2d::CCDictionary* m_mainLevels;
|
||||
cocos2d::CCDictionary* m_searchFilters;
|
||||
cocos2d::CCDictionary* m_onlineLevels;
|
||||
PAD = win 0x4;
|
||||
cocos2d::CCDictionary* m_unkDict;
|
||||
cocos2d::CCDictionary* m_followedCreators;
|
||||
cocos2d::CCDictionary* m_downloadedLevels;
|
||||
cocos2d::CCDictionary* m_likedLevels;
|
||||
|
@ -2856,13 +2874,12 @@ class GameLevelManager : cocos2d::CCNode {
|
|||
int m_dailyTimeLeft;
|
||||
int m_dailyID;
|
||||
int m_dailyIDUnk;
|
||||
PAD = win 0x4;
|
||||
PAD = mac 0x10, win 0x4;
|
||||
int m_weeklyTimeLeft;
|
||||
int m_weeklyID;
|
||||
int m_weeklyIDUnk;
|
||||
cocos2d::CCDictionary* m_gauntletLevels;
|
||||
cocos2d::CCDictionary* m_unkDict13;
|
||||
PAD = win 0x4;
|
||||
gd::map<gd::string, bool> m_availableFilters;
|
||||
cocos2d::CCDictionary* m_timerDict;
|
||||
cocos2d::CCDictionary* m_knownUsers;
|
||||
cocos2d::CCDictionary* m_accountIDtoUserIDDict;
|
||||
|
@ -3524,9 +3541,9 @@ class GameStatsManager : cocos2d::CCNode {
|
|||
void awardCurrencyForLevel(GJGameLevel*) = mac 0x43600;
|
||||
void awardDiamondsForLevel(GJGameLevel*) = mac 0x43c60;
|
||||
void awardSecretKey() = mac 0x4b1e0;
|
||||
int getAwardedCurrencyForLevel(GJGameLevel*) = win 0xf83e0;
|
||||
int getBaseCurrencyForLevel(GJGameLevel*) = win 0xf8530;
|
||||
GJChallengeItem* getChallenge(int id) = win 0xa2fb0;
|
||||
int getAwardedCurrencyForLevel(GJGameLevel*) = mac 0x432E0, win 0xf83e0;
|
||||
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;
|
||||
void hasPendingUserCoin(char const*) = mac 0x42730, win 0xf7c50;
|
||||
|
@ -3541,14 +3558,14 @@ class GameStatsManager : cocos2d::CCNode {
|
|||
void storeUserCoin(char const*) = mac 0x42890;
|
||||
bool isItemUnlocked(UnlockType type, int id) = win 0xfbb80;
|
||||
|
||||
PAD = win 0x28;
|
||||
PAD = mac 0x50, win 0x28;
|
||||
cocos2d::CCDictionary* m_dailyChests;
|
||||
cocos2d::CCDictionary* m_worldAdvertChests;
|
||||
cocos2d::CCDictionary* m_activeChallenges;
|
||||
cocos2d::CCDictionary* m_upcomingChallenges;
|
||||
PAD = win 0xc;
|
||||
PAD = mac 0x18, win 0xc;
|
||||
cocos2d::CCDictionary* m_playerStats;
|
||||
PAD = win 0x10;
|
||||
PAD = mac 0x50, win 0x10;
|
||||
cocos2d::CCDictionary* m_completedLevels;
|
||||
cocos2d::CCDictionary* m_verifiedUserCoins;
|
||||
cocos2d::CCDictionary* m_pendingUserCoins;
|
||||
|
@ -3657,9 +3674,9 @@ class InfoAlertButton : CCMenuItemSpriteExtra {
|
|||
class InfoLayer : FLAlertLayer, LevelCommentDelegate, CommentUploadDelegate, FLAlertLayerProtocol {
|
||||
bool init(GJGameLevel* level, GJUserScore* score) = mac 0x456850, win 0x14f5a0;
|
||||
void setupCommentsBrowser(cocos2d::CCArray* comments) = mac 0x458590, win 0x152270;
|
||||
void onMore(cocos2d::CCObject* sender) = win 0x151500;
|
||||
void onMore(cocos2d::CCObject* sender) = mac 0x459400, win 0x151500;
|
||||
void onLevelInfo(cocos2d::CCObject* sender) = mac 0x459400, win 0x151850;
|
||||
void loadPage(int page, bool) = win 0x151e70;
|
||||
void loadPage(int page, bool) = mac 0x458FB0, win 0x151e70;
|
||||
static InfoLayer* create(GJGameLevel* level, GJUserScore* score) = mac 0x456600, win 0x14f4f0;
|
||||
|
||||
GJGameLevel* m_level;
|
||||
|
@ -3714,7 +3731,7 @@ class LeaderboardManagerDelegate {
|
|||
}
|
||||
|
||||
class LeaderboardsLayer : cocos2d::CCLayer {
|
||||
static LeaderboardsLayer* create(LeaderboardState state) = win 0x158710;
|
||||
static LeaderboardsLayer* create(LeaderboardState state) = mac 0x29F590, win 0x158710;
|
||||
bool init(LeaderboardState state) = mac 0x29f6d0, win 0x1587b0;
|
||||
}
|
||||
|
||||
|
@ -3728,9 +3745,12 @@ class LevelBrowserLayer : cocos2d::CCLayer, LevelManagerDelegate, FLAlertLayerPr
|
|||
}
|
||||
|
||||
bool init(GJSearchObject* search) = mac 0x2513f0, win 0x15a040;
|
||||
void loadPage(GJSearchObject* search) = win 0x15b160;
|
||||
void loadPage(GJSearchObject* search) = mac 0x253650, win 0x15b160;
|
||||
void setupLevelBrowser(cocos2d::CCArray* levels) = win 0x15bb40;
|
||||
void updateLevelsLabel() = win 0x15c350;
|
||||
virtual void setupPageInfo(gd::string, char const*) = mac 0x255050, win 0x15C1C0;
|
||||
void updateLevelsLabel() = mac 0x255450, win 0x15c350;
|
||||
void onRefresh(cocos2d::CCObject* sender) = mac 0x253090;
|
||||
void onInfo(cocos2d::CCObject* sender) = mac 0x253170, win 0x15cb00;
|
||||
static LevelBrowserLayer* create(GJSearchObject* search) = mac 0x251210, win 0x159fa0, ios 0x2d0a00;
|
||||
|
||||
PAD = win 0x4, mac 0x8;
|
||||
|
@ -3760,9 +3780,9 @@ class LevelCell : TableViewCell {
|
|||
|
||||
void onViewProfile(cocos2d::CCObject*) = mac 0x11a4a0, win 0x5c790;
|
||||
void loadCustomLevelCell() = mac 0x1183b0, win 0x5a020;
|
||||
void updateBGColor(int index) = win 0x5c6b0;
|
||||
void loadFromLevel(GJGameLevel* level) = win 0x59FD0;
|
||||
LevelCell(char const* key, float width, float height) = win 0x59F40;
|
||||
void updateBGColor(int index) = mac 0x110460, win 0x5c6b0;
|
||||
void loadFromLevel(GJGameLevel* level) = mac 0x110410, win 0x59FD0;
|
||||
inline LevelCell(char const* identifier, float parentHeight, float height) : TableViewCell(identifier, parentHeight, height) {}
|
||||
}
|
||||
|
||||
class LevelCommentDelegate {
|
||||
|
@ -3994,7 +4014,12 @@ class LevelInfoLayer : cocos2d::CCLayer, LevelDownloadDelegate, LevelUpdateDeleg
|
|||
void onViewProfile(cocos2d::CCObject* sender) = mac 0x1617d0, win 0x17ac90;
|
||||
void onLevelInfo(cocos2d::CCObject* sender) = mac 0x163880, win 0x17acf0;
|
||||
void setupProgressBars() = win 0x177fc0;
|
||||
void setupLevelInfo() = mac 0x161C80, win 0x178680;
|
||||
void downloadLevel() = win 0x177d90;
|
||||
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;
|
||||
|
||||
PAD = win 0x4, mac 0x8;
|
||||
cocos2d::CCMenu* m_playBtnMenu;
|
||||
|
@ -4015,28 +4040,28 @@ class LevelInfoLayer : cocos2d::CCLayer, LevelDownloadDelegate, LevelUpdateDeleg
|
|||
PAD = win 0x4, mac 0x8;
|
||||
}
|
||||
|
||||
class LevelLeaderboard : FLAlertLayer {
|
||||
void onChangeType(cocos2d::CCObject* sender) = win 0x17d090;
|
||||
void onUpdate(cocos2d::CCObject* sender) = win 0x17d1b0;
|
||||
class LevelLeaderboard : FLAlertLayer, LeaderboardManagerDelegate {
|
||||
void onChangeType(cocos2d::CCObject* sender) = mac 0x20e310, win 0x17d090;
|
||||
void onUpdate(cocos2d::CCObject* sender) = mac 0x20e240, win 0x17d1b0;
|
||||
bool init(GJGameLevel* level, LevelLeaderboardType type) = mac 0x20d710, win 0x17c4f0;
|
||||
static LevelLeaderboard* create(GJGameLevel* level, LevelLeaderboardType leaderboardType) = win 0x17c440;
|
||||
static LevelLeaderboard* create(GJGameLevel* level, LevelLeaderboardType leaderboardType) = mac 0x20D550, win 0x17c440;
|
||||
void onClose(cocos2d::CCObject*) = mac 0x20e210, win 0x49C60;
|
||||
|
||||
PAD = win 0x4;
|
||||
GJGameLevel* m_level;
|
||||
LevelLeaderboardType m_type;
|
||||
}
|
||||
|
||||
class LevelManagerDelegate {
|
||||
virtual void loadLevelsFailed(char const*) {}
|
||||
virtual void loadLevelsFinished(cocos2d::CCArray *,char const*) {}
|
||||
virtual void setupPageInfo(std::string,char const*) {}
|
||||
virtual void loadLevelsFailed(char const*) {}
|
||||
virtual void setupPageInfo(gd::string,char const*) {}
|
||||
}
|
||||
|
||||
class LevelPage {
|
||||
PAD = win 0x124;
|
||||
class LevelPage : cocos2d::CCLayer, DialogDelegate {
|
||||
PAD = mac 0x8, win 0x4;
|
||||
GJGameLevel* m_level;
|
||||
|
||||
void onInfo(cocos2d::CCObject* sender) = win 0x189070;
|
||||
void onInfo(cocos2d::CCObject* sender) = mac 0x23AAE0, win 0x189070;
|
||||
}
|
||||
|
||||
class LevelSearchLayer : cocos2d::CCLayer {
|
||||
|
@ -4286,6 +4311,7 @@ class MessageListDelegate {}
|
|||
class MoreSearchLayer : FLAlertLayer {
|
||||
static MoreSearchLayer* create() = mac 0x38ab40, win 0x182520;
|
||||
virtual bool init() = mac 0x3896b0, win 0x1825c0;
|
||||
void onClose(cocos2d::CCObject*) = mac 0x38aa40, win 0x1848f0;
|
||||
}
|
||||
|
||||
class MoreOptionsLayer : FLAlertLayer, TextInputDelegate, GooglePlayDelegate {
|
||||
|
@ -4739,7 +4765,7 @@ class PlayLayer : GJBaseGameLayer, CCCircleWaveDelegate, CurrencyRewardDelegate,
|
|||
bool unk42C;
|
||||
bool m_isPlayer2Frozen;
|
||||
gd::string m_previousRecords;
|
||||
double unknown6a8;
|
||||
cocos2d::CCArray* m_replayInputs;
|
||||
double m_time;
|
||||
int unknown6b8;
|
||||
int unknown6bc;
|
||||
|
@ -5112,18 +5138,18 @@ class PointNode : cocos2d::CCObject {
|
|||
|
||||
class ProfilePage : FLAlertLayer, FLAlertLayerProtocol, LevelCommentDelegate, CommentUploadDelegate, UserInfoDelegate, UploadActionDelegate, UploadPopupDelegate, LeaderboardManagerDelegate {
|
||||
static ProfilePage* create(int accountID, bool idk) = mac 0x45eed0, win 0x20ee50;
|
||||
virtual void getUserInfoFailed(int) = win 0x2133e0;
|
||||
virtual void getUserInfoChanged(GJUserScore*) = win 0x213430;
|
||||
virtual void getUserInfoFailed(int) = mac 0x463FB0, win 0x2133e0;
|
||||
virtual void userInfoChanged(GJUserScore*) = mac 0x464070, win 0x213430;
|
||||
bool init(int accountID, bool idk) = mac 0x45f170, win 0x20ef00;
|
||||
void onMyLevels(cocos2d::CCObject*) = win 0x211bb0;
|
||||
void onUpdate(cocos2d::CCObject*) = win 0x20fa20;
|
||||
void onMyLevels(cocos2d::CCObject*) = mac 0x462d70, win 0x211bb0;
|
||||
void onUpdate(cocos2d::CCObject*) = mac 0x460150, win 0x20fa20;
|
||||
void onClose(cocos2d::CCObject*) = mac 0x45fd20, win 0x49C60;
|
||||
virtual void keyBackClicked() = win 0x49C80;
|
||||
void loadPageFromUserInfo(GJUserScore* score) = win 0x210040;
|
||||
virtual void keyBackClicked() = mac 0x464A60, win 0x49C80;
|
||||
void loadPageFromUserInfo(GJUserScore* score) = mac 0x460480, win 0x210040;
|
||||
|
||||
GJUserScore* m_score;
|
||||
int m_accountID;
|
||||
PAD = win 0x38, android 0x24;
|
||||
PAD = mac 0x44, win 0x38, android 0x24;
|
||||
cocos2d::CCArray* m_buttons;
|
||||
}
|
||||
|
||||
|
@ -5644,7 +5670,7 @@ class TeleportPortalObject : GameObject {
|
|||
}
|
||||
|
||||
class TextAlertPopup : cocos2d::CCNode {
|
||||
static TextAlertPopup* create(gd::string text, float time, float scale) = win 0x1450b0;
|
||||
static TextAlertPopup* create(gd::string text, float time, float scale) = mac 0x157080, win 0x1450b0;
|
||||
}
|
||||
|
||||
class TextArea : cocos2d::CCSprite {
|
||||
|
@ -5752,7 +5778,7 @@ class UploadPopupDelegate {
|
|||
class UserInfoDelegate {
|
||||
virtual void getUserInfoFinished(GJUserScore *) {}
|
||||
virtual void getUserInfoFailed(int) {}
|
||||
virtual void getUserInfoChanged(GJUserScore *) {}
|
||||
virtual void userInfoChanged(GJUserScore *) {}
|
||||
}
|
||||
|
||||
class UserListDelegate {
|
||||
|
|
|
@ -36,7 +36,7 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "MacOS")
|
|||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
|
||||
# only exists as a global property
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13)
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
; pages
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE.txt"
|
||||
!define MUI_COMPONENTSPAGE_NODESC
|
||||
!insertmacro MUI_PAGE_COMPONENTS
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_SHOW DirectoryPageShow
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
@ -189,6 +191,132 @@
|
|||
Exch $R0
|
||||
!macroend
|
||||
|
||||
; https://nsis.sourceforge.io/StrStr
|
||||
Function un.StrStr
|
||||
Exch $R0
|
||||
Exch
|
||||
Exch $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
|
||||
StrLen $R2 $R0
|
||||
StrLen $R3 $R1
|
||||
|
||||
StrCpy $R4 0
|
||||
|
||||
loop:
|
||||
StrCpy $R5 $R1 $R2 $R4
|
||||
StrCmp $R5 $R0 done
|
||||
IntCmp $R4 $R3 done 0 done
|
||||
IntOp $R4 $R4 + 1
|
||||
Goto loop
|
||||
done:
|
||||
StrCpy $R0 $R1 `` $R4
|
||||
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
; https://nsis.sourceforge.io/Get_command_line_parameters
|
||||
Function un.GetParameters
|
||||
Push $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
|
||||
StrCpy $R2 1
|
||||
StrLen $R3 $CMDLINE
|
||||
|
||||
StrCpy $R0 $CMDLINE $R2
|
||||
StrCmp $R0 '"' 0 +3
|
||||
StrCpy $R1 '"'
|
||||
Goto loop
|
||||
StrCpy $R1 " "
|
||||
|
||||
loop:
|
||||
IntOp $R2 $R2 + 1
|
||||
StrCpy $R0 $CMDLINE 1 $R2
|
||||
StrCmp $R0 $R1 get
|
||||
StrCmp $R2 $R3 get
|
||||
Goto loop
|
||||
|
||||
get:
|
||||
IntOp $R2 $R2 + 1
|
||||
StrCpy $R0 $CMDLINE 1 $R2
|
||||
StrCmp $R0 " " get
|
||||
StrCpy $R0 $CMDLINE "" $R2
|
||||
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
; https://nsis.sourceforge.io/Get_command_line_parameter_by_name
|
||||
Function un.GetParameterValue
|
||||
Exch $R0
|
||||
Exch
|
||||
Exch $R1
|
||||
|
||||
Push $R2
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
|
||||
Strlen $R2 $R1+2
|
||||
|
||||
Call un.GetParameters
|
||||
Pop $R3
|
||||
|
||||
StrCpy $R5 '"'
|
||||
Push $R3
|
||||
Push '"/$R1='
|
||||
Call un.StrStr
|
||||
Pop $R4
|
||||
StrCpy $R4 $R4 "" 1
|
||||
StrCmp $R4 "" "" next
|
||||
|
||||
StrCpy $R5 ' '
|
||||
Push $R3
|
||||
Push '/$R1='
|
||||
Call un.StrStr
|
||||
Pop $R4
|
||||
|
||||
next:
|
||||
StrCmp $R4 "" check_for_switch
|
||||
StrCpy $R0 $R4 "" $R2
|
||||
Push $R0
|
||||
Push $R5
|
||||
Call un.StrStr
|
||||
Pop $R4
|
||||
StrCmp $R4 "" done
|
||||
StrLen $R4 $R4
|
||||
StrCpy $R0 $R0 -$R4
|
||||
goto done
|
||||
|
||||
check_for_switch:
|
||||
Push $R3
|
||||
Push '/$R1'
|
||||
Call un.StrStr
|
||||
Pop $R4
|
||||
StrCmp $R4 "" done
|
||||
StrCpy $R0 ""
|
||||
|
||||
done:
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
; actual code
|
||||
|
||||
!define BINDIR ..\..\bin\nightly
|
||||
|
@ -248,16 +376,6 @@ Function FindGamePath
|
|||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
Function .onInit
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
Call FindGamePath
|
||||
IfErrors 0 +3
|
||||
StrCpy $GamePath ""
|
||||
Return
|
||||
StrCpy $INSTDIR "$GamePath\"
|
||||
FunctionEnd
|
||||
|
||||
Function DirectoryPageShow
|
||||
System::Call 'USER32::CreateWindowEx(i${__NSD_Label_EXSTYLE}, t"${__NSD_Label_CLASS}", t"", i${__NSD_Label_STYLE}, i0, i70, i400, i40, p$mui.DirectoryPage, p0, p0, p0)p.s'
|
||||
Pop $geode.DirectoryPage.ErrorText
|
||||
|
@ -309,6 +427,25 @@ Function .onVerifyInstDir
|
|||
Return
|
||||
FunctionEnd
|
||||
|
||||
SectionGroup "Geode"
|
||||
Section "Loader" LOADER_SECTION
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
File ${BINDIR}\Geode.dll
|
||||
File ${BINDIR}\Geode.pdb
|
||||
File ${BINDIR}\GeodeUpdater.exe
|
||||
File ${BINDIR}\XInput9_1_0.dll
|
||||
|
||||
WriteUninstaller "GeodeUninstaller.exe"
|
||||
SectionEnd
|
||||
|
||||
Section "Resources"
|
||||
CreateDirectory $INSTDIR\geode\resources\geode.loader
|
||||
SetOutPath $INSTDIR\geode\resources\geode.loader
|
||||
File /r ${BINDIR}\resources\*
|
||||
SectionEnd
|
||||
SectionGroupEnd
|
||||
|
||||
; download vc redist in compile-time
|
||||
!execute "pwsh -nol -noni -nop dl-vcr.ps1"
|
||||
Section "Visual Studio Runtime"
|
||||
|
@ -318,29 +455,24 @@ Section "Visual Studio Runtime"
|
|||
Delete "$INSTDIR\VC_redist.x86.exe"
|
||||
SectionEnd
|
||||
|
||||
Section "Geode"
|
||||
SetOutPath $INSTDIR
|
||||
Function .onInit
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
File ${BINDIR}\Geode.dll
|
||||
File ${BINDIR}\Geode.pdb
|
||||
File ${BINDIR}\GeodeUpdater.exe
|
||||
File ${BINDIR}\XInput9_1_0.dll
|
||||
IntOp $0 ${SF_SELECTED} | ${SF_RO}
|
||||
SectionSetFlags ${LOADER_SECTION} $0
|
||||
|
||||
WriteUninstaller "geode\Uninstall.exe"
|
||||
|
||||
CreateDirectory $INSTDIR\geode\resources\geode.loader
|
||||
SetOutPath $INSTDIR\geode\resources\geode.loader
|
||||
|
||||
File /r ${BINDIR}\resources\*
|
||||
SectionEnd
|
||||
Call FindGamePath
|
||||
IfErrors 0 +3
|
||||
StrCpy $GamePath ""
|
||||
Return
|
||||
StrCpy $INSTDIR "$GamePath\"
|
||||
FunctionEnd
|
||||
|
||||
; uninstaller
|
||||
|
||||
Function un.onInit
|
||||
!insertmacro MUI_UNGETLANGUAGE
|
||||
|
||||
StrCpy $INSTDIR $INSTDIR -5 ; remove "geode/" at the end
|
||||
|
||||
; verify uninst dir
|
||||
|
||||
; check if there's any exe and libcocos2d.dll (GeometryDash.exe won't work because of GDPSes)
|
||||
|
@ -357,10 +489,47 @@ Function un.onInit
|
|||
Abort
|
||||
FunctionEnd
|
||||
Section "Uninstall"
|
||||
DeleteRegKey /ifempty HKCU "Software\Geode"
|
||||
Delete $INSTDIR\Geode.dll
|
||||
Delete $INSTDIR\Geode.pdb
|
||||
Delete $INSTDIR\GeodeUpdater.exe
|
||||
Delete $INSTDIR\XInput9_1_0.dll
|
||||
RMdir /r $INSTDIR\geode
|
||||
DeleteRegKey /ifempty HKCU "Software\Geode"
|
||||
|
||||
# default value of DATA is an empty string
|
||||
# if DATA is empty, keep user data
|
||||
# otherwise, delete the entire geode and DATA\geode\mods dirs
|
||||
# the reason we're deleting DATA\geode\mods instead of just passing
|
||||
# that dir directly to DATA is so that in case someone (either accidentally or maliciously)
|
||||
# passes the wrong directory, the uninstaller doesn't just blindly clear it
|
||||
# it will also check for the presence of CCGameManager.dat and CCLocalLevels.dat in DATA
|
||||
|
||||
Push "DATA"
|
||||
Push ""
|
||||
Call un.GetParameterValue
|
||||
Pop $0
|
||||
StrCmp $0 "" keep_data remove_data
|
||||
|
||||
keep_data:
|
||||
# keep configs, mods, logs and crash logs
|
||||
RMdir /r $INSTDIR\geode\index
|
||||
RMdir /r $INSTDIR\geode\resources
|
||||
RMdir /r $INSTDIR\geode\temp
|
||||
RMdir /r $INSTDIR\geode\unzipped
|
||||
RMdir /r $INSTDIR\geode\update
|
||||
Return
|
||||
|
||||
remove_data:
|
||||
RMdir /r $INSTDIR\geode
|
||||
IfFileExists $0\CCGameManager.dat 0 invalid
|
||||
IfFileExists $0\CCLocalLevels.dat 0 invalid
|
||||
RMdir /r $0\geode\mods ; delete DATA\geode\mods
|
||||
RMdir $0\geode ; then delete DATA\geode non-recursively, assuming mods is the only directory in DATA\geode
|
||||
Return
|
||||
|
||||
invalid:
|
||||
# this message doesnt rly need translatable as
|
||||
# its only supposed to be used internally by geode itself
|
||||
MessageBox MB_ICONSTOP|MB_OK "The path passed to DATA is not a valid Geometry Dash data folder!"
|
||||
Abort
|
||||
|
||||
SectionEnd
|
||||
|
|
|
@ -11,3 +11,4 @@
|
|||
#include "utils/general.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
#include "utils/MiniFunction.hpp"
|
||||
#include "utils/ObjcHook.hpp"
|
||||
|
|
347
loader/include/Geode/c++stl/gnustl-map.hpp
Normal file
347
loader/include/Geode/c++stl/gnustl-map.hpp
Normal file
|
@ -0,0 +1,347 @@
|
|||
#pragma once
|
||||
|
||||
namespace geode::stl {
|
||||
struct _rb_tree_base {
|
||||
bool m_isblack;
|
||||
_rb_tree_base* m_parent;
|
||||
_rb_tree_base* m_left;
|
||||
_rb_tree_base* m_right;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct _rb_tree_node : public _rb_tree_base {
|
||||
T m_value;
|
||||
};
|
||||
|
||||
inline void _rb_tree_rotate_left(_rb_tree_base* const x, _rb_tree_base*& root);
|
||||
inline void _rb_tree_rotate_right(_rb_tree_base* const x, _rb_tree_base*& root);
|
||||
inline void _rb_insert_rebalance(
|
||||
bool const insert_left, _rb_tree_base* x, _rb_tree_base* p, _rb_tree_base& header
|
||||
);
|
||||
inline _rb_tree_base* _rb_increment(_rb_tree_base* __x) noexcept;
|
||||
inline _rb_tree_base* _rb_decrement(_rb_tree_base* __x) noexcept;
|
||||
inline _rb_tree_base* _rb_rebalance_for_erase(_rb_tree_base* const __z, _rb_tree_base& __header) noexcept;
|
||||
|
||||
template <typename T>
|
||||
struct _rb_tree_iterator {
|
||||
typedef T value_type;
|
||||
typedef T& reference;
|
||||
typedef T* pointer;
|
||||
|
||||
typedef _rb_tree_iterator<T> _Self;
|
||||
typedef _rb_tree_base* _Base_ptr;
|
||||
typedef _rb_tree_node<T>* _Link_type;
|
||||
|
||||
_rb_tree_iterator() : m_node() {}
|
||||
|
||||
explicit _rb_tree_iterator(_Base_ptr __x) noexcept : m_node(__x) {}
|
||||
|
||||
reference operator*() const noexcept {
|
||||
return static_cast<_Link_type>(m_node)->m_value;
|
||||
}
|
||||
|
||||
pointer operator->() const noexcept {
|
||||
return &static_cast<_Link_type>(m_node)->m_value;
|
||||
}
|
||||
|
||||
_Self& operator++() noexcept {
|
||||
m_node = _rb_increment(m_node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_Self operator++(int) noexcept {
|
||||
_Self __tmp = *this;
|
||||
m_node = _rb_increment(m_node);
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
_Self& operator--() noexcept {
|
||||
m_node = _rb_decrement(m_node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_Self operator--(int) noexcept {
|
||||
_Self __tmp = *this;
|
||||
m_node = _rb_decrement(m_node);
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
bool operator==(_Self const& __x) const noexcept {
|
||||
return m_node == __x.m_node;
|
||||
}
|
||||
|
||||
bool operator!=(_Self const& __x) const noexcept {
|
||||
return m_node != __x.m_node;
|
||||
}
|
||||
|
||||
_Base_ptr m_node;
|
||||
};
|
||||
|
||||
inline void _rb_tree_rotate_left(_rb_tree_base* const x, _rb_tree_base*& root) {
|
||||
_rb_tree_base* const y = x->m_right;
|
||||
|
||||
x->m_right = y->m_left;
|
||||
if (y->m_left != 0) y->m_left->m_parent = x;
|
||||
y->m_parent = x->m_parent;
|
||||
|
||||
if (x == root) root = y;
|
||||
else if (x == x->m_parent->m_left) x->m_parent->m_left = y;
|
||||
else x->m_parent->m_right = y;
|
||||
y->m_left = x;
|
||||
x->m_parent = y;
|
||||
}
|
||||
|
||||
inline void _rb_tree_rotate_right(_rb_tree_base* const x, _rb_tree_base*& root) {
|
||||
_rb_tree_base* const y = x->m_left;
|
||||
|
||||
x->m_left = y->m_right;
|
||||
if (y->m_right != 0) y->m_right->m_parent = x;
|
||||
y->m_parent = x->m_parent;
|
||||
|
||||
if (x == root) root = y;
|
||||
else if (x == x->m_parent->m_right) x->m_parent->m_right = y;
|
||||
else x->m_parent->m_left = y;
|
||||
y->m_right = x;
|
||||
x->m_parent = y;
|
||||
}
|
||||
|
||||
inline void _rb_insert_rebalance(
|
||||
bool const insert_left, _rb_tree_base* x, _rb_tree_base* p, _rb_tree_base& header
|
||||
) {
|
||||
_rb_tree_base*& root = header.m_parent;
|
||||
|
||||
x->m_parent = p;
|
||||
x->m_left = 0;
|
||||
x->m_right = 0;
|
||||
x->m_isblack = false;
|
||||
|
||||
if (insert_left) {
|
||||
p->m_left = x;
|
||||
|
||||
if (p == &header) {
|
||||
header.m_parent = x;
|
||||
header.m_right = x;
|
||||
}
|
||||
else if (p == header.m_left) {
|
||||
header.m_left = x;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p->m_right = x;
|
||||
|
||||
if (p == header.m_right) {
|
||||
header.m_right = x;
|
||||
}
|
||||
}
|
||||
|
||||
while (x != root && x->m_parent->m_isblack == false) {
|
||||
_rb_tree_base* const xpp = x->m_parent->m_parent;
|
||||
|
||||
if (x->m_parent == xpp->m_left) {
|
||||
_rb_tree_base* const y = xpp->m_right;
|
||||
if (y && y->m_isblack == false) {
|
||||
x->m_parent->m_isblack = true;
|
||||
y->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
x = xpp;
|
||||
}
|
||||
else {
|
||||
if (x == x->m_parent->m_right) {
|
||||
x = x->m_parent;
|
||||
_rb_tree_rotate_left(x, root);
|
||||
}
|
||||
x->m_parent->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
_rb_tree_rotate_right(xpp, root);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* const y = xpp->m_left;
|
||||
if (y && y->m_isblack == false) {
|
||||
x->m_parent->m_isblack = true;
|
||||
y->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
x = xpp;
|
||||
}
|
||||
else {
|
||||
if (x == x->m_parent->m_left) {
|
||||
x = x->m_parent;
|
||||
_rb_tree_rotate_right(x, root);
|
||||
}
|
||||
x->m_parent->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
_rb_tree_rotate_left(xpp, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
root->m_isblack = true;
|
||||
}
|
||||
|
||||
inline _rb_tree_base* _rb_increment(_rb_tree_base* __x) noexcept {
|
||||
if (__x->m_right != 0) {
|
||||
__x = __x->m_right;
|
||||
while (__x->m_left != 0)
|
||||
__x = __x->m_left;
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* __y = __x->m_parent;
|
||||
while (__x == __y->m_right) {
|
||||
__x = __y;
|
||||
__y = __y->m_parent;
|
||||
}
|
||||
if (__x->m_right != __y) __x = __y;
|
||||
}
|
||||
return __x;
|
||||
}
|
||||
|
||||
inline _rb_tree_base* _rb_decrement(_rb_tree_base* __x) noexcept {
|
||||
if (!__x->m_isblack && __x->m_parent->m_parent == __x) __x = __x->m_right;
|
||||
else if (__x->m_left != 0) {
|
||||
_rb_tree_base* __y = __x->m_left;
|
||||
while (__y->m_right != 0)
|
||||
__y = __y->m_right;
|
||||
__x = __y;
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* __y = __x->m_parent;
|
||||
while (__x == __y->m_left) {
|
||||
__x = __y;
|
||||
__y = __y->m_parent;
|
||||
}
|
||||
__x = __y;
|
||||
}
|
||||
return __x;
|
||||
}
|
||||
|
||||
inline _rb_tree_base* _rb_rebalance_for_erase(
|
||||
_rb_tree_base* const __z, _rb_tree_base& __header
|
||||
) noexcept {
|
||||
_rb_tree_base*& __root = __header.m_parent;
|
||||
_rb_tree_base*& __leftmost = __header.m_left;
|
||||
_rb_tree_base*& __rightmost = __header.m_right;
|
||||
_rb_tree_base* __y = __z;
|
||||
_rb_tree_base* __x = 0;
|
||||
_rb_tree_base* __x_parent = 0;
|
||||
|
||||
if (__y->m_left == 0) // __z has at most one non-null child. y == z.
|
||||
__x = __y->m_right; // __x might be null.
|
||||
else if (__y->m_right == 0) // __z has exactly one non-null child. y == z.
|
||||
__x = __y->m_left; // __x is not null.
|
||||
else {
|
||||
// __z has two non-null children. Set __y to
|
||||
__y = __y->m_right; // __z's successor. __x might be null.
|
||||
while (__y->m_left != 0)
|
||||
__y = __y->m_left;
|
||||
__x = __y->m_right;
|
||||
}
|
||||
if (__y != __z) {
|
||||
// relink y in place of z. y is z's successor
|
||||
__z->m_left->m_parent = __y;
|
||||
__y->m_left = __z->m_left;
|
||||
if (__y != __z->m_right) {
|
||||
__x_parent = __y->m_parent;
|
||||
if (__x) __x->m_parent = __y->m_parent;
|
||||
__y->m_parent->m_left = __x; // __y must be a child of m_left
|
||||
__y->m_right = __z->m_right;
|
||||
__z->m_right->m_parent = __y;
|
||||
}
|
||||
else __x_parent = __y;
|
||||
if (__root == __z) __root = __y;
|
||||
else if (__z->m_parent->m_left == __z) __z->m_parent->m_left = __y;
|
||||
else __z->m_parent->m_right = __y;
|
||||
__y->m_parent = __z->m_parent;
|
||||
std::swap(__y->m_isblack, __z->m_isblack);
|
||||
__y = __z;
|
||||
// __y now points to node to be actually deleted
|
||||
}
|
||||
else { // __y == __z
|
||||
__x_parent = __y->m_parent;
|
||||
if (__x) __x->m_parent = __y->m_parent;
|
||||
if (__root == __z) __root = __x;
|
||||
else if (__z->m_parent->m_left == __z) __z->m_parent->m_left = __x;
|
||||
else __z->m_parent->m_right = __x;
|
||||
if (__leftmost == __z) {
|
||||
if (__z->m_right == 0) // __z->m_left must be null also
|
||||
__leftmost = __z->m_parent;
|
||||
// makes __leftmost == _M_header if __z == __root
|
||||
else {
|
||||
__leftmost = __x;
|
||||
while (__leftmost->m_left != 0)
|
||||
__leftmost = __leftmost->m_left;
|
||||
}
|
||||
}
|
||||
if (__rightmost == __z) {
|
||||
if (__z->m_left == 0) // __z->m_right must be null also
|
||||
__rightmost = __z->m_parent;
|
||||
// makes __rightmost == _M_header if __z == __root
|
||||
else { // __x == __z->m_left
|
||||
__rightmost = __x;
|
||||
while (__rightmost->m_right != 0)
|
||||
__rightmost = __rightmost->m_right;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__y->m_isblack != false) {
|
||||
while (__x != __root && (__x == 0 || __x->m_isblack))
|
||||
if (__x == __x_parent->m_left) {
|
||||
_rb_tree_base* __w = __x_parent->m_right;
|
||||
if (__w->m_isblack == false) {
|
||||
__w->m_isblack = true;
|
||||
__x_parent->m_isblack = false;
|
||||
_rb_tree_rotate_left(__x_parent, __root);
|
||||
__w = __x_parent->m_right;
|
||||
}
|
||||
if ((__w->m_left == 0 || __w->m_left->m_isblack == true) &&
|
||||
(__w->m_right == 0 || __w->m_right->m_isblack == true)) {
|
||||
__w->m_isblack = false;
|
||||
__x = __x_parent;
|
||||
__x_parent = __x_parent->m_parent;
|
||||
}
|
||||
else {
|
||||
if (__w->m_right == 0 || __w->m_right->m_isblack == true) {
|
||||
__w->m_left->m_isblack = true;
|
||||
__w->m_isblack = false;
|
||||
_rb_tree_rotate_right(__w, __root);
|
||||
__w = __x_parent->m_right;
|
||||
}
|
||||
__w->m_isblack = __x_parent->m_isblack;
|
||||
__x_parent->m_isblack = true;
|
||||
if (__w->m_right) __w->m_right->m_isblack = true;
|
||||
_rb_tree_rotate_left(__x_parent, __root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// same as above, with m_right <-> m_left.
|
||||
_rb_tree_base* __w = __x_parent->m_left;
|
||||
if (__w->m_isblack == false) {
|
||||
__w->m_isblack = true;
|
||||
__x_parent->m_isblack = false;
|
||||
_rb_tree_rotate_right(__x_parent, __root);
|
||||
__w = __x_parent->m_left;
|
||||
}
|
||||
if ((__w->m_right == 0 || __w->m_right->m_isblack == true) &&
|
||||
(__w->m_left == 0 || __w->m_left->m_isblack == true)) {
|
||||
__w->m_isblack = false;
|
||||
__x = __x_parent;
|
||||
__x_parent = __x_parent->m_parent;
|
||||
}
|
||||
else {
|
||||
if (__w->m_left == 0 || __w->m_left->m_isblack == true) {
|
||||
__w->m_right->m_isblack = true;
|
||||
__w->m_isblack = false;
|
||||
_rb_tree_rotate_left(__w, __root);
|
||||
__w = __x_parent->m_left;
|
||||
}
|
||||
__w->m_isblack = __x_parent->m_isblack;
|
||||
__x_parent->m_isblack = true;
|
||||
if (__w->m_left) __w->m_left->m_isblack = true;
|
||||
_rb_tree_rotate_right(__x_parent, __root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (__x) __x->m_isblack = true;
|
||||
}
|
||||
return __y;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,12 @@ namespace geode::base {
|
|||
}
|
||||
|
||||
#if defined(GEODE_IS_MACOS) || defined(GEODE_IS_ANDROID)
|
||||
|
||||
#include "gnustl-map.hpp"
|
||||
|
||||
namespace gd {
|
||||
using namespace geode::stl;
|
||||
|
||||
struct _internal_string {
|
||||
size_t m_len;
|
||||
size_t m_capacity;
|
||||
|
@ -48,18 +53,6 @@ namespace gd {
|
|||
_internal_string* m_data;
|
||||
};
|
||||
|
||||
struct _rb_tree_base {
|
||||
bool m_isblack;
|
||||
_rb_tree_base* m_parent;
|
||||
_rb_tree_base* m_left;
|
||||
_rb_tree_base* m_right;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct _rb_tree_node : public _rb_tree_base {
|
||||
T m_value;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
class GEODE_DLL map {
|
||||
protected:
|
||||
|
@ -69,26 +62,204 @@ namespace gd {
|
|||
|
||||
public:
|
||||
typedef _rb_tree_node<std::pair<K, V>>* _tree_node;
|
||||
typedef _rb_tree_iterator<std::pair<K, V>> iterator;
|
||||
|
||||
std::map<K, V> std();
|
||||
std::map<K, V> std() {
|
||||
return (std::map<K, V>)(*this);
|
||||
}
|
||||
|
||||
operator std::map<K, V>();
|
||||
operator std::map<K, V>() {
|
||||
auto iter_node = static_cast<_tree_node>(m_header.m_left);
|
||||
auto end_node = static_cast<_tree_node>(&m_header);
|
||||
std::map<K, V> out;
|
||||
for (; iter_node != end_node;
|
||||
iter_node = static_cast<_tree_node>(_rb_increment(iter_node))) {
|
||||
out[iter_node->m_value.first] = iter_node->m_value.second;
|
||||
}
|
||||
|
||||
operator std::map<K, V>() const;
|
||||
return out;
|
||||
}
|
||||
|
||||
void insert(_tree_node x, _tree_node p, std::pair<K, V> const& val);
|
||||
operator std::map<K, V>() const {
|
||||
auto iter_node = static_cast<_tree_node>(m_header.m_left);
|
||||
auto end_node = (_tree_node)(&m_header);
|
||||
std::map<K, V> out;
|
||||
for (; iter_node != end_node;
|
||||
iter_node = static_cast<_tree_node>(_rb_increment(iter_node))) {
|
||||
out[iter_node->m_value.first] = iter_node->m_value.second;
|
||||
}
|
||||
|
||||
void insert_pair(std::pair<K, V> const& val);
|
||||
return out;
|
||||
}
|
||||
|
||||
map(std::map<K, V> input);
|
||||
void insert(_tree_node x, _tree_node p, std::pair<K, V> const& val) {
|
||||
bool insert_left =
|
||||
(x != 0 || p == static_cast<_tree_node>(&m_header) || val.first < p->m_value.first);
|
||||
|
||||
void erase(_tree_node x);
|
||||
_tree_node z = new _rb_tree_node<std::pair<K, V>>();
|
||||
z->m_value = val;
|
||||
|
||||
map(map const& lol);
|
||||
_rb_insert_rebalance(insert_left, z, p, m_header);
|
||||
++m_nodecount;
|
||||
}
|
||||
|
||||
map();
|
||||
void insert(std::pair<K, V> const& val) {
|
||||
insert_pair(val);
|
||||
}
|
||||
|
||||
~map();
|
||||
void insert_pair(std::pair<K, V> const& val) {
|
||||
_tree_node x = static_cast<_tree_node>(m_header.m_parent);
|
||||
_tree_node y = static_cast<_tree_node>(&m_header);
|
||||
bool comp = true;
|
||||
while (x != 0) {
|
||||
y = x;
|
||||
comp = val.first < x->m_value.first;
|
||||
x = comp ? static_cast<_tree_node>(x->m_left) : static_cast<_tree_node>(x->m_right);
|
||||
}
|
||||
auto iter = y;
|
||||
|
||||
if (comp) {
|
||||
if (iter == static_cast<_tree_node>(m_header.m_left)) {
|
||||
insert(x, y, val);
|
||||
}
|
||||
else {
|
||||
iter = static_cast<_tree_node>(_rb_decrement(iter));
|
||||
}
|
||||
}
|
||||
if (iter->m_value.first < val.first) {
|
||||
insert(x, y, val);
|
||||
}
|
||||
}
|
||||
|
||||
map(std::map<K, V> input) {
|
||||
m_header.m_isblack = false;
|
||||
m_header.m_parent = 0;
|
||||
m_header.m_left = &m_header;
|
||||
m_header.m_right = &m_header;
|
||||
|
||||
for (auto i : input) {
|
||||
insert_pair(i);
|
||||
}
|
||||
}
|
||||
|
||||
void erase(_tree_node x) {
|
||||
while (x != 0) {
|
||||
erase(static_cast<_tree_node>(x->m_right));
|
||||
auto y = static_cast<_tree_node>(x->m_left);
|
||||
delete y;
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(K const& __k) {
|
||||
return std::pair<iterator, iterator>(lower_bound(__k), upper_bound(__k));
|
||||
}
|
||||
|
||||
size_t erase(K const& __x) {
|
||||
std::pair<iterator, iterator> __p = equal_range(__x);
|
||||
size_t __old = size();
|
||||
erase(__p.first, __p.second);
|
||||
return __old - size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
erase(static_cast<_tree_node>(m_header.m_parent));
|
||||
m_header.m_parent = 0;
|
||||
m_header.m_left = &m_header;
|
||||
m_header.m_right = &m_header;
|
||||
m_nodecount = 0;
|
||||
}
|
||||
|
||||
void erase(iterator __first, iterator __last) {
|
||||
if (__first == begin() && __last == end()) {
|
||||
clear();
|
||||
}
|
||||
else {
|
||||
while (__first != __last) {
|
||||
erase(__first++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void erase(iterator __pos) {
|
||||
_tree_node __y = static_cast<_tree_node>(_rb_rebalance_for_erase(
|
||||
__pos.m_node, m_header
|
||||
));
|
||||
delete __y;
|
||||
--m_nodecount;
|
||||
}
|
||||
|
||||
V& operator[](K const& __k) {
|
||||
iterator __i = lower_bound(__k);
|
||||
if (__i == end() || compare(__k, (*__i).first)) {
|
||||
insert_pair(std::pair<K, V>(__k, V()));
|
||||
__i = lower_bound(__k);
|
||||
}
|
||||
return (*__i).second;
|
||||
}
|
||||
|
||||
iterator begin() noexcept {
|
||||
return iterator(m_header.m_left);
|
||||
}
|
||||
|
||||
iterator end() noexcept {
|
||||
return iterator(&m_header);
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return m_nodecount == 0;
|
||||
}
|
||||
|
||||
size_t size() const noexcept {
|
||||
return m_nodecount;
|
||||
}
|
||||
|
||||
iterator lower_bound(K const& __x) {
|
||||
_tree_node __j = static_cast<_tree_node>(m_header.m_left);
|
||||
_tree_node __k = static_cast<_tree_node>(&m_header);
|
||||
while (__j != nullptr) {
|
||||
if (!compare(__j->m_value.first, __x)) {
|
||||
__k = __j;
|
||||
__j = static_cast<_tree_node>(__j->m_left);
|
||||
}
|
||||
else {
|
||||
__j = static_cast<_tree_node>(__j->m_right);
|
||||
}
|
||||
}
|
||||
return iterator(__k);
|
||||
}
|
||||
|
||||
iterator upper_bound(K const& __x) {
|
||||
_tree_node __j = static_cast<_tree_node>(m_header.m_left);
|
||||
_tree_node __k = static_cast<_tree_node>(&m_header);
|
||||
while (__j != nullptr) {
|
||||
if (compare(__x, __j->m_value.first)) {
|
||||
__k = __j;
|
||||
__j = static_cast<_tree_node>(__j->m_left);
|
||||
}
|
||||
else {
|
||||
__j = static_cast<_tree_node>(__j->m_right);
|
||||
}
|
||||
}
|
||||
return iterator(__k);
|
||||
}
|
||||
|
||||
iterator find(K const& __x) {
|
||||
iterator __j = lower_bound(__x);
|
||||
return (__j == end() || compare(__x, (*__j).first)) ? end() : __j;
|
||||
}
|
||||
|
||||
size_t count(K const& __x) {
|
||||
return find(__x) != end() ? 1 : 0;
|
||||
}
|
||||
|
||||
map(map const& lol) : map(std::map<K, V>(lol)) {}
|
||||
|
||||
map() : map(std::map<K, V>()) {}
|
||||
|
||||
~map() {
|
||||
erase(static_cast<_tree_node>(m_header.m_parent));
|
||||
}
|
||||
};
|
||||
|
||||
// template <class Type>
|
||||
|
|
|
@ -118,9 +118,25 @@ namespace gd {
|
|||
return m_data.m_length;
|
||||
}
|
||||
|
||||
bool operator<(const gd::string& other) const {
|
||||
return std::string_view(this->c_str(), this->size()) < std::string_view(other.c_str(), other.size());
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return this->size() == 0;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return !this->empty();
|
||||
}
|
||||
|
||||
operator std::string() const {
|
||||
return std::string(this->c_str(), this->size());
|
||||
}
|
||||
|
||||
operator std::string_view() const {
|
||||
return std::string_view(this->c_str(), this->size());
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
|
|
|
@ -101,7 +101,8 @@ namespace geode {
|
|||
void updateResources();
|
||||
void updateResources(bool forceReload);
|
||||
|
||||
void queueInGDThread(ScheduledFunction func);
|
||||
[[deprecated("use queueInMainThread instead")]] void queueInGDThread(ScheduledFunction func);
|
||||
void queueInMainThread(ScheduledFunction func);
|
||||
void waitForModsToBeLoaded();
|
||||
|
||||
/**
|
||||
|
|
68
loader/include/Geode/utils/ObjcHook.hpp
Normal file
68
loader/include/Geode/utils/ObjcHook.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include "../loader/Hook.hpp"
|
||||
#include "Result.hpp"
|
||||
|
||||
namespace geode {
|
||||
namespace hook {
|
||||
/**
|
||||
* Add a new Objective-C method to a class. This method will be created
|
||||
* using the imp provided. If the method already exists, it won't do
|
||||
* anything.
|
||||
* @param className The name of the class to add the method to
|
||||
* @param selectorName The name of the method to add
|
||||
* @param imp The implementation of the method
|
||||
* @returns Ok() if the method was added successfully, or an error.
|
||||
*/
|
||||
Result<> addObjcMethod(std::string const& className, std::string const& selectorName, void* imp);
|
||||
|
||||
/**
|
||||
* Get the implementation of an Objective-C method.
|
||||
* @param className The name of the class whose method to get
|
||||
* @param selectorName The name of the method to get
|
||||
* @returns The implementation of the method, or an error.
|
||||
*/
|
||||
Result<void*> getObjcMethodImp(std::string const& className, std::string const& selectorName);
|
||||
}
|
||||
|
||||
class ObjcHook {
|
||||
public:
|
||||
/**
|
||||
* Create a hook for an Objective-C method
|
||||
* @param className The name of the class whose method to hook
|
||||
* @param selectorName The name of the method to hook
|
||||
* @param function The detour to run when the method is called
|
||||
* @returns The created hook, or an error.
|
||||
*/
|
||||
template <class Func>
|
||||
static Result<Hook*> create(std::string const& className, std::string const& selectorName, Func function, tulip::hook::HookMetadata const& metadata = tulip::hook::HookMetadata()) {
|
||||
GEODE_UNWRAP_INTO(auto imp, geode::hook::getObjcMethodImp(className, selectorName));
|
||||
|
||||
return Ok(Hook::create(
|
||||
getMod(),
|
||||
imp,
|
||||
function,
|
||||
className + "::" + selectorName,
|
||||
tulip::hook::TulipConvention::Default,
|
||||
metadata
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hook for a new Objective-C method. This method will be
|
||||
* created with a dummy implementation that does nothing.
|
||||
* @param className The name of the class whose method to hook
|
||||
* @param selectorName The name of the method to hook
|
||||
* @param function The detour to run when the method is called
|
||||
* @param empty A function that takes no arguments and returns nothing.
|
||||
* This is used to create a dummy method that can be hooked.
|
||||
* @returns The created hook, or an error.
|
||||
*/
|
||||
template <class Func>
|
||||
static Result<Hook*> create(std::string const& className, std::string const& selectorName, Func function, void(*empty)(), tulip::hook::HookMetadata const& metadata = tulip::hook::HookMetadata()) {
|
||||
GEODE_UNWRAP(geode::hook::addObjcMethod(className, selectorName, (void*)empty));
|
||||
|
||||
return ObjcHook::create(className, selectorName, function, metadata);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -91,6 +91,14 @@ namespace geode::addresser {
|
|||
*reinterpret_cast<intptr_t*>(reinterpret_cast<intptr_t>(ins) + thunk) + index
|
||||
);
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
// if the first instruction is a long jmp then this might still be a thunk
|
||||
if (*reinterpret_cast<uint8_t*>(address) == 0xE9) {
|
||||
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
|
||||
address = address + relative + 5;
|
||||
}
|
||||
#endif
|
||||
|
||||
address = followThunkFunction(address);
|
||||
|
||||
return address;
|
||||
|
|
|
@ -93,4 +93,19 @@ struct FileUtilsUpdatePaths : Modify<FileUtilsUpdatePaths, CCFileUtils> {
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
gd::string fullPathForFilename(const char* filename, bool unk) override {
|
||||
using namespace std::string_literals;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
// this filename in particular (cc_2x2_white_image) is never cached because its not actually present anywhere.
|
||||
// this is only an issue because cocos itself requests the full path for this in CCSprite,
|
||||
// and with a lot of search paths (specially ones added by geode), this can cause a significant amount of lag.
|
||||
// GJ_GameSheetIcons.png comes from an improper plist distributed in GDS :P
|
||||
if (filename == "cc_2x2_white_image"sv || filename == "GJ_GameSheetIcons.png"sv) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
return CCFileUtils::fullPathForFilename(filename, unk);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -91,7 +91,7 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
void loadAssets() {
|
||||
if (Loader::get()->getLoadingState() != Loader::LoadingState::Done) {
|
||||
this->updateLoadedModsLabel();
|
||||
Loader::get()->queueInGDThread([this]() {
|
||||
Loader::get()->queueInMainThread([this]() {
|
||||
this->loadAssets();
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -27,13 +27,13 @@ $register_ids(CustomizeObjectLayer) {
|
|||
0,
|
||||
"ok-button",
|
||||
"info-button",
|
||||
|
||||
// 2
|
||||
"base-tab-button",
|
||||
"detail-tab-button",
|
||||
"base-hsv-button",
|
||||
"detail-hsv-button",
|
||||
"text-tab-button",
|
||||
|
||||
// 7
|
||||
"player-color-1-button",
|
||||
"player-color-2-button",
|
||||
"light-bg-button",
|
||||
|
@ -48,24 +48,40 @@ $register_ids(CustomizeObjectLayer) {
|
|||
"channel-8-button",
|
||||
"channel-9-button",
|
||||
"channel-custom-button",
|
||||
|
||||
// 21
|
||||
"select-channel-up-button",
|
||||
"select-channel-down-button",
|
||||
"select-channel-button",
|
||||
|
||||
// 24
|
||||
"split-text-button",
|
||||
"clear-text-button",
|
||||
|
||||
"next-free-button",
|
||||
|
||||
"copy-button",
|
||||
"paste-button",
|
||||
|
||||
"browse-button",
|
||||
|
||||
"glow-toggle"
|
||||
// 26
|
||||
"next-free-button"
|
||||
// 27
|
||||
);
|
||||
|
||||
if (m_targetObject) {
|
||||
setIDs(
|
||||
m_buttonMenu,
|
||||
27,
|
||||
"copy-button",
|
||||
"paste-button",
|
||||
|
||||
"browse-button",
|
||||
|
||||
"glow-toggle"
|
||||
);
|
||||
}
|
||||
else {
|
||||
setIDs(
|
||||
m_buttonMenu,
|
||||
27,
|
||||
"browse-button",
|
||||
|
||||
"glow-toggle"
|
||||
);
|
||||
}
|
||||
|
||||
auto tabsLayout = RowLayout::create()
|
||||
->setAxisAlignment(AxisAlignment::Center)
|
||||
->setCrossAxisAlignment(AxisAlignment::Center);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "crashlog.hpp"
|
||||
#include <fmt/core.h>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -26,12 +27,13 @@ static void printMods(std::stringstream& stream) {
|
|||
if (!mods.size()) {
|
||||
stream << "<None>\n";
|
||||
}
|
||||
using namespace std::string_view_literals;
|
||||
for (auto& mod : mods) {
|
||||
stream << mod->getID() << " | " << mod->getDeveloper() << " | "
|
||||
<< mod->getVersion().toString() << " | "
|
||||
<< (mod->isEnabled() ? "Enabled" : "Disabled") << " | "
|
||||
<< (mod->isLoaded() ? "Loaded" : "Unloaded") << " | "
|
||||
<< "\n";
|
||||
stream << fmt::format("{:>8} | {:>8} | [{}] {}\n",
|
||||
mod->isLoaded() ? "Loaded"sv : "Unloaded"sv,
|
||||
mod->isEnabled() ? "Enabled"sv : "Disabled"sv,
|
||||
mod->getVersion().toString(), mod->getID()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -599,6 +599,8 @@ Result<IndexInstallList> Index::getInstallList(IndexItemHandle item) const {
|
|||
|
||||
if (dep.importance == ModMetadata::Dependency::Importance::Suggested) continue;
|
||||
|
||||
if (Loader::get()->isModInstalled(dep.id)) continue;
|
||||
|
||||
// check if this dep is available in the index
|
||||
if (auto depItem = this->getItem(dep.id, dep.version)) {
|
||||
if (!depItem->getAvailablePlatforms().count(GEODE_PLATFORM_TARGET)) {
|
||||
|
@ -668,7 +670,7 @@ void Index::Impl::installNext(size_t index, IndexInstallList const& list) {
|
|||
}
|
||||
|
||||
auto const& eventModID = list.target->getMetadata().getID();
|
||||
Loader::get()->queueInGDThread([eventModID]() {
|
||||
Loader::get()->queueInMainThread([eventModID]() {
|
||||
ModInstallEvent(eventModID, UpdateFinished()).post();
|
||||
});
|
||||
|
||||
|
@ -741,7 +743,7 @@ void Index::Impl::installNext(size_t index, IndexInstallList const& list) {
|
|||
}
|
||||
|
||||
void Index::cancelInstall(IndexItemHandle item) {
|
||||
Loader::get()->queueInGDThread([this, item]() {
|
||||
Loader::get()->queueInMainThread([this, item]() {
|
||||
if (m_impl->m_runningInstallations.count(item)) {
|
||||
m_impl->m_runningInstallations.at(item)->cancel();
|
||||
m_impl->m_runningInstallations.erase(item);
|
||||
|
@ -750,13 +752,13 @@ void Index::cancelInstall(IndexItemHandle item) {
|
|||
}
|
||||
|
||||
void Index::install(IndexInstallList const& list) {
|
||||
Loader::get()->queueInGDThread([this, list]() {
|
||||
Loader::get()->queueInMainThread([this, list]() {
|
||||
m_impl->installNext(0, list);
|
||||
});
|
||||
}
|
||||
|
||||
void Index::install(IndexItemHandle item) {
|
||||
Loader::get()->queueInGDThread([this, item]() {
|
||||
Loader::get()->queueInMainThread([this, item]() {
|
||||
if (m_impl->m_runningInstallations.count(item)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -112,7 +112,11 @@ void Loader::updateResources(bool forceReload) {
|
|||
}
|
||||
|
||||
void Loader::queueInGDThread(ScheduledFunction func) {
|
||||
return m_impl->queueInGDThread(func);
|
||||
return m_impl->queueInMainThread(func);
|
||||
}
|
||||
|
||||
void Loader::queueInMainThread(ScheduledFunction func) {
|
||||
return m_impl->queueInMainThread(func);
|
||||
}
|
||||
|
||||
void Loader::waitForModsToBeLoaded() {
|
||||
|
|
|
@ -216,6 +216,13 @@ Mod* Loader::Impl::getLoadedMod(std::string const& id) const {
|
|||
}
|
||||
|
||||
void Loader::Impl::updateModResources(Mod* mod) {
|
||||
if (mod != Mod::get()) {
|
||||
// geode.loader resource is stored somewhere else, which is already added anyway
|
||||
auto searchPathRoot = dirs::getModRuntimeDir() / mod->getID() / "resources";
|
||||
CCFileUtils::get()->addSearchPath(searchPathRoot.string().c_str());
|
||||
}
|
||||
|
||||
// only thing needs previous setup is spritesheets
|
||||
if (mod->getMetadata().getSpritesheets().empty())
|
||||
return;
|
||||
|
||||
|
@ -347,12 +354,6 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
|
|||
|
||||
m_mods.insert({metadata.getID(), mod});
|
||||
|
||||
queueInGDThread([this, mod]() {
|
||||
auto searchPath = dirs::getModRuntimeDir() / mod->getID() / "resources";
|
||||
CCFileUtils::get()->addSearchPath(searchPath.string().c_str());
|
||||
updateModResources(mod);
|
||||
});
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
@ -579,7 +580,7 @@ void Loader::Impl::refreshModGraph() {
|
|||
else
|
||||
m_loadingState = LoadingState::Mods;
|
||||
|
||||
queueInGDThread([]() {
|
||||
queueInMainThread([]() {
|
||||
Loader::get()->m_impl->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
@ -619,7 +620,7 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||
|
||||
if (m_loadingState != LoadingState::Done) {
|
||||
queueInGDThread([]() {
|
||||
queueInMainThread([]() {
|
||||
Loader::get()->m_impl->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
@ -675,7 +676,7 @@ bool Loader::Impl::loadHooks() {
|
|||
return !thereWereErrors;
|
||||
}
|
||||
|
||||
void Loader::Impl::queueInGDThread(ScheduledFunction func) {
|
||||
void Loader::Impl::queueInMainThread(ScheduledFunction func) {
|
||||
std::lock_guard<std::mutex> lock(m_gdThreadMutex);
|
||||
m_gdThreadQueue.push_back(func);
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace geode {
|
|||
|
||||
json::Value processRawIPC(void* rawHandle, std::string const& buffer);
|
||||
|
||||
void queueInGDThread(ScheduledFunction func);
|
||||
void queueInMainThread(ScheduledFunction func);
|
||||
void executeGDThreadQueue();
|
||||
|
||||
void logConsoleMessage(std::string const& msg);
|
||||
|
|
|
@ -155,7 +155,7 @@ std::vector<Hook*> Mod::Impl::getHooks() const {
|
|||
// Settings and saved values
|
||||
|
||||
Result<> Mod::Impl::loadData() {
|
||||
Loader::get()->queueInGDThread([&]() {
|
||||
Loader::get()->queueInMainThread([&]() {
|
||||
ModStateEvent(m_self, ModEventType::DataLoaded).post();
|
||||
});
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ static void handlerThread() {
|
|||
|
||||
auto text = crashlog::writeCrashlog(faultyMod, getInfo(signalAddress, faultyMod), getStacktrace(), getRegisters());
|
||||
|
||||
log::error("Geode crashed!\n{}" + text);
|
||||
log::error("Geode crashed!\n{}", text);
|
||||
|
||||
s_signal = 0;
|
||||
s_cv.notify_all();
|
||||
|
@ -375,4 +375,4 @@ ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
|||
}
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -57,241 +57,7 @@ namespace gd {
|
|||
return std::string(*this) == std::string(other);
|
||||
}
|
||||
|
||||
static void _rb_tree_rotate_left(_rb_tree_base* const x, _rb_tree_base*& root) {
|
||||
_rb_tree_base* const y = x->m_right;
|
||||
|
||||
x->m_right = y->m_left;
|
||||
if (y->m_left != 0) y->m_left->m_parent = x;
|
||||
y->m_parent = x->m_parent;
|
||||
|
||||
if (x == root) root = y;
|
||||
else if (x == x->m_parent->m_left) x->m_parent->m_left = y;
|
||||
else x->m_parent->m_right = y;
|
||||
y->m_left = x;
|
||||
x->m_parent = y;
|
||||
}
|
||||
|
||||
static void _rb_tree_rotate_right(_rb_tree_base* const x, _rb_tree_base*& root) {
|
||||
_rb_tree_base* const y = x->m_left;
|
||||
|
||||
x->m_left = y->m_right;
|
||||
if (y->m_right != 0) y->m_right->m_parent = x;
|
||||
y->m_parent = x->m_parent;
|
||||
|
||||
if (x == root) root = y;
|
||||
else if (x == x->m_parent->m_right) x->m_parent->m_right = y;
|
||||
else x->m_parent->m_left = y;
|
||||
y->m_right = x;
|
||||
x->m_parent = y;
|
||||
}
|
||||
|
||||
static void _rb_insert_rebalance(
|
||||
bool const insert_left, _rb_tree_base* x, _rb_tree_base* p, _rb_tree_base& header
|
||||
) {
|
||||
_rb_tree_base*& root = header.m_parent;
|
||||
|
||||
x->m_parent = p;
|
||||
x->m_left = 0;
|
||||
x->m_right = 0;
|
||||
x->m_isblack = false;
|
||||
|
||||
if (insert_left) {
|
||||
p->m_left = x;
|
||||
|
||||
if (p == &header) {
|
||||
header.m_parent = x;
|
||||
header.m_right = x;
|
||||
}
|
||||
else if (p == header.m_left) {
|
||||
header.m_left = x;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p->m_right = x;
|
||||
|
||||
if (p == header.m_right) {
|
||||
header.m_right = x;
|
||||
}
|
||||
}
|
||||
|
||||
while (x != root && x->m_parent->m_isblack == false) {
|
||||
_rb_tree_base* const xpp = x->m_parent->m_parent;
|
||||
|
||||
if (x->m_parent == xpp->m_left) {
|
||||
_rb_tree_base* const y = xpp->m_right;
|
||||
if (y && y->m_isblack == false) {
|
||||
x->m_parent->m_isblack = true;
|
||||
y->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
x = xpp;
|
||||
}
|
||||
else {
|
||||
if (x == x->m_parent->m_right) {
|
||||
x = x->m_parent;
|
||||
_rb_tree_rotate_left(x, root);
|
||||
}
|
||||
x->m_parent->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
_rb_tree_rotate_right(xpp, root);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* const y = xpp->m_left;
|
||||
if (y && y->m_isblack == false) {
|
||||
x->m_parent->m_isblack = true;
|
||||
y->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
x = xpp;
|
||||
}
|
||||
else {
|
||||
if (x == x->m_parent->m_left) {
|
||||
x = x->m_parent;
|
||||
_rb_tree_rotate_right(x, root);
|
||||
}
|
||||
x->m_parent->m_isblack = true;
|
||||
xpp->m_isblack = false;
|
||||
_rb_tree_rotate_left(xpp, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
root->m_isblack = true;
|
||||
}
|
||||
|
||||
static _rb_tree_base* _rb_increment(_rb_tree_base* __x) throw() {
|
||||
if (__x->m_right != 0) {
|
||||
__x = __x->m_right;
|
||||
while (__x->m_left != 0)
|
||||
__x = __x->m_left;
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* __y = __x->m_parent;
|
||||
while (__x == __y->m_right) {
|
||||
__x = __y;
|
||||
__y = __y->m_parent;
|
||||
}
|
||||
if (__x->m_right != __y) __x = __y;
|
||||
}
|
||||
return __x;
|
||||
}
|
||||
|
||||
static _rb_tree_base* _rb_decrement(_rb_tree_base* __x) throw() {
|
||||
if (!__x->m_isblack && __x->m_parent->m_parent == __x) __x = __x->m_right;
|
||||
else if (__x->m_left != 0) {
|
||||
_rb_tree_base* __y = __x->m_left;
|
||||
while (__y->m_right != 0)
|
||||
__y = __y->m_right;
|
||||
__x = __y;
|
||||
}
|
||||
else {
|
||||
_rb_tree_base* __y = __x->m_parent;
|
||||
while (__x == __y->m_left) {
|
||||
__x = __y;
|
||||
__y = __y->m_parent;
|
||||
}
|
||||
__x = __y;
|
||||
}
|
||||
return __x;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
std::map<K, V> map<K, V>::std() {
|
||||
return (std::map<K, V>)(*this);
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::operator std::map<K, V>() {
|
||||
auto iter_node = static_cast<_tree_node>(m_header.m_left);
|
||||
auto end_node = static_cast<_tree_node>(&m_header);
|
||||
std::map<K, V> out;
|
||||
for (; iter_node != end_node; iter_node = static_cast<_tree_node>(_rb_increment(iter_node))) {
|
||||
out[iter_node->m_value.first] = iter_node->m_value.second;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::operator std::map<K, V>() const {
|
||||
auto iter_node = static_cast<_tree_node>(m_header.m_left);
|
||||
auto end_node = (_tree_node)(&m_header);
|
||||
std::map<K, V> out;
|
||||
for (; iter_node != end_node; iter_node = static_cast<_tree_node>(_rb_increment(iter_node))) {
|
||||
out[iter_node->m_value.first] = iter_node->m_value.second;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void map<K, V>::insert(_tree_node x, _tree_node p, std::pair<K, V> const& val) {
|
||||
bool insert_left =
|
||||
(x != 0 || p == static_cast<_tree_node>(&m_header) || val.first < p->m_value.first);
|
||||
|
||||
_tree_node z = new _rb_tree_node<std::pair<K, V>>();
|
||||
z->m_value = val;
|
||||
|
||||
_rb_insert_rebalance(insert_left, z, p, m_header);
|
||||
++m_nodecount;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void map<K, V>::insert_pair(std::pair<K, V> const& val) {
|
||||
_tree_node x = static_cast<_tree_node>(m_header.m_parent);
|
||||
_tree_node y = static_cast<_tree_node>(&m_header);
|
||||
bool comp = true;
|
||||
while (x != 0) {
|
||||
y = x;
|
||||
comp = val.first < x->m_value.first;
|
||||
x = comp ? static_cast<_tree_node>(x->m_left) : static_cast<_tree_node>(x->m_right);
|
||||
}
|
||||
auto iter = y;
|
||||
|
||||
if (comp) {
|
||||
if (iter == static_cast<_tree_node>(m_header.m_left)) {
|
||||
insert(x, y, val);
|
||||
}
|
||||
else {
|
||||
iter = static_cast<_tree_node>(_rb_decrement(iter));
|
||||
}
|
||||
}
|
||||
if (iter->m_value.first < val.first) {
|
||||
insert(x, y, val);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::map(std::map<K, V> input) {
|
||||
m_header.m_isblack = false;
|
||||
m_header.m_parent = 0;
|
||||
m_header.m_left = &m_header;
|
||||
m_header.m_right = &m_header;
|
||||
|
||||
for (auto i : input) {
|
||||
insert_pair(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void map<K, V>::erase(_tree_node x) {
|
||||
while (x != 0) {
|
||||
erase(static_cast<_tree_node>(x->m_right));
|
||||
auto y = static_cast<_tree_node>(x->m_left);
|
||||
delete y;
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::map(map const& lol) : map(std::map<K, V>(lol)) {}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::map() : map(std::map<K, V>()) {}
|
||||
|
||||
template <class K, class V>
|
||||
map<K, V>::~map() {
|
||||
erase(static_cast<_tree_node>(m_header.m_parent));
|
||||
}
|
||||
|
||||
// TODO: these need to stay for old mods linking against geode <1.2.0
|
||||
template class map<int, int>;
|
||||
template class map<gd::string, gd::string>;
|
||||
template class map<gd::string, bool>;
|
||||
|
|
|
@ -7,10 +7,9 @@ using namespace geode::prelude;
|
|||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/Utils.hpp>
|
||||
#include <Geode/binding/GameManager.hpp>
|
||||
#include <objc/runtime.h>
|
||||
|
||||
bool utils::clipboard::write(std::string const& data) {
|
||||
[[NSPasteboard generalPasteboard] clearContents];
|
||||
|
@ -235,4 +234,29 @@ void geode::utils::game::restart() {
|
|||
), CCDirector::get()->getRunningScene(), false);
|
||||
}
|
||||
|
||||
Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) {
|
||||
auto cls = objc_getClass(className.c_str());
|
||||
if (!cls)
|
||||
return Err("Class not found");
|
||||
|
||||
auto sel = sel_registerName(selectorName.c_str());
|
||||
|
||||
class_addMethod(cls, sel, (IMP)imp, "v@:");
|
||||
|
||||
return Ok();
|
||||
}
|
||||
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
||||
auto cls = objc_getClass(className.c_str());
|
||||
if (!cls)
|
||||
return Err("Class not found");
|
||||
|
||||
auto sel = sel_registerName(selectorName.c_str());
|
||||
|
||||
auto method = class_getInstanceMethod(cls, sel);
|
||||
if (!method)
|
||||
return Err("Method not found");
|
||||
|
||||
return Ok((void*)method_getImplementation(method));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fmt/core.h>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -174,18 +175,26 @@ static std::string getStacktrace(PCONTEXT context) {
|
|||
}
|
||||
|
||||
static std::string getRegisters(PCONTEXT context) {
|
||||
std::stringstream stream;
|
||||
stream << std::hex << "EAX: " << context->Eax << "\n"
|
||||
<< "EBX: " << context->Ebx << "\n"
|
||||
<< "ECX: " << context->Ecx << "\n"
|
||||
<< "EDX: " << context->Edx << "\n"
|
||||
<< "EBP: " << context->Ebp << "\n"
|
||||
<< "ESP: " << context->Esp << "\n"
|
||||
<< "EDI: " << context->Edi << "\n"
|
||||
<< "ESI: " << context->Esi << "\n"
|
||||
<< "EIP: " << context->Eip << "\n"
|
||||
<< std::dec;
|
||||
return stream.str();
|
||||
return fmt::format(
|
||||
"EAX: {:08x}\n"
|
||||
"EBX: {:08x}\n"
|
||||
"ECX: {:08x}\n"
|
||||
"EDX: {:08x}\n"
|
||||
"EBP: {:08x}\n"
|
||||
"ESP: {:08x}\n"
|
||||
"EDI: {:08x}\n"
|
||||
"ESI: {:08x}\n"
|
||||
"EIP: {:08x}\n",
|
||||
context->Eax,
|
||||
context->Ebx,
|
||||
context->Ecx,
|
||||
context->Edx,
|
||||
context->Ebp,
|
||||
context->Esp,
|
||||
context->Edi,
|
||||
context->Esi,
|
||||
context->Eip
|
||||
);
|
||||
}
|
||||
|
||||
static std::string getInfo(LPEXCEPTION_POINTERS info, Mod* faultyMod) {
|
||||
|
|
|
@ -184,4 +184,11 @@ void geode::utils::game::restart() {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) {
|
||||
return Err("Wrong platform");
|
||||
}
|
||||
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
||||
return Err("Wrong platform");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -227,7 +227,7 @@ void ModCell::onEnable(CCObject* sender) {
|
|||
else {
|
||||
tryOrAlert(m_mod->disable(), "Error disabling mod");
|
||||
}
|
||||
Loader::get()->queueInGDThread([this]() {
|
||||
Loader::get()->queueInMainThread([this]() {
|
||||
if (m_layer) {
|
||||
m_layer->updateAllStates();
|
||||
}
|
||||
|
|
|
@ -602,7 +602,7 @@ Result<> file::watchFile(ghc::filesystem::path const& file) {
|
|||
auto watcher = std::make_unique<FileWatcher>(
|
||||
file,
|
||||
[](auto const& path) {
|
||||
Loader::get()->queueInGDThread([=] {
|
||||
Loader::get()->queueInMainThread([=] {
|
||||
FileWatchEvent(path).post();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
Loader::get()->queueInGDThread([self = data->self, now, total]() {
|
||||
Loader::get()->queueInMainThread([self = data->self, now, total]() {
|
||||
std::lock_guard _(self->m_mutex);
|
||||
for (auto& prog : self->m_progresses) {
|
||||
prog(*self->m_self, now, total);
|
||||
|
@ -302,7 +302,7 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
// request, then they may still cancel it
|
||||
m_finished = true;
|
||||
|
||||
Loader::get()->queueInGDThread([this, ret]() {
|
||||
Loader::get()->queueInMainThread([this, ret]() {
|
||||
std::lock_guard _(m_mutex);
|
||||
for (auto& then : m_thens) {
|
||||
then(*m_self, ret);
|
||||
|
@ -329,7 +329,7 @@ void SentAsyncWebRequest::Impl::doCancel() {
|
|||
}
|
||||
}
|
||||
|
||||
Loader::get()->queueInGDThread([this]() {
|
||||
Loader::get()->queueInMainThread([this]() {
|
||||
std::lock_guard _(m_mutex);
|
||||
for (auto& canc : m_cancelleds) {
|
||||
canc(*m_self);
|
||||
|
@ -366,7 +366,7 @@ void SentAsyncWebRequest::Impl::error(std::string const& error, int code) {
|
|||
m_statusCV.wait(lock, [this]() {
|
||||
return !m_paused;
|
||||
});
|
||||
Loader::get()->queueInGDThread([this, error, code]() {
|
||||
Loader::get()->queueInMainThread([this, error, code]() {
|
||||
{
|
||||
std::lock_guard _(m_mutex);
|
||||
for (auto& expect : m_expects) {
|
||||
|
|
|
@ -11,6 +11,10 @@ GEODE_MEMBER_CHECK(PlayerObject, m_playerColor1, 0x7c2);
|
|||
|
||||
// EditorUI
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_buttonBar, 0x1a0);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_scaleControl, 0x208);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_swipeEnabled, 0x23c);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_updateTimeMarkers, 0x244);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_selectedObjects, 0x260);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_selectedObject, 0x440);
|
||||
|
||||
// LevelEditorLayer
|
||||
|
@ -21,7 +25,7 @@ GEODE_MEMBER_CHECK(LevelEditorLayer, m_level, 0x618);
|
|||
GEODE_MEMBER_CHECK(PlayLayer, unknown518, 0x518);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, unknown5f4, 0x5f4);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, unknown680, 0x680);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, unknown6a8, 0x6a8);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_replayInputs, 0x6a8);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_level, 0x728);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_shouldTryToKick, 0x7a8);
|
||||
|
||||
|
@ -76,4 +80,19 @@ GEODE_MEMBER_CHECK(LevelBrowserLayer, m_itemCount, 0x208);
|
|||
// LocalLevelManager
|
||||
GEODE_MEMBER_CHECK(LocalLevelManager, m_localLevels, 0x140);
|
||||
|
||||
// GameStatsManager
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_dailyChests, 0x170);
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_completedLevels, 0x200);
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_weeklyChest, 0x278);
|
||||
|
||||
// DailyLevelPage
|
||||
GEODE_MEMBER_CHECK(DailyLevelPage, m_weekly, 0x291);
|
||||
|
||||
// GameLevelManager
|
||||
GEODE_MEMBER_CHECK(GameLevelManager, m_weeklyID, 0x1a8);
|
||||
GEODE_MEMBER_CHECK(GameLevelManager, m_gauntletLevels, 0x1b0);
|
||||
GEODE_MEMBER_CHECK(GameLevelManager, m_timerDict, 0x1e8);
|
||||
GEODE_MEMBER_CHECK(GameLevelManager, m_accountIDtoUserIDDict, 0x1f8);
|
||||
GEODE_MEMBER_CHECK(GameLevelManager, m_userIDtoAccountIDDict, 0x200);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -56,4 +56,10 @@ static_assert(sizeof(GJBaseGameLayer) == 0x2cc);
|
|||
|
||||
GEODE_MEMBER_CHECK(AudioEffectsLayer, m_unk19C, 0x19c);
|
||||
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_dailyChests, 0x114);
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_completedLevels, 0x144);
|
||||
|
||||
GEODE_MEMBER_CHECK(DailyLevelPage, m_weekly, 0x1ed);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue