mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-13 22:49:52 -04:00
Merge remote-tracking branch 'refs/remotes/origin/1.4.0-dev' into 1.4.0-dev
This commit is contained in:
commit
acaa8ca4b7
63 changed files with 3059 additions and 1078 deletions
11
.github/ISSUE_TEMPLATE/crash-report.yml
vendored
11
.github/ISSUE_TEMPLATE/crash-report.yml
vendored
|
@ -2,17 +2,16 @@ name: Crash Report
|
|||
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
|
||||
- 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 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.
|
||||
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.
|
||||
Failing to do this will get your issue *closed without explanation*.
|
||||
placeholder: "Please, read the text below."
|
||||
validations:
|
||||
required: true
|
||||
options:
|
||||
- label: I confirm that this crash is NOT related to a mod but directly to Geode Loader itself.
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
|
|
46
.github/workflows/build.yml
vendored
46
.github/workflows/build.yml
vendored
|
@ -20,6 +20,7 @@ jobs:
|
|||
- name: Windows
|
||||
os: windows-latest
|
||||
id: win
|
||||
cli_id: win
|
||||
extra_flags: -T host=x64 -A win32 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DGEODE_DEBUG=On
|
||||
# uncomment to use vs clang-cl and ninja
|
||||
#extra_flags: >
|
||||
|
@ -34,6 +35,7 @@ jobs:
|
|||
- name: macOS
|
||||
os: macos-latest
|
||||
id: mac
|
||||
cli_id: mac
|
||||
extra_flags: >
|
||||
-DCMAKE_C_COMPILER=clang
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
|
@ -43,6 +45,21 @@ jobs:
|
|||
cli_cmd: 'chmod +x $GITHUB_WORKSPACE/cli/geode'
|
||||
package_cmd: './installer/mac/package.sh ./bin/nightly ./installer/mac/geode-installer-mac.pkg'
|
||||
installer_path: './installer/mac/geode-installer-mac.pkg'
|
||||
|
||||
- name: Android
|
||||
os: ubuntu-latest
|
||||
id: android
|
||||
cli_id: linux
|
||||
extra_flags: >
|
||||
-DCMAKE_TOOLCHAIN_FILE=$NDK_HOME/build/cmake/android.toolchain.cmake
|
||||
-DANDROID_ABI=armeabi-v7a
|
||||
-DANDROID_PLATFORM=android-23
|
||||
-DGEODE_DONT_BUILD_TEST_MODS=1
|
||||
-G Ninja
|
||||
cli_cmd: 'chmod +x $GITHUB_WORKSPACE/cli/geode'
|
||||
package_cmd: ''
|
||||
installer_path: ''
|
||||
|
||||
|
||||
name: Build ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
@ -57,7 +74,7 @@ jobs:
|
|||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ matrix.config.os }}
|
||||
if: matrix.config.id == 'mac'
|
||||
if: matrix.config.id != 'win'
|
||||
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||
|
@ -65,22 +82,36 @@ jobs:
|
|||
arch: amd64_x86
|
||||
if: matrix.config.id == 'win'
|
||||
|
||||
- name: Setup NDK
|
||||
uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r26b
|
||||
add-to-path: false
|
||||
if: matrix.config.id == 'android'
|
||||
|
||||
- name: Download CLI
|
||||
uses: robinraju/release-downloader@v1.8
|
||||
with:
|
||||
repository: geode-sdk/cli
|
||||
latest: true
|
||||
fileName: '*-${{ matrix.config.id }}.zip'
|
||||
fileName: '*-${{ matrix.config.cli_id }}.zip'
|
||||
tarBall: false
|
||||
zipBall: false
|
||||
out-file-path: "cli"
|
||||
|
||||
- name: Setup CLI
|
||||
run: |
|
||||
7z x "${{ github.workspace }}/cli/*-${{ matrix.config.id }}.zip" -o"${{ github.workspace }}/cli"
|
||||
7z x "${{ github.workspace }}/cli/*-${{ matrix.config.cli_id }}.zip" -o"${{ github.workspace }}/cli"
|
||||
${{ matrix.config.cli_cmd }}
|
||||
echo "${{ github.workspace }}/cli" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Android Env
|
||||
run: |
|
||||
echo "NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV"
|
||||
sudo apt install ninja-build
|
||||
if: matrix.config.id == 'android'
|
||||
|
||||
- name: Configure
|
||||
run: >
|
||||
cmake -B ${{ github.workspace }}/build
|
||||
|
@ -101,12 +132,14 @@ jobs:
|
|||
|
||||
- name: Package Installer
|
||||
run: ${{ matrix.config.package_cmd }}
|
||||
if: matrix.config.id != 'android'
|
||||
|
||||
- name: Upload Installer
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: geode-installer-${{ matrix.config.id }}
|
||||
path: ${{ matrix.config.installer_path }}
|
||||
if: matrix.config.id != 'android'
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
|
@ -146,6 +179,12 @@ 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
|
||||
|
||||
- name: Zip Android Artifacts
|
||||
uses: vimtor/action-zip@v1.1
|
||||
with:
|
||||
files: geode-android/Geode.so
|
||||
dest: geode-${{ steps.ref.outputs.hash }}-android.zip
|
||||
|
||||
- name: Zip Resources
|
||||
uses: vimtor/action-zip@v1.1
|
||||
with:
|
||||
|
@ -166,4 +205,5 @@ jobs:
|
|||
./geode-installer-${{ steps.ref.outputs.hash }}-win.exe
|
||||
./geode-${{ steps.ref.outputs.hash }}-mac.zip
|
||||
./geode-${{ steps.ref.outputs.hash }}-win.zip
|
||||
./geode-${{ steps.ref.outputs.hash }}-android.zip
|
||||
./resources.zip
|
||||
|
|
2
.github/workflows/draft.yml
vendored
2
.github/workflows/draft.yml
vendored
|
@ -28,6 +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/geode-*-android.zip geode-v${{ steps.ref.outputs.version }}-android.zip
|
||||
mv dev/resources.zip resources.zip
|
||||
|
||||
- name: Create Draft Release
|
||||
|
@ -48,4 +49,5 @@ 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
|
||||
./geode-v${{ steps.ref.outputs.version }}-android.zip
|
||||
./resources.zip
|
||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -1,5 +1,32 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v1.4.0
|
||||
* Add Android support !!!!
|
||||
* Implement every Geode functionality except `utils::file::openFolder`
|
||||
* Requires the Geode launcher in order to be used
|
||||
* Fixes the text input node allowing typing for 1 less character
|
||||
* Uses `logcat` in order to get crash reports, so reopening the game is required to generate them
|
||||
* Broma requires classes to be added `[[link(android)]]` in order to be linked
|
||||
* All Geode and GD files are stored in `Android/data/com.geode.launcher/files`
|
||||
* Game files in `game`, save files in `save`
|
||||
* Allow logging to be disabled per mod (6d599a5)
|
||||
* Mod cells use layouts (114fa46)
|
||||
* MacOS console is now separate (182984d)
|
||||
* Add uninstall button to Geode mod (Only functional in Windows currently) (a738320)
|
||||
* Make new version label invisible on download (0f179da)
|
||||
* Fix the toggling of disabled dependencies (cd89ef1)
|
||||
* Fix spritesheet issues (ef47647)
|
||||
* Change `LoadingLayer` (ef47647)
|
||||
* Make mod info popup top a layout (dd806e0)
|
||||
* Add `GEODE_HIDDEN` to inline unique functions (71a79ab)
|
||||
* Fix big mod icons (26a6c7e)
|
||||
* Fix `CCNode::removeChildByID` export (23cd456)
|
||||
* Make `MDTextArea` fit its size (140f38b)
|
||||
* Enable ESC/Back to go back in Geode mod list (2847bee)
|
||||
* Add `SimpleTextArea` (7f277a7)
|
||||
* Check modified date when unzipping `.geode` files (5c765c6)
|
||||
* Only hash markdown files on resource checking (f563c46)
|
||||
|
||||
## v1.3.5
|
||||
* Follow redirect in web::utils functions (a942a45)
|
||||
* Lots of bindings
|
||||
|
@ -221,7 +248,7 @@ Thank you to [Fleeym](https://github.com/Fleeym/Fleeym) for contributing to this
|
|||
* Fix recursive comparison in VersionTag
|
||||
* `geode/unzipped` is now deleted on startup if it exists
|
||||
|
||||
## v1.0.0-beta.9
|
||||
## v1.0.0-beta.9
|
||||
* Fix multiple modifiers not being able to have fields on same class due to having same field index
|
||||
* Add `Result::ok` and `Result::err` for converting the `Result` into `std::optional`
|
||||
|
||||
|
@ -289,7 +316,7 @@ Thank you to [Fleeym](https://github.com/Fleeym/Fleeym) for contributing to this
|
|||
- Something related to codegen and addresser? I have no clue what it does, so you probably won't have either
|
||||
- MacOS minimum version bumped to 10.14
|
||||
|
||||
## v1.0.0-beta.2
|
||||
## v1.0.0-beta.2
|
||||
* Fixed bug where `Mod::getSavedValue` would cause a crash due to trying operator on a null JSON value
|
||||
* Fixed bug where loading would crash if one of the mods' binaries failed to load
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@ set(GEODE_BIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
|||
set(GEODE_LOADER_PATH ${CMAKE_CURRENT_SOURCE_DIR}/loader)
|
||||
set(GEODE_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include(cmake/GeodeFile.cmake)
|
||||
include(cmake/Platform.cmake)
|
||||
include(cmake/GeodeFile.cmake)
|
||||
include(cmake/CPM.cmake)
|
||||
|
||||
if (PROJECT_IS_TOP_LEVEL AND NOT GEODE_BUILDING_DOCS)
|
||||
|
@ -96,7 +96,7 @@ if (PROJECT_IS_TOP_LEVEL AND NOT GEODE_BUILDING_DOCS)
|
|||
set(TULIP_LINK_SOURCE ON)
|
||||
endif()
|
||||
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE)
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook#3423a29")
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook#d2132de")
|
||||
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
||||
|
||||
# Silence warnings from dependencies
|
||||
|
@ -191,6 +191,9 @@ 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)
|
||||
elseif(ANDROID)
|
||||
target_compile_options(GeodeCodegenSources PUBLIC -ffunction-sections -fdata-sections)
|
||||
target_link_options(GeodeCodegenSources PUBLIC -Wl,--gc-sections)
|
||||
endif()
|
||||
|
||||
if (NOT GEODE_DISABLE_PRECOMPILED_HEADERS)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// clang-format off
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCActionTween {
|
||||
static cocos2d::CCActionTween* create(float, char const*, float, float) = mac 0x447590;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCActionManager {
|
||||
CCActionManager() {
|
||||
m_pTargets = nullptr;
|
||||
|
@ -21,17 +21,17 @@ class cocos2d::CCActionManager {
|
|||
auto resumeTarget(cocos2d::CCObject*) = mac 0x10bd20;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCAnimate {
|
||||
static cocos2d::CCAnimate* create(cocos2d::CCAnimation*) = mac 0x1f8fc0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCAnimation {
|
||||
static auto createWithSpriteFrames(cocos2d::CCArray*, float) = mac 0x140df0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCApplication {
|
||||
virtual auto run();
|
||||
virtual auto getCurrentLanguage() = mac 0x1a3f40, ios 0x10e508;
|
||||
|
@ -39,13 +39,15 @@ class cocos2d::CCApplication {
|
|||
virtual auto openURL(char const*) = mac 0x1a4550, ios 0x10e7a4;
|
||||
virtual auto setAnimationInterval(double) = mac 0x1a3ee0, ios 0x10e494;
|
||||
static auto sharedApplication() = mac 0x1a3f30;
|
||||
|
||||
[[link(win)]]
|
||||
bool getControllerConnected() const = mac 0x27d1b0;
|
||||
// ~CCApplication() = mac 0x1a3d10, ios 0x10e384;
|
||||
CCApplication() {}
|
||||
~CCApplication() {}
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCArray {
|
||||
// auto addObject(cocos2d::CCObject*) = mac 0x419f90, ios 0x16504c;
|
||||
auto addObjectNew(cocos2d::CCObject*) = mac 0x41a450;
|
||||
|
@ -67,27 +69,32 @@ class cocos2d::CCArray {
|
|||
// auto stringAtIndex(unsigned int) = mac 0x41a320;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCBezierTo {
|
||||
static cocos2d::CCBezierTo* create(float, cocos2d::_ccBezierConfig const&) = mac 0x1f6c10;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCBMFontConfiguration {
|
||||
static cocos2d::CCBMFontConfiguration* create(char const*) = mac 0x3450f0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCCallFunc {
|
||||
static auto create(cocos2d::CCObject*, cocos2d::SEL_CallFunc) = mac 0x454d90;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCCallFuncO {
|
||||
static auto create(cocos2d::CCObject*, cocos2d::SEL_CallFuncO, cocos2d::CCObject*) = mac 0x455940;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCCallFuncND {
|
||||
static auto create(cocos2d::CCObject*, cocos2d::SEL_CallFuncND, void*) = mac 0x455470;
|
||||
}
|
||||
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCClippingNode {
|
||||
CCClippingNode() {
|
||||
m_pStencil = nullptr;
|
||||
|
@ -121,7 +128,7 @@ class cocos2d::CCClippingNode {
|
|||
// void updateConnected() = win 0xc7fb0;
|
||||
//}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCConfiguration {
|
||||
void gatherGPUInfo() = mac 0x2a6e10;
|
||||
}
|
||||
|
@ -131,7 +138,7 @@ class cocos2d::CCDelayTime {
|
|||
static cocos2d::CCDelayTime* create(float) = mac 0x1f4380;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCDictionary {
|
||||
auto allKeys() = mac 0x190450, ios 0x2de774;
|
||||
auto count() = mac 0x190430;
|
||||
|
@ -147,7 +154,7 @@ class cocos2d::CCDictionary {
|
|||
auto valueForKey(gd::string const&) = mac 0x1907a0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCDirector {
|
||||
CCDirector() {}
|
||||
~CCDirector() {}
|
||||
|
@ -190,7 +197,7 @@ class cocos2d::CCDirector {
|
|||
auto popSceneWithTransition(float, cocos2d::PopTransition) = mac 0x24a8b0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCDrawNode {
|
||||
CCDrawNode() = mac 0x378b40, win 0x6b9f0;
|
||||
auto clear() = mac 0x379e80;
|
||||
|
@ -206,7 +213,7 @@ class cocos2d::CCDrawNode {
|
|||
virtual ~CCDrawNode() = mac 0x378cc0, win 0x6ba60;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCAction {
|
||||
CCAction() = mac 0x35b610, win 0x7a6d0;
|
||||
virtual ~CCAction() = mac 0x35b6b0, win 0x7a7f0;
|
||||
|
@ -218,13 +225,13 @@ class cocos2d::CCAction {
|
|||
auto update(float time) = mac 0x35b890;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCFiniteTimeAction {
|
||||
// same as CCActionInterval::reverse i think
|
||||
auto reverse() = mac 0x1f2720;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCActionInterval {
|
||||
auto copyWithZone(cocos2d::CCZone* zone) = mac 0x1f2550;
|
||||
auto isDone() = mac 0x1f2640;
|
||||
|
@ -234,32 +241,32 @@ class cocos2d::CCActionInterval {
|
|||
bool initWithDuration(float d) = mac 0x1f2510;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseBackIn {
|
||||
static cocos2d::CCEaseBackIn* create(cocos2d::CCActionInterval*) = mac 0x2a41b0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseElasticIn {
|
||||
static cocos2d::CCEaseElasticIn* create(cocos2d::CCActionInterval*, float) = mac 0x2a2e00;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseElasticOut {
|
||||
static cocos2d::CCEaseElasticOut* create(cocos2d::CCActionInterval*, float) = mac 0x2a3080;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseIn {
|
||||
static cocos2d::CCEaseIn* create(cocos2d::CCActionInterval*, float) = mac 0x2a1960;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseInOut {
|
||||
static cocos2d::CCEaseInOut* create(cocos2d::CCActionInterval*, float) = mac 0x2a1d80;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEaseOut {
|
||||
static cocos2d::CCEaseOut* create(cocos2d::CCActionInterval*, float) = mac 0x2a1b70;
|
||||
}
|
||||
|
@ -288,7 +295,7 @@ class cocos2d::CCEGLView {
|
|||
void onGLFWWindowSizeFunCallback(GLFWwindow* window, int width, int height);
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCEGLViewProtocol {
|
||||
CCEGLViewProtocol() = win 0xbac00;
|
||||
virtual ~CCEGLViewProtocol() = win 0xbacc0;
|
||||
|
@ -299,17 +306,17 @@ class cocos2d::CCEGLViewProtocol {
|
|||
virtual void setFrameSize(float, float) = mac 0x29d960;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCFadeOut {
|
||||
static cocos2d::CCFadeOut* create(float) = mac 0x1f7d80;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCFadeTo {
|
||||
static cocos2d::CCFadeTo* create(float, unsigned char) = mac 0x1f7ff0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCFileUtils : cocos2d::TypeInfo {
|
||||
static cocos2d::CCFileUtils* sharedFileUtils() = mac 0x377030, ios 0x159450;
|
||||
static void purgeFileUtils();
|
||||
|
@ -319,19 +326,19 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo {
|
|||
void removeAllPaths() = mac 0x241600;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCGLProgram {
|
||||
auto setUniformsForBuiltins() = mac 0x232c70;
|
||||
auto use() = mac 0x231d70;
|
||||
bool compileShader(unsigned int* shader, unsigned int type, const char* source) = mac 0x231a30;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCHide {
|
||||
static cocos2d::CCHide* create() = mac 0x4543e0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCIMEDelegate {
|
||||
~CCIMEDelegate() {
|
||||
CCIMEDispatcher::sharedDispatcher()->removeDelegate(this);
|
||||
|
@ -343,7 +350,7 @@ class cocos2d::CCIMEDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCIMEDispatcher {
|
||||
static auto sharedDispatcher() = mac 0x2773f0, ios 0x12d170;
|
||||
auto addDelegate(cocos2d::CCIMEDelegate*) = mac 0x277480, ios 0x12d204;
|
||||
|
@ -352,7 +359,7 @@ class cocos2d::CCIMEDispatcher {
|
|||
void dispatchDeleteBackward() = mac 0x277af0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCImage {
|
||||
CCImage() = mac 0x24fa00, win 0xc5fd0;
|
||||
virtual ~CCImage() = mac 0x24fa80, win 0xc6100;
|
||||
|
@ -361,21 +368,21 @@ class cocos2d::CCImage {
|
|||
auto initWithImageData(void*, int, cocos2d::CCImage::EImageFormat, int, int, int) = mac 0x24fcb0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
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)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCKeyboardHandler {
|
||||
static cocos2d::CCKeyboardHandler* handlerWithDelegate(cocos2d::CCKeyboardDelegate*) = mac 0x242030;
|
||||
virtual auto initWithDelegate(cocos2d::CCKeyboardDelegate*) = mac 0x241ff0, ios 0x13f8b8;
|
||||
~CCKeyboardHandler() = mac 0x241e90, ios 0x13f87c, win 0x99a10;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCKeypadHandler {
|
||||
static cocos2d::CCKeypadHandler* handlerWithDelegate(cocos2d::CCKeypadDelegate*) = mac 0x1ff2d0;
|
||||
virtual auto initWithDelegate(cocos2d::CCKeypadDelegate*) = mac 0x1ff290, ios 0x69; // iOS stub
|
||||
|
@ -384,7 +391,7 @@ class cocos2d::CCKeypadHandler {
|
|||
}
|
||||
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCLabelBMFont {
|
||||
CCLabelBMFont() = mac 0x347b60;
|
||||
static cocos2d::CCLabelBMFont* create(char const*, char const*) = mac 0x347660;
|
||||
|
@ -425,7 +432,7 @@ class cocos2d::CCLabelBMFont {
|
|||
virtual ~CCLabelBMFont() = mac 0x347e80, win 0x9be70;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCLabelTTF {
|
||||
static cocos2d::CCLabelTTF* create() = mac 0x1fa7e0;
|
||||
static cocos2d::CCLabelTTF* create(char const*, char const*, float) = mac 0x1fa840;
|
||||
|
@ -433,7 +440,7 @@ class cocos2d::CCLabelTTF {
|
|||
virtual auto setString(char const*) = mac 0x1fad70;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCLayer {
|
||||
CCLayer() = mac 0x2725b0, ios 0xc7708, win 0xa15e0;
|
||||
virtual auto ccTouchBegan(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x2734d0, ios 0xc810c;
|
||||
|
@ -474,7 +481,7 @@ class cocos2d::CCLayer {
|
|||
virtual ~CCLayer() = mac 0x2727b0, ios 0xc7848, win 0xa1940;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCLayerColor {
|
||||
CCLayerColor() = mac 0x274320, ios 0xc8aec, win 0xa1710;
|
||||
static cocos2d::CCLayerColor* create(cocos2d::_ccColor4B const&, float, float) = mac 0x2745e0;
|
||||
|
@ -492,7 +499,7 @@ class cocos2d::CCLayerColor {
|
|||
virtual ~CCLayerColor() = mac 0x2743d0, ios 0x2743e0, win 0xa1a20;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCLayerRGBA {
|
||||
CCLayerRGBA() = mac 0x2738d0, ios 0xc85cc, win 0xa1890;
|
||||
virtual auto init() = mac 0x273b40, ios 0xc8de8;
|
||||
|
@ -513,7 +520,7 @@ class cocos2d::CCLayerRGBA {
|
|||
virtual ~CCLayerRGBA() = mac 0x273aa0, ios 0xc77b0, win 0xa1b20;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMenu {
|
||||
auto alignItemsHorizontallyWithPadding(float) = mac 0x4393e0, ios 0x132508;
|
||||
auto alignItemsVerticallyWithPadding(float) = mac 0x439190;
|
||||
|
@ -537,7 +544,7 @@ class cocos2d::CCMenu {
|
|||
cocos2d::CCMenuItem* itemForTouch(cocos2d::CCTouch*) = mac 0x438dd0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMenuItem {
|
||||
bool initWithTarget(cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fb7f0;
|
||||
virtual ~CCMenuItem() = mac 0x1fb8e0, ios 0x2cdf4, win 0xab9c0;
|
||||
|
@ -553,13 +560,13 @@ class cocos2d::CCMenuItem {
|
|||
auto rect() = mac 0x1fbb00, ios 0x2cf3c;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMenuItemImage {
|
||||
// virtual ~CCMenuItemImage() = mac 0x1febb0;
|
||||
virtual auto init() = mac 0x1fd750;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMenuItemLabel {
|
||||
virtual ~CCMenuItemLabel() = mac 0x1fc0d0;
|
||||
virtual auto activate() = mac 0x1fc240;
|
||||
|
@ -572,7 +579,7 @@ class cocos2d::CCMenuItemLabel {
|
|||
virtual auto setLabel(cocos2d::CCNode*) = mac 0x1fbbc0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMenuItemSprite {
|
||||
// virtual ~CCMenuItemSprite() = mac 0x1feab0;
|
||||
virtual auto selected() = mac 0x1fd3f0, ios 0x2d2cc;
|
||||
|
@ -592,7 +599,7 @@ class cocos2d::CCMenuItemSprite {
|
|||
static auto create(cocos2d::CCNode*, cocos2d::CCNode*, cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fd2d0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMotionStreak {
|
||||
CCMotionStreak() = win 0xae310;
|
||||
virtual ~CCMotionStreak() = win 0xae450;
|
||||
|
@ -603,30 +610,30 @@ class cocos2d::CCMotionStreak {
|
|||
virtual auto draw();
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMouseDispatcher {
|
||||
bool dispatchScrollMSG(float x, float y) = mac 0x2e8f40;
|
||||
void removeDelegate(cocos2d::CCMouseDelegate* delegate);
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMouseHandler {
|
||||
static cocos2d::CCMouseHandler* handlerWithDelegate(cocos2d::CCMouseDelegate*) = mac 0x12ef80;
|
||||
virtual auto initWithDelegate(cocos2d::CCMouseDelegate*) = mac 0x12ef40, ios 0x43798;
|
||||
~CCMouseHandler() = mac 0x12ede0, ios 0x4375c, win 0xb1fd0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMoveBy {
|
||||
static cocos2d::CCMoveBy* create(float, cocos2d::CCPoint const&) = mac 0x1f50e0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCMoveTo {
|
||||
static cocos2d::CCMoveTo* create(float, cocos2d::CCPoint const&) = mac 0x1f54d0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCNode {
|
||||
CCNode() = mac 0x122550, win 0x5e7d0;
|
||||
auto boundingBox() = mac 0x123030;
|
||||
|
@ -752,7 +759,7 @@ class cocos2d::CCNode {
|
|||
virtual ~CCNode() = mac 0x122750, ios 0x6c98, win 0x5ea40;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCNodeRGBA {
|
||||
CCNodeRGBA() = mac 0x124b30, win 0x5e9d0;
|
||||
virtual ~CCNodeRGBA() = mac 0x124bb0, ios 0x15f748, win 0x5ebb0;
|
||||
|
@ -771,7 +778,7 @@ class cocos2d::CCNodeRGBA {
|
|||
virtual auto setCascadeColorEnabled(bool) = mac 0x125340, ios 0x15fb80;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCObject {
|
||||
CCObject() = mac 0x250ca0, ios 0x43864, win 0x69230;
|
||||
auto acceptVisitor(cocos2d::CCDataVisitor&) = mac 0x250f30, ios 0x439f0;
|
||||
|
@ -787,7 +794,7 @@ class cocos2d::CCObject {
|
|||
~CCObject() = mac 0x250d20, ios 0x6ac0, win 0x69270;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCParticleSystem {
|
||||
CCParticleSystem() = win 0xb6650;
|
||||
virtual ~CCParticleSystem() = win 0xb68e0;
|
||||
|
@ -798,7 +805,7 @@ class cocos2d::CCParticleSystem {
|
|||
auto stopSystem() = mac 0x46bd10;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCParticleSystemQuad {
|
||||
CCParticleSystemQuad() = win 0xb9bd0;
|
||||
virtual ~CCParticleSystemQuad() = win 0xb9c10;
|
||||
|
@ -807,18 +814,18 @@ class cocos2d::CCParticleSystemQuad {
|
|||
auto setupVBO();
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCPoolManager {
|
||||
auto pop() = mac 0x214620;
|
||||
static cocos2d::CCPoolManager* sharedPoolManager() = mac 0x2142c0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRemoveSelf {
|
||||
static cocos2d::CCRemoveSelf* create(bool) = mac 0x454700;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRenderTexture {
|
||||
auto begin() = mac 0x35ce10;
|
||||
auto end() = mac 0x35d2c0;
|
||||
|
@ -828,45 +835,45 @@ class cocos2d::CCRenderTexture {
|
|||
auto beginWithClear(float r, float g, float b, float a) = mac 0x35d010;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRepeat {
|
||||
static cocos2d::CCRepeat* create(cocos2d::CCFiniteTimeAction*, unsigned int) = mac 0x1f3230;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRepeatForever {
|
||||
static cocos2d::CCRepeatForever* create(cocos2d::CCActionInterval*) = mac 0x1f3920;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRotateBy {
|
||||
static cocos2d::CCRotateBy* create(float, float) = mac 0x1f4c50;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCRotateTo {
|
||||
static cocos2d::CCRotateTo* create(float, float) = mac 0x1f47b0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCScaleTo {
|
||||
static cocos2d::CCScaleTo* create(float, float) = mac 0x1f6ff0;
|
||||
static cocos2d::CCScaleTo* create(float, float, float) = mac 0x1f70f0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCScaleBy {
|
||||
static cocos2d::CCScaleTo* create(float, float) = mac 0x1f73c0;
|
||||
static cocos2d::CCScaleTo* create(float, float, float) = mac 0x1f7480;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCScene {
|
||||
static cocos2d::CCScene* create() = mac 0x13c140, ios 0x163070;
|
||||
auto getHighestChildZ() = mac 0x13c200, ios 0x1630e4;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCScheduler {
|
||||
auto scheduleSelector(cocos2d::SEL_SCHEDULE, cocos2d::CCObject*, float, unsigned int, float, bool) = mac 0x242b20;
|
||||
void scheduleSelector(cocos2d::SEL_SCHEDULE selector, cocos2d::CCObject* target, float interval, bool paused) {
|
||||
|
@ -881,12 +888,12 @@ class cocos2d::CCScheduler {
|
|||
virtual void update(float delta) = mac 0x2446d0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSequence {
|
||||
static auto createWithVariableList(cocos2d::CCFiniteTimeAction*, va_list) = mac 0x1f2910;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSet {
|
||||
CCSet() = mac 0x45ad80, ios 0x10e870, win 0x699e0;
|
||||
static auto create() = mac 0x45b0b0;
|
||||
|
@ -901,14 +908,14 @@ class cocos2d::CCSet {
|
|||
}
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCShaderCache {
|
||||
static auto sharedShaderCache() = mac 0xe6d10;
|
||||
auto programForKey(const char*) = mac 0xe7d40;
|
||||
void reloadDefaultShaders();
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSprite {
|
||||
virtual ~CCSprite() = mac 0x133430, ios 0x15b92c, win 0xd2f90;
|
||||
virtual auto init() = mac 0x132ef0, ios 0x15b488;
|
||||
|
@ -983,7 +990,7 @@ class cocos2d::CCSprite {
|
|||
void setFlipY(bool) = mac 0x134c30;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSpriteBatchNode {
|
||||
static cocos2d::CCSpriteBatchNode* create(char const*, unsigned int) = mac 0xbb540;
|
||||
static auto createWithTexture(cocos2d::CCTexture2D*, unsigned int) = mac 0xbb310;
|
||||
|
@ -1008,28 +1015,28 @@ class cocos2d::CCSpriteBatchNode {
|
|||
virtual auto getBlendFunc() = mac 0xbcd50, ios 0x131a60;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSpriteFrame {
|
||||
static auto createWithTexture(cocos2d::CCTexture2D*, cocos2d::CCRect const&, bool, cocos2d::CCPoint const&, cocos2d::CCSize const&) = mac 0x1ac7f0;
|
||||
static auto createWithTexture(cocos2d::CCTexture2D*, cocos2d::CCRect const&) = mac 0x1ac5c0;
|
||||
auto getTexture() = mac 0x1ad250;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCSpriteFrameCache {
|
||||
auto addSpriteFramesWithFile(char const*) = mac 0x199a10, ios 0x29e818;
|
||||
static cocos2d::CCSpriteFrameCache* sharedSpriteFrameCache() = mac 0x198970, ios 0x29dc4c;
|
||||
auto spriteFrameByName(char const*) = mac 0x19a7e0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCStandardTouchHandler {
|
||||
static cocos2d::CCStandardTouchHandler* handlerWithDelegate(cocos2d::CCTouchDelegate*, int) = mac 0x247f30;
|
||||
virtual auto initWithDelegate(cocos2d::CCTouchDelegate*, int) = mac 0x247ed0, ios 0x69; // iOS stub
|
||||
~CCStandardTouchHandler() = mac 0x2482a0, ios 0x6d28, win 0xf5a40;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCString {
|
||||
// virtual ~CCString() = mac 0x44c590;
|
||||
virtual auto isEqual(cocos2d::CCObject const*) = mac 0x44c8f0, ios 0x1a1e6c;
|
||||
|
@ -1045,7 +1052,7 @@ class cocos2d::CCString {
|
|||
auto intValue() const = mac 0x44c780, ios 0x1a1ca8;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTargetedTouchHandler {
|
||||
static cocos2d::CCTargetedTouchHandler* handlerWithDelegate(cocos2d::CCTouchDelegate*, int, bool) = mac 0x248010;
|
||||
auto initWithDelegate(cocos2d::CCTouchDelegate*, int, bool) = mac 0x2480f0, ios 0x69; // iOS stub
|
||||
|
@ -1053,7 +1060,7 @@ class cocos2d::CCTargetedTouchHandler {
|
|||
}
|
||||
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTexture2D {
|
||||
CCTexture2D() = mac 0x246280, win 0xe9300;
|
||||
~CCTexture2D() = mac 0x246350, win 0xe93f0;
|
||||
|
@ -1078,7 +1085,7 @@ class cocos2d::CCTexture2D {
|
|||
auto setTexParameters(cocos2d::_ccTexParams*) = mac 0x247980;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTextureAtlas {
|
||||
CCTextureAtlas() = win 0xea680;
|
||||
virtual ~CCTextureAtlas() = win 0xea6c0;
|
||||
|
@ -1086,12 +1093,12 @@ class cocos2d::CCTextureAtlas {
|
|||
auto mapBuffers();
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTextFieldTTF {
|
||||
static auto textFieldWithPlaceHolder(char const*, char const*, float) = mac 0x126220;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTextureCache {
|
||||
auto addImage(char const*, bool) = mac 0x358120, ios 0xa8388;
|
||||
auto textureForKey(char const*) = mac 0x359050;
|
||||
|
@ -1099,17 +1106,17 @@ class cocos2d::CCTextureCache {
|
|||
static cocos2d::CCTextureCache* sharedTextureCache() = mac 0x356e00, ios 0xa81ec;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTime {
|
||||
static auto gettimeofdayCocos2d(cocos2d::cc_timeval*, void*) = mac 0x19eac0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTintTo {
|
||||
static cocos2d::CCTintTo* create(float, unsigned char, unsigned char, unsigned char) = mac 0x1f82a0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTouch {
|
||||
auto getDelta() const = mac 0x38340;
|
||||
auto getLocationInView() const = mac 0x38250;
|
||||
|
@ -1119,7 +1126,7 @@ class cocos2d::CCTouch {
|
|||
auto getStartLocation() const = mac 0x38310;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTouchDispatcher {
|
||||
auto addTargetedDelegate(cocos2d::CCTouchDelegate*, int, bool) = mac 0x281180;
|
||||
auto addStandardDelegate(cocos2d::CCTouchDelegate*, int) = mac 0x281060;
|
||||
|
@ -1129,7 +1136,7 @@ class cocos2d::CCTouchDispatcher {
|
|||
void touches(cocos2d::CCSet*, cocos2d::CCEvent*, unsigned int) = mac 0x281a60;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::CCTouchHandler {
|
||||
virtual auto initWithDelegate(cocos2d::CCTouchDelegate*, int) = mac 0x247d10, ios 0x69f8;
|
||||
auto getPriority() = mac 0x247c20;
|
||||
|
@ -1139,20 +1146,20 @@ class cocos2d::CCTouchHandler {
|
|||
~CCTouchHandler() = mac 0x247de0, ios 0x6ac0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
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)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::ZipUtils {
|
||||
static auto compressString(gd::string, bool, int) = mac 0xe9a50;
|
||||
static auto decompressString(gd::string, bool, int) = mac 0xea380;
|
||||
static int ccDeflateMemory(unsigned char*, unsigned int, unsigned char**) = mac 0xe9cf0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::extension::CCControl {
|
||||
CCControl() {}
|
||||
virtual bool init() = mac 0x1a71c0;
|
||||
|
@ -1176,27 +1183,34 @@ class cocos2d::extension::CCControl {
|
|||
auto isSelected() = mac 0x1a7ec0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
|
||||
class cocos2d::extension::CCControlColourPicker {
|
||||
CCControlColourPicker() {}
|
||||
[[link(win)]]
|
||||
CCControlColourPicker() {}
|
||||
[[link(win)]]
|
||||
~CCControlColourPicker() = mac 0x1aae30;
|
||||
auto setColorValue(cocos2d::_ccColor3B const&) = mac 0x1aac10;
|
||||
[[link(win, android)]]
|
||||
auto setColorValue(cocos2d::_ccColor3B const&) = mac 0x1aac10;
|
||||
[[link(win)]]
|
||||
auto ccTouchBegan(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x1aae10;
|
||||
[[link(win)]]
|
||||
auto init() = mac 0x1aa400;
|
||||
[[link(win)]]
|
||||
static auto colourPicker() = mac 0x1aaa30;
|
||||
[[link(win)]]
|
||||
cocos2d::ccColor3B const& getColorValue() const {
|
||||
return m_rgb;
|
||||
return m_rgb;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::extension::CCControlUtils {
|
||||
static cocos2d::extension::HSV HSVfromRGB(cocos2d::extension::RGBA) = mac 0x1e6750;
|
||||
static cocos2d::extension::RGBA RGBfromHSV(cocos2d::extension::HSV) = mac 0x1e6850;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::extension::CCScale9Sprite {
|
||||
CCScale9Sprite() = mac 0x211330;
|
||||
static cocos2d::extension::CCScale9Sprite* create(char const*) = mac 0x2130d0;
|
||||
|
@ -1242,7 +1256,7 @@ class cocos2d::extension::CCScale9Sprite {
|
|||
virtual auto setSpriteFrame(cocos2d::CCSpriteFrame*) = mac 0x213a90;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d::extension::CCScrollView {
|
||||
CCScrollView() = mac 0x214800;
|
||||
virtual ~CCScrollView() = mac 0x214c30;
|
||||
|
@ -1262,7 +1276,7 @@ class cocos2d::extension::CCScrollView {
|
|||
virtual auto setTouchEnabled(bool) = mac 0x215250;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class cocos2d {
|
||||
static auto FNTConfigLoadFile(char const*) = mac 0x344f10;
|
||||
static auto ccGLUseProgram(GLuint) = mac 0x1ae540;
|
||||
|
@ -1278,17 +1292,6 @@ class cocos2d {
|
|||
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<int*>(macNumberOfDraws()) += n;
|
||||
//#else
|
||||
// CC_INCREMENT_GL_DRAWS(n);
|
||||
//#endif
|
||||
//}
|
||||
|
||||
[[link(win)]]
|
||||
class DS_Dictionary {
|
||||
DS_Dictionary() = mac 0xbe9a0;
|
||||
|
@ -1302,7 +1305,7 @@ class DS_Dictionary {
|
|||
auto getObjectForKey(char const*) = mac 0xC4BB0;
|
||||
}
|
||||
|
||||
[[link(win)]]
|
||||
[[link(win, android)]]
|
||||
class pugi::xml_document {
|
||||
xml_document() = mac 0x393a80;
|
||||
~xml_document() = mac 0x393b50;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
set(GEODE_CLI_MINIMUM_VERSION 1.0.5)
|
||||
|
||||
# Find Geode CLI
|
||||
if (NOT DEFINED GEODE_CLI)
|
||||
if (NOT DEFINED GEODE_CLI OR GEODE_CLI STREQUAL "GEODE_CLI-NOTFOUND")
|
||||
find_program(GEODE_CLI NAMES geode.exe geode-cli.exe geode geode-cli PATHS ${CLI_PATH})
|
||||
endif()
|
||||
|
||||
|
@ -59,7 +59,7 @@ function(setup_geode_mod proname)
|
|||
if(GEODE_CLI STREQUAL "GEODE_CLI-NOTFOUND")
|
||||
message(FATAL_ERROR
|
||||
"setup_geode_mod called, but Geode CLI was not found - "
|
||||
"Please install CLI: https://docs.geode-sdk.org/info/installcli/"
|
||||
"Please install CLI: https://docs.geode-sdk.org/"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
@ -130,9 +130,8 @@ function(setup_geode_mod proname)
|
|||
set(HAS_HEADERS Off)
|
||||
endif()
|
||||
|
||||
# todo: figure out how to either not make cmake shit itself and print out --binary path/to/dll "" or
|
||||
# make cli not shit itself when it sees that
|
||||
if (HAS_HEADERS)
|
||||
if (HAS_HEADERS AND WIN32)
|
||||
# this adds the .lib file on windows, which is needed for linking with the headers
|
||||
add_custom_target(${proname}_PACKAGE ALL
|
||||
DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json
|
||||
COMMAND ${GEODE_CLI} package new ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
@ -188,6 +187,9 @@ function(setup_geode_mod proname)
|
|||
elseif (APPLE)
|
||||
file(GLOB libs ${dir}/*.dylib)
|
||||
list(APPEND libs_to_link ${libs})
|
||||
elseif (ANDROID)
|
||||
file(GLOB libs ${dir}/*.so)
|
||||
list(APPEND libs_to_link ${libs})
|
||||
else()
|
||||
message(FATAL_ERROR "Library extension not defined on this platform")
|
||||
endif()
|
||||
|
@ -205,6 +207,17 @@ function(setup_geode_mod proname)
|
|||
set_target_properties(${proname} PROPERTIES PREFIX "")
|
||||
set_target_properties(${proname} PROPERTIES OUTPUT_NAME ${MOD_ID})
|
||||
|
||||
if (ANDROID)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
add_custom_command(
|
||||
TARGET "${PROJECT_NAME}" POST_BUILD
|
||||
DEPENDS "${PROJECT_NAME}"
|
||||
COMMAND $<$<CONFIG:release>:${CMAKE_STRIP}>
|
||||
ARGS -S $<TARGET_FILE:${PROJECT_NAME}>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
function(create_geode_file proname)
|
||||
|
@ -294,9 +307,10 @@ function(package_geode_resources_now proname src dest header_dest)
|
|||
# "LOADER_RESOURCE_FILES {\n"
|
||||
)
|
||||
|
||||
list(APPEND HASHED_EXTENSIONS ".png")
|
||||
list(APPEND HASHED_EXTENSIONS ".mp3")
|
||||
list(APPEND HASHED_EXTENSIONS ".ogg")
|
||||
# yeah don't think we need to check too many stuff
|
||||
# list(APPEND HASHED_EXTENSIONS ".png")
|
||||
# list(APPEND HASHED_EXTENSIONS ".mp3")
|
||||
# list(APPEND HASHED_EXTENSIONS ".ogg")
|
||||
list(APPEND HASHED_EXTENSIONS ".md")
|
||||
|
||||
foreach(file ${RESOURCE_FILES})
|
||||
|
|
|
@ -78,10 +78,10 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Android")
|
|||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_LOADER_PATH}/include/link/android/libcocos2dcpp.so
|
||||
${GEODE_LOADER_PATH}/include/link/android/libcurl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android/libssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android/libcrypto.a
|
||||
${GEODE_LOADER_PATH}/include/link/android/libcocos2dcpp.so
|
||||
log
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ project(Codegen LANGUAGES C CXX)
|
|||
include(../cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage("gh:fmtlib/fmt#9.1.0")
|
||||
CPMAddPackage("gh:geode-sdk/Broma#460f82d")
|
||||
CPMAddPackage("gh:geode-sdk/Broma#38a3bba")
|
||||
|
||||
file(GLOB SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Shared.hpp"
|
||||
#include "AndroidSymbol.hpp"
|
||||
|
||||
namespace {
|
||||
namespace format_strings {
|
||||
|
@ -78,6 +79,8 @@ std::string generateAddressHeader(Root const& root) {
|
|||
output += format_strings::address_begin;
|
||||
|
||||
for (auto& f : root.functions) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
std::string address_str;
|
||||
|
||||
if (codegen::getStatus(f) == BindStatus::Binded) {
|
||||
|
@ -103,6 +106,8 @@ std::string generateAddressHeader(Root const& root) {
|
|||
|
||||
for (auto& c : root.classes) {
|
||||
for (auto& field : c.fields) {
|
||||
if (codegen::getStatus(field) == BindStatus::Missing) continue;
|
||||
|
||||
std::string address_str;
|
||||
|
||||
auto fn = field.get_as<FunctionBindField>();
|
||||
|
@ -111,8 +116,7 @@ std::string generateAddressHeader(Root const& root) {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (codegen::getStatus(field) == BindStatus::NeedsBinding || codegen::platformNumber(field)) {
|
||||
if (codegen::getStatus(field) == BindStatus::NeedsBinding || codegen::platformNumber(field) != -1) {
|
||||
if (is_cocos_class(field.parent) && codegen::platform == Platform::Windows) {
|
||||
address_str = fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
|
||||
}
|
||||
|
@ -120,6 +124,13 @@ std::string generateAddressHeader(Root const& root) {
|
|||
address_str = fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
|
||||
}
|
||||
}
|
||||
else if (codegen::shouldAndroidBind(fn)) {
|
||||
auto const mangled = generateAndroidSymbol(c, fn);
|
||||
address_str = fmt::format( // thumb
|
||||
"reinterpret_cast<uintptr_t>(dlsym(dlopen(\"libcocos2dcpp.so\", RTLD_NOW), \"{}\"))",
|
||||
mangled
|
||||
);
|
||||
}
|
||||
else if (codegen::getStatus(field) == BindStatus::Binded && fn->prototype.type == FunctionType::Normal) {
|
||||
address_str = fmt::format(
|
||||
"addresser::get{}Virtual(Resolve<{}>::func(&{}::{}))",
|
||||
|
|
155
codegen/src/AndroidSymbol.hpp
Normal file
155
codegen/src/AndroidSymbol.hpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include "Shared.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
std::string mangleIdent(std::string_view str, bool ne = true) {
|
||||
if (str.find("::") != -1) {
|
||||
std::string result = ne ? "N" : "";
|
||||
auto s = str;
|
||||
do {
|
||||
const auto i = s.find("::");
|
||||
const auto t = s.substr(0, i);
|
||||
result += std::to_string(t.size()) + std::string(t);
|
||||
if (i == -1) s = "";
|
||||
else
|
||||
s = s.substr(i + 2);
|
||||
} while(s.size());
|
||||
return result + (ne ? "E" : "");
|
||||
} else {
|
||||
return std::to_string(str.size()) + std::string(str);
|
||||
}
|
||||
};
|
||||
|
||||
std::string intToString(unsigned int value, unsigned int radix) {
|
||||
static constexpr char base36[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
std::string result;
|
||||
do {
|
||||
unsigned int remainder = value % radix;
|
||||
value /= radix;
|
||||
result.insert(result.begin(), base36[remainder]);
|
||||
} while (value);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string lookForSeen(std::vector<std::string>& seen, std::string mangled) {
|
||||
for (int i = 0; i < seen.size(); ++i) {
|
||||
if (seen[i] == mangled) {
|
||||
if (i == 0) return "S_";
|
||||
// yes, its base 36
|
||||
return "S" + intToString(i - 1, 36) + "_";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string subsSeen(std::vector<std::string>& seen, std::string mangled, bool subs) {
|
||||
if (!subs) return mangled;
|
||||
if (mangled.empty()) return mangled;
|
||||
if (auto x = lookForSeen(seen, mangled); !x.empty()) return x;
|
||||
seen.push_back(mangled);
|
||||
return mangled;
|
||||
}
|
||||
|
||||
std::string mangleType(std::vector<std::string>& seen, std::string name, bool subs = true) {
|
||||
if (name == "int") return "i";
|
||||
if (name == "float") return "f";
|
||||
if (name == "bool") return "b";
|
||||
if (name == "char") return "c";
|
||||
if (name == "gd::string") return "Ss";
|
||||
if (name == "cocos2d::ccColor3B") return mangleType(seen, "cocos2d::_ccColor3B", subs);
|
||||
// too lazy
|
||||
if (name == "gd::map<gd::string, gd::string>") return "St3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE";
|
||||
if (name == "cocos2d::SEL_MenuHandler") {
|
||||
const auto a = mangleType(seen, "cocos2d::CCObject", subs);
|
||||
const auto b = mangleType(seen, "cocos2d::CCObject*", subs);
|
||||
const auto fnptr = subsSeen(seen, "Fv" + b + "E", subs);
|
||||
return subsSeen(seen, "M" + a + fnptr, subs);
|
||||
}
|
||||
if (name.find('*') == name.size() - 1) {
|
||||
auto inner = mangleType(seen, name.substr(0, name.size() - 1), false);
|
||||
if (auto x = lookForSeen(seen, "P" + inner); !x.empty()) return x;
|
||||
inner = mangleType(seen, name.substr(0, name.size() - 1), subs);
|
||||
return subsSeen(seen, "P" + inner, subs);
|
||||
}
|
||||
if (name.find('&') == name.size() - 1) {
|
||||
auto inner = mangleType(seen, name.substr(0, name.size() - 1), false);
|
||||
if (auto x = lookForSeen(seen, "R" + inner); !x.empty()) return x;
|
||||
inner = mangleType(seen, name.substr(0, name.size() - 1), subs);
|
||||
return subsSeen(seen, "R" + inner, subs);
|
||||
}
|
||||
if (auto i = name.find("const"); i != -1) {
|
||||
std::string inner;
|
||||
// at the end of the name
|
||||
if (i == name.size() - 5) {
|
||||
inner = mangleType(seen, name.substr(0, i - 1));
|
||||
} else if (i == 0) {
|
||||
inner = mangleType(seen, name.substr(6));
|
||||
} else {
|
||||
inner = "v";
|
||||
std::cout << "um " << name << std::endl;
|
||||
}
|
||||
return subsSeen(seen, "K" + inner, subs);
|
||||
}
|
||||
|
||||
if (name.find("::") != -1) {
|
||||
std::string result = "";
|
||||
std::string substituted = "";
|
||||
auto s = name;
|
||||
do {
|
||||
const auto i = s.find("::");
|
||||
const auto t = s.substr(0, i);
|
||||
auto part = std::to_string(t.size()) + std::string(t);
|
||||
if (auto x = lookForSeen(seen, result + part); !x.empty()) {
|
||||
substituted = x;
|
||||
} else {
|
||||
substituted = subsSeen(seen, substituted + part, subs);
|
||||
}
|
||||
result += part;
|
||||
|
||||
if (i == -1) s = "";
|
||||
else s = s.substr(i + 2);
|
||||
} while(s.size());
|
||||
if (substituted.size() == 3 && substituted[0] == 'S')
|
||||
return substituted;
|
||||
return "N" + substituted + "E";
|
||||
} else {
|
||||
return subsSeen(seen, mangleIdent(name), subs);
|
||||
}
|
||||
};
|
||||
|
||||
std::string generateAndroidSymbol(const Class& clazz, const FunctionBindField* fn) {
|
||||
auto& decl = fn->prototype;
|
||||
|
||||
|
||||
std::string mangledSymbol;
|
||||
switch (decl.type) {
|
||||
case FunctionType::Ctor:
|
||||
mangledSymbol = "_ZN" + mangleIdent(clazz.name, false) + "C2E";
|
||||
break;
|
||||
case FunctionType::Dtor:
|
||||
mangledSymbol = "_ZN" + mangleIdent(clazz.name, false) + "D2E";
|
||||
break;
|
||||
default:
|
||||
mangledSymbol = "_Z" + mangleIdent(clazz.name + "::" + decl.name);
|
||||
break;
|
||||
}
|
||||
if (decl.args.empty()) {
|
||||
mangledSymbol += "v";
|
||||
} else {
|
||||
std::vector<std::string> seen;
|
||||
static constexpr auto firstPart = [](std::string_view str, std::string_view sep) {
|
||||
return str.substr(0, str.find(sep));
|
||||
};
|
||||
// this is S_
|
||||
seen.push_back(mangleIdent(firstPart(clazz.name, "::")));
|
||||
for (auto& [ty, _] : decl.args) {
|
||||
mangledSymbol += mangleType(seen, ty.name);
|
||||
}
|
||||
}
|
||||
return mangledSymbol;
|
||||
}
|
|
@ -131,6 +131,8 @@ std::string generateBindingHeader(Root const& root, ghc::filesystem::path const&
|
|||
single_output += format_strings::class_includes;
|
||||
|
||||
for (auto& f : root.functions) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
FunctionProto const* fb = &f.prototype;
|
||||
char const* used_format = format_strings::function_definition;
|
||||
|
||||
|
@ -202,6 +204,8 @@ std::string generateBindingHeader(Root const& root, ghc::filesystem::path const&
|
|||
|
||||
bool unimplementedField = false;
|
||||
for (auto field : cls.fields) {
|
||||
if (codegen::getStatus(field) == BindStatus::Missing) continue;
|
||||
|
||||
MemberFunctionProto* fb;
|
||||
char const* used_format = format_strings::function_definition;
|
||||
|
||||
|
@ -221,9 +225,13 @@ std::string generateBindingHeader(Root const& root, ghc::filesystem::path const&
|
|||
} else if (auto p = field.get_as<PadField>()) {
|
||||
auto hardcode = codegen::platformNumber(p->amount);
|
||||
|
||||
if (hardcode) {
|
||||
if (hardcode > 0) {
|
||||
single_output += fmt::format(format_strings::pad_definition, fmt::arg("hardcode", hardcode));
|
||||
} else {
|
||||
}
|
||||
else if (hardcode == 0) {
|
||||
single_output += " // no padding\n";
|
||||
}
|
||||
else {
|
||||
unimplementedField = true;
|
||||
}
|
||||
continue;
|
||||
|
@ -234,7 +242,7 @@ std::string generateBindingHeader(Root const& root, ghc::filesystem::path const&
|
|||
} else if (auto fn = field.get_as<FunctionBindField>()) {
|
||||
fb = &fn->prototype;
|
||||
|
||||
if (!codegen::platformNumber(fn->binds)) {
|
||||
if (codegen::platformNumber(fn->binds) == -1 && codegen::getStatus(field) != BindStatus::Binded) {
|
||||
used_format = format_strings::error_definition;
|
||||
|
||||
if (fb->type != FunctionType::Normal)
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace geode::modifier {{
|
|||
std::string generateModifyHeader(Root const& root, ghc::filesystem::path const& singleFolder) {
|
||||
std::string output;
|
||||
|
||||
for (auto& c : root.classes) {
|
||||
for (auto& c : root.classes) {
|
||||
if (c.name == "cocos2d") continue;
|
||||
|
||||
std::string filename = (codegen::getUnqualifiedClassName(c.name) + ".hpp");
|
||||
|
@ -83,6 +83,8 @@ std::string generateModifyHeader(Root const& root, ghc::filesystem::path const&
|
|||
std::string statics;
|
||||
std::set<std::string> used;
|
||||
for (auto& f : c.fields) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
if (auto fn = f.get_as<FunctionBindField>()) {
|
||||
if (fn->prototype.type == FunctionType::Normal && !used.count(fn->prototype.name)) {
|
||||
used.insert(fn->prototype.name);
|
||||
|
@ -102,13 +104,15 @@ std::string generateModifyHeader(Root const& root, ghc::filesystem::path const&
|
|||
|
||||
// modify
|
||||
for (auto& f : c.fields) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
auto fn = f.get_as<FunctionBindField>();
|
||||
|
||||
if (!fn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (codegen::getStatus(f) == BindStatus::NeedsBinding || codegen::platformNumber(f)) {
|
||||
if (codegen::getStatus(f) == BindStatus::NeedsBinding || codegen::platformNumber(f) != -1) {
|
||||
|
||||
}
|
||||
else if (codegen::getStatus(f) == BindStatus::Binded && fn->prototype.type == FunctionType::Normal) {
|
||||
|
|
|
@ -51,7 +51,8 @@ inline bool is_cocos_class(std::string const& str) {
|
|||
enum class BindStatus {
|
||||
Binded,
|
||||
NeedsBinding,
|
||||
Unbindable
|
||||
Unbindable,
|
||||
Missing,
|
||||
};
|
||||
|
||||
struct codegen_error : std::runtime_error {
|
||||
|
@ -91,7 +92,7 @@ namespace codegen {
|
|||
|
||||
inline Platform platform;
|
||||
|
||||
inline uintptr_t platformNumberWithPlatform(Platform p, PlatformNumber const& pn) {
|
||||
inline ptrdiff_t platformNumberWithPlatform(Platform p, PlatformNumber const& pn) {
|
||||
switch (p) {
|
||||
case Platform::Mac: return pn.mac;
|
||||
case Platform::Windows: return pn.win;
|
||||
|
@ -102,7 +103,7 @@ namespace codegen {
|
|||
}
|
||||
}
|
||||
|
||||
inline uintptr_t platformNumber(PlatformNumber const& p) {
|
||||
inline ptrdiff_t platformNumber(PlatformNumber const& p) {
|
||||
return platformNumberWithPlatform(codegen::platform, p);
|
||||
}
|
||||
|
||||
|
@ -114,21 +115,35 @@ namespace codegen {
|
|||
}
|
||||
|
||||
inline BindStatus getStatusWithPlatform(Platform p, Field const& field) {
|
||||
if ((field.missing & p) != Platform::None) return BindStatus::Missing;
|
||||
|
||||
if (auto fn = field.get_as<FunctionBindField>()) {
|
||||
if ((fn->links & p) != Platform::None) return BindStatus::Binded;
|
||||
if (platformNumberWithPlatform(p, fn->binds)) return BindStatus::NeedsBinding;
|
||||
if ((field.links & p) != Platform::None) return BindStatus::Binded;
|
||||
if (platformNumberWithPlatform(p, fn->binds) != -1) return BindStatus::NeedsBinding;
|
||||
}
|
||||
|
||||
return BindStatus::Unbindable;
|
||||
}
|
||||
|
||||
inline BindStatus getStatusWithPlatform(Platform p, Function const& f) {
|
||||
if ((f.missing & p) != Platform::None) return BindStatus::Missing;
|
||||
|
||||
if ((f.links & p) != Platform::None) return BindStatus::Binded;
|
||||
if (platformNumberWithPlatform(p, f.binds)) return BindStatus::NeedsBinding;
|
||||
if (platformNumberWithPlatform(p, f.binds) != -1) return BindStatus::NeedsBinding;
|
||||
|
||||
return BindStatus::Unbindable;
|
||||
}
|
||||
|
||||
inline bool shouldAndroidBind(const FunctionBindField* fn) {
|
||||
if (codegen::platform == Platform::Android) {
|
||||
if (fn->prototype.type != FunctionType::Normal) return true;
|
||||
for (auto& [type, name] : fn->prototype.args) {
|
||||
if (can_find(type.name, "gd::")) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline BindStatus getStatus(Field const& field) {
|
||||
return getStatusWithPlatform(codegen::platform, field);
|
||||
}
|
||||
|
|
|
@ -135,6 +135,8 @@ std::string generateBindingSource(Root const& root) {
|
|||
std::string output(format_strings::source_start);
|
||||
|
||||
for (auto& f : root.functions) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
if (codegen::getStatus(f) != BindStatus::NeedsBinding) {
|
||||
continue;
|
||||
}
|
||||
|
@ -153,11 +155,13 @@ std::string generateBindingSource(Root const& root) {
|
|||
for (auto& c : root.classes) {
|
||||
|
||||
for (auto& f : c.fields) {
|
||||
if (codegen::getStatus(f) == BindStatus::Missing) continue;
|
||||
|
||||
if (auto i = f.get_as<InlineField>()) {
|
||||
// yeah there are no inlines on cocos
|
||||
}
|
||||
else if (auto fn = f.get_as<OutOfLineField>()) {
|
||||
if ((c.links & codegen::platform) != Platform::None) {
|
||||
if (is_cocos_class(c.name) && (c.links & codegen::platform) != Platform::None) {
|
||||
continue;
|
||||
}
|
||||
if (codegen::getStatus(f) != BindStatus::Unbindable) {
|
||||
|
@ -193,15 +197,15 @@ std::string generateBindingSource(Root const& root) {
|
|||
|
||||
if (
|
||||
codegen::getStatus(f) == BindStatus::Unbindable &&
|
||||
!codegen::platformNumber(fn->binds) &&
|
||||
codegen::platformNumber(fn->binds) == -1 &&
|
||||
fn->prototype.is_virtual && fn->prototype.type != FunctionType::Dtor
|
||||
) {
|
||||
used_declare_format = format_strings::declare_virtual_error;
|
||||
}
|
||||
else if (codegen::getStatus(f) != BindStatus::NeedsBinding) {
|
||||
else if (codegen::getStatus(f) != BindStatus::NeedsBinding && !codegen::shouldAndroidBind(fn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!used_declare_format) {
|
||||
switch (fn->prototype.type) {
|
||||
|
|
|
@ -149,6 +149,11 @@ if (APPLE)
|
|||
file(COPY ${GEODE_LOADER_PATH}/include/link/libfmod.dylib DESTINATION ${GEODE_BIN_PATH}/nightly)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
# needed to define some opengl functions
|
||||
target_link_libraries(${PROJECT_NAME} EGL)
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
src/
|
||||
src/loader/
|
||||
|
|
|
@ -81,8 +81,7 @@ namespace geode {
|
|||
GEODE_IOS(GEODE_FILL_CONSTRUCTOR(Class_, 0){}) \
|
||||
GEODE_WINDOWS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Class_() {}) \
|
||||
GEODE_ANDROID(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Class_() {})
|
||||
GEODE_ANDROID(GEODE_FILL_CONSTRUCTOR(Class_, 0){})
|
||||
|
||||
#define GEODE_CUTOFF_CONSTRUCTOR_COCOS(Class_, Base_) \
|
||||
GEODE_MACOS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
|
@ -91,8 +90,8 @@ namespace geode {
|
|||
: Base_(geode::CutoffConstructor, fill){}) \
|
||||
GEODE_WINDOWS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Class_() {}) \
|
||||
GEODE_ANDROID(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Class_() {})
|
||||
GEODE_ANDROID(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Base_(geode::CutoffConstructor, fill){})
|
||||
|
||||
#define GEODE_CUTOFF_CONSTRUCTOR_GD(Class_, Base_) \
|
||||
GEODE_WINDOWS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
|
@ -106,7 +105,8 @@ namespace geode {
|
|||
|
||||
#define GEODE_CUTOFF_CONSTRUCTOR_CUTOFF(Class_, Base_) \
|
||||
GEODE_WINDOWS(GEODE_FILL_CONSTRUCTOR(Class_, sizeof(Base_)) : Base_(){}) \
|
||||
GEODE_ANDROID(GEODE_FILL_CONSTRUCTOR(Class_, sizeof(Base_)) : Base_(){}) \
|
||||
GEODE_ANDROID(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Base_(geode::CutoffConstructor, fill){}) \
|
||||
GEODE_MACOS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
: Base_(geode::CutoffConstructor, fill){}) \
|
||||
GEODE_IOS(Class_(geode::CutoffConstructorType, size_t fill) \
|
||||
|
|
|
@ -295,7 +295,9 @@ enum class GJRewardType
|
|||
{
|
||||
Unknown = 0x0,
|
||||
Small = 0x1,
|
||||
Large = 0x2
|
||||
Large = 0x2,
|
||||
SmallTreasure = 0x3,
|
||||
LargeTreasure = 0x4
|
||||
};
|
||||
|
||||
enum class IconType {
|
||||
|
|
|
@ -405,8 +405,11 @@ namespace gd {
|
|||
}
|
||||
|
||||
~vector() {
|
||||
for (auto i = m_start; i != m_finish; ++i) {
|
||||
delete i;
|
||||
if (m_start) {
|
||||
for (auto& x : *this) {
|
||||
x.~T();
|
||||
}
|
||||
delete m_start;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ class CC_DLL CCEGLView : public CCEGLViewProtocol
|
|||
{
|
||||
GEODE_FRIEND_MODIFY
|
||||
public:
|
||||
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCEGLView, CCEGLViewProtocol)
|
||||
|
||||
CCEGLView();
|
||||
virtual ~CCEGLView();
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ namespace geode {
|
|||
LoadFailed,
|
||||
EnableFailed,
|
||||
MissingDependency,
|
||||
PresentIncompatibility
|
||||
PresentIncompatibility,
|
||||
UnzipFailed
|
||||
};
|
||||
Type type;
|
||||
std::variant<ghc::filesystem::path, ModMetadata, Mod*> cause;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include "Traits.hpp"
|
||||
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include <cocos2d.h>
|
||||
#include <vector>
|
||||
|
||||
|
@ -49,8 +49,7 @@ namespace geode::modifier {
|
|||
}
|
||||
};
|
||||
|
||||
[[deprecated("Will be removed in 1.0.0")]]
|
||||
GEODE_DLL size_t getFieldIndexForClass(size_t hash);
|
||||
[[deprecated("Will be removed in 1.0.0")]] GEODE_DLL size_t getFieldIndexForClass(size_t hash);
|
||||
GEODE_DLL size_t getFieldIndexForClass(char const* name);
|
||||
|
||||
template <class Parent, class Base>
|
||||
|
@ -58,7 +57,7 @@ namespace geode::modifier {
|
|||
using Intermediate = Modify<Parent, Base>;
|
||||
// Padding used for guaranteeing any member of parents
|
||||
// will be in between sizeof(Intermediate) and sizeof(Parent)
|
||||
uintptr_t m_padding;
|
||||
alignas(std::max(alignof(Base), alignof(uintptr_t))) uintptr_t m_padding;
|
||||
|
||||
public:
|
||||
// the constructor that constructs the fields.
|
||||
|
@ -120,7 +119,7 @@ namespace geode::modifier {
|
|||
reinterpret_cast<std::byte*>(offsetField) - sizeof(Intermediate)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Parent* self() {
|
||||
return this->operator Parent*();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace geode::cast {
|
||||
using uinthalf_t = uint32_t;
|
||||
using inthalf_t = int32_t;
|
||||
|
||||
struct DummyClass {
|
||||
virtual ~DummyClass() {}
|
||||
|
@ -14,8 +12,10 @@ namespace geode::cast {
|
|||
|
||||
struct DummyMultipleClass : DummySingleClass, DummyClass2 {};
|
||||
|
||||
struct VtableType;
|
||||
|
||||
struct ClassTypeinfoType {
|
||||
void** m_typeinfoVtable;
|
||||
VtableType* m_typeinfoVtable;
|
||||
char const* m_typeinfoName;
|
||||
};
|
||||
|
||||
|
@ -23,16 +23,17 @@ namespace geode::cast {
|
|||
ClassTypeinfoType* m_baseClassTypeinfo;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct MultipleClassSingleEntryType {
|
||||
ClassTypeinfoType* m_baseClassTypeinfo;
|
||||
uint8_t m_visibilityFlag;
|
||||
inthalf_t m_offset;
|
||||
uint8_t m_padding[sizeof(inthalf_t) - 1];
|
||||
};
|
||||
intptr_t m_metadata;
|
||||
|
||||
#pragma pack(pop)
|
||||
uint8_t visibilityFlag() const {
|
||||
return m_metadata & 0xFF;
|
||||
}
|
||||
intptr_t offset() const {
|
||||
return m_metadata >> 8;
|
||||
}
|
||||
};
|
||||
|
||||
struct MultipleClassTypeinfoType : ClassTypeinfoType {
|
||||
uint32_t m_flags;
|
||||
|
@ -41,7 +42,7 @@ namespace geode::cast {
|
|||
};
|
||||
|
||||
struct VtableTypeinfoType {
|
||||
inthalf_t m_offset;
|
||||
intptr_t m_offset;
|
||||
ClassTypeinfoType* m_typeinfo;
|
||||
};
|
||||
|
||||
|
@ -51,36 +52,25 @@ namespace geode::cast {
|
|||
|
||||
struct CompleteVtableType : VtableTypeinfoType, VtableType {};
|
||||
|
||||
inline void** typeinfoVtableOf(void* ptr) {
|
||||
auto vftable = *reinterpret_cast<VtableType**>(ptr);
|
||||
|
||||
auto typeinfoPtr =
|
||||
static_cast<VtableTypeinfoType*>(static_cast<CompleteVtableType*>(vftable));
|
||||
|
||||
return typeinfoPtr->m_typeinfo->m_typeinfoVtable;
|
||||
}
|
||||
|
||||
inline void* traverseTypeinfoFor(
|
||||
void* ptr, ClassTypeinfoType const* typeinfo, char const* afterIdent
|
||||
) {
|
||||
DummySingleClass dummySingleClass;
|
||||
DummyMultipleClass dummyMultipleClass;
|
||||
|
||||
{
|
||||
auto optionIdent = typeinfo->m_typeinfoName;
|
||||
if (std::strcmp(optionIdent, afterIdent) == 0) {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummySingleClass)) {
|
||||
auto typeinfoVtableName = static_cast<CompleteVtableType*>(typeinfo->m_typeinfoVtable)->m_typeinfo->m_typeinfoName;
|
||||
if (std::strcmp(typeinfoVtableName, "N10__cxxabiv120__si_class_type_infoE") == 0) {
|
||||
auto siTypeinfo = static_cast<SingleClassTypeinfoType const*>(typeinfo);
|
||||
return traverseTypeinfoFor(ptr, siTypeinfo->m_baseClassTypeinfo, afterIdent);
|
||||
}
|
||||
else if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummyMultipleClass)) {
|
||||
else if (std::strcmp(typeinfoVtableName, "N10__cxxabiv121__vmi_class_type_infoE") == 0) {
|
||||
auto vmiTypeinfo = static_cast<MultipleClassTypeinfoType const*>(typeinfo);
|
||||
for (int i = 0; i < vmiTypeinfo->m_numBaseClass; ++i) {
|
||||
auto& entry = vmiTypeinfo->m_baseClasses[i];
|
||||
auto optionPtr = reinterpret_cast<std::byte*>(ptr) + entry.m_offset;
|
||||
auto optionPtr = reinterpret_cast<std::byte*>(ptr) + entry.offset();
|
||||
auto ret = traverseTypeinfoFor(optionPtr, entry.m_baseClassTypeinfo, afterIdent);
|
||||
if (ret != nullptr) return ret;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include "ItaniumCast.hpp"
|
||||
|
||||
namespace geode {
|
||||
struct PlatformInfo {
|
||||
|
@ -9,16 +10,6 @@ namespace geode {
|
|||
}
|
||||
|
||||
namespace geode::base {
|
||||
GEODE_NOINLINE inline uintptr_t get() {
|
||||
static uintptr_t base = reinterpret_cast<uintptr_t>(dlopen("libcocos2dcpp.so", RTLD_LAZY));
|
||||
return base;
|
||||
}
|
||||
/*GEODE_NOINLINE inline*/ uintptr_t get();
|
||||
}
|
||||
|
||||
namespace geode::cast {
|
||||
template <class After, class Before>
|
||||
After typeinfo_cast(Before ptr) {
|
||||
// yall have symbols smh
|
||||
return dynamic_cast<After>(ptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace geode {
|
|||
class GEODE_DLL SceneManager {
|
||||
protected:
|
||||
cocos2d::CCArray* m_persistedNodes;
|
||||
cocos2d::CCScene* m_lastScene = nullptr;
|
||||
|
||||
bool setup();
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ namespace geode {
|
|||
size_t getMaxLines();
|
||||
void setWidth(const float width);
|
||||
float getWidth();
|
||||
void setScale(const float scale);
|
||||
float getScale();
|
||||
void setScale(const float scale) override;
|
||||
float getScale() override;
|
||||
void setLinePadding(const float padding);
|
||||
float getLinePadding();
|
||||
std::vector<cocos2d::CCLabelBMFont*> getLines();
|
||||
|
|
|
@ -244,16 +244,31 @@ namespace geode::utils::file {
|
|||
|
||||
/**
|
||||
* Prompt the user to pick a file using the system's file system picker
|
||||
* @note Will not work on Android, use the callback version instead
|
||||
* @param mode Type of file selection prompt to show
|
||||
* @param options Picker options
|
||||
*/
|
||||
GEODE_DLL Result<ghc::filesystem::path> pickFile(PickMode mode, FilePickOptions const& options);
|
||||
|
||||
GEODE_DLL void pickFile(
|
||||
PickMode mode, FilePickOptions const& options,
|
||||
utils::MiniFunction<void(ghc::filesystem::path)> callback,
|
||||
utils::MiniFunction<void()> failed = {}
|
||||
);
|
||||
|
||||
/**
|
||||
* Prompt the user to pick a bunch of files for opening using the system's file system picker
|
||||
* @note Will not work on Android, use the callback version instead
|
||||
* @param options Picker options
|
||||
*/
|
||||
GEODE_DLL Result<std::vector<ghc::filesystem::path>> pickFiles(FilePickOptions const& options);
|
||||
|
||||
GEODE_DLL void pickFiles(
|
||||
FilePickOptions const& options,
|
||||
utils::MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
||||
utils::MiniFunction<void()> failed = {}
|
||||
);
|
||||
|
||||
class GEODE_DLL FileWatchEvent : public Event {
|
||||
protected:
|
||||
ghc::filesystem::path m_path;
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/modify/Modify.hpp>
|
||||
|
||||
$execute {
|
||||
// this replaces the call to __dynamic_cast with a call to our own
|
||||
// this is needed because the transitions in cocos uses dynamic cast to check
|
||||
// layers, which fail on user layers due to typeinfo not matching
|
||||
(void)Mod::get()->patch(
|
||||
reinterpret_cast<void*>(base::get() + 0x603948), toByteArray(&cast::typeinfoCastInternal)
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
#if defined(GEODE_IS_MACOS)
|
||||
(void)Mod::get()->patch(
|
||||
reinterpret_cast<void*>(base::get() + 0x603948), toByteArray(&cast::typeinfoCastInternal)
|
||||
);
|
||||
#elif defined(GEODE_IS_ANDROID)
|
||||
(void)Mod::get()->addHook(reinterpret_cast<void*>(base::get() + 0x519a8c), &cast::typeinfoCastInternal, "__dynamic_cast");
|
||||
#endif
|
||||
|
||||
|
||||
}
|
|
@ -9,30 +9,38 @@ using namespace geode::prelude;
|
|||
|
||||
struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
||||
CCLabelBMFont* m_smallLabel = nullptr;
|
||||
CCLabelBMFont* m_smallLabel2 = nullptr;
|
||||
int m_geodeLoadStep = 0;
|
||||
int m_totalMods = 0;
|
||||
|
||||
void updateLoadedModsLabel() {
|
||||
auto allMods = Loader::get()->getAllMods();
|
||||
auto count = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) {
|
||||
return item->isEnabled();
|
||||
});
|
||||
auto totalCount = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) {
|
||||
return item->shouldLoad();
|
||||
});
|
||||
auto str = fmt::format("Geode: Loaded {}/{} mods", count, totalCount);
|
||||
auto str = fmt::format("Geode: Loaded {}/{} mods", count, m_totalMods);
|
||||
this->setSmallText(str);
|
||||
auto currentMod = LoaderImpl::get()->m_currentlyLoadingMod;
|
||||
auto modName = currentMod ? currentMod->getName() : "Unknown";
|
||||
this->setSmallText2(modName);
|
||||
}
|
||||
|
||||
void setSmallText(std::string const& text) {
|
||||
m_fields->m_smallLabel->setString(text.c_str());
|
||||
}
|
||||
|
||||
void setSmallText2(std::string const& text) {
|
||||
m_fields->m_smallLabel2->setString(text.c_str());
|
||||
}
|
||||
|
||||
// hook
|
||||
bool init(bool fromReload) {
|
||||
CCFileUtils::get()->updatePaths();
|
||||
|
||||
if (!LoadingLayer::init(fromReload)) return false;
|
||||
|
||||
m_totalMods = Loader::get()->getAllMods().size();
|
||||
|
||||
auto winSize = CCDirector::sharedDirector()->getWinSize();
|
||||
|
||||
m_fields->m_smallLabel = CCLabelBMFont::create("", "goldFont.fnt");
|
||||
|
@ -41,6 +49,12 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
m_fields->m_smallLabel->setID("geode-small-label");
|
||||
this->addChild(m_fields->m_smallLabel);
|
||||
|
||||
m_fields->m_smallLabel2 = CCLabelBMFont::create("", "goldFont.fnt");
|
||||
m_fields->m_smallLabel2->setPosition(winSize.width / 2, 15.f);
|
||||
m_fields->m_smallLabel2->setScale(.45f);
|
||||
m_fields->m_smallLabel2->setID("geode-small-label");
|
||||
this->addChild(m_fields->m_smallLabel2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -51,22 +65,29 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
}
|
||||
else {
|
||||
this->continueLoadAssets();
|
||||
this->setSmallText2("");
|
||||
}
|
||||
}
|
||||
|
||||
void setupLoaderResources() {
|
||||
log::debug("Verifying Loader Resources");
|
||||
this->setSmallText("Verifying Loader Resources");
|
||||
// verify loader resources
|
||||
if (!LoaderImpl::get()->verifyLoaderResources()) {
|
||||
this->setSmallText("Downloading Loader Resources");
|
||||
this->addChild(EventListenerNode<ResourceDownloadFilter>::create(
|
||||
this, &CustomLoadingLayer::updateResourcesProgress
|
||||
));
|
||||
}
|
||||
else {
|
||||
this->setSmallText("Loading Loader Resources");
|
||||
LoaderImpl::get()->updateSpecialFiles();
|
||||
this->continueLoadAssets();
|
||||
}
|
||||
Loader::get()->queueInMainThread([&]() {
|
||||
if (!LoaderImpl::get()->verifyLoaderResources()) {
|
||||
log::debug("Downloading Loader Resources");
|
||||
this->setSmallText("Downloading Loader Resources");
|
||||
this->addChild(EventListenerNode<ResourceDownloadFilter>::create(
|
||||
this, &CustomLoadingLayer::updateResourcesProgress
|
||||
));
|
||||
}
|
||||
else {
|
||||
log::debug("Loading Loader Resources");
|
||||
this->setSmallText("Loading Loader Resources");
|
||||
LoaderImpl::get()->updateSpecialFiles();
|
||||
this->continueLoadAssets();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateResourcesProgress(ResourceDownloadEvent* event) {
|
||||
|
@ -77,10 +98,12 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
));
|
||||
},
|
||||
[&](UpdateFinished) {
|
||||
log::debug("Downloaded Loader Resources");
|
||||
this->setSmallText("Downloaded Loader Resources");
|
||||
this->continueLoadAssets();
|
||||
},
|
||||
[&](UpdateFailed const& error) {
|
||||
log::debug("Failed Loader Resources");
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
"Error updating resources",
|
||||
error + ".\n"
|
||||
|
@ -104,11 +127,11 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
}
|
||||
|
||||
int getCurrentStep() {
|
||||
return m_fields->m_geodeLoadStep + m_loadStep + 1;
|
||||
return m_fields->m_geodeLoadStep + m_loadStep + 1 + LoaderImpl::get()->m_refreshedModCount;
|
||||
}
|
||||
|
||||
int getTotalStep() {
|
||||
return 18;
|
||||
return 18 + m_totalMods;
|
||||
}
|
||||
|
||||
void updateLoadingBar() {
|
||||
|
|
18
loader/src/hooks/TextInputNodeFix.cpp
Normal file
18
loader/src/hooks/TextInputNodeFix.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <Geode/modify/CCTextInputNode.hpp>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
struct TextNodeFix : Modify<TextNodeFix, CCTextInputNode> {
|
||||
bool onTextFieldInsertText(cocos2d::CCTextFieldTTF* field, char const* text, int count) {
|
||||
auto change = count >= m_maxLabelLength ? 1 : 0;
|
||||
|
||||
m_maxLabelLength += change;
|
||||
auto ret = CCTextInputNode::onTextFieldInsertText(field, text, count);
|
||||
m_maxLabelLength -= change;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -170,4 +170,4 @@ struct LevelBrowserLayerIDs : Modify<LevelBrowserLayerIDs, LevelBrowserLayer> {
|
|||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -134,4 +134,4 @@ struct LevelInfoLayerIDs : Modify<LevelInfoLayerIDs, LevelInfoLayer> {
|
|||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using namespace geode::prelude;
|
||||
|
||||
static std::string getDateString(bool filesafe) {
|
||||
std::string crashlog::getDateString(bool filesafe) {
|
||||
auto const now = std::time(nullptr);
|
||||
auto const tm = *std::localtime(&now);
|
||||
std::ostringstream oss;
|
||||
|
@ -16,13 +16,13 @@ static std::string getDateString(bool filesafe) {
|
|||
return oss.str();
|
||||
}
|
||||
|
||||
static void printGeodeInfo(std::stringstream& stream) {
|
||||
void crashlog::printGeodeInfo(std::stringstream& stream) {
|
||||
stream << "Loader Version: " << Loader::get()->getVersion().toString() << "\n"
|
||||
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
|
||||
<< "Problems: " << Loader::get()->getProblems().size() << "\n";
|
||||
}
|
||||
|
||||
static void printMods(std::stringstream& stream) {
|
||||
void crashlog::printMods(std::stringstream& stream) {
|
||||
auto mods = Loader::get()->getAllMods();
|
||||
if (mods.empty()) {
|
||||
stream << "<None>\n";
|
||||
|
@ -30,7 +30,7 @@ static void printMods(std::stringstream& stream) {
|
|||
using namespace std::string_view_literals;
|
||||
for (auto& mod : mods) {
|
||||
stream << fmt::format("{} | [{}] {}\n",
|
||||
mod->isEnabled() ? "x"sv : " "sv,
|
||||
mod->isEnabled() ? "x"sv : mod->shouldLoad() ? "~"sv : " "sv,
|
||||
mod->getVersion().toString(), mod->getID()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,12 @@ namespace crashlog {
|
|||
* @returns True if the handler was successfully installed, false otherwise
|
||||
*/
|
||||
bool GEODE_DLL setupPlatformHandler();
|
||||
|
||||
/**
|
||||
* Setup platform-specific crashlog handler for post-launch
|
||||
*/
|
||||
void GEODE_DLL setupPlatformHandlerPost();
|
||||
|
||||
/**
|
||||
* Check if previous launch of GD crashed unexpectedly
|
||||
* @returns True if the launch crashed, false otherwise or if indeterminate
|
||||
|
@ -26,4 +32,11 @@ namespace crashlog {
|
|||
ghc::filesystem::path GEODE_DLL getCrashLogDirectory();
|
||||
|
||||
std::string GEODE_DLL writeCrashlog(geode::Mod* faultyMod, std::string const& info, std::string const& stacktrace, std::string const& registers);
|
||||
|
||||
std::string getDateString(bool filesafe);
|
||||
|
||||
void printGeodeInfo(std::stringstream& stream);
|
||||
void printMods(std::stringstream& stream);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -88,6 +88,8 @@ int geodeEntry(void* platformData) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
crashlog::setupPlatformHandlerPost();
|
||||
|
||||
log::info("Set up loader");
|
||||
|
||||
// download and install new loader update in the background
|
||||
|
|
|
@ -35,10 +35,6 @@ ghc::filesystem::path dirs::getModsSaveDir() {
|
|||
return getGeodeSaveDir() / "mods";
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getModRuntimeDir() {
|
||||
return dirs::getGeodeDir() / "unzipped";
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getModConfigDir() {
|
||||
return dirs::getGeodeDir() / "config";
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ private:
|
|||
friend class Index;
|
||||
|
||||
void cleanupItems();
|
||||
void downloadIndex();
|
||||
void downloadIndex(std::string commitHash = "");
|
||||
void checkForUpdates();
|
||||
void updateFromLocalTree();
|
||||
void installNext(size_t index, IndexInstallList const& list);
|
||||
|
@ -296,7 +296,7 @@ bool Index::hasTriedToUpdate() const {
|
|||
return m_impl->m_triedToUpdate;
|
||||
}
|
||||
|
||||
void Index::Impl::downloadIndex() {
|
||||
void Index::Impl::downloadIndex(std::string commitHash) {
|
||||
log::debug("Downloading index");
|
||||
|
||||
IndexUpdateEvent(UpdateProgress(0, "Beginning download")).post();
|
||||
|
@ -307,7 +307,7 @@ void Index::Impl::downloadIndex() {
|
|||
.join("index-download")
|
||||
.fetch("https://github.com/geode-sdk/mods/zipball/main")
|
||||
.into(targetFile)
|
||||
.then([this, targetFile](auto) {
|
||||
.then([this, targetFile, commitHash](auto) {
|
||||
auto targetDir = dirs::getIndexDir() / "v0";
|
||||
// delete old unzipped index
|
||||
try {
|
||||
|
@ -333,6 +333,10 @@ void Index::Impl::downloadIndex() {
|
|||
|
||||
// remove the directory github adds to the root of the zip
|
||||
(void)flattenGithubRepo(targetDir);
|
||||
if (!commitHash.empty()) {
|
||||
auto const checksumPath = dirs::getIndexDir() / ".checksum";
|
||||
(void)file::writeString(checksumPath, commitHash);
|
||||
}
|
||||
|
||||
Loader::get()->queueInMainThread([this] {
|
||||
// update index
|
||||
|
@ -387,8 +391,7 @@ void Index::Impl::checkForUpdates() {
|
|||
}
|
||||
// otherwise save hash and download source
|
||||
else {
|
||||
(void)file::writeString(checksum, newSHA);
|
||||
this->downloadIndex();
|
||||
this->downloadIndex(newSHA);
|
||||
}
|
||||
})
|
||||
.expect([](std::string const& err) {
|
||||
|
@ -591,7 +594,7 @@ bool Index::isUpdateAvailable(IndexItemHandle item) const {
|
|||
bool Index::areUpdatesAvailable() const {
|
||||
for (auto& mod : Loader::get()->getAllMods()) {
|
||||
auto item = this->getMajorItem(mod->getID());
|
||||
if (item && item->getMetadata().getVersion() > mod->getVersion()) {
|
||||
if (item && item->getMetadata().getVersion() > mod->getVersion() && mod->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,15 +41,12 @@ void Loader::Impl::createDirectories() {
|
|||
ghc::filesystem::create_directory(dirs::getSaveDir());
|
||||
#endif
|
||||
|
||||
// try deleting geode/unzipped if it already exists
|
||||
try { ghc::filesystem::remove_all(dirs::getModRuntimeDir()); } catch(...) {}
|
||||
|
||||
(void) utils::file::createDirectoryAll(dirs::getGeodeResourcesDir());
|
||||
(void) utils::file::createDirectory(dirs::getModConfigDir());
|
||||
(void) utils::file::createDirectory(dirs::getModsDir());
|
||||
(void) utils::file::createDirectory(dirs::getGeodeLogDir());
|
||||
(void) utils::file::createDirectory(dirs::getTempDir());
|
||||
(void) utils::file::createDirectory(dirs::getModRuntimeDir());
|
||||
(void) utils::file::createDirectoryAll(dirs::getModConfigDir());
|
||||
(void) utils::file::createDirectoryAll(dirs::getModsDir());
|
||||
(void) utils::file::createDirectoryAll(dirs::getGeodeLogDir());
|
||||
(void) utils::file::createDirectoryAll(dirs::getTempDir());
|
||||
(void) utils::file::createDirectoryAll(dirs::getModRuntimeDir());
|
||||
|
||||
if (!ranges::contains(m_modSearchDirectories, dirs::getModsDir())) {
|
||||
m_modSearchDirectories.push_back(dirs::getModsDir());
|
||||
|
@ -388,7 +385,7 @@ void Loader::Impl::buildModGraph() {
|
|||
|
||||
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||
if (early && !node->needsEarlyLoad()) {
|
||||
m_modsToLoad.push(node);
|
||||
m_modsToLoad.push_back(node);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -402,32 +399,83 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
|
||||
if (node->isEnabled()) {
|
||||
for (auto const& dep : node->m_impl->m_dependants) {
|
||||
this->loadModGraph(dep, early);
|
||||
m_modsToLoad.push_front(dep);
|
||||
}
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->shouldLoad()) {
|
||||
log::debug("Load");
|
||||
auto res = node->m_impl->loadBinary();
|
||||
m_currentlyLoadingMod = node;
|
||||
m_refreshingModCount += 1;
|
||||
m_refreshedModCount += 1;
|
||||
m_lateRefreshedModCount += early ? 0 : 1;
|
||||
|
||||
auto unzipFunction = [this, node]() {
|
||||
log::debug("Unzip");
|
||||
auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
|
||||
return res;
|
||||
};
|
||||
|
||||
auto loadFunction = [this, node, early]() {
|
||||
if (node->shouldLoad()) {
|
||||
log::debug("Load");
|
||||
auto res = node->m_impl->loadBinary();
|
||||
if (!res) {
|
||||
m_problems.push_back({
|
||||
LoadProblem::Type::LoadFailed,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to load binary: {}", res.unwrapErr());
|
||||
log::popNest();
|
||||
m_refreshingModCount -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& dep : node->m_impl->m_dependants) {
|
||||
m_modsToLoad.push_front(dep);
|
||||
}
|
||||
}
|
||||
|
||||
m_refreshingModCount -= 1;
|
||||
|
||||
log::popNest();
|
||||
};
|
||||
|
||||
if (early) {
|
||||
auto res = unzipFunction();
|
||||
if (!res) {
|
||||
m_problems.push_back({
|
||||
LoadProblem::Type::LoadFailed,
|
||||
LoadProblem::Type::UnzipFailed,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to load binary: {}", res.unwrapErr());
|
||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||
log::popNest();
|
||||
m_refreshingModCount -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& dep : node->m_impl->m_dependants) {
|
||||
this->loadModGraph(dep, early);
|
||||
}
|
||||
loadFunction();
|
||||
}
|
||||
else {
|
||||
std::thread([=]() {
|
||||
auto res = unzipFunction();
|
||||
queueInMainThread([=]() {
|
||||
if (!res) {
|
||||
m_problems.push_back({
|
||||
LoadProblem::Type::UnzipFailed,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||
log::popNest();
|
||||
m_refreshingModCount -= 1;
|
||||
return;
|
||||
}
|
||||
loadFunction();
|
||||
});
|
||||
}).detach();
|
||||
}
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
void Loader::Impl::findProblems() {
|
||||
|
@ -568,16 +616,29 @@ void Loader::Impl::refreshModGraph() {
|
|||
else
|
||||
m_loadingState = LoadingState::Mods;
|
||||
|
||||
queueInMainThread([]() {
|
||||
Loader::get()->m_impl->continueRefreshModGraph();
|
||||
queueInMainThread([&]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
||||
void Loader::Impl::continueRefreshModGraph() {
|
||||
if (m_refreshingModCount != 0) {
|
||||
queueInMainThread([&]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_lateRefreshedModCount > 0) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - m_timerBegin).count();
|
||||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||
}
|
||||
|
||||
log::info("Continuing mod graph refresh...");
|
||||
log::pushNest();
|
||||
|
||||
auto begin = std::chrono::high_resolution_clock::now();
|
||||
m_timerBegin = std::chrono::high_resolution_clock::now();
|
||||
|
||||
switch (m_loadingState) {
|
||||
case LoadingState::Mods:
|
||||
|
@ -585,7 +646,7 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
log::pushNest();
|
||||
this->loadModGraph(m_modsToLoad.front(), false);
|
||||
log::popNest();
|
||||
m_modsToLoad.pop();
|
||||
m_modsToLoad.pop_front();
|
||||
if (m_modsToLoad.empty())
|
||||
m_loadingState = LoadingState::Problems;
|
||||
break;
|
||||
|
@ -595,6 +656,11 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
this->findProblems();
|
||||
log::popNest();
|
||||
m_loadingState = LoadingState::Done;
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - m_timerBegin).count();
|
||||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
m_loadingState = LoadingState::Done;
|
||||
|
@ -603,13 +669,9 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
break;
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
||||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||
|
||||
if (m_loadingState != LoadingState::Done) {
|
||||
queueInMainThread([]() {
|
||||
Loader::get()->m_impl->continueRefreshModGraph();
|
||||
queueInMainThread([&]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -742,14 +804,12 @@ void Loader::Impl::tryDownloadLoaderResources(
|
|||
.expect([this, tryLatestOnError](std::string const& info, int code) {
|
||||
// if the url was not found, try downloading latest release instead
|
||||
// (for development versions)
|
||||
if (code == 404 && tryLatestOnError) {
|
||||
this->downloadLoaderResources(true);
|
||||
}
|
||||
else {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
).post();
|
||||
if (code == 404) {
|
||||
log::warn("Unable to download resources: {}", info);
|
||||
}
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
).post();
|
||||
})
|
||||
.progress([](auto&, double now, double total) {
|
||||
ResourceDownloadEvent(
|
||||
|
@ -770,65 +830,67 @@ void Loader::Impl::updateSpecialFiles() {
|
|||
}
|
||||
|
||||
void Loader::Impl::downloadLoaderResources(bool useLatestRelease) {
|
||||
if (!useLatestRelease) {
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-tag-exists-check")
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch(fmt::format(
|
||||
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-tag-exists-check")
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch(fmt::format(
|
||||
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
||||
this->getVersion().toString()
|
||||
))
|
||||
.json()
|
||||
.then([this](json::Value const& json) {
|
||||
this->tryDownloadLoaderResources(fmt::format(
|
||||
"https://github.com/geode-sdk/geode/releases/download/{}/resources.zip",
|
||||
this->getVersion().toString()
|
||||
))
|
||||
.json()
|
||||
.then([this](json::Value const& json) {
|
||||
this->tryDownloadLoaderResources(fmt::format(
|
||||
"https://github.com/geode-sdk/geode/releases/download/{}/resources.zip",
|
||||
this->getVersion().toString()
|
||||
), true);
|
||||
})
|
||||
.expect([this](std::string const& info, int code) {
|
||||
if (code == 404) {
|
||||
log::debug("Loader version {} does not exist on Github, not downloading the resources", this->getVersion().toString());
|
||||
ResourceDownloadEvent(
|
||||
UpdateFinished()
|
||||
).post();
|
||||
), true);
|
||||
})
|
||||
.expect([=](std::string const& info, int code) {
|
||||
if (code == 404) {
|
||||
if (useLatestRelease) {
|
||||
log::debug("Loader version {} does not exist on Github, downloading latest resources", this->getVersion().toString());
|
||||
fetchLatestGithubRelease(
|
||||
[this](json::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
this->tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to find resources in latest GitHub release")
|
||||
).post();
|
||||
},
|
||||
[this](std::string const& info) {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
).post();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to check if tag exists: " + info)
|
||||
).post();
|
||||
log::debug("Loader version {} does not exist on Github, not downloading the resources", this->getVersion().toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
fetchLatestGithubRelease(
|
||||
[this](json::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
this->tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to find resources in latest GitHub release")
|
||||
).post();
|
||||
},
|
||||
[this](std::string const& info) {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
UpdateFinished()
|
||||
).post();
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to check if tag exists: " + info)
|
||||
).post();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool Loader::Impl::verifyLoaderResources() {
|
||||
|
@ -846,7 +908,7 @@ bool Loader::Impl::verifyLoaderResources() {
|
|||
ghc::filesystem::is_directory(resourcesDir)
|
||||
)) {
|
||||
log::debug("Resources directory does not exist");
|
||||
this->downloadLoaderResources();
|
||||
this->downloadLoaderResources(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace geode {
|
|||
std::vector<ghc::filesystem::path> m_modSearchDirectories;
|
||||
std::vector<LoadProblem> m_problems;
|
||||
std::unordered_map<std::string, Mod*> m_mods;
|
||||
std::queue<Mod*> m_modsToLoad;
|
||||
std::deque<Mod*> m_modsToLoad;
|
||||
std::vector<ghc::filesystem::path> m_texturePaths;
|
||||
bool m_isSetup = false;
|
||||
|
||||
|
@ -83,6 +83,14 @@ namespace geode {
|
|||
std::mutex m_nextModAccessMutex;
|
||||
Mod* m_nextMod = nullptr;
|
||||
|
||||
Mod* m_currentlyLoadingMod = nullptr;
|
||||
|
||||
int m_refreshingModCount = 0;
|
||||
int m_refreshedModCount = 0;
|
||||
int m_lateRefreshedModCount = 0;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_timerBegin;
|
||||
|
||||
void provideNextMod(Mod* mod);
|
||||
Mod* takeNextMod();
|
||||
void releaseNextMod();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "ModMetadataImpl.hpp"
|
||||
#include "about.hpp"
|
||||
|
||||
#include <hash/hash.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Hook.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -44,7 +45,7 @@ Result<> Mod::Impl::setup() {
|
|||
}
|
||||
if (!m_resourcesLoaded) {
|
||||
auto searchPathRoot = dirs::getModRuntimeDir() / m_metadata.getID() / "resources";
|
||||
CCFileUtils::get()->addSearchPath(searchPathRoot.string().c_str());
|
||||
// CCFileUtils::get()->addSearchPath(searchPathRoot.string().c_str());
|
||||
|
||||
m_resourcesLoaded = true;
|
||||
}
|
||||
|
@ -568,21 +569,52 @@ Result<> Mod::Impl::createTempDir() {
|
|||
return Err("Unable to create mod runtime directory");
|
||||
}
|
||||
|
||||
// Unzip .geode file into temp dir
|
||||
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_metadata.getPath()));
|
||||
if (!unzip.hasEntry(m_metadata.getBinaryName())) {
|
||||
return Err(
|
||||
fmt::format("Unable to find platform binary under the name \"{}\"", m_metadata.getBinaryName())
|
||||
);
|
||||
}
|
||||
GEODE_UNWRAP(unzip.extractAllTo(tempPath));
|
||||
|
||||
// Mark temp dir creation as succesful
|
||||
m_tempDirName = tempPath;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> Mod::Impl::unzipGeodeFile(ModMetadata metadata) {
|
||||
// Unzip .geode file into temp dir
|
||||
auto tempDir = dirs::getModRuntimeDir() / metadata.getID();
|
||||
|
||||
auto datePath = tempDir / "modified-at";
|
||||
std::string currentHash = file::readString(datePath).unwrapOr("");
|
||||
|
||||
auto modifiedDate = ghc::filesystem::last_write_time(metadata.getPath());
|
||||
auto modifiedCount = std::chrono::duration_cast<std::chrono::milliseconds>(modifiedDate.time_since_epoch());
|
||||
auto modifiedHash = std::to_string(modifiedCount.count());
|
||||
if (currentHash == modifiedHash) {
|
||||
log::debug("Same hash detected, skipping unzip");
|
||||
return Ok();
|
||||
}
|
||||
log::debug("Hash mismatch detected, unzipping");
|
||||
|
||||
std::error_code ec;
|
||||
ghc::filesystem::remove_all(tempDir, ec);
|
||||
if (ec) {
|
||||
return Err("Unable to delete temp dir: " + ec.message());
|
||||
}
|
||||
|
||||
(void)utils::file::createDirectoryAll(tempDir);
|
||||
auto res = file::writeString(datePath, modifiedHash);
|
||||
if (!res) {
|
||||
log::warn("Failed to write modified date of geode zip: {}", res.unwrapErr());
|
||||
}
|
||||
|
||||
|
||||
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(metadata.getPath()));
|
||||
if (!unzip.hasEntry(metadata.getBinaryName())) {
|
||||
return Err(
|
||||
fmt::format("Unable to find platform binary under the name \"{}\"", metadata.getBinaryName())
|
||||
);
|
||||
}
|
||||
GEODE_UNWRAP(unzip.extractAllTo(tempDir));
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
ghc::filesystem::path Mod::Impl::getConfigDir(bool create) const {
|
||||
auto dir = dirs::getModConfigDir() / m_metadata.getID();
|
||||
if (create) {
|
||||
|
|
|
@ -75,6 +75,9 @@ namespace geode {
|
|||
Result<> unloadPlatformBinary();
|
||||
Result<> createTempDir();
|
||||
|
||||
// called on a separate thread
|
||||
Result<> unzipGeodeFile(ModMetadata metadata);
|
||||
|
||||
void setupSettings();
|
||||
|
||||
std::string getID() const;
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
using namespace geode::prelude;
|
||||
|
||||
Result<> Mod::Impl::loadPlatformBinary() {
|
||||
auto so = dlopen((m_tempDirName / m_info.binaryName()).string().c_str(), RTLD_LAZY);
|
||||
auto so =
|
||||
dlopen((m_tempDirName / m_metadata.getBinaryName()).string().c_str(), RTLD_LAZY);
|
||||
if (so) {
|
||||
if (m_platformInfo) {
|
||||
delete m_platformInfo;
|
||||
|
|
55
loader/src/platform/android/backtrace/ScopedFd.hpp
Normal file
55
loader/src/platform/android/backtrace/ScopedFd.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
class ScopedFd final {
|
||||
public:
|
||||
explicit ScopedFd(int fd) : fd_(fd) {
|
||||
}
|
||||
|
||||
ScopedFd() : fd_(-1) {
|
||||
}
|
||||
|
||||
~ScopedFd() {
|
||||
reset(-1);
|
||||
}
|
||||
|
||||
void reset(int fd = -1) {
|
||||
fd_ = fd;
|
||||
}
|
||||
|
||||
int get() const {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
191
loader/src/platform/android/backtrace/execinfo.hpp
Normal file
191
loader/src/platform/android/backtrace/execinfo.hpp
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <unwind.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/memfd.h>
|
||||
|
||||
#include "ScopedFd.hpp"
|
||||
|
||||
struct StackState {
|
||||
void** frames;
|
||||
int frame_count;
|
||||
int cur_frame = 0;
|
||||
|
||||
StackState(void** frames, int frame_count) : frames(frames), frame_count(frame_count) {}
|
||||
};
|
||||
|
||||
static _Unwind_Reason_Code TraceFunction(_Unwind_Context* context, void* arg) {
|
||||
// The instruction pointer is pointing at the instruction after the return
|
||||
// call on all architectures.
|
||||
// Modify the pc to point at the real function.
|
||||
uintptr_t ip = _Unwind_GetIP(context);
|
||||
if (ip != 0) {
|
||||
#if defined(__arm__)
|
||||
// If the ip is suspiciously low, do nothing to avoid a segfault trying
|
||||
// to access this memory.
|
||||
if (ip >= 4096) {
|
||||
// Check bits [15:11] of the first halfword assuming the instruction
|
||||
// is 32 bits long. If the bits are any of these values, then our
|
||||
// assumption was correct:
|
||||
// b11101
|
||||
// b11110
|
||||
// b11111
|
||||
// Otherwise, this is a 16 bit instruction.
|
||||
uint16_t value = (*reinterpret_cast<uint16_t*>(ip - 2)) >> 11;
|
||||
if (value == 0x1f || value == 0x1e || value == 0x1d) {
|
||||
ip -= 4;
|
||||
} else {
|
||||
ip -= 2;
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
// All instructions are 4 bytes long, skip back one instruction.
|
||||
ip -= 4;
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
// It's difficult to decode exactly where the previous instruction is,
|
||||
// so subtract 1 to estimate where the instruction lives.
|
||||
ip--;
|
||||
#endif
|
||||
}
|
||||
|
||||
StackState* state = static_cast<StackState*>(arg);
|
||||
state->frames[state->cur_frame++] = reinterpret_cast<void*>(ip);
|
||||
return (state->cur_frame >= state->frame_count) ? _URC_END_OF_STACK : _URC_NO_REASON;
|
||||
}
|
||||
|
||||
int backtrace(void** buffer, int size) {
|
||||
if (size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
StackState state(buffer, size);
|
||||
_Unwind_Backtrace(TraceFunction, &state);
|
||||
return state.cur_frame;
|
||||
}
|
||||
|
||||
void backtrace_symbols_fd(void* const* buffer, int size, int fd);
|
||||
|
||||
char** backtrace_symbols(void* const* buffer, int size) {
|
||||
if (size <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// Do this calculation first in case the user passes in a bad value.
|
||||
size_t ptr_size;
|
||||
if (__builtin_mul_overflow(sizeof(char*), size, &ptr_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
ScopedFd fd(syscall(SYS_memfd_create, "backtrace_symbols_fd", MFD_CLOEXEC));
|
||||
// ScopedFd fd(memfd_create("backtrace_symbols_fd", MFD_CLOEXEC));
|
||||
if (fd.get() == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
backtrace_symbols_fd(buffer, size, fd.get());
|
||||
|
||||
// Get the size of the file.
|
||||
off_t file_size = lseek(fd.get(), 0, SEEK_END);
|
||||
if (file_size <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The interface for backtrace_symbols indicates that only the single
|
||||
// returned pointer must be freed by the caller. Therefore, allocate a
|
||||
// buffer that includes the memory for the strings and all of the pointers.
|
||||
// Add one byte at the end just in case the file didn't end with a '\n'.
|
||||
size_t symbol_data_size;
|
||||
if (__builtin_add_overflow(ptr_size, file_size, &symbol_data_size) ||
|
||||
__builtin_add_overflow(symbol_data_size, 1, &symbol_data_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t* symbol_data = reinterpret_cast<uint8_t*>(malloc(symbol_data_size));
|
||||
if (symbol_data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Copy the string data into the buffer.
|
||||
char* cur_string = reinterpret_cast<char*>(&symbol_data[ptr_size]);
|
||||
// If this fails, the read won't read back the correct number of bytes.
|
||||
lseek(fd.get(), 0, SEEK_SET);
|
||||
ssize_t num_read = read(fd.get(), cur_string, file_size);
|
||||
fd.reset(-1);
|
||||
if (num_read != file_size) {
|
||||
free(symbol_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure the last character in the file is '\n'.
|
||||
if (cur_string[file_size] != '\n') {
|
||||
cur_string[file_size++] = '\n';
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
(reinterpret_cast<char**>(symbol_data))[i] = cur_string;
|
||||
cur_string = strchr(cur_string, '\n');
|
||||
if (cur_string == nullptr) {
|
||||
free(symbol_data);
|
||||
return nullptr;
|
||||
}
|
||||
cur_string[0] = '\0';
|
||||
cur_string++;
|
||||
}
|
||||
return reinterpret_cast<char**>(symbol_data);
|
||||
}
|
||||
|
||||
// This function should do no allocations if possible.
|
||||
void backtrace_symbols_fd(void* const* buffer, int size, int fd) {
|
||||
if (size <= 0 || fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int frame_num = 0; frame_num < size; frame_num++) {
|
||||
void* address = buffer[frame_num];
|
||||
Dl_info info;
|
||||
if (dladdr(address, &info) != 0) {
|
||||
if (info.dli_fname != nullptr) {
|
||||
write(fd, info.dli_fname, strlen(info.dli_fname));
|
||||
}
|
||||
if (info.dli_sname != nullptr) {
|
||||
dprintf(fd, "(%s+0x%" PRIxPTR ") ", info.dli_sname,
|
||||
reinterpret_cast<uintptr_t>(address) - reinterpret_cast<uintptr_t>(info.dli_saddr));
|
||||
} else {
|
||||
dprintf(fd, "(+%p) ", info.dli_saddr);
|
||||
}
|
||||
}
|
||||
|
||||
dprintf(fd, "[%p]\n", address);
|
||||
}
|
||||
}
|
|
@ -2,16 +2,523 @@
|
|||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return geode::dirs::getSaveDir();
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <execinfo.h>
|
||||
#include <dlfcn.h>
|
||||
#include <cxxabi.h>
|
||||
#include <algorithm>
|
||||
#include <link.h>
|
||||
#include <unwind.h>
|
||||
|
||||
|
||||
#include <jni.h>
|
||||
#include <Geode/cocos/platform/android/jni/JniHelper.h>
|
||||
|
||||
#include "backtrace/execinfo.hpp"
|
||||
|
||||
static constexpr size_t FRAME_SIZE = 64;
|
||||
static std::mutex s_mutex;
|
||||
static std::condition_variable s_cv;
|
||||
static int s_signal = 0;
|
||||
static siginfo_t* s_siginfo = nullptr;
|
||||
static ucontext_t* s_context = nullptr;
|
||||
static size_t s_backtraceSize = 0;
|
||||
static std::array<void*, FRAME_SIZE> s_backtrace;
|
||||
|
||||
static std::string_view getSignalCodeString() {
|
||||
switch(s_signal) {
|
||||
case SIGSEGV: return "SIGSEGV: Segmentation Fault";
|
||||
case SIGINT: return "SIGINT: Interactive attention signal, (usually ctrl+c)";
|
||||
case SIGFPE:
|
||||
switch(s_siginfo->si_code) {
|
||||
case FPE_INTDIV: return "SIGFPE: (integer divide by zero)";
|
||||
case FPE_INTOVF: return "SIGFPE: (integer overflow)";
|
||||
case FPE_FLTDIV: return "SIGFPE: (floating-point divide by zero)";
|
||||
case FPE_FLTOVF: return "SIGFPE: (floating-point overflow)";
|
||||
case FPE_FLTUND: return "SIGFPE: (floating-point underflow)";
|
||||
case FPE_FLTRES: return "SIGFPE: (floating-point inexact result)";
|
||||
case FPE_FLTINV: return "SIGFPE: (floating-point invalid operation)";
|
||||
case FPE_FLTSUB: return "SIGFPE: (subscript out of range)";
|
||||
default: return "SIGFPE: Arithmetic Exception";
|
||||
}
|
||||
case SIGILL:
|
||||
switch(s_siginfo->si_code) {
|
||||
case ILL_ILLOPC: return "SIGILL: (illegal opcode)";
|
||||
case ILL_ILLOPN: return "SIGILL: (illegal operand)";
|
||||
case ILL_ILLADR: return "SIGILL: (illegal addressing mode)";
|
||||
case ILL_ILLTRP: return "SIGILL: (illegal trap)";
|
||||
case ILL_PRVOPC: return "SIGILL: (privileged opcode)";
|
||||
case ILL_PRVREG: return "SIGILL: (privileged register)";
|
||||
case ILL_COPROC: return "SIGILL: (coprocessor error)";
|
||||
case ILL_BADSTK: return "SIGILL: (internal stack error)";
|
||||
default: return "SIGILL: Illegal Instruction";
|
||||
}
|
||||
case SIGTERM: return "SIGTERM: a termination request was sent to the program";
|
||||
case SIGABRT: return "SIGABRT: usually caused by an abort() or assert()";
|
||||
case SIGBUS: return "SIGBUS: Bus error (bad memory access)";
|
||||
default: return "Unknown signal code";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getImageName(Elf32_Phdr const* image) {
|
||||
if (image == nullptr) {
|
||||
return "<Unknown>";
|
||||
}
|
||||
std::string imageName;// = image->imageFilePath;
|
||||
if (imageName.empty()) {
|
||||
imageName = "<Unknown>";
|
||||
}
|
||||
return imageName;
|
||||
}
|
||||
|
||||
// static std::vector<struct dyld_image_info const*> getAllImages() {
|
||||
// std::vector<struct dyld_image_info const*> images;
|
||||
// struct task_dyld_info dyldInfo;
|
||||
// mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
// if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count) == KERN_SUCCESS) {
|
||||
// struct dyld_all_image_infos* imageInfos = (struct dyld_all_image_infos*)dyldInfo.all_image_info_addr;
|
||||
|
||||
// for (size_t i = 0; i < imageInfos->infoArrayCount; ++i) {
|
||||
// images.push_back(&imageInfos->infoArray[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return images;
|
||||
// }
|
||||
|
||||
static Elf32_Phdr const* imageFromAddress(void const* addr) {
|
||||
if (addr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// auto loadedImages = getAllImages();
|
||||
// std::sort(loadedImages.begin(), loadedImages.end(), [](auto const a, auto const b) {
|
||||
// return (uintptr_t)a->imageLoadAddress < (uintptr_t)b->imageLoadAddress;
|
||||
// });
|
||||
// auto iter = std::upper_bound(loadedImages.begin(), loadedImages.end(), addr, [](auto const addr, auto const image) {
|
||||
// return (uintptr_t)addr < (uintptr_t)image->imageLoadAddress;
|
||||
// });
|
||||
|
||||
// if (iter == loadedImages.begin()) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// --iter;
|
||||
|
||||
// auto image = *iter;
|
||||
// // auto imageSize = getImageSize((struct mach_header_64 const*)image->imageLoadAddress);
|
||||
// auto imageAddress = (uintptr_t)image->imageLoadAddress;
|
||||
// if ((uintptr_t)addr >= imageAddress/* && (uintptr_t)addr < imageAddress + imageSize*/) {
|
||||
// return image;
|
||||
// }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Mod* modFromAddress(void const* addr) {
|
||||
if (addr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
// auto image = imageFromAddress(addr);
|
||||
// if (image == nullptr) {
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// ghc::filesystem::path imagePath = getImageName(image);
|
||||
// if (!ghc::filesystem::exists(imagePath)) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// auto geodePath = dirs::getGameDir() / "Frameworks" / "Geode.dylib";
|
||||
// if (ghc::filesystem::equivalent(imagePath, geodePath)) {
|
||||
// return Mod::get();
|
||||
// }
|
||||
|
||||
// for (auto& mod : Loader::get()->getAllMods()) {
|
||||
// if (!mod->isEnabled() || !ghc::filesystem::exists(mod->getBinaryPath())) {
|
||||
// continue;
|
||||
// }
|
||||
// if (ghc::filesystem::equivalent(imagePath, mod->getBinaryPath())) {
|
||||
// return mod;
|
||||
// }
|
||||
// }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::string getInfo(void* address, Mod* faultyMod) {
|
||||
std::stringstream stream;
|
||||
stream << "Faulty Lib: " << getImageName(imageFromAddress(address)) << "\n";
|
||||
stream << "Faulty Mod: " << (faultyMod ? faultyMod->getID() : "<Unknown>") << "\n";
|
||||
stream << "Instruction Address: " << address << "\n";
|
||||
stream << "Signal Code: " << std::hex << s_signal << " (" << getSignalCodeString() << ")" << std::dec << "\n";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
static struct sigaction oldActionSEGV;
|
||||
static struct sigaction oldActionBUS;
|
||||
static struct sigaction oldActionILL;
|
||||
|
||||
extern "C" void signalHandler(int signal, siginfo_t* signalInfo, void* vcontext) {
|
||||
auto context = reinterpret_cast<ucontext_t*>(vcontext);
|
||||
s_backtraceSize = 0;
|
||||
// s_backtraceSize = backtrace(s_backtrace.data(), FRAME_SIZE);
|
||||
|
||||
// for some reason this is needed, dont ask me why
|
||||
// s_backtrace[1] = reinterpret_cast<void*>(context->uc_mcontext.fault_address);
|
||||
// if (s_backtraceSize < FRAME_SIZE) {
|
||||
// s_backtrace[s_backtraceSize] = nullptr;
|
||||
// }
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(s_mutex);
|
||||
s_signal = signal;
|
||||
s_siginfo = signalInfo;
|
||||
s_context = context;
|
||||
}
|
||||
|
||||
s_cv.notify_all();
|
||||
std::unique_lock<std::mutex> lock(s_mutex);
|
||||
s_cv.wait(lock, [] { return s_signal == 0; });
|
||||
// std::_Exit(EXIT_FAILURE);
|
||||
|
||||
switch (signal) {
|
||||
case SIGSEGV:
|
||||
sigaction(signal, &oldActionSEGV, nullptr);
|
||||
oldActionSEGV.sa_sigaction(signal, signalInfo, vcontext);
|
||||
break;
|
||||
case SIGBUS:
|
||||
sigaction(signal, &oldActionBUS, nullptr);
|
||||
oldActionBUS.sa_sigaction(signal, signalInfo, vcontext);
|
||||
break;
|
||||
case SIGILL:
|
||||
sigaction(signal, &oldActionILL, nullptr);
|
||||
oldActionILL.sa_sigaction(signal, signalInfo, vcontext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getStacktrace() {
|
||||
std::stringstream stacktrace;
|
||||
|
||||
if (s_backtraceSize == 0) {
|
||||
return stacktrace.str();
|
||||
}
|
||||
|
||||
auto messages = backtrace_symbols(s_backtrace.data(), s_backtraceSize);
|
||||
if (s_backtraceSize < FRAME_SIZE) {
|
||||
messages[s_backtraceSize] = nullptr;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 1; i < s_backtraceSize; ++i) {
|
||||
auto message = std::string(messages[i]);
|
||||
|
||||
// TODO: parse the message
|
||||
stacktrace << message << "\n";
|
||||
|
||||
|
||||
// auto stream = std::stringstream(message);
|
||||
// int index;
|
||||
// std::string binary;
|
||||
// uintptr_t address;
|
||||
// std::string function;
|
||||
// uintptr_t offset;
|
||||
// std::string line;
|
||||
|
||||
// stream >> index;
|
||||
|
||||
// if (!lines.eof()) {
|
||||
// std::getline(lines, line);
|
||||
// }
|
||||
// std::getline(stream, binary);
|
||||
// auto cutoff = binary.find("0x");
|
||||
// stream = std::stringstream(binary.substr(cutoff));
|
||||
// binary = geode::utils::string::trim(binary.substr(0, cutoff));
|
||||
// stream >> std::hex >> address >> std::dec;
|
||||
|
||||
// if (!line.empty()) {
|
||||
// // log::debug("address: {}", address);
|
||||
// auto image = imageFromAddress(reinterpret_cast<void*>(address));
|
||||
// // log::debug("image: {}", image);
|
||||
// stacktrace << " - " << std::showbase << std::hex;
|
||||
|
||||
// if (image) {
|
||||
// auto baseAddress = image->imageLoadAddress;
|
||||
// auto imageName = getImageName(image);
|
||||
// stacktrace << imageName << " + " << (address - (uintptr_t)baseAddress);
|
||||
// }
|
||||
// else {
|
||||
// stacktrace << address;
|
||||
// }
|
||||
// stacktrace << std::dec;
|
||||
// stacktrace << ": " << line << "\n";
|
||||
// }
|
||||
// else {
|
||||
// std::getline(stream, function);
|
||||
// cutoff = function.find("+");
|
||||
// stream = std::stringstream(function.substr(cutoff));
|
||||
// stream >> offset;
|
||||
// function = geode::utils::string::trim(function.substr(0, cutoff));
|
||||
|
||||
// {
|
||||
// int status;
|
||||
// auto demangle = abi::__cxa_demangle(function.c_str(), 0, 0, &status);
|
||||
// if (status == 0) {
|
||||
// function = demangle;
|
||||
// }
|
||||
// free(demangle);
|
||||
// }
|
||||
|
||||
// stacktrace << "- " << binary;
|
||||
// stacktrace << " @ " << std::showbase << std::hex << address << std::dec;
|
||||
// stacktrace << " (" << function << " + " << offset << ")\n";
|
||||
// }
|
||||
}
|
||||
|
||||
free(messages);
|
||||
|
||||
return stacktrace.str();
|
||||
}
|
||||
|
||||
static std::string getRegisters() {
|
||||
std::stringstream registers;
|
||||
|
||||
auto context = s_context;
|
||||
auto& ctx = context->uc_mcontext;
|
||||
|
||||
// geez
|
||||
registers << std::showbase << std::hex /*<< std::setfill('0') << std::setw(16) */;
|
||||
registers << "r0: " << ctx.arm_r0 << "\n";
|
||||
registers << "r1: " << ctx.arm_r1 << "\n";
|
||||
registers << "r2: " << ctx.arm_r2 << "\n";
|
||||
registers << "r3: " << ctx.arm_r3 << "\n";
|
||||
registers << "r4: " << ctx.arm_r4 << "\n";
|
||||
registers << "r5: " << ctx.arm_r5 << "\n";
|
||||
registers << "r6: " << ctx.arm_r6 << "\n";
|
||||
registers << "r7: " << ctx.arm_r7 << "\n";
|
||||
registers << "r8: " << ctx.arm_r8 << "\n";
|
||||
registers << "r9: " << ctx.arm_r9 << "\n";
|
||||
registers << "r10: " << ctx.arm_r10 << "\n";
|
||||
registers << "r11: " << ctx.arm_fp << "\n";
|
||||
registers << "r12: " << ctx.arm_ip << "\n";
|
||||
registers << "sp: " << ctx.arm_sp << "\n";
|
||||
registers << "lr: " << ctx.arm_lr << "\n";
|
||||
registers << "pc: " << ctx.arm_pc << "\n";
|
||||
registers << "cpsr: " << ctx.arm_cpsr << "\n";
|
||||
|
||||
return registers.str();
|
||||
}
|
||||
|
||||
static void handlerThread() {
|
||||
std::unique_lock<std::mutex> lock(s_mutex);
|
||||
s_cv.wait(lock, [] { return s_signal != 0; });
|
||||
|
||||
auto signalAddress = reinterpret_cast<void*>(s_context->uc_mcontext.fault_address);
|
||||
// Mod* faultyMod = nullptr;
|
||||
// for (int i = 1; i < s_backtraceSize; ++i) {
|
||||
// auto mod = modFromAddress(s_backtrace[i]);
|
||||
// if (mod != nullptr) {
|
||||
// faultyMod = mod;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
Mod* faultyMod = modFromAddress(signalAddress);
|
||||
|
||||
auto text = crashlog::writeCrashlog(faultyMod, getInfo(signalAddress, faultyMod), getStacktrace(), getRegisters());
|
||||
|
||||
log::error("Geode crashed!\n{}", text);
|
||||
|
||||
s_signal = 0;
|
||||
s_cv.notify_all();
|
||||
|
||||
log::debug("Notified");
|
||||
}
|
||||
|
||||
static bool s_lastLaunchCrashed = false;
|
||||
|
||||
// bool crashlog::setupPlatformHandler() {
|
||||
// auto pidFile = crashlog::getCrashLogDirectory() / "last-pid";
|
||||
|
||||
// int lastPid = 0;
|
||||
|
||||
// if (ghc::filesystem::exists(pidFile)) {
|
||||
|
||||
// auto res = file::readString(pidFile);
|
||||
// if (!res) {
|
||||
// log::warn("Failed to read last-pid file: {}", res.error());
|
||||
// }
|
||||
// else {
|
||||
// lastPid = std::stoi(res.unwrap());
|
||||
// }
|
||||
|
||||
// std::error_code ec;
|
||||
// ghc::filesystem::remove(pidFile, ec);
|
||||
|
||||
// if (ec) {
|
||||
// log::warn("Failed to remove last-pid file: {}", ec.message());
|
||||
// }
|
||||
// }
|
||||
|
||||
// auto res = file::writeString(pidFile, std::to_string(getpid()));
|
||||
// if (!res) {
|
||||
// log::warn("Failed to write last-pid file: {}", res.error());
|
||||
// }
|
||||
|
||||
// lastPid = 1513;
|
||||
|
||||
|
||||
// if (lastPid == 0) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// // TODO: get logcat crash
|
||||
|
||||
// std::string logcatCrash = R"RAW()RAW";
|
||||
|
||||
// std::string crashTrace;
|
||||
// auto findLast = logcatCrash.find_last_of(fmt::format("pid {} (.geode.launcher)", lastPid));
|
||||
// if (findLast != std::string::npos) {
|
||||
// auto begin = logcatCrash.substr(0, findLast).find_last_of("F/libc");
|
||||
// if (begin != std::string::npos) {
|
||||
// crashTrace = logcatCrash.substr(begin);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// auto text = crashlog::writeCrashlog(nullptr, "", crashTrace, "");
|
||||
// s_lastLaunchCrashed = true;
|
||||
|
||||
// auto lastCrashedFile = crashlog::getCrashLogDirectory() / "last-crashed";
|
||||
// if (ghc::filesystem::exists(lastCrashedFile)) {
|
||||
// std::error_code ec;
|
||||
// ghc::filesystem::remove(lastCrashedFile, ec);
|
||||
|
||||
// if (ec) {
|
||||
// log::warn("Failed to remove last-crashed file: {}", ec.message());
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool crashlog::didLastLaunchCrash() {
|
||||
// return s_lastLaunchCrashed;
|
||||
// }
|
||||
|
||||
// ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
// return dirs::getGeodeDir() / "crashlogs";
|
||||
// }
|
||||
|
||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return dirs::getGeodeDir() / "crashlogs";
|
||||
}
|
||||
|
||||
int writeAndGetPid() {
|
||||
auto pidFile = crashlog::getCrashLogDirectory() / "last-pid";
|
||||
|
||||
int lastPid = 0;
|
||||
|
||||
if (ghc::filesystem::exists(pidFile)) {
|
||||
|
||||
auto res = file::readString(pidFile);
|
||||
if (!res) {
|
||||
log::warn("Failed to read last-pid file: {}", res.error());
|
||||
}
|
||||
else {
|
||||
lastPid = std::stoi(res.unwrap());
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
ghc::filesystem::remove(pidFile, ec);
|
||||
|
||||
if (ec) {
|
||||
log::warn("Failed to remove last-pid file: {}", ec.message());
|
||||
}
|
||||
}
|
||||
|
||||
auto res = file::writeString(pidFile, std::to_string(getpid()));
|
||||
if (!res) {
|
||||
log::warn("Failed to write last-pid file: {}", res.error());
|
||||
}
|
||||
|
||||
return lastPid;
|
||||
}
|
||||
|
||||
void printModsAndroid(std::stringstream& stream) {
|
||||
auto mods = Loader::get()->getAllMods();
|
||||
if (mods.empty()) {
|
||||
stream << "<None>\n";
|
||||
}
|
||||
using namespace std::string_view_literals;
|
||||
for (auto& mod : mods) {
|
||||
stream << fmt::format("{} | [{}] {}\n",
|
||||
mod->shouldLoad() ? "x"sv : " "sv,
|
||||
mod->getVersion().toString(), mod->getID()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string s_result;
|
||||
bool crashlog::setupPlatformHandler() {
|
||||
return false;
|
||||
JniMethodInfo t;
|
||||
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "getLogcatCrashBuffer", "()Ljava/lang/String;")) {
|
||||
jstring stringResult = (jstring)t.env->CallStaticObjectMethod(t.classID, t.methodID);
|
||||
|
||||
s_result = JniHelper::jstring2string(stringResult);
|
||||
|
||||
t.env->DeleteLocalRef(stringResult);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
|
||||
if (s_result.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
|
||||
auto lastPid = writeAndGetPid();
|
||||
|
||||
auto index = s_result.rfind(fmt::format("pid {}", lastPid));
|
||||
if (index != std::string::npos) {
|
||||
auto begin = s_result.substr(0, index).rfind("F/libc");
|
||||
if (begin != std::string::npos) {
|
||||
s_result = s_result.substr(begin);
|
||||
}
|
||||
s_lastLaunchCrashed = true;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void crashlog::setupPlatformHandlerPost() {
|
||||
if (s_result.empty()) return;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Geode crashed!\n";
|
||||
ss << "Please submit this crash report to the developer of the mod that caused it.\n";
|
||||
ss << "\n== Geode Information ==\n";
|
||||
crashlog::printGeodeInfo(ss);
|
||||
|
||||
ss << "\n== Installed Mods ==\n";
|
||||
printModsAndroid(ss);
|
||||
|
||||
ss << "\n== Crash Report (Logcat) ==\n";
|
||||
ss << s_result;
|
||||
|
||||
std::ofstream actualFile;
|
||||
actualFile.open(
|
||||
crashlog::getCrashLogDirectory() / (crashlog::getDateString(true) + ".log"), std::ios::app
|
||||
);
|
||||
actualFile << ss.rdbuf() << std::flush;
|
||||
actualFile.close();
|
||||
}
|
||||
|
||||
bool crashlog::didLastLaunchCrash() {
|
||||
return false;
|
||||
return s_lastLaunchCrashed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
68
loader/src/platform/android/gdstdlib.cpp
Normal file
68
loader/src/platform/android/gdstdlib.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include <Geode/c++stl/gdstdlib.hpp>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
namespace geode::base {
|
||||
uintptr_t get() {
|
||||
static uintptr_t base = (reinterpret_cast<uintptr_t>(&UILayer::create) - 0x20f168) & (~0x1);
|
||||
// static uintptr_t base = reinterpret_cast<uintptr_t>(dlopen("libcocos2dcpp.so", RTLD_NOW));
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
namespace {
|
||||
static inline auto emptyInternalString() {
|
||||
return reinterpret_cast<_internal_string*>(
|
||||
geode::base::get() + 0x75fb24 + sizeof(_internal_string)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
string::string() : m_data(nullptr) {
|
||||
m_data = emptyInternalString();
|
||||
}
|
||||
|
||||
string::string(char const* ok) : m_data(nullptr) {
|
||||
reinterpret_cast<void (*)(string*, char const*)>(geode::base::get() + 0x506c08)(this, ok);
|
||||
}
|
||||
|
||||
string::string(string const& ok) : m_data(nullptr) {
|
||||
if (*(string**)(&ok) == nullptr) return;
|
||||
reinterpret_cast<void (*)(string*, string const&)>(geode::base::get() + 0x506634)(this, ok);
|
||||
}
|
||||
|
||||
string& string::operator=(char const* ok) {
|
||||
this->~string();
|
||||
new (this) string(ok);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(string const& ok) {
|
||||
this->~string();
|
||||
new (this) string(ok);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
if (m_data == nullptr) return;
|
||||
|
||||
reinterpret_cast<void (*)(string*)>(geode::base::get() + 0x5054bc)(this);
|
||||
}
|
||||
|
||||
bool string::operator<(string const& other) const {
|
||||
return std::string(*this) < std::string(other);
|
||||
}
|
||||
|
||||
bool string::operator==(string const& other) const {
|
||||
return std::string(*this) == std::string(other);
|
||||
}
|
||||
|
||||
// TODO: these need to stay for old mods linking against geode <1.2.0, remove in 2.0.0
|
||||
template class map<int, int>;
|
||||
template class map<gd::string, gd::string>;
|
||||
template class map<gd::string, bool>;
|
||||
template class map<short, bool>;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,9 +5,30 @@
|
|||
#include "../load.hpp"
|
||||
#include <jni.h>
|
||||
|
||||
// idk where to put this
|
||||
#include <EGL/egl.h>
|
||||
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0;
|
||||
PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0;
|
||||
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]] jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
|
||||
glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
|
||||
glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
|
||||
|
||||
auto updatePath = geode::dirs::getGameDir() / "update";
|
||||
std::error_code ec;
|
||||
ghc::filesystem::remove_all(updatePath, ec);
|
||||
if (ec) {
|
||||
geode::log::warn("Failed to remove update directory: {}", ec.message());
|
||||
}
|
||||
|
||||
geodeEntry(nullptr);
|
||||
return JNI_VERSION_1_1;
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
extern "C" [[gnu::visibility("default")]] void emptyFunction(void*) {
|
||||
// empty
|
||||
}
|
||||
|
||||
#endif
|
|
@ -9,25 +9,245 @@ using namespace geode::prelude;
|
|||
#include <Geode/utils/web.hpp>
|
||||
#include <ghc/filesystem.hpp>
|
||||
|
||||
#include <jni.h>
|
||||
#include <Geode/cocos/platform/android/jni/JniHelper.h>
|
||||
|
||||
bool utils::clipboard::write(std::string const& data) {
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "writeClipboard", "(Ljava/lang/String;)V")) {
|
||||
jstring stringArg1 = t.env->NewStringUTF(data.c_str());
|
||||
|
||||
t.env->CallStaticVoidMethod(t.classID, t.methodID, stringArg1);
|
||||
|
||||
t.env->DeleteLocalRef(stringArg1);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string utils::clipboard::read() {
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "readClipboard", "()Ljava/lang/String;")) {
|
||||
jstring stringResult = (jstring)t.env->CallStaticObjectMethod(t.classID, t.methodID);
|
||||
|
||||
std::string result = JniHelper::jstring2string(stringResult);
|
||||
|
||||
t.env->DeleteLocalRef(stringResult);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
CCPoint cocos::getMousePos() {
|
||||
return CCPoint(0, 0);
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getGameDir() {
|
||||
return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
|
||||
return ghc::filesystem::path(
|
||||
"/storage/emulated/0/Android/data/com.geode.launcher/files/game"
|
||||
// "/data/user/0/com.geode.launcher/files/"
|
||||
/*CCFileUtils::sharedFileUtils()->getWritablePath().c_str()*/
|
||||
);
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getSaveDir() {
|
||||
return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
|
||||
return ghc::filesystem::path(
|
||||
"/storage/emulated/0/Android/data/com.geode.launcher/files/save"
|
||||
// "/data/user/0/com.geode.launcher/files/"
|
||||
/*CCFileUtils::sharedFileUtils()->getWritablePath().c_str()*/
|
||||
);
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getModRuntimeDir() {
|
||||
return ghc::filesystem::path(
|
||||
"/data/user/0/com.geode.launcher/files/geode/unzipped"
|
||||
);
|
||||
// return dirs::getGeodeDir() / "unzipped";
|
||||
}
|
||||
|
||||
void utils::web::openLinkInBrowser(std::string const& url) {
|
||||
CCApplication::sharedApplication()->openURL(url.c_str());
|
||||
}
|
||||
|
||||
bool utils::file::openFolder(ghc::filesystem::path const&) {
|
||||
bool utils::file::openFolder(ghc::filesystem::path const& path) {
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "openFolder", "(Ljava/lang/String;)Z")) {
|
||||
jstring stringArg1 = t.env->NewStringUTF(path.string().c_str());
|
||||
|
||||
jboolean result = t.env->CallStaticBooleanMethod(t.classID, t.methodID, stringArg1);
|
||||
|
||||
t.env->DeleteLocalRef(stringArg1);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
geode::Result<ghc::filesystem::path> utils::file::
|
||||
pickFile(geode::utils::file::PickMode, geode::utils::file::FilePickOptions const&) {
|
||||
return geode::Err("This function is currently unimplemented");
|
||||
|
||||
static utils::MiniFunction<void(ghc::filesystem::path)> s_fileCallback;
|
||||
static utils::MiniFunction<void(std::vector<ghc::filesystem::path>)> s_filesCallback;
|
||||
static utils::MiniFunction<void()> s_failedCallback;
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_selectFileCallback(
|
||||
JNIEnv *env,
|
||||
jobject,
|
||||
jstring data
|
||||
) {
|
||||
auto isCopy = jboolean();
|
||||
auto dataStr = env->GetStringUTFChars(data, &isCopy);
|
||||
|
||||
log::debug("Selected file: {}", dataStr);
|
||||
|
||||
s_fileCallback(dataStr);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_selectFilesCallback(
|
||||
JNIEnv *env,
|
||||
jobject,
|
||||
jobjectArray datas
|
||||
) {
|
||||
auto isCopy = jboolean();
|
||||
auto count = env->GetArrayLength(datas);
|
||||
auto result = std::vector<ghc::filesystem::path>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto data = (jstring)env->GetObjectArrayElement(datas, i);
|
||||
auto dataStr = env->GetStringUTFChars(data, &isCopy);
|
||||
result.push_back(dataStr);
|
||||
|
||||
log::debug("Selected file {}: {}", i, dataStr);
|
||||
}
|
||||
|
||||
s_filesCallback(result);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_failedCallback(
|
||||
JNIEnv *env,
|
||||
jobject
|
||||
) {
|
||||
if (s_failedCallback) s_failedCallback();
|
||||
}
|
||||
|
||||
Result<ghc::filesystem::path> file::pickFile(file::PickMode mode, file::FilePickOptions const& options) {
|
||||
return Err("Use the callback version");
|
||||
}
|
||||
|
||||
void file::pickFile(
|
||||
PickMode mode, FilePickOptions const& options,
|
||||
MiniFunction<void(ghc::filesystem::path)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
s_fileCallback = callback;
|
||||
s_failedCallback = failed;
|
||||
|
||||
std::string method;
|
||||
switch (mode) {
|
||||
case file::PickMode::OpenFile:
|
||||
method = "selectFile";
|
||||
break;
|
||||
case file::PickMode::SaveFile:
|
||||
method = "createFile";
|
||||
break;
|
||||
case file::PickMode::OpenFolder:
|
||||
method = "selectFolder";
|
||||
break;
|
||||
}
|
||||
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", method.c_str(), "(Ljava/lang/String;)Z")) {
|
||||
jstring stringArg1 = t.env->NewStringUTF(options.defaultPath.value_or(ghc::filesystem::path()).string().c_str());
|
||||
|
||||
jboolean result = t.env->CallStaticBooleanMethod(t.classID, t.methodID, stringArg1);
|
||||
|
||||
t.env->DeleteLocalRef(stringArg1);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (s_failedCallback) s_failedCallback();
|
||||
}
|
||||
|
||||
Result<std::vector<ghc::filesystem::path>> file::pickFiles(file::FilePickOptions const& options) {
|
||||
return Err("Use the callback version");
|
||||
}
|
||||
|
||||
void file::pickFiles(
|
||||
FilePickOptions const& options,
|
||||
MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
s_filesCallback = callback;
|
||||
s_failedCallback = failed;
|
||||
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "selectFiles", "(Ljava/lang/String;)Z")) {
|
||||
jstring stringArg1 = t.env->NewStringUTF(options.defaultPath.value_or(ghc::filesystem::path()).string().c_str());
|
||||
|
||||
jboolean result = t.env->CallStaticBooleanMethod(t.classID, t.methodID, stringArg1);
|
||||
|
||||
t.env->DeleteLocalRef(stringArg1);
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (s_failedCallback) s_failedCallback();
|
||||
}
|
||||
|
||||
void geode::utils::game::launchLoaderUninstaller(bool deleteSaveData) {
|
||||
log::error("Launching Geode uninstaller is not supported on android");
|
||||
}
|
||||
|
||||
void geode::utils::game::exit() {
|
||||
if (CCApplication::sharedApplication() &&
|
||||
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
||||
log::error("Cannot exit in PlayLayer or LevelEditorLayer!");
|
||||
return;
|
||||
}
|
||||
AppDelegate::get()->trySaveGame();
|
||||
// AppDelegate::get()->showLoadingCircle(false, true);
|
||||
|
||||
CCDirector::get()->getActionManager()->addAction(CCSequence::create(
|
||||
CCDelayTime::create(0.5f),
|
||||
CCCallFunc::create(nullptr, callfunc_selector(MenuLayer::endGame)),
|
||||
nullptr
|
||||
), CCDirector::get()->getRunningScene(), false);
|
||||
}
|
||||
|
||||
void geode::utils::game::restart() {
|
||||
if (CCApplication::sharedApplication() &&
|
||||
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
||||
log::error("Cannot restart in PlayLayer or LevelEditorLayer!");
|
||||
return;
|
||||
}
|
||||
|
||||
class Exit : public CCObject {
|
||||
public:
|
||||
void restart() {
|
||||
JniMethodInfo t;
|
||||
if (JniHelper::getStaticMethodInfo(t, "com/geode/launcher/utils/GeodeUtils", "restartGame", "()V")) {
|
||||
t.env->CallStaticVoidMethod(t.classID, t.methodID);
|
||||
|
||||
t.env->DeleteLocalRef(t.classID);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Not implemented
|
||||
// log::error("Restarting the game is not implemented on android");
|
||||
|
||||
AppDelegate::get()->trySaveGame();
|
||||
// AppDelegate::get()->showLoadingCircle(false, true);
|
||||
|
||||
CCDirector::get()->getActionManager()->addAction(CCSequence::create(
|
||||
CCDelayTime::create(0.5f),
|
||||
CCCallFunc::create(nullptr, callfunc_selector(Exit::restart)),
|
||||
nullptr
|
||||
), CCDirector::get()->getRunningScene(), false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -382,6 +382,8 @@ bool crashlog::setupPlatformHandler() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void crashlog::setupPlatformHandlerPost() {}
|
||||
|
||||
bool crashlog::didLastLaunchCrash() {
|
||||
return s_lastLaunchCrashed;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ void utils::web::openLinkInBrowser(std::string const& url) {
|
|||
}
|
||||
@end
|
||||
|
||||
Result<ghc::filesystem::path> utils::file::pickFile(
|
||||
Result<ghc::filesystem::path> file::pickFile(
|
||||
file::PickMode mode, file::FilePickOptions const& options
|
||||
) {
|
||||
auto result = [FileDialog filePickerWithMode:mode options:options multiple: false];
|
||||
|
@ -157,13 +157,41 @@ Result<ghc::filesystem::path> utils::file::pickFile(
|
|||
}
|
||||
}
|
||||
|
||||
Result<std::vector<ghc::filesystem::path>> utils::file::pickFiles(
|
||||
GEODE_DLL void file::pickFile(
|
||||
PickMode mode, FilePickOptions const& options,
|
||||
MiniFunction<void(ghc::filesystem::path)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
auto result = file::pickFile(mode, options);
|
||||
|
||||
if (result.isOk()) {
|
||||
callback(std::move(result.unwrap()));
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::vector<ghc::filesystem::path>> file::pickFiles(
|
||||
file::FilePickOptions const& options
|
||||
) {
|
||||
//return Err("utils::file::pickFiles is not implemented");
|
||||
return [FileDialog filePickerWithMode: file::PickMode::OpenFile options:options multiple:true];
|
||||
}
|
||||
|
||||
GEODE_DLL void file::pickFiles(
|
||||
FilePickOptions const& options,
|
||||
MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
auto result = file::pickFiles(options);
|
||||
|
||||
if (result.isOk()) {
|
||||
callback(std::move(result.unwrap()));
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
}
|
||||
|
||||
CCPoint cocos::getMousePos() {
|
||||
auto windowFrame = NSApp.mainWindow.frame;
|
||||
auto viewFrame = NSApp.mainWindow.contentView.frame;
|
||||
|
@ -200,10 +228,14 @@ ghc::filesystem::path dirs::getSaveDir() {
|
|||
return path;
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getModRuntimeDir() {
|
||||
return dirs::getGeodeDir() / "unzipped";
|
||||
}
|
||||
|
||||
void geode::utils::game::exit() {
|
||||
if (CCApplication::sharedApplication() &&
|
||||
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
||||
log::error("Cannot restart in PlayLayer or LevelEditorLayer!");
|
||||
log::error("Cannot exit in PlayLayer or LevelEditorLayer!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -252,6 +252,8 @@ bool crashlog::didLastLaunchCrash() {
|
|||
return g_lastLaunchCrashed;
|
||||
}
|
||||
|
||||
void crashlog::setupPlatformHandlerPost() {}
|
||||
|
||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return dirs::getGeodeDir() / "crashlogs";
|
||||
}
|
||||
|
|
|
@ -97,6 +97,20 @@ Result<ghc::filesystem::path> utils::file::pickFile(
|
|||
return Ok(path);
|
||||
}
|
||||
|
||||
GEODE_DLL void file::pickFile(
|
||||
PickMode mode, FilePickOptions const& options,
|
||||
MiniFunction<void(ghc::filesystem::path)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
auto result = file::pickFile(mode, options);
|
||||
|
||||
if (result.isOk()) {
|
||||
callback(std::move(result.unwrap()));
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::vector<ghc::filesystem::path>> utils::file::pickFiles(
|
||||
file::FilePickOptions const& options
|
||||
) {
|
||||
|
@ -105,6 +119,20 @@ Result<std::vector<ghc::filesystem::path>> utils::file::pickFiles(
|
|||
return Ok(paths);
|
||||
}
|
||||
|
||||
GEODE_DLL void file::pickFiles(
|
||||
FilePickOptions const& options,
|
||||
MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
||||
MiniFunction<void()> failed
|
||||
) {
|
||||
auto result = file::pickFiles(options);
|
||||
|
||||
if (result.isOk()) {
|
||||
callback(std::move(result.unwrap()));
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
}
|
||||
|
||||
void utils::web::openLinkInBrowser(std::string const& url) {
|
||||
ShellExecuteA(0, 0, url.c_str(), 0, 0, SW_SHOW);
|
||||
}
|
||||
|
@ -158,6 +186,10 @@ ghc::filesystem::path dirs::getSaveDir() {
|
|||
return path;
|
||||
}
|
||||
|
||||
ghc::filesystem::path dirs::getModRuntimeDir() {
|
||||
return dirs::getGeodeDir() / "unzipped";
|
||||
}
|
||||
|
||||
void geode::utils::game::exit() {
|
||||
if (CCApplication::sharedApplication() &&
|
||||
(GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) {
|
||||
|
|
|
@ -17,18 +17,13 @@ void geode::openIssueReportPopup(Mod* mod) {
|
|||
if (mod->getMetadata().getIssues()) {
|
||||
MDPopup::create(
|
||||
"Issue Report",
|
||||
mod->getMetadata().getIssues().value().info +
|
||||
"\n\n"
|
||||
"Please report the issue to the mod that caused the crash.\n"
|
||||
"If your issue relates to a <cr>game crash</c>, <cb>please include</c> the "
|
||||
"latest crash log(s) from `" +
|
||||
dirs::getCrashlogsDir().string() + "`",
|
||||
"OK", (mod->getMetadata().getIssues().value().url ? "Open URL" : ""),
|
||||
"OK", "Open Folder",
|
||||
[mod](bool btn2) {
|
||||
if (btn2) {
|
||||
web::openLinkInBrowser(
|
||||
mod->getMetadata().getIssues().value().url.value()
|
||||
);
|
||||
}
|
||||
file::openFolder(dirs::getCrashlogsDir());
|
||||
}
|
||||
)->show();
|
||||
}
|
||||
|
|
|
@ -638,6 +638,10 @@ void ModListLayer::onExit(CCObject*) {
|
|||
);
|
||||
}
|
||||
|
||||
void ModListLayer::keyBackClicked() {
|
||||
this->onExit(nullptr);
|
||||
}
|
||||
|
||||
void ModListLayer::onReload(CCObject*) {
|
||||
this->reloadList();
|
||||
}
|
||||
|
|
|
@ -82,6 +82,9 @@ protected:
|
|||
void createSearchControl();
|
||||
void onIndexUpdate(IndexUpdateEvent* event);
|
||||
|
||||
// most requested feature of all time
|
||||
void keyBackClicked() override;
|
||||
|
||||
CCArray* createModCells(ModListType type, ModListQuery const& query);
|
||||
CCSize getCellSize() const;
|
||||
CCSize getListSize() const;
|
||||
|
|
|
@ -87,6 +87,11 @@ bool ProblemsListCell::init(LoadProblem problem, ProblemsListPopup* list, CCSize
|
|||
icon = "info-alert.png"_spr;
|
||||
message = fmt::format("{} is incompatible with {}", cause, problem.message);
|
||||
break;
|
||||
case LoadProblem::Type::UnzipFailed:
|
||||
icon = "info-alert.png"_spr;
|
||||
message = fmt::format("{} has failed unzipping", cause);
|
||||
m_longMessage = problem.message;
|
||||
break;
|
||||
}
|
||||
|
||||
m_problem = std::move(problem);
|
||||
|
|
|
@ -355,16 +355,17 @@ void FileSettingNode::valueChanged(bool updateText) {
|
|||
}
|
||||
|
||||
void FileSettingNode::onPickFile(CCObject*) {
|
||||
if (auto path = file::pickFile(
|
||||
file::pickFile(
|
||||
file::PickMode::OpenFile,
|
||||
{
|
||||
dirs::getGameDir(),
|
||||
setting()->castDefinition().controls.filters
|
||||
},
|
||||
[&](auto path) {
|
||||
m_uncommittedValue = path;
|
||||
this->valueChanged(true);
|
||||
}
|
||||
)) {
|
||||
m_uncommittedValue = path.unwrap();
|
||||
this->valueChanged(true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool FileSettingNode::setup(FileSettingValue* setting, float width) {
|
||||
|
|
|
@ -173,7 +173,6 @@ void Notification::show() {
|
|||
auto winSize = CCDirector::get()->getWinSize();
|
||||
this->setPosition(winSize.width / 2, winSize.height / 4);
|
||||
this->setZOrder(CCScene::get()->getHighestChildZ() + 100);
|
||||
CCScene::get()->addChild(this);
|
||||
}
|
||||
SceneManager::get()->keepAcrossScenes(this);
|
||||
m_showing = true;
|
||||
|
|
|
@ -23,6 +23,10 @@ SceneManager::~SceneManager() {
|
|||
}
|
||||
|
||||
void SceneManager::keepAcrossScenes(CCNode* node) {
|
||||
if (m_lastScene) {
|
||||
node->removeFromParentAndCleanup(false);
|
||||
m_lastScene->addChild(node);
|
||||
}
|
||||
m_persistedNodes->addObject(node);
|
||||
}
|
||||
|
||||
|
@ -36,4 +40,5 @@ void SceneManager::willSwitchToScene(CCScene* scene) {
|
|||
node->removeFromParentAndCleanup(false);
|
||||
scene->addChild(node);
|
||||
}
|
||||
m_lastScene = scene;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
36
loader/test/members/Android.cpp
Normal file
36
loader/test/members/Android.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "Common.hpp"
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
// Add known android struct members here
|
||||
|
||||
// needed classes are ones in the ids folder and some generic ones (i think they are already done though so only ids)
|
||||
|
||||
GEODE_MEMBER_CHECK(GameManager, m_playLayer, 0x138);
|
||||
GEODE_MEMBER_CHECK(GameManager, m_levelEditorLayer, 0x13c);
|
||||
GEODE_MEMBER_CHECK(GameManager, m_canGetLevelSaveData, 0x28c);
|
||||
|
||||
static_assert(sizeof(GJBaseGameLayer) == 0x2cc);
|
||||
|
||||
GEODE_MEMBER_CHECK(PlayLayer, unknown4e8, 0x2e8);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_endPortal, 0x324);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_bottomGround, 0x37c);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_topGround, 0x380);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_level, 0x470);
|
||||
GEODE_MEMBER_CHECK(PlayLayer, m_shouldTryToKick, 0x4e0);
|
||||
|
||||
static_assert(sizeof(GameObject) == 0x42c);
|
||||
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_dailyChests, 0x110);
|
||||
GEODE_MEMBER_CHECK(GameStatsManager, m_completedLevels, 0x164);
|
||||
|
||||
GEODE_MEMBER_CHECK(DailyLevelPage, m_weekly, 0x1ed);
|
||||
|
||||
GEODE_MEMBER_CHECK(TeleportPortalObject, m_orangePortal, 0x430);
|
||||
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_rotationControl, 0x16c);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_updateTimeMarkers, 0x1a4);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_selectedObjects, 0x1bc);
|
||||
GEODE_MEMBER_CHECK(EditorUI, m_selectedObject, 0x2c4);
|
||||
|
||||
#endif
|
|
@ -4,7 +4,7 @@ set(PROJECT_NAME TestMembers)
|
|||
|
||||
project(${PROJECT_NAME} VERSION 1.0.0)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED MacOS.cpp Windows.cpp)
|
||||
add_library(${PROJECT_NAME} SHARED MacOS.cpp Windows.cpp Android.cpp)
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
|
|
Loading…
Reference in a new issue