mirror of
https://github.com/geode-sdk/geode.git
synced 2025-04-24 05:14:40 -04:00
Merge branch 'main' into layout
This commit is contained in:
commit
c926f69790
135 changed files with 3119 additions and 1429 deletions
.github
ISSUE_TEMPLATE
workflows
bindings
cmake
codegen/src
entry.cpploader
CMakeLists.txt
hash
include/Geode
Bindings.hppDefaultInclude.hppEnums.hppModify.hpp
cocos/cocos2dx
loader
modify
platform
ui
BasedButton.hppBasedButtonSprite.hppColorPickPopup.hppIconButtonSprite.hppInputNode.hppListView.hppMDTextArea.hppNotification.hppPopup.hppSceneManager.hppScrollLayer.hppScrollbar.hppSelectList.hppTextRenderer.hpp
utils
lilac
src
65
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
name: Bug Report
|
||||
description: Report a bug where something is not working as expected, which does not crash the game.
|
||||
labels: [ "unverified", "bug" ]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: The platform you were using when this bug was encountered.
|
||||
options:
|
||||
- "MacOS"
|
||||
- "Windows"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Geode Version
|
||||
description: The version of Geode you were using when this bug was encountered. If you do not know where to find this, look for the mods listing.
|
||||
placeholder: "Example: v0.5.4"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: mods
|
||||
attributes:
|
||||
label: Mods Installed
|
||||
description: The mods and their respective versions used when this bug was encountered. If you were not using mods, please leave this field blank.
|
||||
placeholder: "Example: geode.test v1.0.0, geode.testdep v0.1.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What you expected to happen. Attach screenshots here as necessary.
|
||||
placeholder: "Example: Expected to produce X by consuming Y."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: result
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: What happened despite your expectations. Attach screenshots here as necessary.
|
||||
placeholder: "Example: Produced one X but Y was not consumed."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: How to reproduce the bug.
|
||||
placeholder: "Example: 1) I did X..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Any additional information you wish to provide. Please add anything which did not fit into the other sections here.
|
||||
placeholder: "Example: This is likely caused by X because..."
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to fill out this bug report.
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord link
|
||||
about: Find us at https://discord.gg/9e43WMKzhp
|
57
.github/ISSUE_TEMPLATE/crash-report.yml
vendored
Normal file
57
.github/ISSUE_TEMPLATE/crash-report.yml
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
name: Crash Report
|
||||
description: Report a bug that crashes the game or prevents startup.
|
||||
labels: [ "unverified", "crash" ]
|
||||
body:
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: The platform you were using when this bug was encountered.
|
||||
options:
|
||||
- "MacOS"
|
||||
- "Windows"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Geode Version
|
||||
description: The version of Geode you were using when this bug was encountered. If you do not know where to find this, look for the mods listing.
|
||||
placeholder: "Example: v0.5.4"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: mods
|
||||
attributes:
|
||||
label: Mods Installed
|
||||
description: The mods and their respective versions used when this bug was encountered. If you were not using mods, please leave this field blank.
|
||||
placeholder: "Example: geode.test v1.0.0, geode.testdep v0.1.0"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: How to reproduce the crash.
|
||||
placeholder: "Example: 1) I did X..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: crash-report
|
||||
attributes:
|
||||
label: Crash Report
|
||||
description: "You can find the crash report in `geode/crashlogs/`, if one was created. If you are using MacOS you can find the crash report in `Console.app` too. Additionally, please attach latest log in `geode/log/`. If either or both files do not exist, state such. Please link to a paste site with their content, such as GitHub Gists or Pastebin. **Do not paste the contents of either these files directly into the text box.**"
|
||||
placeholder: "Example: a link to a paste site with the crash report and latest.log."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Any additional information you wish to provide. Please add anything which did not fit into the other sections here.
|
||||
placeholder: "Example: This is likely caused by X because..."
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to fill out this crash report.
|
39
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
39
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: Request a Feature
|
||||
description: Request a new feature or a change to an existing one.
|
||||
labels: [ "feature" ]
|
||||
body:
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Geode Version
|
||||
description: The version of Geode you are using for suggesting a feature on. If you do not know where to find this, look for the mods listing.
|
||||
placeholder: "Example: v0.5.4"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Related Problem
|
||||
description: If the feature you wish to change is related to a problem, please describe it. Leave this field blank if it is not related to a problem.
|
||||
placeholder: "Example: I'm always frustrated when..."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Your Solution
|
||||
description: Describe the solution you would like to have happen.
|
||||
placeholder: "Example: If I could..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Any additional information you wish to provide. Please add anything which did not fit into the other sections here.
|
||||
placeholder: "Example: This is likely achieveable by doing X because..."
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to fill out this feature request.
|
19
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: Ask a Question
|
||||
description: Ask a question regarding this project.
|
||||
labels: [ "question" ]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
If you are in need of quick response, Discord may be a better place. We are quite active on Discord, so you may get responses quicker.
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Your Question
|
||||
description: Feel free to ask any question regarding this project here.
|
||||
placeholder: "Example: How can I...?"
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to ask me a question.
|
33
.github/workflows/build.yml
vendored
33
.github/workflows/build.yml
vendored
|
@ -17,12 +17,18 @@ jobs:
|
|||
os: windows-2019
|
||||
prefixes: ''
|
||||
extra_flags: '-G "Visual Studio 16 2019" -T host=x86 -A win32'
|
||||
out_paths: './bin/nightly/geode.dll ./bin/nightly/GeodeBootstrapper.dll ./bin/nightly/geode.lib ./bin/nightly/XInput9_1_0.dll'
|
||||
out_paths: './bin/nightly/Geode.dll ./bin/nightly/GeodeBootstrapper.dll ./bin/nightly/Geode.lib ./bin/nightly/XInput9_1_0.dll'
|
||||
cli_name: '*-win.zip'
|
||||
cli_cmd: ''
|
||||
|
||||
- name: "macOS"
|
||||
os: macos-latest
|
||||
prefixes: 'PATH="/usr/local/opt/ccache/libexec:$PATH"'
|
||||
extra_flags: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON"
|
||||
extra_flags: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
|
||||
out_paths: './bin/nightly/Geode.dylib ./bin/nightly/GeodeBootstrapper.dylib'
|
||||
cli_name: '*-mac.zip'
|
||||
cli_cmd: 'chmod +x $GITHUB_WORKSPACE/cli/geode'
|
||||
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
|
@ -42,14 +48,33 @@ jobs:
|
|||
key: ${{ matrix.config.os }}
|
||||
if: matrix.config.os == 'macos-latest'
|
||||
|
||||
- name: Download CLI
|
||||
uses: robinraju/release-downloader@v1.5
|
||||
with:
|
||||
repository: geode-sdk/cli
|
||||
latest: true
|
||||
fileName: ${{ matrix.config.cli_name }}
|
||||
tarBall: false
|
||||
zipBall: false
|
||||
out-file-path: "cli"
|
||||
|
||||
- name: Unzip CLI
|
||||
run: |
|
||||
7z x "${{ github.workspace }}/cli/${{ matrix.config.cli_name }}" -o"${{ github.workspace }}/cli"
|
||||
|
||||
- name: Add CLI to Path
|
||||
run: |
|
||||
${{ matrix.config.cli_cmd }}
|
||||
echo "${{ github.workspace }}/cli" >> $GITHUB_PATH
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
${{ matrix.config.prefixes }} cmake -B ${{ github.workspace }}/build ${{ matrix.config.extra_flags }} -DGEODE_DISABLE_CLI_CALLS=ON
|
||||
${{ matrix.config.prefixes }} cmake -B ${{ github.workspace }}/build ${{ matrix.config.extra_flags }} -DGEODE_DISABLE_CLI_CALLS=ON -DCLI_PATH="${{ github.workspace }}/cli"
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config RelWithDebInfo
|
||||
|
||||
- name: Move to output folder
|
||||
shell: bash
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -43,6 +43,7 @@ build
|
|||
bin
|
||||
|
||||
loader/src/internal/about.hpp
|
||||
loader/src/internal/resources.hpp
|
||||
loader/resources/mod.json
|
||||
fods-catgirl-hideout.txt
|
||||
|
||||
|
|
51
CHANGELOG.md
51
CHANGELOG.md
|
@ -1,5 +1,56 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v0.4.8
|
||||
|
||||
- CLI issues fixed in v1.0.6 so loader again verifies if loader resources are missing / corrupt on startup
|
||||
- Resource download text is no longer a popup on top of the title but instead just replaces the loading text
|
||||
- Add delegates to `EditLevelLayer`
|
||||
|
||||
## v0.4.7
|
||||
|
||||
- Loader resources check would always fail due to CLI issues, so for now loader just checks if the resources folder exists
|
||||
|
||||
## v0.4.6
|
||||
|
||||
- Automatically checks & downloads loader resources if they are missing / corrupt on startup
|
||||
- CMake rework; `GeodeFile.cmake` now checks and verifies CLI version
|
||||
- Add optional `DONT_INSTALL` parameter to `create_geode_file`
|
||||
- Test mods are now no longer automatically installed
|
||||
- Add `package_geode_resources_now` command for packaging resources at configure time and creating a header with their calculated hashes
|
||||
- Fix `getSceneDelegate`
|
||||
- Change `CCArrayExt` to use `Ref`
|
||||
|
||||
## v0.4.5
|
||||
|
||||
- Rework bindings and codegen to improve compile times, now individual bindings can be included with `<Geode/binding/{ClassName}.hpp>`
|
||||
- Modify has also been separated, you can now include individual modifiers with `<Geode/modify/{ClassName}.hpp>`
|
||||
- Various other fixes to improve compile times
|
||||
- Fix mod resources not being loaded when installed from Index
|
||||
- Fix crashes related to downloading mods
|
||||
- Fix `Loader::queueInGDThread` sometimes leaving out functions
|
||||
- Fix crashes related to logging
|
||||
- Add new overloads to `file` utils and deprecate ones that don't use `ghc::filesystem::path`
|
||||
- Index mods now show their `about.md` files
|
||||
- More addresses
|
||||
- Various other fixes & improvements
|
||||
- Index reworked
|
||||
- Fix issues with `VERSION` file
|
||||
- Add `GEODE_DEBUG` macro for enabling `log::debug` to actually print stuff
|
||||
- Show crashlog on crash when `GEODE_DEBUG` is enabled
|
||||
- Add `JsonChecker::at` and `JsonChecker::array` for dealing with arrays
|
||||
- Add `geode::utils::web::fetchBytes` for fetching a byte array synchronously
|
||||
- Add `geode::utils::web::AsyncWebRequest` for creating thread-safe asynchronous web requests
|
||||
- Add `Loader::updateModResourcePaths` for adding a mods' resources to search paths. Not recommended to be called manually
|
||||
- Add an overload to `geode::createQuickPopup` for specifying popup width
|
||||
- `ModInfo::createFromFile` now checks for `about.md` and other special files in the same directory
|
||||
- Remove automatic mod updating for now, however automatic update checking for mods is still there
|
||||
|
||||
## v0.4.4
|
||||
|
||||
- New `listenForSettingChanges` API for more ergonomically listening for setting changes
|
||||
- Fixed bug where GD was unopenable through Steam
|
||||
- Various other internal fixes
|
||||
|
||||
## v0.4.3
|
||||
|
||||
- Simplified the minimum and maximum loader versions, loader will now load any mod whose target version major and minor match. In practice, this means that for example mods whose target version is v0.4.8 can be loaded by loader of version v0.4.6.
|
||||
|
|
|
@ -2,19 +2,20 @@ cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
|
|||
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build libraries static" FORCE)
|
||||
file(READ VERSION GEODE_VERSION)
|
||||
string(STRIP "${GEODE_VERSION}" GEODE_VERSION)
|
||||
|
||||
project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if (APPLE)
|
||||
if (PROJECT_IS_TOP_LEVEL AND APPLE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
|
||||
if (GEODE_DEBUG)
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE GEODE_DEBUG)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE -DGEODE_DEBUG)
|
||||
endif()
|
||||
|
||||
# Rerun CMake on VERSION file change
|
||||
|
@ -22,6 +23,10 @@ set_target_properties(${PROJECT_NAME} PROPERTIES CMAKE_CONFIGURE_DEPENDS VERSION
|
|||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE -DPROJECT_NAME=${CMAKE_PROJECT_NAME})
|
||||
|
||||
if (GEODE_DEBUG)
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE GEODE_DEBUG)
|
||||
endif()
|
||||
|
||||
set(GEODE_CODEGEN_PATH ${CMAKE_CURRENT_BINARY_DIR}/codegenned)
|
||||
set(GEODE_BIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
set(GEODE_LOADER_PATH ${CMAKE_CURRENT_SOURCE_DIR}/loader)
|
||||
|
@ -38,21 +43,22 @@ add_custom_target(CodegenRun ALL
|
|||
COMMAND Codegen ${GEODE_TARGET_PLATFORM} bindings ${GEODE_CODEGEN_PATH}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Run Codegen"
|
||||
BYPRODUCTS ${GEODE_CODEGEN_PATH}/GeneratedSource.cpp
|
||||
BYPRODUCTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp
|
||||
)
|
||||
add_dependencies(${PROJECT_NAME} CodegenRun)
|
||||
add_dependencies(CodegenRun Codegen)
|
||||
|
||||
# Hacky way to supress the not generated error
|
||||
if (NOT EXISTS ${GEODE_CODEGEN_PATH}/GeneratedSource.cpp)
|
||||
if (NOT EXISTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
|
||||
make_directory(${GEODE_CODEGEN_PATH})
|
||||
file(TOUCH ${GEODE_CODEGEN_PATH}/GeneratedSource.cpp)
|
||||
make_directory(${GEODE_CODEGEN_PATH}/Geode)
|
||||
file(TOUCH ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} INTERFACE ${GEODE_CODEGEN_PATH}/GeneratedSource.cpp)
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE ${GEODE_CODEGEN_PATH}/..)
|
||||
target_sources(${PROJECT_NAME} INTERFACE ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_CODEGEN_PATH}
|
||||
${GEODE_LOADER_PATH}/include
|
||||
${GEODE_LOADER_PATH}/include/Geode/cocos/
|
||||
${GEODE_LOADER_PATH}/include/Geode/cocos/cocos2dx
|
||||
|
@ -87,7 +93,13 @@ if (PROJECT_IS_TOP_LEVEL)
|
|||
target_link_libraries(${PROJECT_NAME} INTERFACE geode-loader)
|
||||
elseif(EXISTS ${GEODE_PLATFORM_BIN_PATH})
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE "${GEODE_PLATFORM_BIN_PATH}")
|
||||
target_precompile_headers(${PROJECT_NAME} INTERFACE "${GEODE_LOADER_PATH}/include/Geode/Geode.hpp")
|
||||
target_precompile_headers(${PROJECT_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/DefaultInclude.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Loader.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/UI.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/cocos2dx/include/cocos2d.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/extensions/cocos-ext.h"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "No valid loader binary to link to! Install a pre-built binary for version ${PROJECT_VERSION} with Geode CLI or build it from source.")
|
||||
endif()
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
There's nothing worse than having to read thousands of words just to see what some library's code actually looks like, so instead, here's a **Hello World** in Geode right off the bat:
|
||||
|
||||
```cpp
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/bindings.hpp>
|
||||
#include <Geode/modify/MenuLayer.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.4.3
|
||||
0.4.8
|
|
@ -115,9 +115,6 @@ class cocos2d::CCDirector {
|
|||
virtual auto getAccelerometer() = mac 0x24b0e0;
|
||||
virtual auto setAccelerometer(cocos2d::CCAccelerometer*) = mac 0x24b0a0;
|
||||
virtual auto getDeltaTime() = mac 0x249bd0;
|
||||
virtual auto getSceneDelegate() = mac 0x24b320;
|
||||
virtual auto setSceneDelegate(cocos2d::CCSceneDelegate*) = mac 0x24b330;
|
||||
|
||||
|
||||
auto getWinSize() = mac 0x24a0f0, ios 0xece34;
|
||||
auto pushScene(cocos2d::CCScene*) = mac 0x24a620;
|
||||
|
@ -341,17 +338,17 @@ class cocos2d::CCLayerColor {
|
|||
CCLayerColor() = mac 0x274320, ios 0xc8aec;
|
||||
static cocos2d::CCLayerColor* create(cocos2d::_ccColor4B const&, float, float) = mac 0x2745e0;
|
||||
static cocos2d::CCLayerColor* create(cocos2d::_ccColor4B const&) = mac 0x2744c0;
|
||||
auto draw() = mac 0x274b50, ios 0xc8fe0;
|
||||
auto getBlendFunc() = mac 0x274480, ios 0xc8bcc;
|
||||
auto init() = mac 0x274800, ios 0xc8de8;
|
||||
auto initWithColor(cocos2d::_ccColor4B const&) = mac 0x2749a0, ios 0xc8f14;
|
||||
auto initWithColor(cocos2d::_ccColor4B const&, float, float) = mac 0x274850, ios 0xc8e34;
|
||||
auto setBlendFunc(cocos2d::_ccBlendFunc) = mac 0x2744a0, ios 0xc8bdc;
|
||||
auto setColor(cocos2d::_ccColor3B const&) = mac 0x274c20, ios 0xc90ac;
|
||||
auto setContentSize(cocos2d::CCSize const&) = mac 0x2749f0, ios 0xc8f64;
|
||||
auto setOpacity(unsigned char) = mac 0x274db0, ios 0xc9108;
|
||||
auto updateColor() = mac 0x274ae0, ios 0xc8f80;
|
||||
~CCLayerColor() = mac 0x2743d0, ios 0x2743e0;
|
||||
virtual auto draw() = mac 0x274b50, ios 0xc8fe0;
|
||||
virtual auto getBlendFunc() = mac 0x274480, ios 0xc8bcc;
|
||||
virtual auto init() = mac 0x274800, ios 0xc8de8;
|
||||
virtual auto initWithColor(cocos2d::_ccColor4B const&) = mac 0x2749a0, ios 0xc8f14;
|
||||
virtual auto initWithColor(cocos2d::_ccColor4B const&, float, float) = mac 0x274850, ios 0xc8e34;
|
||||
virtual auto setBlendFunc(cocos2d::_ccBlendFunc) = mac 0x2744a0, ios 0xc8bdc;
|
||||
virtual auto setColor(cocos2d::_ccColor3B const&) = mac 0x274c20, ios 0xc90ac;
|
||||
virtual auto setContentSize(cocos2d::CCSize const&) = mac 0x2749f0, ios 0xc8f64;
|
||||
virtual auto setOpacity(unsigned char) = mac 0x274db0, ios 0xc9108;
|
||||
virtual auto updateColor() = mac 0x274ae0, ios 0xc8f80;
|
||||
virtual ~CCLayerColor() = mac 0x2743d0, ios 0x2743e0;
|
||||
}
|
||||
|
||||
class cocos2d::CCLayerRGBA {
|
||||
|
@ -644,6 +641,10 @@ class cocos2d::CCPoolManager {
|
|||
static cocos2d::CCPoolManager* sharedPoolManager() = mac 0x2142c0;
|
||||
}
|
||||
|
||||
class cocos2d::CCRemoveSelf {
|
||||
static cocos2d::CCRemoveSelf* create(bool) = mac 0x454700;
|
||||
}
|
||||
|
||||
class cocos2d::CCRenderTexture {
|
||||
auto begin() = mac 0x35ce10;
|
||||
auto end() = mac 0x35d2c0;
|
||||
|
|
|
@ -92,8 +92,8 @@ class AudioEffectsLayer {
|
|||
}
|
||||
|
||||
class BoomListView : cocos2d::CCLayer, TableViewDelegate, TableViewDataSource {
|
||||
inline BoomListView() {}
|
||||
inline ~BoomListView() {
|
||||
BoomListView() {}
|
||||
~BoomListView() {
|
||||
CC_SAFE_RELEASE(m_entries);
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1034,7 @@ class EditButtonBar : cocos2d::CCNode {
|
|||
cocos2d::CCArray* m_pagesArray;
|
||||
}
|
||||
|
||||
class EditLevelLayer : cocos2d::CCLayer {
|
||||
class EditLevelLayer : cocos2d::CCLayer, FLAlertLayerProtocol, TextInputDelegate, UploadActionDelegate, UploadPopupDelegate, SetIDPopupDelegate {
|
||||
static void scene(GJGameLevel* level) {
|
||||
auto scene = cocos2d::CCScene::create();
|
||||
|
||||
|
@ -3760,7 +3760,7 @@ class LevelPage {
|
|||
|
||||
class LevelSearchLayer : cocos2d::CCLayer {
|
||||
static LevelSearchLayer* create() = mac 0x0, win 0x17d9c0, ios 0x0;
|
||||
bool init() = mac 0x384770, win 0x0, ios 0x0;
|
||||
bool init() = mac 0x384770, win 0x17da60, ios 0x0;
|
||||
GJSearchObject* getSearchObject(SearchType, gd::string) = mac 0x388a50, win 0x1805f0, ios 0x0;
|
||||
void onMoreOptions(cocos2d::CCObject*) = mac 0x0, win 0x17f500, ios 0x0;
|
||||
void onSearch(cocos2d::CCObject*) = mac 0x0, win 0x180fc0, ios 0x0;
|
||||
|
@ -3865,6 +3865,7 @@ class LoadingLayer : cocos2d::CCLayer {
|
|||
m_fromRefresh = value;
|
||||
}
|
||||
|
||||
LoadingLayer() {}
|
||||
static LoadingLayer* create(bool fromReload) = mac 0x1df1f0, win 0x18bfe0, ios 0x130278;
|
||||
bool init(bool fromReload) = mac 0x1df2f0, win 0x18c080, ios 0x0;
|
||||
const char* getLoadingString() = mac 0x0, win 0x18cf40, ios 0x0;
|
||||
|
@ -4122,6 +4123,8 @@ class PlayLayer : GJBaseGameLayer, CCCircleWaveDelegate, CurrencyRewardDelegate,
|
|||
return GameManager::sharedState()->getPlayLayer();
|
||||
}
|
||||
|
||||
PlayLayer() = win 0x1FAA90;
|
||||
|
||||
void addCircle(CCCircleWave*) = mac 0x7e0f0, win 0x0, ios 0x0;
|
||||
void addObject(GameObject*) = mac 0x70e50, win 0x2017e0, ios 0x0;
|
||||
void addToGroupOld(GameObject*) = mac 0x77680, win 0x0, ios 0x0;
|
||||
|
|
|
@ -1,11 +1,39 @@
|
|||
set(GEODE_CLI_MINIMUM_VERSION 1.0.5)
|
||||
|
||||
# for passing CLI through CMake arguments
|
||||
if (DEFINED CLI_PATH)
|
||||
list(APPEND CMAKE_PROGRAM_PATH ${CLI_PATH})
|
||||
endif()
|
||||
|
||||
# Find Geode CLI
|
||||
if (NOT DEFINED GEODE_CLI)
|
||||
find_program(GEODE_CLI NAMES geode.exe geode-cli.exe geode geode-cli)
|
||||
endif()
|
||||
|
||||
# Check if CLI was found
|
||||
if (GEODE_CLI STREQUAL "GEODE_CLI-NOTFOUND")
|
||||
message(STATUS "Unable to find Geode CLI")
|
||||
else()
|
||||
message(STATUS "Found Geode CLI: ${GEODE_CLI}")
|
||||
# `geode --version` returns `geode x.x.x\n` so gotta do some wacky shit
|
||||
execute_process(
|
||||
COMMAND ${GEODE_CLI} --version
|
||||
OUTPUT_VARIABLE GEODE_CLI_VERSION
|
||||
)
|
||||
# Remove trailing newline
|
||||
string(STRIP ${GEODE_CLI_VERSION} GEODE_CLI_VERSION)
|
||||
# Remove program name
|
||||
string(REPLACE "geode " "" GEODE_CLI_VERSION ${GEODE_CLI_VERSION})
|
||||
|
||||
# Need at least v1.0.5 (--shut-up arg in geode package resources)
|
||||
if (${GEODE_CLI_VERSION} VERSION_LESS ${GEODE_CLI_MINIMUM_VERSION})
|
||||
message(FATAL_ERROR
|
||||
"Found Geode CLI: ${GEODE_CLI}, however it is version ${GEODE_CLI_VERSION} "
|
||||
"while minimum required is version ${GEODE_CLI_MINIMUM_VERSION}. Please update: "
|
||||
"https://github.com/geode-sdk/cli/releases/latest"
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Found Geode CLI: ${GEODE_CLI} (version ${GEODE_CLI_VERSION})")
|
||||
endif()
|
||||
|
||||
function(create_geode_file_old proname)
|
||||
|
@ -36,6 +64,10 @@ function(create_geode_file_old proname)
|
|||
endfunction()
|
||||
|
||||
function(create_geode_file proname)
|
||||
# Get DONT_INSTALL argument
|
||||
set(options DONT_INSTALL)
|
||||
cmake_parse_arguments(CREATE_GEODE_FILE "${options}" "" "" ${ARGN})
|
||||
|
||||
if (GEODE_DISABLE_CLI_CALLS)
|
||||
message("Skipping creating geode file for ${proname}")
|
||||
return()
|
||||
|
@ -57,11 +89,17 @@ function(create_geode_file proname)
|
|||
OUTPUT_VARIABLE MOD_ID
|
||||
)
|
||||
|
||||
if (CREATE_GEODE_FILE_DONT_INSTALL)
|
||||
set(INSTALL_ARG "")
|
||||
else()
|
||||
set(INSTALL_ARG "--install")
|
||||
endif()
|
||||
|
||||
set_target_properties(${proname} PROPERTIES PREFIX "")
|
||||
set_target_properties(${proname} PROPERTIES OUTPUT_NAME ${MOD_ID})
|
||||
add_custom_target(${proname}_PACKAGE ALL
|
||||
DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json
|
||||
COMMAND ${GEODE_CLI} package new ${CMAKE_CURRENT_SOURCE_DIR} --binary $<TARGET_FILE:${proname}> --output $<TARGET_FILE_DIR:${proname}>/${proname}.geode --install
|
||||
COMMAND ${GEODE_CLI} package new ${CMAKE_CURRENT_SOURCE_DIR} --binary $<TARGET_FILE:${proname}> --output $<TARGET_FILE_DIR:${proname}>/${proname}.geode ${INSTALL_ARG}
|
||||
VERBATIM USES_TERMINAL
|
||||
)
|
||||
endfunction()
|
||||
|
@ -75,7 +113,10 @@ function(package_geode_resources proname src dest)
|
|||
message(STATUS "Packaging resources from ${src} into ${dest}")
|
||||
|
||||
if(GEODE_CLI STREQUAL "GEODE_CLI-NOTFOUND")
|
||||
message(WARNING "package_geode_resources called, but Geode CLI was not found - You will need to manually package the resources")
|
||||
message(WARNING
|
||||
"package_geode_resources called, but Geode CLI was "
|
||||
"not found - You will need to manually package the resources"
|
||||
)
|
||||
else()
|
||||
|
||||
add_custom_target(${proname}_PACKAGE ALL
|
||||
|
@ -83,5 +124,64 @@ function(package_geode_resources proname src dest)
|
|||
COMMAND ${GEODE_CLI} package resources ${src} ${dest}
|
||||
VERBATIM USES_TERMINAL
|
||||
)
|
||||
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(package_geode_resources_now proname src dest header_dest)
|
||||
if (GEODE_DISABLE_CLI_CALLS)
|
||||
message(WARNING
|
||||
"package_geode_resources_now called, but GEODE_DISABLE_CLI_CALLS
|
||||
is set to true - Ignoring it as this function requires CLI calls
|
||||
in order to work"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(GEODE_CLI STREQUAL "GEODE_CLI-NOTFOUND")
|
||||
message(FATAL_ERROR
|
||||
"package_geode_resources_now called, but Geode CLI "
|
||||
"was not found - Please install Geode CLI from "
|
||||
"https://github.com/geode-sdk/cli/releases/latest"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "Packaging resources now from ${src} into ${dest}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GEODE_CLI} package resources ${src} ${dest} --shut-up
|
||||
RESULT_VARIABLE GEODE_PACKAGE_RES
|
||||
)
|
||||
|
||||
if (NOT GEODE_PACKAGE_RES EQUAL "0")
|
||||
message(FATAL_ERROR
|
||||
"Command \"${GEODE_CLI} package resources ${src} ${dest}\" returned "
|
||||
"${GEODE_PACKAGE_RES} - Expected 0"
|
||||
)
|
||||
endif()
|
||||
|
||||
file(GLOB RESOURCE_FILES "${dest}/*.*")
|
||||
|
||||
set(HEADER_FILE
|
||||
"#include <unordered_map>\n\n"
|
||||
"static const std::unordered_map<std::string, std::string> "
|
||||
"LOADER_RESOURCE_HASHES {\n"
|
||||
)
|
||||
|
||||
foreach(file ${RESOURCE_FILES})
|
||||
cmake_path(GET file FILENAME FILE_NAME)
|
||||
if (NOT FILE_NAME STREQUAL ".geode_cache")
|
||||
|
||||
file(SHA256 ${file} COMPUTED_HASH)
|
||||
list(APPEND HEADER_FILE "\t{ \"${FILE_NAME}\", \"${COMPUTED_HASH}\" },\n")
|
||||
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
|
||||
list(APPEND HEADER_FILE "}\;\n")
|
||||
|
||||
file(WRITE ${header_dest} ${HEADER_FILE})
|
||||
message(STATUS "Wrote resource hashes to ${header_dest}")
|
||||
|
||||
endfunction()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace { namespace format_strings {
|
||||
|
||||
char const* declare_address = R"GEN(
|
||||
GEODE_NOINLINE GEODE_HIDDEN inline static uintptr_t address{index}() {{
|
||||
GEODE_INLINE GEODE_HIDDEN static uintptr_t address{index}() {{
|
||||
static uintptr_t ret = {address};
|
||||
return ret;
|
||||
}}
|
||||
|
|
|
@ -3,34 +3,43 @@
|
|||
#include <set>
|
||||
|
||||
namespace { namespace format_strings {
|
||||
char const* class_predeclare = "class {class_name};\n";
|
||||
// requires: base_classes, class_name
|
||||
char const* binding_include = R"GEN(#include "binding/{file_name}"
|
||||
)GEN";
|
||||
|
||||
char const* class_includes = R"GEN(#pragma once
|
||||
#include <Geode/c++stl/gdstdlib.hpp>
|
||||
#include <cocos2d.h>
|
||||
#include <cocos-ext.h>
|
||||
#include <Geode/GeneratedPredeclare.hpp>
|
||||
#include <Geode/Enums.hpp>
|
||||
|
||||
)GEN";
|
||||
|
||||
char const* class_include_prereq = R"GEN(#include "{file_name}"
|
||||
)GEN";
|
||||
|
||||
char const* class_start = R"GEN(
|
||||
class {class_name}{base_classes} {{
|
||||
public:
|
||||
)GEN";
|
||||
|
||||
char const* monostate_constructor = R"GEN(
|
||||
GEODE_MONOSTATE_CONSTRUCTOR_GD({class_name}, {first_base})
|
||||
char const* monostate_constructor = R"GEN( GEODE_MONOSTATE_CONSTRUCTOR_GD({class_name}, {first_base})
|
||||
)GEN";
|
||||
|
||||
char const* monostate_constructor_cutoff = R"GEN(
|
||||
GEODE_MONOSTATE_CONSTRUCTOR_CUTOFF({class_name}, {first_base})
|
||||
char const* monostate_constructor_cutoff = R"GEN( GEODE_MONOSTATE_CONSTRUCTOR_CUTOFF({class_name}, {first_base})
|
||||
)GEN";
|
||||
|
||||
char const* function_definition = R"GEN(
|
||||
{docs}{static}{virtual}{return_type} {function_name}({parameters}){const};
|
||||
char const* function_definition = R"GEN({docs} {static}{virtual}{return_type} {function_name}({parameters}){const};
|
||||
)GEN";
|
||||
|
||||
char const* error_definition = R"GEN(
|
||||
template <bool T=false>
|
||||
char const* error_definition = R"GEN( template <bool T=false>
|
||||
{static}{return_type} {function_name}({parameters}){const}{{
|
||||
static_assert(T, "Implement {class_name}::{function_name}");
|
||||
}}
|
||||
)GEN";
|
||||
|
||||
char const* error_definition_virtual = R"GEN(
|
||||
[[deprecated("Use of undefined virtual function - will crash at runtime!!!")]]
|
||||
char const* error_definition_virtual = R"GEN( [[deprecated("Use of undefined virtual function - will crash at runtime!!!")]]
|
||||
{virtual}{return_type} {function_name}({parameters}){const}{{
|
||||
#ifdef GEODE_NO_UNDEFINED_VIRTUALS
|
||||
static_assert(false, "Undefined virtual function - implement in GeometryDash.bro");
|
||||
|
@ -43,52 +52,52 @@ public:
|
|||
{function_name}({parameters});)GEN";
|
||||
|
||||
// requires: type, member_name, array
|
||||
char const* member_definition = R"GEN(
|
||||
{type} {member_name};)GEN";
|
||||
char const* member_definition = R"GEN( {type} {member_name};
|
||||
)GEN";
|
||||
|
||||
char const* pad_definition = R"GEN(
|
||||
GEODE_PAD({hardcode});)GEN";
|
||||
char const* unimplemented_definition = R"GEN(
|
||||
GEODE_UNIMPLEMENTED_PAD)GEN";
|
||||
char const* pad_definition = R"GEN( GEODE_PAD({hardcode});
|
||||
)GEN";
|
||||
|
||||
// requires: hardcode_macro, type, member_name, hardcode
|
||||
char const* hardcode_definition = R"GEN(
|
||||
CLASSPARAM({type}, {member_name}, {hardcode});)GEN";
|
||||
|
||||
char const* class_end = R"GEN(
|
||||
};
|
||||
char const* class_end = R"GEN(};
|
||||
)GEN";
|
||||
}}
|
||||
|
||||
std::string generateGDHeader(Root& root) {
|
||||
std::string output("#pragma once\n#include <Geode/c++stl/gdstdlib.hpp>\n#include <cocos2d.h>\n");
|
||||
|
||||
for (auto& cls : root.classes) {
|
||||
if (can_find(cls.name, "cocos2d"))
|
||||
continue;
|
||||
|
||||
output += fmt::format(::format_strings::class_predeclare,
|
||||
fmt::arg("class_name", cls.name)
|
||||
);
|
||||
}
|
||||
std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singleFolder) {
|
||||
std::string output;
|
||||
|
||||
for (auto& cls : root.classes) {
|
||||
if (can_find(cls.name, "cocos2d"))
|
||||
continue;
|
||||
|
||||
std::string filename = (codegen::getUnqualifiedClassName(cls.name) + ".hpp");
|
||||
output += fmt::format(format_strings::binding_include,
|
||||
fmt::arg("file_name", filename)
|
||||
);
|
||||
|
||||
std::string single_output;
|
||||
single_output += format_strings::class_includes;
|
||||
|
||||
for (auto dep : cls.depends) {
|
||||
if (can_find(dep, "cocos2d::")) continue;
|
||||
|
||||
std::string depfilename = (codegen::getUnqualifiedClassName(dep) + ".hpp");
|
||||
|
||||
single_output += fmt::format(format_strings::class_include_prereq, fmt::arg("file_name", depfilename));
|
||||
}
|
||||
|
||||
std::string supers = str_if(
|
||||
fmt::format(" : public {}", fmt::join(cls.superclasses, ", ")),
|
||||
!cls.superclasses.empty()
|
||||
);
|
||||
|
||||
output += fmt::format(::format_strings::class_start,
|
||||
single_output += fmt::format(::format_strings::class_start,
|
||||
fmt::arg("class_name", cls.name),
|
||||
fmt::arg("base_classes", supers)
|
||||
);
|
||||
|
||||
// what.
|
||||
if (!cls.superclasses.empty()) {
|
||||
output += fmt::format(
|
||||
single_output += fmt::format(
|
||||
can_find(cls.superclasses[0], "cocos2d")
|
||||
? format_strings::monostate_constructor_cutoff
|
||||
: format_strings::monostate_constructor,
|
||||
|
@ -97,15 +106,17 @@ std::string generateGDHeader(Root& root) {
|
|||
);
|
||||
}
|
||||
|
||||
bool unimplementedField = false;
|
||||
for (auto field : cls.fields) {
|
||||
FunctionBegin* fb;
|
||||
char const* used_format = format_strings::function_definition;
|
||||
|
||||
if (auto i = field.get_as<InlineField>()) {
|
||||
output += "\t" + i->inner + "\n";
|
||||
single_output += "\t" + i->inner + "\n";
|
||||
continue;
|
||||
} else if (auto m = field.get_as<MemberField>()) {
|
||||
output += fmt::format(format_strings::member_definition,
|
||||
if (unimplementedField) single_output += "\t[[deprecated(\"Member placed incorrectly - will crash at runtime!!!\")]]\n";
|
||||
single_output += fmt::format(format_strings::member_definition,
|
||||
fmt::arg("type", m->type.name),
|
||||
fmt::arg("member_name", m->name + str_if(fmt::format("[{}]", m->count), m->count))
|
||||
);
|
||||
|
@ -114,9 +125,9 @@ std::string generateGDHeader(Root& root) {
|
|||
auto hardcode = codegen::platformNumber(p->amount);
|
||||
|
||||
if (hardcode) {
|
||||
output += fmt::format(format_strings::pad_definition, fmt::arg("hardcode", hardcode));
|
||||
single_output += fmt::format(format_strings::pad_definition, fmt::arg("hardcode", hardcode));
|
||||
} else {
|
||||
output += "\n GEODE_UNIMPLEMENTED_PAD";
|
||||
unimplementedField = true;
|
||||
}
|
||||
continue;
|
||||
} else if (auto fn = field.get_as<OutOfLineField>()) {
|
||||
|
@ -135,7 +146,7 @@ std::string generateGDHeader(Root& root) {
|
|||
}
|
||||
}
|
||||
|
||||
output += fmt::format(used_format,
|
||||
single_output += fmt::format(used_format,
|
||||
fmt::arg("virtual", str_if("virtual ", fb->is_virtual)),
|
||||
fmt::arg("static", str_if("static ", fb->is_static)),
|
||||
fmt::arg("class_name", cls.name),
|
||||
|
@ -149,9 +160,9 @@ std::string generateGDHeader(Root& root) {
|
|||
}
|
||||
|
||||
// if (hasClass)
|
||||
output += ::format_strings::class_end;
|
||||
single_output += ::format_strings::class_end;
|
||||
|
||||
// queued.pop_front();
|
||||
writeFile(singleFolder / filename, single_output);
|
||||
}
|
||||
|
||||
return output;
|
|
@ -3,21 +3,6 @@
|
|||
|
||||
using namespace codegen;
|
||||
|
||||
void writeFile(ghc::filesystem::path const& writePath, std::string const& output) {
|
||||
std::ifstream readfile;
|
||||
readfile >> std::noskipws;
|
||||
readfile.open(writePath);
|
||||
std::string data((std::istreambuf_iterator<char>(readfile)), std::istreambuf_iterator<char>());
|
||||
readfile.close();
|
||||
|
||||
if (data != output) {
|
||||
std::ofstream writefile;
|
||||
writefile.open(writePath);
|
||||
writefile << output;
|
||||
writefile.close();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) try {
|
||||
if (argc != 4) throw codegen::error("Invalid number of parameters (expected 3 found {})", argc-1);
|
||||
|
||||
|
@ -31,8 +16,10 @@ int main(int argc, char** argv) try {
|
|||
|
||||
chdir(argv[2]);
|
||||
|
||||
ghc::filesystem::path writeDir = argv[3];
|
||||
auto writeDir = ghc::filesystem::path(argv[3]) / "Geode";
|
||||
ghc::filesystem::create_directories(writeDir);
|
||||
ghc::filesystem::create_directories(writeDir / "modify");
|
||||
ghc::filesystem::create_directories(writeDir / "binding");
|
||||
|
||||
Root root = broma::parse_file("Entry.bro");
|
||||
|
||||
|
@ -45,11 +32,12 @@ int main(int argc, char** argv) try {
|
|||
}
|
||||
|
||||
writeFile(writeDir / "GeneratedAddress.hpp", generateAddressHeader(root));
|
||||
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root)); // pretty much obsolete with a custom compiler
|
||||
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify")); // pretty much obsolete with a custom compiler
|
||||
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root)); // pretty much obsolete with a custom compiler
|
||||
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root)); // pretty much obsolete with a custom compiler
|
||||
writeFile(writeDir / "GeneratedHeader.hpp", generateGDHeader(root));
|
||||
writeFile(writeDir / "GeneratedSource.cpp", generateGDSource(root));
|
||||
writeFile(writeDir / "GeneratedBinding.hpp", generateBindingHeader(root, writeDir / "binding"));
|
||||
writeFile(writeDir / "GeneratedPredeclare.hpp", generatePredeclareHeader(root));
|
||||
writeFile(writeDir / "GeneratedSource.cpp", generateBindingSource(root));
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Codegen error: " << e.what() << "\n";
|
||||
return 1;
|
||||
|
|
|
@ -3,34 +3,51 @@
|
|||
|
||||
namespace { namespace format_strings {
|
||||
// requires: class_name
|
||||
char const* modify_start = R"GEN(
|
||||
template<class Derived>
|
||||
struct Modify<Derived, {class_name}> : ModifyBase<Modify<Derived, {class_name}>> {{
|
||||
using ModifyBase<Modify<Derived, {class_name}>>::ModifyBase;
|
||||
using Base = {class_name};
|
||||
static void apply() {{
|
||||
using namespace geode::core::meta;
|
||||
char const* modify_start = R"GEN(#pragma once
|
||||
#include <Geode/modify/Modify.hpp>
|
||||
#include <Geode/modify/Field.hpp>
|
||||
#include <Geode/modify/InternalMacros.hpp>
|
||||
using namespace geode::modifier;
|
||||
|
||||
namespace geode::modifier {{
|
||||
template<class Derived>
|
||||
struct Modify<Derived, {class_name}> : ModifyBase<Modify<Derived, {class_name}>> {{
|
||||
using ModifyBase<Modify<Derived, {class_name}>>::ModifyBase;
|
||||
using Base = {class_name};
|
||||
static void apply() {{
|
||||
using namespace geode::core::meta;
|
||||
)GEN";
|
||||
|
||||
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
|
||||
char const* apply_function = R"GEN(
|
||||
GEODE_APPLY_MODIFY_FOR_FUNCTION({index}, {function_convention}, {class_name}, {function_name}))GEN";
|
||||
GEODE_APPLY_MODIFY_FOR_FUNCTION({index}, {function_convention}, {class_name}, {function_name}))GEN";
|
||||
|
||||
char const* modify_end = R"GEN(
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
)GEN";
|
||||
|
||||
char const* modify_include = R"GEN(#include "modify/{file_name}"
|
||||
)GEN";
|
||||
}}
|
||||
|
||||
|
||||
std::string generateModifyHeader(Root& root) {
|
||||
std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder) {
|
||||
std::string output;
|
||||
|
||||
for (auto c : root.classes) {
|
||||
if (c.name == "cocos2d")
|
||||
continue;
|
||||
|
||||
output += fmt::format(format_strings::modify_start,
|
||||
std::string filename = (codegen::getUnqualifiedClassName(c.name) + ".hpp");
|
||||
output += fmt::format(format_strings::modify_include,
|
||||
fmt::arg("file_name", filename)
|
||||
);
|
||||
|
||||
std::string single_output;
|
||||
|
||||
single_output += fmt::format(format_strings::modify_start,
|
||||
fmt::arg("class_name", c.name)
|
||||
);
|
||||
|
||||
|
@ -52,7 +69,7 @@ std::string generateModifyHeader(Root& root) {
|
|||
break;
|
||||
}
|
||||
|
||||
output += fmt::format(format_strings::apply_function,
|
||||
single_output += fmt::format(format_strings::apply_function,
|
||||
fmt::arg("index", f.field_id),
|
||||
fmt::arg("class_name", c.name),
|
||||
fmt::arg("function_name", function_name),
|
||||
|
@ -61,7 +78,9 @@ std::string generateModifyHeader(Root& root) {
|
|||
}
|
||||
}
|
||||
|
||||
output += format_strings::modify_end;
|
||||
single_output += format_strings::modify_end;
|
||||
|
||||
writeFile(singleFolder / filename, single_output);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
|
22
codegen/src/PredeclareGen.cpp
Normal file
22
codegen/src/PredeclareGen.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "Shared.hpp"
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace { namespace format_strings {
|
||||
char const* class_predeclare = "class {class_name};\n";
|
||||
}}
|
||||
|
||||
std::string generatePredeclareHeader(Root& root) {
|
||||
std::string output("#pragma once\n");
|
||||
|
||||
for (auto& cls : root.classes) {
|
||||
if (can_find(cls.name, "cocos2d"))
|
||||
continue;
|
||||
|
||||
output += fmt::format(::format_strings::class_predeclare,
|
||||
fmt::arg("class_name", cls.name)
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <fmt/format.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include <fstream>
|
||||
#include <fs/filesystem.hpp> // bruh
|
||||
|
||||
using std::istreambuf_iterator;
|
||||
|
||||
|
@ -19,13 +20,29 @@ using std::istreambuf_iterator;
|
|||
#endif
|
||||
|
||||
std::string generateAddressHeader(Root& root);
|
||||
std::string generateModifyHeader(Root& root);
|
||||
std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder);
|
||||
std::string generateWrapperHeader(Root& root);
|
||||
std::string generateTypeHeader(Root& root);
|
||||
std::string generateGDHeader(Root& root);
|
||||
std::string generateGDSource(Root& root);
|
||||
std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singleFolder);
|
||||
std::string generatePredeclareHeader(Root& root);
|
||||
std::string generateBindingSource(Root& root);
|
||||
std::string generateTidyHeader(Root& root);
|
||||
|
||||
inline void writeFile(ghc::filesystem::path const& writePath, std::string const& output) {
|
||||
std::ifstream readfile;
|
||||
readfile >> std::noskipws;
|
||||
readfile.open(writePath);
|
||||
std::string data((std::istreambuf_iterator<char>(readfile)), std::istreambuf_iterator<char>());
|
||||
readfile.close();
|
||||
|
||||
if (data != output) {
|
||||
std::ofstream writefile;
|
||||
writefile.open(writePath);
|
||||
writefile << output;
|
||||
writefile.close();
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string str_if(std::string&& str, bool cond) {
|
||||
return cond ? str : "";
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
|
|||
)GEN";
|
||||
}}
|
||||
|
||||
std::string generateGDSource(Root& root) {
|
||||
std::string generateBindingSource(Root& root) {
|
||||
std::string output(format_strings::source_start);
|
||||
|
||||
for (auto& c : root.classes) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// included by default in every geode project
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/Loader.hpp>
|
||||
|
||||
GEODE_API bool GEODE_CALL geode_implicit_load(geode::Mod* m) {
|
||||
geode::Mod::setSharedMod(m);
|
||||
|
|
|
@ -82,14 +82,13 @@ if (GEODE_NO_UNDEFINED_VIRTUALS)
|
|||
target_compile_definitions(${PROJECT_NAME} PUBLIC GEODE_NO_UNDEFINED_VIRTUALS)
|
||||
endif()
|
||||
|
||||
if (NOT GEODE_DISABLE_CLI_CALLS)
|
||||
# Package resources for UI
|
||||
package_geode_resources(
|
||||
${PROJECT_NAME}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||
${GEODE_BIN_PATH}/nightly/resources
|
||||
)
|
||||
endif()
|
||||
# Package resources for UI
|
||||
package_geode_resources_now(
|
||||
${PROJECT_NAME}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||
${GEODE_BIN_PATH}/nightly/resources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/internal/resources.hpp
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
src/internal/
|
||||
|
@ -118,12 +117,13 @@ target_link_libraries(${PROJECT_NAME} z lilac_hook geode-sdk)
|
|||
set_source_files_properties(${OBJC_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
target_precompile_headers(${PROJECT_NAME} PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/DefaultInclude.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Utils.hpp"
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Utils.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Loader.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/UI.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Bindings.hpp"
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Bindings.hpp"
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/Modify.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/cocos2dx/include/cocos2d.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/Geode/cocos/extensions/cocos-ext.h"
|
||||
)
|
||||
|
||||
# Create launcher
|
||||
|
|
|
@ -6,4 +6,6 @@ project(GeodeChecksum VERSION 1.0)
|
|||
|
||||
add_executable(${PROJECT_NAME} hash.cpp)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC filesystem)
|
||||
|
||||
message(STATUS "Building Checksum Exe")
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <iostream>
|
||||
#include "hash.hpp"
|
||||
|
||||
int main(int ac, char* av[]) {
|
||||
if (ac < 2) {
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2 || !ghc::filesystem::exists(argv[1])) {
|
||||
std::cout << "Usage: \"checksum <file>\"\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << calculateHash(av[1]) << std::hex;
|
||||
std::cout << calculateHash(argv[1]) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,24 @@
|
|||
#include <fstream>
|
||||
#include <ciso646>
|
||||
#include "picosha3.h"
|
||||
#include "picosha2.h"
|
||||
#include <vector>
|
||||
#include <fs/filesystem.hpp>
|
||||
|
||||
static std::string calculateHash(std::string const& path) {
|
||||
static std::string calculateSHA3_256(ghc::filesystem::path const& path) {
|
||||
std::vector<uint8_t> s(picosha3::bits_to_bytes(256));
|
||||
auto sha3_256 = picosha3::get_sha3_generator<256>();
|
||||
std::ifstream file(path);
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
return sha3_256.get_hex_string(file);
|
||||
}
|
||||
|
||||
static std::string calculateSHA256(ghc::filesystem::path const& path) {
|
||||
std::vector<uint8_t> hash(picosha2::k_digest_size);
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
picosha2::hash256(file, hash.begin(), hash.end());
|
||||
return picosha2::bytes_to_hex_string(hash.begin(), hash.end());
|
||||
}
|
||||
|
||||
static std::string calculateHash(ghc::filesystem::path const& path) {
|
||||
return calculateSHA3_256(path);
|
||||
}
|
||||
|
|
377
loader/hash/picosha2.h
Normal file
377
loader/hash/picosha2.h
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2017 okdshin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
#ifndef PICOSHA2_H
|
||||
#define PICOSHA2_H
|
||||
// picosha2:20140213
|
||||
|
||||
#ifndef PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR
|
||||
#define PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR \
|
||||
1048576 //=1024*1024: default is 1MB memory
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
namespace picosha2 {
|
||||
typedef unsigned long word_t;
|
||||
typedef unsigned char byte_t;
|
||||
|
||||
static const size_t k_digest_size = 32;
|
||||
|
||||
namespace detail {
|
||||
inline byte_t mask_8bit(byte_t x) { return x & 0xff; }
|
||||
|
||||
inline word_t mask_32bit(word_t x) { return x & 0xffffffff; }
|
||||
|
||||
const word_t add_constant[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
||||
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
|
||||
|
||||
const word_t initial_message_digest[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372,
|
||||
0xa54ff53a, 0x510e527f, 0x9b05688c,
|
||||
0x1f83d9ab, 0x5be0cd19};
|
||||
|
||||
inline word_t ch(word_t x, word_t y, word_t z) { return (x & y) ^ ((~x) & z); }
|
||||
|
||||
inline word_t maj(word_t x, word_t y, word_t z) {
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
inline word_t rotr(word_t x, std::size_t n) {
|
||||
assert(n < 32);
|
||||
return mask_32bit((x >> n) | (x << (32 - n)));
|
||||
}
|
||||
|
||||
inline word_t bsig0(word_t x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); }
|
||||
|
||||
inline word_t bsig1(word_t x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); }
|
||||
|
||||
inline word_t shr(word_t x, std::size_t n) {
|
||||
assert(n < 32);
|
||||
return x >> n;
|
||||
}
|
||||
|
||||
inline word_t ssig0(word_t x) { return rotr(x, 7) ^ rotr(x, 18) ^ shr(x, 3); }
|
||||
|
||||
inline word_t ssig1(word_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ shr(x, 10); }
|
||||
|
||||
template <typename RaIter1, typename RaIter2>
|
||||
void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last) {
|
||||
assert(first + 64 == last);
|
||||
static_cast<void>(last); // for avoiding unused-variable warning
|
||||
word_t w[64];
|
||||
std::fill(w, w + 64, word_t(0));
|
||||
for (std::size_t i = 0; i < 16; ++i) {
|
||||
w[i] = (static_cast<word_t>(mask_8bit(*(first + i * 4))) << 24) |
|
||||
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 1))) << 16) |
|
||||
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 2))) << 8) |
|
||||
(static_cast<word_t>(mask_8bit(*(first + i * 4 + 3))));
|
||||
}
|
||||
for (std::size_t i = 16; i < 64; ++i) {
|
||||
w[i] = mask_32bit(ssig1(w[i - 2]) + w[i - 7] + ssig0(w[i - 15]) +
|
||||
w[i - 16]);
|
||||
}
|
||||
|
||||
word_t a = *message_digest;
|
||||
word_t b = *(message_digest + 1);
|
||||
word_t c = *(message_digest + 2);
|
||||
word_t d = *(message_digest + 3);
|
||||
word_t e = *(message_digest + 4);
|
||||
word_t f = *(message_digest + 5);
|
||||
word_t g = *(message_digest + 6);
|
||||
word_t h = *(message_digest + 7);
|
||||
|
||||
for (std::size_t i = 0; i < 64; ++i) {
|
||||
word_t temp1 = h + bsig1(e) + ch(e, f, g) + add_constant[i] + w[i];
|
||||
word_t temp2 = bsig0(a) + maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = mask_32bit(d + temp1);
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = mask_32bit(temp1 + temp2);
|
||||
}
|
||||
*message_digest += a;
|
||||
*(message_digest + 1) += b;
|
||||
*(message_digest + 2) += c;
|
||||
*(message_digest + 3) += d;
|
||||
*(message_digest + 4) += e;
|
||||
*(message_digest + 5) += f;
|
||||
*(message_digest + 6) += g;
|
||||
*(message_digest + 7) += h;
|
||||
for (std::size_t i = 0; i < 8; ++i) {
|
||||
*(message_digest + i) = mask_32bit(*(message_digest + i));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename InIter>
|
||||
void output_hex(InIter first, InIter last, std::ostream& os) {
|
||||
os.setf(std::ios::hex, std::ios::basefield);
|
||||
while (first != last) {
|
||||
os.width(2);
|
||||
os.fill('0');
|
||||
os << static_cast<unsigned int>(*first);
|
||||
++first;
|
||||
}
|
||||
os.setf(std::ios::dec, std::ios::basefield);
|
||||
}
|
||||
|
||||
template <typename InIter>
|
||||
void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str) {
|
||||
std::ostringstream oss;
|
||||
output_hex(first, last, oss);
|
||||
hex_str.assign(oss.str());
|
||||
}
|
||||
|
||||
template <typename InContainer>
|
||||
void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str) {
|
||||
bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str);
|
||||
}
|
||||
|
||||
template <typename InIter>
|
||||
std::string bytes_to_hex_string(InIter first, InIter last) {
|
||||
std::string hex_str;
|
||||
bytes_to_hex_string(first, last, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
template <typename InContainer>
|
||||
std::string bytes_to_hex_string(const InContainer& bytes) {
|
||||
std::string hex_str;
|
||||
bytes_to_hex_string(bytes, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
class hash256_one_by_one {
|
||||
public:
|
||||
hash256_one_by_one() { init(); }
|
||||
|
||||
void init() {
|
||||
buffer_.clear();
|
||||
std::fill(data_length_digits_, data_length_digits_ + 4, word_t(0));
|
||||
std::copy(detail::initial_message_digest,
|
||||
detail::initial_message_digest + 8, h_);
|
||||
}
|
||||
|
||||
template <typename RaIter>
|
||||
void process(RaIter first, RaIter last) {
|
||||
add_to_data_length(static_cast<word_t>(std::distance(first, last)));
|
||||
std::copy(first, last, std::back_inserter(buffer_));
|
||||
std::size_t i = 0;
|
||||
for (; i + 64 <= buffer_.size(); i += 64) {
|
||||
detail::hash256_block(h_, buffer_.begin() + i,
|
||||
buffer_.begin() + i + 64);
|
||||
}
|
||||
buffer_.erase(buffer_.begin(), buffer_.begin() + i);
|
||||
}
|
||||
|
||||
void finish() {
|
||||
byte_t temp[64];
|
||||
std::fill(temp, temp + 64, byte_t(0));
|
||||
std::size_t remains = buffer_.size();
|
||||
std::copy(buffer_.begin(), buffer_.end(), temp);
|
||||
temp[remains] = 0x80;
|
||||
|
||||
if (remains > 55) {
|
||||
std::fill(temp + remains + 1, temp + 64, byte_t(0));
|
||||
detail::hash256_block(h_, temp, temp + 64);
|
||||
std::fill(temp, temp + 64 - 4, byte_t(0));
|
||||
} else {
|
||||
std::fill(temp + remains + 1, temp + 64 - 4, byte_t(0));
|
||||
}
|
||||
|
||||
write_data_bit_length(&(temp[56]));
|
||||
detail::hash256_block(h_, temp, temp + 64);
|
||||
}
|
||||
|
||||
template <typename OutIter>
|
||||
void get_hash_bytes(OutIter first, OutIter last) const {
|
||||
for (const word_t* iter = h_; iter != h_ + 8; ++iter) {
|
||||
for (std::size_t i = 0; i < 4 && first != last; ++i) {
|
||||
*(first++) = detail::mask_8bit(
|
||||
static_cast<byte_t>((*iter >> (24 - 8 * i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void add_to_data_length(word_t n) {
|
||||
word_t carry = 0;
|
||||
data_length_digits_[0] += n;
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
data_length_digits_[i] += carry;
|
||||
if (data_length_digits_[i] >= 65536u) {
|
||||
carry = data_length_digits_[i] >> 16;
|
||||
data_length_digits_[i] &= 65535u;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void write_data_bit_length(byte_t* begin) {
|
||||
word_t data_bit_length_digits[4];
|
||||
std::copy(data_length_digits_, data_length_digits_ + 4,
|
||||
data_bit_length_digits);
|
||||
|
||||
// convert byte length to bit length (multiply 8 or shift 3 times left)
|
||||
word_t carry = 0;
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
word_t before_val = data_bit_length_digits[i];
|
||||
data_bit_length_digits[i] <<= 3;
|
||||
data_bit_length_digits[i] |= carry;
|
||||
data_bit_length_digits[i] &= 65535u;
|
||||
carry = (before_val >> (16 - 3)) & 65535u;
|
||||
}
|
||||
|
||||
// write data_bit_length
|
||||
for (int i = 3; i >= 0; --i) {
|
||||
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i] >> 8);
|
||||
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i]);
|
||||
}
|
||||
}
|
||||
std::vector<byte_t> buffer_;
|
||||
word_t data_length_digits_[4]; // as 64bit integer (16bit x 4 integer)
|
||||
word_t h_[8];
|
||||
};
|
||||
|
||||
inline void get_hash_hex_string(const hash256_one_by_one& hasher,
|
||||
std::string& hex_str) {
|
||||
byte_t hash[k_digest_size];
|
||||
hasher.get_hash_bytes(hash, hash + k_digest_size);
|
||||
return bytes_to_hex_string(hash, hash + k_digest_size, hex_str);
|
||||
}
|
||||
|
||||
inline std::string get_hash_hex_string(const hash256_one_by_one& hasher) {
|
||||
std::string hex_str;
|
||||
get_hash_hex_string(hasher, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
template <typename RaIter, typename OutIter>
|
||||
void hash256_impl(RaIter first, RaIter last, OutIter first2, OutIter last2, int,
|
||||
std::random_access_iterator_tag) {
|
||||
hash256_one_by_one hasher;
|
||||
// hasher.init();
|
||||
hasher.process(first, last);
|
||||
hasher.finish();
|
||||
hasher.get_hash_bytes(first2, last2);
|
||||
}
|
||||
|
||||
template <typename InputIter, typename OutIter>
|
||||
void hash256_impl(InputIter first, InputIter last, OutIter first2,
|
||||
OutIter last2, int buffer_size, std::input_iterator_tag) {
|
||||
std::vector<byte_t> buffer(buffer_size);
|
||||
hash256_one_by_one hasher;
|
||||
// hasher.init();
|
||||
while (first != last) {
|
||||
int size = buffer_size;
|
||||
for (int i = 0; i != buffer_size; ++i, ++first) {
|
||||
if (first == last) {
|
||||
size = i;
|
||||
break;
|
||||
}
|
||||
buffer[i] = *first;
|
||||
}
|
||||
hasher.process(buffer.begin(), buffer.begin() + size);
|
||||
}
|
||||
hasher.finish();
|
||||
hasher.get_hash_bytes(first2, last2);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InIter, typename OutIter>
|
||||
void hash256(InIter first, InIter last, OutIter first2, OutIter last2,
|
||||
int buffer_size = PICOSHA2_BUFFER_SIZE_FOR_INPUT_ITERATOR) {
|
||||
picosha2::impl::hash256_impl(
|
||||
first, last, first2, last2, buffer_size,
|
||||
typename std::iterator_traits<InIter>::iterator_category());
|
||||
}
|
||||
|
||||
template <typename InIter, typename OutContainer>
|
||||
void hash256(InIter first, InIter last, OutContainer& dst) {
|
||||
hash256(first, last, dst.begin(), dst.end());
|
||||
}
|
||||
|
||||
template <typename InContainer, typename OutIter>
|
||||
void hash256(const InContainer& src, OutIter first, OutIter last) {
|
||||
hash256(src.begin(), src.end(), first, last);
|
||||
}
|
||||
|
||||
template <typename InContainer, typename OutContainer>
|
||||
void hash256(const InContainer& src, OutContainer& dst) {
|
||||
hash256(src.begin(), src.end(), dst.begin(), dst.end());
|
||||
}
|
||||
|
||||
template <typename InIter>
|
||||
void hash256_hex_string(InIter first, InIter last, std::string& hex_str) {
|
||||
byte_t hashed[k_digest_size];
|
||||
hash256(first, last, hashed, hashed + k_digest_size);
|
||||
std::ostringstream oss;
|
||||
output_hex(hashed, hashed + k_digest_size, oss);
|
||||
hex_str.assign(oss.str());
|
||||
}
|
||||
|
||||
template <typename InIter>
|
||||
std::string hash256_hex_string(InIter first, InIter last) {
|
||||
std::string hex_str;
|
||||
hash256_hex_string(first, last, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
inline void hash256_hex_string(const std::string& src, std::string& hex_str) {
|
||||
hash256_hex_string(src.begin(), src.end(), hex_str);
|
||||
}
|
||||
|
||||
template <typename InContainer>
|
||||
void hash256_hex_string(const InContainer& src, std::string& hex_str) {
|
||||
hash256_hex_string(src.begin(), src.end(), hex_str);
|
||||
}
|
||||
|
||||
template <typename InContainer>
|
||||
std::string hash256_hex_string(const InContainer& src) {
|
||||
return hash256_hex_string(src.begin(), src.end());
|
||||
}
|
||||
template<typename OutIter>void hash256(std::ifstream& f, OutIter first, OutIter last){
|
||||
hash256(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>(), first,last);
|
||||
|
||||
}
|
||||
}// namespace picosha2
|
||||
#endif // PICOSHA2_H
|
|
@ -15,341 +15,11 @@
|
|||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <Geode/utils/addresser.hpp>
|
||||
#include "Enums.hpp"
|
||||
|
||||
namespace geode::core::meta {}
|
||||
|
||||
template<auto F>
|
||||
struct address_of_t {
|
||||
static inline auto value = geode::base::get();
|
||||
};
|
||||
|
||||
template<auto F>
|
||||
inline auto address_of = address_of_t<F>::value;
|
||||
|
||||
//thanks pie
|
||||
enum class SearchType {
|
||||
Search = 0,
|
||||
Downloaded = 1,
|
||||
MostLiked = 2,
|
||||
Trending = 3,
|
||||
Recent = 4,
|
||||
UsersLevels = 5,
|
||||
Featured = 6,
|
||||
Magic = 7,
|
||||
Sends = 8,
|
||||
MapPack = 9,
|
||||
MapPackOnClick = 10,
|
||||
Awarded = 11,
|
||||
Followed = 12,
|
||||
Friends = 13,
|
||||
Users = 14,
|
||||
LikedGDW = 15,
|
||||
HallOfFame = 16,
|
||||
FeaturedGDW = 17,
|
||||
Similar = 18,
|
||||
MyLevels = 98,
|
||||
SavedLevels = 99,
|
||||
FavouriteLevels = 100
|
||||
};
|
||||
|
||||
// jesus fucking christ (painfully written by @hjfod)
|
||||
enum class GameObjectType {
|
||||
Solid = 0,
|
||||
Hazard = 2,
|
||||
InverseGravityPortal = 3,
|
||||
NormalGravityPortal = 4,
|
||||
ShipPortal = 5,
|
||||
CubePortal = 6,
|
||||
Decoration = 7,
|
||||
YellowJumpPad = 8,
|
||||
PinkJumpPad = 9,
|
||||
GravityPad = 10,
|
||||
YellowJumpRing = 11,
|
||||
PinkJumpRing = 12,
|
||||
GravityRing = 13,
|
||||
InverseMirrorPortal = 14,
|
||||
NormalMirrorPortal = 15,
|
||||
BallPortal = 16,
|
||||
RegularSizePortal = 17,
|
||||
MiniSizePortal = 18,
|
||||
UfoPortal = 19,
|
||||
Modifier = 20,
|
||||
SecretCoin = 22,
|
||||
DualPortal = 23,
|
||||
SoloPortal = 24,
|
||||
Slope = 25,
|
||||
WavePortal = 26,
|
||||
RobotPortal = 27,
|
||||
TeleportPortal = 28,
|
||||
GreenRing = 29,
|
||||
Collectible = 30,
|
||||
UserCoin = 31,
|
||||
DropRing = 32,
|
||||
SpiderPortal = 33,
|
||||
RedJumpPad = 34,
|
||||
RedJumpRing = 35,
|
||||
CustomRing = 36,
|
||||
DashRing = 37,
|
||||
GravityDashRing = 38,
|
||||
CollisionObject = 39,
|
||||
Special = 40,
|
||||
};
|
||||
|
||||
enum class PulseEffectType {};
|
||||
enum class TouchTriggerType {};
|
||||
enum class PlayerButton {};
|
||||
enum class GhostType {};
|
||||
enum class TableViewCellEditingStyle {};
|
||||
enum class UserListType {};
|
||||
enum class GJErrorCode {};
|
||||
enum class AccountError {};
|
||||
enum class GJSongError {};
|
||||
enum class LikeItemType {
|
||||
Unknown = 0,
|
||||
Level = 1,
|
||||
Comment = 2,
|
||||
AccountComment = 3
|
||||
};
|
||||
|
||||
enum class GJStoreItem {};
|
||||
enum class CommentError {};
|
||||
enum class BackupAccountError {};
|
||||
|
||||
enum class BoomListType {
|
||||
Default = 0x0,
|
||||
User = 0x2,
|
||||
Stats = 0x3,
|
||||
Achievement = 0x4,
|
||||
Level = 0x5,
|
||||
Level2 = 0x6,
|
||||
Comment = 0x7,
|
||||
Comment2 = 0x8,
|
||||
Song = 0xb,
|
||||
Score = 0xc,
|
||||
MapPack = 0xd,
|
||||
CustomSong = 0xe,
|
||||
Comment3 = 0xf,
|
||||
User2 = 0x10,
|
||||
Request = 0x11,
|
||||
Message = 0x12,
|
||||
LevelScore = 0x13,
|
||||
Artist = 0x14,
|
||||
};
|
||||
|
||||
enum class MenuAnimationType {
|
||||
Scale = 0,
|
||||
Move = 1,
|
||||
};
|
||||
|
||||
enum class ShopType {
|
||||
Normal,
|
||||
Secret,
|
||||
Community
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class ZLayer {
|
||||
B4 = -3,
|
||||
B3 = -1,
|
||||
B2 = 1,
|
||||
B1 = 3,
|
||||
Default = 0,
|
||||
T1 = 5,
|
||||
T2 = 7,
|
||||
T3 = 9,
|
||||
};
|
||||
|
||||
enum class UpdateResponse {
|
||||
Unknown,
|
||||
UpToDate,
|
||||
GameVerOutOfDate,
|
||||
UpdateSuccess,
|
||||
};
|
||||
|
||||
enum class UnlockType {
|
||||
Cube = 0x1,
|
||||
Col1 = 0x2,
|
||||
Col2 = 0x3,
|
||||
Ship = 0x4,
|
||||
Ball = 0x5,
|
||||
Bird = 0x6,
|
||||
Dart = 0x7,
|
||||
Robot = 0x8,
|
||||
Spider = 0x9,
|
||||
Streak = 0xA,
|
||||
Death = 0xB,
|
||||
GJItem = 0xC,
|
||||
};
|
||||
|
||||
enum class SpecialRewardItem {
|
||||
FireShard = 0x1,
|
||||
IceShard = 0x2,
|
||||
PoisonShard = 0x3,
|
||||
ShadowShard = 0x4,
|
||||
LavaShard = 0x5,
|
||||
BonusKey = 0x6,
|
||||
Orbs = 0x7,
|
||||
Diamonds = 0x8,
|
||||
CustomItem = 0x9,
|
||||
};
|
||||
|
||||
enum class EditCommand {
|
||||
SmallLeft = 1,
|
||||
SmallRight = 2,
|
||||
SmallUp = 3,
|
||||
SmallDown = 4,
|
||||
|
||||
Left = 5,
|
||||
Right = 6,
|
||||
Up = 7,
|
||||
Down = 8,
|
||||
|
||||
BigLeft = 9,
|
||||
BigRight = 10,
|
||||
BigUp = 11,
|
||||
BigDown = 12,
|
||||
|
||||
TinyLeft = 13,
|
||||
TinyRight = 14,
|
||||
TinyUp = 15,
|
||||
TinyDown = 16,
|
||||
|
||||
FlipX = 17,
|
||||
FlipY = 18,
|
||||
RotateCW = 19,
|
||||
RotateCCW = 20,
|
||||
RotateCW45 = 21,
|
||||
RotateCCW45 = 22,
|
||||
RotateFree = 23,
|
||||
RotateSnap = 24,
|
||||
|
||||
Scale = 25,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class PlaybackMode {
|
||||
Not = 0,
|
||||
Playing = 1,
|
||||
Paused = 2,
|
||||
};
|
||||
|
||||
enum class SelectArtType {
|
||||
Background = 0,
|
||||
Ground = 1,
|
||||
};
|
||||
|
||||
enum class UndoCommand {
|
||||
Delete = 1,
|
||||
New = 2,
|
||||
Paste = 3,
|
||||
DeleteMulti = 4,
|
||||
Transform = 5,
|
||||
Select = 6,
|
||||
};
|
||||
|
||||
enum class EasingType {
|
||||
None = 0,
|
||||
EaseInOut = 1,
|
||||
EaseIn = 2,
|
||||
EaseOut = 3,
|
||||
ElasticInOut = 4,
|
||||
ElasticIn = 5,
|
||||
ElasticOut = 6,
|
||||
BounceInOut = 7,
|
||||
BounceIn = 8,
|
||||
BounceOut = 9,
|
||||
ExponentialInOut = 10,
|
||||
ExponentialIn = 11,
|
||||
ExponentialOut = 12,
|
||||
SineInOut = 13,
|
||||
SineIn = 14,
|
||||
SineOut = 15,
|
||||
BackInOut = 16,
|
||||
BackIn = 17,
|
||||
BackOut = 18,
|
||||
};
|
||||
|
||||
enum class GJDifficulty {
|
||||
Auto = 0,
|
||||
Easy = 1,
|
||||
Normal = 2,
|
||||
Hard = 3,
|
||||
Harder = 4,
|
||||
Insane = 5,
|
||||
Demon = 6,
|
||||
DemonEasy = 7,
|
||||
DemonMedium = 8,
|
||||
DemonInsane = 9,
|
||||
DemonExtreme = 10
|
||||
};
|
||||
|
||||
enum class GJLevelType {
|
||||
Local = 1,
|
||||
Editor = 2,
|
||||
Saved = 3
|
||||
};
|
||||
|
||||
enum class IconType {
|
||||
Cube = 0,
|
||||
Ship = 1,
|
||||
Ball = 2,
|
||||
Ufo = 3,
|
||||
Wave = 4,
|
||||
Robot = 5,
|
||||
Spider = 6,
|
||||
DeathEffect = 98,
|
||||
Special = 99,
|
||||
};
|
||||
|
||||
enum class GJChallengeType {
|
||||
Unknown = 0,
|
||||
Orbs = 1,
|
||||
UserCoins = 2,
|
||||
Stars = 3
|
||||
};
|
||||
|
||||
enum class GJScoreType {
|
||||
Unknown = 0,
|
||||
Creator = 1
|
||||
};
|
||||
|
||||
enum class LevelLeaderboardType {
|
||||
Friends = 0,
|
||||
Global = 1,
|
||||
Weekly = 2
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class ComparisonType {
|
||||
Equals = 0,
|
||||
Larger = 1,
|
||||
Smaller = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class MoveTargetType {
|
||||
Both = 0,
|
||||
XOnly = 1,
|
||||
YOnly = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class TouchToggleMode {
|
||||
Normal = 0,
|
||||
ToggleOn = 1,
|
||||
ToggleOff = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class LeaderboardState {
|
||||
Default = 0,
|
||||
Top100 = 1,
|
||||
Global = 2,
|
||||
Creator = 3,
|
||||
Friends = 4,
|
||||
};
|
||||
|
||||
#define CLASSPARAM(...)
|
||||
#define STRUCTPARAM(...)
|
||||
|
||||
#include <codegenned/GeneratedHeader.hpp>
|
||||
#include <Geode/GeneratedBinding.hpp>
|
||||
|
|
|
@ -103,82 +103,82 @@ _61,_62,_63,N,...) N
|
|||
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
|
||||
|
||||
#define GEODE_NEST1(macro, begin) \
|
||||
macro(GEODE_CONCAT(begin, 0)), \
|
||||
macro(GEODE_CONCAT(begin, 1)), \
|
||||
macro(GEODE_CONCAT(begin, 2)), \
|
||||
macro(GEODE_CONCAT(begin, 3)), \
|
||||
macro(GEODE_CONCAT(begin, 4)), \
|
||||
macro(GEODE_CONCAT(begin, 5)), \
|
||||
macro(GEODE_CONCAT(begin, 6)), \
|
||||
macro(GEODE_CONCAT(begin, 7)), \
|
||||
macro(GEODE_CONCAT(begin, 8)), \
|
||||
macro(GEODE_CONCAT(begin, 9)), \
|
||||
macro(GEODE_CONCAT(begin, a)), \
|
||||
macro(GEODE_CONCAT(begin, b)), \
|
||||
macro(GEODE_CONCAT(begin, c)), \
|
||||
macro(GEODE_CONCAT(begin, d)), \
|
||||
macro(GEODE_CONCAT(begin, e)), \
|
||||
macro(GEODE_CONCAT(begin, f))
|
||||
// #define GEODE_NEST1(macro, begin) \
|
||||
// macro(GEODE_CONCAT(begin, 0)), \
|
||||
// macro(GEODE_CONCAT(begin, 1)), \
|
||||
// macro(GEODE_CONCAT(begin, 2)), \
|
||||
// macro(GEODE_CONCAT(begin, 3)), \
|
||||
// macro(GEODE_CONCAT(begin, 4)), \
|
||||
// macro(GEODE_CONCAT(begin, 5)), \
|
||||
// macro(GEODE_CONCAT(begin, 6)), \
|
||||
// macro(GEODE_CONCAT(begin, 7)), \
|
||||
// macro(GEODE_CONCAT(begin, 8)), \
|
||||
// macro(GEODE_CONCAT(begin, 9)), \
|
||||
// macro(GEODE_CONCAT(begin, a)), \
|
||||
// macro(GEODE_CONCAT(begin, b)), \
|
||||
// macro(GEODE_CONCAT(begin, c)), \
|
||||
// macro(GEODE_CONCAT(begin, d)), \
|
||||
// macro(GEODE_CONCAT(begin, e)), \
|
||||
// macro(GEODE_CONCAT(begin, f))
|
||||
|
||||
#define GEODE_NEST2(macro, begin) \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 0)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 1)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 2)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 3)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 4)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 5)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 6)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 7)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 8)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, 9)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, a)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, b)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, c)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, d)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, e)), \
|
||||
GEODE_NEST1(macro, GEODE_CONCAT(begin, f))
|
||||
// #define GEODE_NEST2(macro, begin) \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 0)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 1)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 2)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 3)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 4)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 5)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 6)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 7)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 8)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, 9)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, a)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, b)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, c)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, d)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, e)), \
|
||||
// GEODE_NEST1(macro, GEODE_CONCAT(begin, f))
|
||||
|
||||
#define GEODE_NEST3(macro, begin) \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 0)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 1)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 2)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 3)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 4)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 5)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 6)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 7)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 8)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, 9)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, a)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, b)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, c)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, d)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, e)), \
|
||||
GEODE_NEST2(macro, GEODE_CONCAT(begin, f))
|
||||
// #define GEODE_NEST3(macro, begin) \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 0)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 1)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 2)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 3)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 4)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 5)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 6)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 7)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 8)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, 9)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, a)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, b)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, c)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, d)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, e)), \
|
||||
// GEODE_NEST2(macro, GEODE_CONCAT(begin, f))
|
||||
|
||||
#define GEODE_NEST4(macro, begin) \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 0)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 1)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 2)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 3)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 4)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 5)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 6)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 7)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 8)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, 9)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, a)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, b)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, c)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, d)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, e)), \
|
||||
GEODE_NEST3(macro, GEODE_CONCAT(begin, f))
|
||||
// #define GEODE_NEST4(macro, begin) \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 0)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 1)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 2)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 3)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 4)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 5)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 6)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 7)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 8)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, 9)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, a)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, b)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, c)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, d)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, e)), \
|
||||
// GEODE_NEST3(macro, GEODE_CONCAT(begin, f))
|
||||
|
||||
|
||||
#define GEODE_ENUM_OFFSETS_DEFINE(hex) GEODE_CONCAT($, hex)
|
||||
#define GEODE_ENUM_OFFSETS_SET() GEODE_NEST4(GEODE_ENUM_OFFSETS_DEFINE, 0x)
|
||||
// #define GEODE_ENUM_OFFSETS_DEFINE(hex) GEODE_CONCAT($, hex)
|
||||
// #define GEODE_ENUM_OFFSETS_SET() GEODE_NEST4(GEODE_ENUM_OFFSETS_DEFINE, 0x)
|
||||
|
||||
enum class PrinterOffsets {
|
||||
GEODE_ENUM_OFFSETS_SET()
|
||||
};
|
||||
// enum class PrinterOffsets {
|
||||
// GEODE_ENUM_OFFSETS_SET()
|
||||
// };
|
||||
|
|
324
loader/include/Geode/Enums.hpp
Normal file
324
loader/include/Geode/Enums.hpp
Normal file
|
@ -0,0 +1,324 @@
|
|||
#pragma once
|
||||
|
||||
//thanks pie
|
||||
enum class SearchType {
|
||||
Search = 0,
|
||||
Downloaded = 1,
|
||||
MostLiked = 2,
|
||||
Trending = 3,
|
||||
Recent = 4,
|
||||
UsersLevels = 5,
|
||||
Featured = 6,
|
||||
Magic = 7,
|
||||
Sends = 8,
|
||||
MapPack = 9,
|
||||
MapPackOnClick = 10,
|
||||
Awarded = 11,
|
||||
Followed = 12,
|
||||
Friends = 13,
|
||||
Users = 14,
|
||||
LikedGDW = 15,
|
||||
HallOfFame = 16,
|
||||
FeaturedGDW = 17,
|
||||
Similar = 18,
|
||||
MyLevels = 98,
|
||||
SavedLevels = 99,
|
||||
FavouriteLevels = 100
|
||||
};
|
||||
|
||||
// jesus fucking christ (painfully written by @hjfod)
|
||||
enum class GameObjectType {
|
||||
Solid = 0,
|
||||
Hazard = 2,
|
||||
InverseGravityPortal = 3,
|
||||
NormalGravityPortal = 4,
|
||||
ShipPortal = 5,
|
||||
CubePortal = 6,
|
||||
Decoration = 7,
|
||||
YellowJumpPad = 8,
|
||||
PinkJumpPad = 9,
|
||||
GravityPad = 10,
|
||||
YellowJumpRing = 11,
|
||||
PinkJumpRing = 12,
|
||||
GravityRing = 13,
|
||||
InverseMirrorPortal = 14,
|
||||
NormalMirrorPortal = 15,
|
||||
BallPortal = 16,
|
||||
RegularSizePortal = 17,
|
||||
MiniSizePortal = 18,
|
||||
UfoPortal = 19,
|
||||
Modifier = 20,
|
||||
SecretCoin = 22,
|
||||
DualPortal = 23,
|
||||
SoloPortal = 24,
|
||||
Slope = 25,
|
||||
WavePortal = 26,
|
||||
RobotPortal = 27,
|
||||
TeleportPortal = 28,
|
||||
GreenRing = 29,
|
||||
Collectible = 30,
|
||||
UserCoin = 31,
|
||||
DropRing = 32,
|
||||
SpiderPortal = 33,
|
||||
RedJumpPad = 34,
|
||||
RedJumpRing = 35,
|
||||
CustomRing = 36,
|
||||
DashRing = 37,
|
||||
GravityDashRing = 38,
|
||||
CollisionObject = 39,
|
||||
Special = 40,
|
||||
};
|
||||
|
||||
enum class PulseEffectType {};
|
||||
enum class TouchTriggerType {};
|
||||
enum class PlayerButton {};
|
||||
enum class GhostType {};
|
||||
enum class TableViewCellEditingStyle {};
|
||||
enum class UserListType {};
|
||||
enum class GJErrorCode {};
|
||||
enum class AccountError {};
|
||||
enum class GJSongError {};
|
||||
enum class LikeItemType {
|
||||
Unknown = 0,
|
||||
Level = 1,
|
||||
Comment = 2,
|
||||
AccountComment = 3
|
||||
};
|
||||
|
||||
enum class GJStoreItem {};
|
||||
enum class CommentError {};
|
||||
enum class BackupAccountError {};
|
||||
|
||||
enum class BoomListType {
|
||||
Default = 0x0,
|
||||
User = 0x2,
|
||||
Stats = 0x3,
|
||||
Achievement = 0x4,
|
||||
Level = 0x5,
|
||||
Level2 = 0x6,
|
||||
Comment = 0x7,
|
||||
Comment2 = 0x8,
|
||||
Song = 0xb,
|
||||
Score = 0xc,
|
||||
MapPack = 0xd,
|
||||
CustomSong = 0xe,
|
||||
Comment3 = 0xf,
|
||||
User2 = 0x10,
|
||||
Request = 0x11,
|
||||
Message = 0x12,
|
||||
LevelScore = 0x13,
|
||||
Artist = 0x14,
|
||||
};
|
||||
|
||||
enum class MenuAnimationType {
|
||||
Scale = 0,
|
||||
Move = 1,
|
||||
};
|
||||
|
||||
enum class ShopType {
|
||||
Normal,
|
||||
Secret,
|
||||
Community
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class ZLayer {
|
||||
B4 = -3,
|
||||
B3 = -1,
|
||||
B2 = 1,
|
||||
B1 = 3,
|
||||
Default = 0,
|
||||
T1 = 5,
|
||||
T2 = 7,
|
||||
T3 = 9,
|
||||
};
|
||||
|
||||
enum class UpdateResponse {
|
||||
Unknown,
|
||||
UpToDate,
|
||||
GameVerOutOfDate,
|
||||
UpdateSuccess,
|
||||
};
|
||||
|
||||
enum class UnlockType {
|
||||
Cube = 0x1,
|
||||
Col1 = 0x2,
|
||||
Col2 = 0x3,
|
||||
Ship = 0x4,
|
||||
Ball = 0x5,
|
||||
Bird = 0x6,
|
||||
Dart = 0x7,
|
||||
Robot = 0x8,
|
||||
Spider = 0x9,
|
||||
Streak = 0xA,
|
||||
Death = 0xB,
|
||||
GJItem = 0xC,
|
||||
};
|
||||
|
||||
enum class SpecialRewardItem {
|
||||
FireShard = 0x1,
|
||||
IceShard = 0x2,
|
||||
PoisonShard = 0x3,
|
||||
ShadowShard = 0x4,
|
||||
LavaShard = 0x5,
|
||||
BonusKey = 0x6,
|
||||
Orbs = 0x7,
|
||||
Diamonds = 0x8,
|
||||
CustomItem = 0x9,
|
||||
};
|
||||
|
||||
enum class EditCommand {
|
||||
SmallLeft = 1,
|
||||
SmallRight = 2,
|
||||
SmallUp = 3,
|
||||
SmallDown = 4,
|
||||
|
||||
Left = 5,
|
||||
Right = 6,
|
||||
Up = 7,
|
||||
Down = 8,
|
||||
|
||||
BigLeft = 9,
|
||||
BigRight = 10,
|
||||
BigUp = 11,
|
||||
BigDown = 12,
|
||||
|
||||
TinyLeft = 13,
|
||||
TinyRight = 14,
|
||||
TinyUp = 15,
|
||||
TinyDown = 16,
|
||||
|
||||
FlipX = 17,
|
||||
FlipY = 18,
|
||||
RotateCW = 19,
|
||||
RotateCCW = 20,
|
||||
RotateCW45 = 21,
|
||||
RotateCCW45 = 22,
|
||||
RotateFree = 23,
|
||||
RotateSnap = 24,
|
||||
|
||||
Scale = 25,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class PlaybackMode {
|
||||
Not = 0,
|
||||
Playing = 1,
|
||||
Paused = 2,
|
||||
};
|
||||
|
||||
enum class SelectArtType {
|
||||
Background = 0,
|
||||
Ground = 1,
|
||||
};
|
||||
|
||||
enum class UndoCommand {
|
||||
Delete = 1,
|
||||
New = 2,
|
||||
Paste = 3,
|
||||
DeleteMulti = 4,
|
||||
Transform = 5,
|
||||
Select = 6,
|
||||
};
|
||||
|
||||
enum class EasingType {
|
||||
None = 0,
|
||||
EaseInOut = 1,
|
||||
EaseIn = 2,
|
||||
EaseOut = 3,
|
||||
ElasticInOut = 4,
|
||||
ElasticIn = 5,
|
||||
ElasticOut = 6,
|
||||
BounceInOut = 7,
|
||||
BounceIn = 8,
|
||||
BounceOut = 9,
|
||||
ExponentialInOut = 10,
|
||||
ExponentialIn = 11,
|
||||
ExponentialOut = 12,
|
||||
SineInOut = 13,
|
||||
SineIn = 14,
|
||||
SineOut = 15,
|
||||
BackInOut = 16,
|
||||
BackIn = 17,
|
||||
BackOut = 18,
|
||||
};
|
||||
|
||||
enum class GJDifficulty {
|
||||
Auto = 0,
|
||||
Easy = 1,
|
||||
Normal = 2,
|
||||
Hard = 3,
|
||||
Harder = 4,
|
||||
Insane = 5,
|
||||
Demon = 6,
|
||||
DemonEasy = 7,
|
||||
DemonMedium = 8,
|
||||
DemonInsane = 9,
|
||||
DemonExtreme = 10
|
||||
};
|
||||
|
||||
enum class GJLevelType {
|
||||
Local = 1,
|
||||
Editor = 2,
|
||||
Saved = 3
|
||||
};
|
||||
|
||||
enum class IconType {
|
||||
Cube = 0,
|
||||
Ship = 1,
|
||||
Ball = 2,
|
||||
Ufo = 3,
|
||||
Wave = 4,
|
||||
Robot = 5,
|
||||
Spider = 6,
|
||||
DeathEffect = 98,
|
||||
Special = 99,
|
||||
};
|
||||
|
||||
enum class GJChallengeType {
|
||||
Unknown = 0,
|
||||
Orbs = 1,
|
||||
UserCoins = 2,
|
||||
Stars = 3
|
||||
};
|
||||
|
||||
enum class GJScoreType {
|
||||
Unknown = 0,
|
||||
Creator = 1
|
||||
};
|
||||
|
||||
enum class LevelLeaderboardType {
|
||||
Friends = 0,
|
||||
Global = 1,
|
||||
Weekly = 2
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class ComparisonType {
|
||||
Equals = 0,
|
||||
Larger = 1,
|
||||
Smaller = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class MoveTargetType {
|
||||
Both = 0,
|
||||
XOnly = 1,
|
||||
YOnly = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class TouchToggleMode {
|
||||
Normal = 0,
|
||||
ToggleOn = 1,
|
||||
ToggleOff = 2,
|
||||
};
|
||||
|
||||
// Geode Addition
|
||||
enum class LeaderboardState {
|
||||
Default = 0,
|
||||
Top100 = 1,
|
||||
Global = 2,
|
||||
Creator = 3,
|
||||
Friends = 4,
|
||||
};
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#include "modify/Traits.hpp"
|
||||
#include "modify/Modify.hpp"
|
||||
#include <Geode/GeneratedModify.hpp>
|
||||
#include "modify/Field.hpp"
|
||||
#include "modify/InternalMacros.hpp"
|
||||
|
||||
|
|
|
@ -529,7 +529,7 @@ protected:
|
|||
CCDirectorDelegate *m_pProjectionDelegate;
|
||||
|
||||
RT_ADD(
|
||||
CC_PROPERTY(CCSceneDelegate*, m_pAppDelegate, SceneDelegate);
|
||||
CC_SYNTHESIZE(CCSceneDelegate*, m_pAppDelegate, SceneDelegate);
|
||||
CCSize m_obScaleFactor;
|
||||
CCSize m_obResolutionInPixels;
|
||||
TextureQuality m_eTextureQuality;
|
||||
|
|
|
@ -107,7 +107,6 @@ It's new in cocos2d-x since v0.99.5
|
|||
class GeodeNodeMetadata;
|
||||
namespace geode {
|
||||
struct modify;
|
||||
struct temp_name_find_better;
|
||||
namespace modifier {
|
||||
struct addresses;
|
||||
struct types;
|
||||
|
@ -115,10 +114,9 @@ namespace geode {
|
|||
}
|
||||
}
|
||||
#define GEODE_FRIEND_MODIFY GEODE_ADD(\
|
||||
friend struct geode::modify;\
|
||||
friend struct geode::modifier::addresses;\
|
||||
friend struct geode::modifier::types;\
|
||||
friend struct geode::temp_name_find_better;\
|
||||
friend struct ::geode::modify;\
|
||||
friend struct ::geode::modifier::addresses;\
|
||||
friend struct ::geode::modifier::types;\
|
||||
friend class ::GeodeNodeMetadata;\
|
||||
)
|
||||
#define GEODE_ADD(...) __VA_ARGS__
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
virtual void setupGLView();
|
||||
virtual void platformShutdown();
|
||||
void toggleVerticalSync(bool);
|
||||
bool getVerticalSyncEnabled() const;
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Event.hpp>
|
||||
#include "Event.hpp"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <Geode/utils/types.hpp>
|
||||
#include <string_view>
|
||||
|
||||
#include "../hook-core/Hook.hpp"
|
||||
#include <Geode/hook-core/Hook.hpp>
|
||||
|
||||
namespace geode {
|
||||
class Mod;
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <fs/filesystem.hpp>
|
||||
#include <Geode/utils/json.hpp>
|
||||
#include "Log.hpp"
|
||||
#include <Geode/utils/VersionInfo.hpp>
|
||||
|
||||
namespace geode {
|
||||
#pragma warning(disable: 4251)
|
||||
|
@ -27,6 +25,7 @@ namespace geode {
|
|||
class Mod;
|
||||
class Hook;
|
||||
struct ModInfo;
|
||||
class VersionInfo;
|
||||
|
||||
namespace modifier {
|
||||
template<class, class, class>
|
||||
|
@ -183,6 +182,7 @@ namespace geode {
|
|||
* Mod::m_addResourcesToSearchPath to true
|
||||
* first
|
||||
*/
|
||||
void updateModResourcePaths(Mod*);
|
||||
void updateResourcePaths();
|
||||
void updateModResources(Mod* mod);
|
||||
void updateResources();
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
#include "Types.hpp"
|
||||
#include "Hook.hpp"
|
||||
#include "../utils/types.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/VersionInfo.hpp"
|
||||
#include <Geode/utils/types.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/utils/VersionInfo.hpp>
|
||||
#include <Geode/utils/json.hpp>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
#include <cocos2d.h>
|
||||
#include "Setting.hpp"
|
||||
#include <optional>
|
||||
|
||||
|
@ -176,6 +175,14 @@ namespace geode {
|
|||
* format
|
||||
*/
|
||||
static Result<ModInfo> createFromSchemaV010(ModJson const& json);
|
||||
|
||||
Result<> addSpecialFiles(ghc::filesystem::path const& dir);
|
||||
Result<> addSpecialFiles(cocos2d::ZipFile& zip);
|
||||
|
||||
std::vector<std::pair<
|
||||
std::string,
|
||||
std::optional<std::string>*
|
||||
>> getSpecialFiles();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include "../utils/container.hpp"
|
||||
#include "../utils/json.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/JsonValidation.hpp"
|
||||
#include "../utils/convert.hpp"
|
||||
#include "../utils/platform.hpp"
|
||||
#include <Geode/utils/container.hpp>
|
||||
#include <Geode/utils/json.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <Geode/utils/convert.hpp>
|
||||
#include <Geode/utils/platform.hpp>
|
||||
#include <regex>
|
||||
|
||||
#pragma warning(push)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Setting.hpp"
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
class SettingNode;
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
namespace geode::modifier {
|
||||
struct addresses {
|
||||
#include <codegenned/GeneratedAddress.hpp>
|
||||
#include <Geode/GeneratedAddress.hpp>
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,6 +26,6 @@ namespace geode::modifier {
|
|||
>> {
|
||||
constexpr static inline bool value = true;
|
||||
};
|
||||
#include <codegenned/GeneratedCompare.hpp>
|
||||
#include <Geode/GeneratedCompare.hpp>
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include "Traits.hpp"
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace cocos2d {
|
||||
class CCNode;
|
||||
}
|
||||
|
||||
namespace geode::modifier {
|
||||
class FieldContainer {
|
||||
private:
|
||||
|
|
|
@ -43,6 +43,4 @@ namespace geode::modifier {
|
|||
static_assert(core::meta::always_false<Derived>, "Custom Modify not implemented.");
|
||||
}
|
||||
};
|
||||
|
||||
#include <codegenned/GeneratedModify.hpp>
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace geode::modifier {
|
|||
using type = FunctionType*;
|
||||
};
|
||||
|
||||
using geode::core::meta::always_false;
|
||||
using ::geode::core::meta::always_false;
|
||||
/**
|
||||
* The ~unevaluated~ function that gets the appropriate
|
||||
* version of a function type from its return, parameters, and classes.
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
namespace geode::modifier {
|
||||
struct types {
|
||||
#include <codegenned/GeneratedType.hpp>
|
||||
#include <Geode/GeneratedType.hpp>
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace geode::modifier {
|
|||
struct wrap {
|
||||
GEODE_WRAPPER_FOR_IDENTIFIER(constructor)
|
||||
GEODE_WRAPPER_FOR_IDENTIFIER(destructor)
|
||||
#include <codegenned/GeneratedWrapper.hpp>
|
||||
#include <Geode/GeneratedWrapper.hpp>
|
||||
};
|
||||
|
||||
// template <template<class, class, class=void> class Identifier, class Base, class Derived, class ...Types>
|
||||
|
|
|
@ -71,7 +71,7 @@ namespace std {
|
|||
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::Windows
|
||||
#define GEODE_HIDDEN
|
||||
#define GEODE_DUPABLE __forceinline
|
||||
#define GEODE_INLINE __forceinline
|
||||
#define GEODE_VIRTUAL_CONSTEXPR
|
||||
#define GEODE_NOINLINE __declspec(noinline)
|
||||
|
||||
|
@ -90,7 +90,7 @@ namespace std {
|
|||
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::MacOS
|
||||
#define GEODE_HIDDEN __attribute__((visibility("hidden")))
|
||||
#define GEODE_DUPABLE __attribute__((always_inline))
|
||||
#define GEODE_INLINE inline __attribute__((always_inline))
|
||||
#define GEODE_VIRTUAL_CONSTEXPR constexpr
|
||||
#define GEODE_NOINLINE __attribute__((noinline))
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace std {
|
|||
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::iOS
|
||||
#define GEODE_HIDDEN __attribute__((visibility("hidden")))
|
||||
#define GEODE_DUPABLE __attribute__((always_inline))
|
||||
#define GEODE_INLINE inline __attribute__((always_inline))
|
||||
#define GEODE_VIRTUAL_CONSTEXPR constexpr
|
||||
#define GEODE_NOINLINE __attribute__((noinline))
|
||||
|
||||
|
@ -128,7 +128,7 @@ namespace std {
|
|||
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::Android
|
||||
#define GEODE_HIDDEN __attribute__((visibility("hidden")))
|
||||
#define GEODE_DUPABLE __attribute__((always_inline))
|
||||
#define GEODE_INLINE inline __attribute__((always_inline))
|
||||
#define GEODE_VIRTUAL_CONSTEXPR constexpr
|
||||
#define GEODE_NOINLINE __attribute__((noinline))
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "BasedButtonSprite.hpp"
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
|
||||
#pragma warning(disable : 4275)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
enum class CircleBaseSize {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "Popup.hpp"
|
||||
#include "InputNode.hpp"
|
||||
|
||||
#include <Geode/binding/TextInputDelegate.hpp>
|
||||
|
||||
namespace geode {
|
||||
class ColorPickPopupDelegate {
|
||||
public:
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace cocos2d::extension {
|
||||
class CCScale9Sprite;
|
||||
}
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL IconButtonSprite :
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL InputNode : public cocos2d::CCMenuItem {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <Geode/binding/TableViewCell.hpp>
|
||||
#include <Geode/binding/CustomListView.hpp>
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL GenericListCell : public TableViewCell {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <Geode/binding/FLAlertLayerProtocol.hpp>
|
||||
#include "TextRenderer.hpp"
|
||||
#include "ScrollLayer.hpp"
|
||||
|
||||
struct MDParser;
|
||||
class CCScrollLayerExt;
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
#include "SceneManager.hpp"
|
||||
#include <chrono>
|
||||
#include "../utils/Ref.hpp"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
|
||||
namespace geode {
|
||||
template<typename... InitArgs>
|
||||
|
@ -101,6 +102,16 @@ namespace geode {
|
|||
std::function<void(FLAlertLayer*, bool)> selected,
|
||||
bool doShow = true
|
||||
);
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
const char* title,
|
||||
std::string const& content,
|
||||
const char* btn1,
|
||||
const char* btn2,
|
||||
float width,
|
||||
std::function<void(FLAlertLayer*, bool)> selected,
|
||||
bool doShow = true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
namespace cocos2d {
|
||||
class CCArray;
|
||||
class CCNode;
|
||||
}
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL SceneManager {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <Geode/binding/CCContentLayer.hpp>
|
||||
#include <Geode/binding/CCScrollLayerExt.hpp>
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL Scrollbar : public cocos2d::CCLayer {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
|
||||
namespace geode {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
enum class TextAlignment {
|
||||
|
|
|
@ -157,6 +157,11 @@ namespace geode {
|
|||
return *this;
|
||||
}
|
||||
|
||||
JsonMaybeValue<Json> array() {
|
||||
this->as<value_t::array>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<nlohmann::detail::value_t... T>
|
||||
JsonMaybeValue<Json> asOneOf() {
|
||||
if (this->isError()) return *this;
|
||||
|
@ -273,6 +278,24 @@ namespace geode {
|
|||
}
|
||||
};
|
||||
|
||||
JsonMaybeValue<Json> at(size_t i) {
|
||||
this->as<value_t::array>();
|
||||
if (this->isError()) return *this;
|
||||
if (self().m_json.size() <= i) {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": has " +
|
||||
std::to_string(self().m_json.size()) + "items "
|
||||
", expected to have at least " + std::to_string(i + 1)
|
||||
);
|
||||
return *this;
|
||||
}
|
||||
return JsonMaybeValue<Json>(
|
||||
self().m_checker, self().m_json.at(i),
|
||||
self().m_hierarchy + "." + std::to_string(i),
|
||||
self().m_hasValue
|
||||
);
|
||||
}
|
||||
|
||||
Iterator<JsonMaybeValue<Json>> iterate() {
|
||||
this->as<value_t::array>();
|
||||
Iterator<JsonMaybeValue<Json>> iter;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <cocos2d.h>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include "Ref.hpp"
|
||||
|
||||
namespace geode::cocos {
|
||||
/**
|
||||
|
@ -247,14 +248,20 @@ namespace geode::cocos {
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
struct std::iterator_traits<CCArrayIterator<T>> {
|
||||
struct iterator_traits<geode::cocos::CCArrayIterator<T>> {
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using iterator_category = std::random_access_iterator_tag; // its random access but im too lazy to implement it
|
||||
};
|
||||
}
|
||||
|
||||
namespace geode::cocos {
|
||||
|
||||
struct GEODE_DLL CCArrayInserter {
|
||||
public:
|
||||
|
@ -278,25 +285,16 @@ namespace geode::cocos {
|
|||
template <typename _Type>
|
||||
class CCArrayExt {
|
||||
protected:
|
||||
cocos2d::CCArray* m_arr;
|
||||
Ref<cocos2d::CCArray> m_arr;
|
||||
using T = std::remove_pointer_t<_Type>;
|
||||
public:
|
||||
CCArrayExt() : m_arr(cocos2d::CCArray::create()) {
|
||||
m_arr->retain();
|
||||
}
|
||||
CCArrayExt(cocos2d::CCArray* arr) : m_arr(arr) {
|
||||
m_arr->retain();
|
||||
}
|
||||
CCArrayExt(CCArrayExt const& a) : m_arr(a.m_arr) {
|
||||
m_arr->retain();
|
||||
}
|
||||
CCArrayExt() : m_arr(cocos2d::CCArray::create()) {}
|
||||
CCArrayExt(cocos2d::CCArray* arr) : m_arr(arr) {}
|
||||
CCArrayExt(CCArrayExt const& a) : m_arr(a.m_arr) {}
|
||||
CCArrayExt(CCArrayExt&& a) : m_arr(a.m_arr) {
|
||||
a.m_arr = nullptr;
|
||||
}
|
||||
~CCArrayExt() {
|
||||
if (m_arr)
|
||||
m_arr->release();
|
||||
}
|
||||
~CCArrayExt() {}
|
||||
|
||||
auto begin() {
|
||||
return CCArrayIterator<T*>(reinterpret_cast<T**>(m_arr->data->arr));
|
||||
|
|
|
@ -4,10 +4,18 @@
|
|||
#include <fs/filesystem.hpp>
|
||||
#include "Result.hpp"
|
||||
#include "json.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace geode::utils::web {
|
||||
using FileProgressCallback = std::function<bool(double, double)>;
|
||||
|
||||
/**
|
||||
* Synchronously fetch data from the internet
|
||||
* @param url URL to fetch
|
||||
* @returns Returned data as bytes, or error on error
|
||||
*/
|
||||
GEODE_DLL Result<byte_array> fetchBytes(std::string const& url);
|
||||
|
||||
/**
|
||||
* Synchronously fetch data from the internet
|
||||
* @param url URL to fetch
|
||||
|
@ -46,5 +54,283 @@ namespace geode::utils::web {
|
|||
return Err(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
class SentAsyncWebRequest;
|
||||
template<class T>
|
||||
class AsyncWebResult;
|
||||
class AsyncWebResponse;
|
||||
class AsyncWebRequest;
|
||||
|
||||
using AsyncProgress = std::function<void(SentAsyncWebRequest&, double, double)>;
|
||||
using AsyncExpect = std::function<void(std::string const&)>;
|
||||
using AsyncThen = std::function<void(SentAsyncWebRequest&, byte_array const&)>;
|
||||
using AsyncCancelled = std::function<void(SentAsyncWebRequest&)>;
|
||||
|
||||
/**
|
||||
* A handle to an in-progress sent asynchronous web request. Use this to
|
||||
* cancel the request / query information about it
|
||||
*/
|
||||
class SentAsyncWebRequest {
|
||||
private:
|
||||
std::string m_id;
|
||||
std::string m_url;
|
||||
std::vector<AsyncThen> m_thens;
|
||||
std::vector<AsyncExpect> m_expects;
|
||||
std::vector<AsyncProgress> m_progresses;
|
||||
std::vector<AsyncCancelled> m_cancelleds;
|
||||
std::atomic<bool> m_paused = true;
|
||||
std::atomic<bool> m_cancelled = false;
|
||||
std::atomic<bool> m_finished = false;
|
||||
std::atomic<bool> m_cleanedUp = false;
|
||||
mutable std::mutex m_mutex;
|
||||
std::variant<
|
||||
std::monostate,
|
||||
std::ostream*,
|
||||
ghc::filesystem::path
|
||||
> m_target = std::monostate();
|
||||
|
||||
template<class T>
|
||||
friend class AsyncWebResult;
|
||||
friend class AsyncWebRequest;
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
void error(std::string const& error);
|
||||
void doCancel();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Do not call this manually.
|
||||
*/
|
||||
SentAsyncWebRequest(AsyncWebRequest const&, std::string const& id);
|
||||
|
||||
/**
|
||||
* Cancel the request. Cleans up any downloaded files, but if you run
|
||||
* extra code in `then`, you will have to clean it up manually in
|
||||
* `cancelled`
|
||||
*/
|
||||
void cancel();
|
||||
/**
|
||||
* Check if the request is finished
|
||||
*/
|
||||
bool finished() const;
|
||||
};
|
||||
|
||||
using SentAsyncWebRequestHandle = std::shared_ptr<SentAsyncWebRequest>;
|
||||
|
||||
template<class T>
|
||||
using DataConverter = Result<T>(*)(byte_array const&);
|
||||
|
||||
/**
|
||||
* An asynchronous, thread-safe web request. Downloads data from the
|
||||
* internet without slowing the main thread. All callbacks are run in the
|
||||
* GD thread, so interacting with the Cocos2d UI is perfectly safe
|
||||
*/
|
||||
class GEODE_DLL AsyncWebRequest {
|
||||
private:
|
||||
std::optional<std::string> m_joinID;
|
||||
std::string m_url;
|
||||
AsyncThen m_then = nullptr;
|
||||
AsyncExpect m_expect = nullptr;
|
||||
AsyncProgress m_progress = nullptr;
|
||||
AsyncCancelled m_cancelled = nullptr;
|
||||
bool m_sent = false;
|
||||
std::variant<
|
||||
std::monostate,
|
||||
std::ostream*,
|
||||
ghc::filesystem::path
|
||||
> m_target;
|
||||
|
||||
template<class T>
|
||||
friend class AsyncWebResult;
|
||||
friend class SentAsyncWebRequest;
|
||||
friend class AsyncWebResponse;
|
||||
|
||||
public:
|
||||
/**
|
||||
* An asynchronous, thread-safe web request. Downloads data from the
|
||||
* internet without slowing the main thread. All callbacks are run in the
|
||||
* GD thread, so interacting with the Cocos2d UI is perfectly safe
|
||||
*/
|
||||
AsyncWebRequest() = default;
|
||||
|
||||
/**
|
||||
* If you only want one instance of this web request to run (for example,
|
||||
* you're downloading some global data for a manager), then use this
|
||||
* to specify a Join ID. If another request with the same ID is
|
||||
* already running, this request's callbacks will be appended to the
|
||||
* existing one instead of creating a new request
|
||||
* @param requestID The Join ID of the request. Can be anything,
|
||||
* recommended to be something unique
|
||||
* @returns Same AsyncWebRequest
|
||||
*/
|
||||
AsyncWebRequest& join(std::string const& requestID);
|
||||
/**
|
||||
* URL to fetch from the internet asynchronously
|
||||
* @param url URL of the data to download. Redirects will be
|
||||
* automatically followed
|
||||
* @returns Same AsyncWebRequest
|
||||
*/
|
||||
AsyncWebResponse fetch(std::string const& url);
|
||||
/**
|
||||
* Specify a callback to run if the download fails. Runs in the GD
|
||||
* thread, so interacting with UI is safe
|
||||
* @param handler Callback to run if the download fails
|
||||
* @returns Same AsyncWebRequest
|
||||
*/
|
||||
AsyncWebRequest& expect(AsyncExpect handler);
|
||||
/**
|
||||
* Specify a callback to run when the download progresses. Runs in the
|
||||
* GD thread, so interacting with UI is safe
|
||||
* @param handler Callback to run when the download progresses
|
||||
* @returns Same AsyncWebRequest
|
||||
*/
|
||||
AsyncWebRequest& progress(AsyncProgress handler);
|
||||
/**
|
||||
* Specify a callback to run if the download is cancelled. Runs in the
|
||||
* GD thread, so interacting with UI is safe. Web requests may be
|
||||
* cancelled after they are finished (for example, if downloading files
|
||||
* in bulk and one fails). In that case, handle freeing up the results
|
||||
* of `then` in this handler
|
||||
* @param handler Callback to run if the download is cancelled
|
||||
* @returns Same AsyncWebRequest
|
||||
*/
|
||||
AsyncWebRequest& cancelled(AsyncCancelled handler);
|
||||
/**
|
||||
* Begin the web request. It's not always necessary to call this as the
|
||||
* destructor calls it automatically, but if you need access to the
|
||||
* handle of the sent request, use this
|
||||
* @returns Handle to the sent web request
|
||||
*/
|
||||
SentAsyncWebRequestHandle send();
|
||||
~AsyncWebRequest();
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class AsyncWebResult {
|
||||
private:
|
||||
AsyncWebRequest& m_request;
|
||||
DataConverter<T> m_converter;
|
||||
|
||||
AsyncWebResult(AsyncWebRequest& request, DataConverter<T> converter)
|
||||
: m_request(request), m_converter(converter) {}
|
||||
|
||||
friend class AsyncWebResponse;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Specify a callback to run after a download is finished. Runs in the
|
||||
* GD thread, so interacting with UI is safe
|
||||
* @param handle Callback to run
|
||||
* @returns The original AsyncWebRequest, where you can specify more
|
||||
* aspects about the request like failure and progress callbacks
|
||||
*/
|
||||
AsyncWebRequest& then(std::function<void(T)> handle);
|
||||
/**
|
||||
* Specify a callback to run after a download is finished. Runs in the
|
||||
* GD thread, so interacting with UI is safe
|
||||
* @param handle Callback to run
|
||||
* @returns The original AsyncWebRequest, where you can specify more
|
||||
* aspects about the request like failure and progress callbacks
|
||||
*/
|
||||
AsyncWebRequest& then(std::function<void(SentAsyncWebRequest&, T)> handle);
|
||||
};
|
||||
|
||||
class GEODE_DLL AsyncWebResponse {
|
||||
private:
|
||||
AsyncWebRequest& m_request;
|
||||
|
||||
inline AsyncWebResponse(AsyncWebRequest& request) : m_request(request) {}
|
||||
|
||||
friend class AsyncWebRequest;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Download into a stream. Make sure the stream lives for the entire
|
||||
* duration of the request. If you want to download a file, use the
|
||||
* `ghc::filesystem::path` overload of `into` instead
|
||||
* @param stream Stream to download into. Make sure it lives long
|
||||
* enough, otherwise the web request will crash
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished. The result has a `std::monostate`
|
||||
* template parameter, as it can be assumed you know what you passed
|
||||
* into `into`
|
||||
*/
|
||||
AsyncWebResult<std::monostate> into(std::ostream& stream);
|
||||
/**
|
||||
* Download into a file
|
||||
* @param path File to download into. If it already exists, it will
|
||||
* be overwritten.
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished. The result has a `std::monostate`
|
||||
* template parameter, as it can be assumed you know what you passed
|
||||
* into `into`
|
||||
*/
|
||||
AsyncWebResult<std::monostate> into(ghc::filesystem::path const& path);
|
||||
/**
|
||||
* Download into memory as a string
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished
|
||||
*/
|
||||
AsyncWebResult<std::string> text();
|
||||
/**
|
||||
* Download into memory as a byte array
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished
|
||||
*/
|
||||
AsyncWebResult<byte_array> bytes();
|
||||
/**
|
||||
* Download into memory as JSON
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished
|
||||
*/
|
||||
AsyncWebResult<nlohmann::json> json();
|
||||
|
||||
/**
|
||||
* Download into memory as a custom type. The data will first be
|
||||
* downloaded into memory as a byte array, and then converted using
|
||||
* the specified converter function
|
||||
* @param converter Function that converts the data from a byte array
|
||||
* to the desired type
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
* after the download is finished
|
||||
*/
|
||||
template<class T>
|
||||
AsyncWebResult<T> as(DataConverter<T> converter) {
|
||||
return AsyncWebResult(m_request, converter);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
AsyncWebRequest& AsyncWebResult<T>::then(std::function<void(T)> handle) {
|
||||
m_request.m_then = [
|
||||
converter = m_converter,
|
||||
handle
|
||||
](SentAsyncWebRequest& req, byte_array const& arr) {
|
||||
auto conv = converter(arr);
|
||||
if (conv) {
|
||||
handle(conv.value());
|
||||
} else {
|
||||
req.error("Unable to convert value: " + conv.error());
|
||||
}
|
||||
};
|
||||
return m_request;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
AsyncWebRequest& AsyncWebResult<T>::then(std::function<void(SentAsyncWebRequest&, T)> handle) {
|
||||
m_request.m_then = [
|
||||
converter = m_converter,
|
||||
handle
|
||||
](SentAsyncWebRequest& req, byte_array const& arr) {
|
||||
auto conv = converter(arr);
|
||||
if (conv) {
|
||||
handle(req, conv.value());
|
||||
} else {
|
||||
req.error("Unable to convert value: " + conv.error());
|
||||
}
|
||||
};
|
||||
return m_request;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,22 +7,34 @@
|
|||
#include <fs/filesystem.hpp>
|
||||
|
||||
namespace geode::utils::file {
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<std::string> readString(std::string const& path);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<std::string> readString(std::wstring const& path);
|
||||
GEODE_DLL Result<std::string> readString(ghc::filesystem::path const& path);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<byte_array> readBinary(std::string const& path);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<byte_array> readBinary(std::wstring const& path);
|
||||
GEODE_DLL Result<byte_array> readBinary(ghc::filesystem::path const& path);
|
||||
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> writeString(std::string const& path, std::string const& data);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> writeString(std::wstring const& path, std::string const& data);
|
||||
GEODE_DLL Result<> writeString(ghc::filesystem::path const& path, std::string const& data);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> writeBinary(std::string const& path, byte_array const& data);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> writeBinary(std::wstring const& path, byte_array const& data);
|
||||
GEODE_DLL Result<> writeBinary(ghc::filesystem::path const& path, byte_array const& data);
|
||||
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> createDirectory(std::string const& path);
|
||||
GEODE_DLL Result<> createDirectory(ghc::filesystem::path const& path);
|
||||
[[deprecated("Use the ghc::filesystem::path version")]]
|
||||
GEODE_DLL Result<> createDirectoryAll(std::string const& path);
|
||||
GEODE_DLL Result<> createDirectoryAll(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<std::vector<std::string>> listFiles(std::string const& path);
|
||||
GEODE_DLL Result<std::vector<std::string>> listFilesRecursively(std::string const& path);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
static cocos2d::CCPoint& operator*=(cocos2d::CCPoint & pos, float mul) {
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace geode::utils::vector {
|
|||
* @returns Reference to vector.
|
||||
*/
|
||||
template<class T>
|
||||
std::vector<T>& erase(std::vector<T>& vec, T& element) {
|
||||
std::vector<T>& erase(std::vector<T>& vec, T const& element) {
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), element), vec.end());
|
||||
return vec;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
add_subdirectory("test")
|
||||
endif()
|
||||
|
||||
message(STATUS ${GEODE_LOADER_PATH}/include)
|
||||
|
||||
target_include_directories(
|
||||
lilac_hook INTERFACE
|
||||
${lilac_SOURCE_DIR}/include/geode
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <Geode/Bindings.hpp>
|
||||
#include <cocos2d.h>
|
||||
#include <Geode/utils/Ref.hpp>
|
||||
#include <Geode/Modify.hpp>
|
||||
#include <Geode/utils/WackyGeodeMacros.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/modify/Field.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
using namespace geode::modifier;
|
||||
|
@ -61,6 +61,7 @@ public:
|
|||
};
|
||||
|
||||
// proxy forwards
|
||||
#include <Geode/modify/CCNode.hpp>
|
||||
class $modify(ProxyCCNode, CCNode) {
|
||||
virtual CCObject* getUserObject() {
|
||||
return GeodeNodeMetadata::set(this)->m_userObject;
|
||||
|
@ -84,7 +85,7 @@ void CCNode::setID(std::string const& id) {
|
|||
}
|
||||
|
||||
CCNode* CCNode::getChildByID(std::string const& id) {
|
||||
CCARRAY_FOREACH_B_TYPE(m_pChildren, child, CCNode) {
|
||||
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
|
||||
if (child->getID() == id) {
|
||||
return child;
|
||||
}
|
||||
|
@ -96,7 +97,7 @@ CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
|
|||
if (auto child = this->getChildByID(id)) {
|
||||
return child;
|
||||
}
|
||||
CCARRAY_FOREACH_B_TYPE(m_pChildren, child, CCNode) {
|
||||
for (auto child : CCArrayExt<CCNode>(m_pChildren)) {
|
||||
if ((child = child->getChildByIDRecursive(id))) {
|
||||
return child;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
#include <Geode/modify/LoadingLayer.hpp>
|
||||
#include <array>
|
||||
#include <InternalLoader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
class $modify(CustomLoadingLayer, LoadingLayer) {
|
||||
bool m_updatingResources;
|
||||
|
||||
CustomLoadingLayer() : m_updatingResources(false) {}
|
||||
|
||||
bool init(bool fromReload) {
|
||||
if (!LoadingLayer::init(fromReload))
|
||||
return false;
|
||||
|
@ -21,9 +26,89 @@ class $modify(CustomLoadingLayer, LoadingLayer) {
|
|||
);
|
||||
label->setPosition(winSize.width / 2, 30.f);
|
||||
label->setScale(.45f);
|
||||
label->setTag(5);
|
||||
label->setID("geode-loaded-info");
|
||||
this->addChild(label);
|
||||
|
||||
// verify loader resources
|
||||
if (!InternalLoader::get()->verifyLoaderResources(
|
||||
std::bind(
|
||||
&CustomLoadingLayer::updateResourcesProgress, this,
|
||||
std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3
|
||||
)
|
||||
)) {
|
||||
// auto bg = CCScale9Sprite::create(
|
||||
// "square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }
|
||||
// );
|
||||
// bg->setScale(.6f);
|
||||
// bg->setColor({ 0, 0, 0 });
|
||||
// bg->setOpacity(150);
|
||||
// bg->setPosition(winSize / 2);
|
||||
// this->addChild(bg);
|
||||
|
||||
// m_fields->m_updatingResourcesBG = bg;
|
||||
|
||||
// auto label = CCLabelBMFont::create("", "goldFont.fnt");
|
||||
// label->setScale(1.1f);
|
||||
// bg->addChild(label);
|
||||
|
||||
m_fields->m_updatingResources = true;
|
||||
|
||||
this->setUpdateText("Downloading Resources");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setUpdateText(std::string const& text) {
|
||||
m_textArea->setString(text.c_str());
|
||||
// m_fields->m_updatingResources->setString(text.c_str());
|
||||
// m_fields->m_updatingResourcesBG->setContentSize({
|
||||
// m_fields->m_updatingResources->getScaledContentSize().width + 30.f,
|
||||
// 50.f
|
||||
// });
|
||||
// m_fields->m_updatingResources->setPosition(
|
||||
// m_fields->m_updatingResourcesBG->getContentSize() / 2
|
||||
// );
|
||||
}
|
||||
|
||||
void updateResourcesProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t progress
|
||||
) {
|
||||
switch (status) {
|
||||
case UpdateStatus::Progress: {
|
||||
this->setUpdateText(
|
||||
"Downloading Resources: " +
|
||||
std::to_string(progress) + "%"
|
||||
);
|
||||
} break;
|
||||
|
||||
case UpdateStatus::Finished: {
|
||||
this->setUpdateText("Resources Downloaded");
|
||||
m_fields->m_updatingResources = false;
|
||||
this->loadAssets();
|
||||
} break;
|
||||
|
||||
case UpdateStatus::Failed: {
|
||||
InternalLoader::platformMessageBox(
|
||||
"Error updating resources",
|
||||
"Unable to update Geode resources: " + info + ".\n"
|
||||
"The game will be loaded as normal, but please be aware "
|
||||
"that it may very likely crash."
|
||||
);
|
||||
this->setUpdateText("Resource Download Failed");
|
||||
m_fields->m_updatingResources = false;
|
||||
this->loadAssets();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void loadAssets() {
|
||||
if (m_fields->m_updatingResources) {
|
||||
return;
|
||||
}
|
||||
LoadingLayer::loadAssets();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <Geode/Bindings.hpp>
|
||||
|
||||
#include <Geode/utils/WackyGeodeMacros.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/utils/Ref.hpp>
|
||||
#include <Geode/ui/BasedButtonSprite.hpp>
|
||||
#include <Geode/ui/Notification.hpp>
|
||||
#include <Index.hpp>
|
||||
|
@ -32,42 +34,6 @@ static void addUpdateIcon(const char* icon = "updates-available.png"_spr) {
|
|||
}
|
||||
}
|
||||
|
||||
static void updateModsProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t progress
|
||||
) {
|
||||
if (status == UpdateStatus::Failed) {
|
||||
g_indexUpdateNotif->hide();
|
||||
g_indexUpdateNotif = nullptr;
|
||||
NotificationBuilder()
|
||||
.title("Some Updates Failed")
|
||||
.text("Some mods failed to update, click for details")
|
||||
.icon("info-alert.png"_spr)
|
||||
.clicked([info](auto) -> void {
|
||||
FLAlertLayer::create("Info", info, "OK")->show();
|
||||
})
|
||||
.show();
|
||||
addUpdateIcon("updates-failed.png"_spr);
|
||||
}
|
||||
|
||||
if (status == UpdateStatus::Finished) {
|
||||
g_indexUpdateNotif->hide();
|
||||
g_indexUpdateNotif = nullptr;
|
||||
NotificationBuilder()
|
||||
.title("Updates Installed")
|
||||
.text(
|
||||
"Mods have been updated, please "
|
||||
"restart to apply changes"
|
||||
)
|
||||
.icon("updates-available.png"_spr)
|
||||
.clicked([info](auto) -> void {
|
||||
FLAlertLayer::create("Info", info, "OK")->show();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
static void updateIndexProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
|
@ -88,49 +54,20 @@ static void updateIndexProgress(
|
|||
g_indexUpdateNotif->hide();
|
||||
g_indexUpdateNotif = nullptr;
|
||||
if (Index::get()->areUpdatesAvailable()) {
|
||||
if (Mod::get()->getSettingValue<bool>("auto-update-mods")) {
|
||||
auto ticket = Index::get()->installUpdates(updateModsProgress);
|
||||
if (!ticket) {
|
||||
NotificationBuilder()
|
||||
.title("Unable to auto-update")
|
||||
.text("Unable to update mods :(")
|
||||
.icon("updates-failed.png"_spr)
|
||||
.show();
|
||||
} else {
|
||||
g_indexUpdateNotif = NotificationBuilder()
|
||||
.title("Installing updates")
|
||||
.text("Installing updates...")
|
||||
.clicked([ticket](auto) -> void {
|
||||
createQuickPopup(
|
||||
"Cancel Updates",
|
||||
"Do you want to <cr>cancel</c> updates?",
|
||||
"Don't Cancel", "Cancel Updates",
|
||||
[ticket](auto, bool btn2) -> void {
|
||||
if (g_indexUpdateNotif && btn2) {
|
||||
ticket.value()->cancel();
|
||||
}
|
||||
}
|
||||
);
|
||||
}, false)
|
||||
.loading()
|
||||
.stay()
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
NotificationBuilder()
|
||||
.title("Updates available")
|
||||
.text("Some mods have updates available!")
|
||||
.icon("updates-available.png"_spr)
|
||||
.clicked([](auto) -> void {
|
||||
ModListLayer::scene();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
NotificationBuilder()
|
||||
.title("Updates available")
|
||||
.text("Some mods have updates available!")
|
||||
.icon("updates-available.png"_spr)
|
||||
.clicked([](auto) -> void {
|
||||
ModListLayer::scene();
|
||||
})
|
||||
.show();
|
||||
addUpdateIcon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <Geode/modify/MenuLayer.hpp>
|
||||
class $modify(CustomMenuLayer, MenuLayer) {
|
||||
void destructor() {
|
||||
g_geodeButton = nullptr;
|
||||
|
@ -170,7 +107,19 @@ class $modify(CustomMenuLayer, MenuLayer) {
|
|||
btn->setID("geode-button");
|
||||
bottomMenu->addChild(btn);
|
||||
|
||||
<<<<<<< HEAD
|
||||
bottomMenu->updateLayout();
|
||||
=======
|
||||
bottomMenu->alignItemsHorizontallyWithPadding(3.f);
|
||||
|
||||
for (auto node : CCArrayExt<CCNode>(bottomMenu->getChildren())) {
|
||||
node->setPositionY(y);
|
||||
}
|
||||
if (chest) {
|
||||
bottomMenu->addChild(chest);
|
||||
chest->release();
|
||||
}
|
||||
>>>>>>> main
|
||||
|
||||
if (auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||
node->setPositionX(bottomMenu->getChildByID(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/modify/InternalMacros.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
using geode::core::meta::x86::Thiscall;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
#include <Geode/modify/CCTouchDispatcher.hpp>
|
||||
class $modify(CCTouchDispatcher) {
|
||||
void addTargetedDelegate(CCTouchDelegate *delegate, int priority, bool swallowsTouches) {
|
||||
m_bForcePrio = false;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// #include <Geode/Geode.hpp>
|
||||
// // this is the fix for the dynamic_cast problems
|
||||
|
||||
// USE_GEODE_NAMESPACE();
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
|
||||
|
||||
// this is the fix for the dynamic_cast problems
|
||||
// TODO: completely replace dynamic_cast on macos
|
||||
|
||||
using namespace cocos2d;
|
||||
using namespace geode::modifier;
|
||||
|
||||
#if defined(GEODE_IS_IOS) || defined(GEODE_IS_MACOS)
|
||||
namespace geode::fixes {
|
||||
using namespace geode::cast;
|
||||
|
||||
#define HandlerFixFor(CCUtility) \
|
||||
|
@ -49,10 +46,14 @@ class $modify(CCUtility##HandlerTypeinfoFix, CCUtility##Handler) {
|
|||
} \
|
||||
}
|
||||
|
||||
#include <Geode/modify/CCKeypadHandler.hpp>
|
||||
HandlerFixFor(CCKeypad);
|
||||
#include <Geode/modify/CCKeyboardHandler.hpp>
|
||||
HandlerFixFor(CCKeyboard);
|
||||
#include <Geode/modify/CCMouseHandler.hpp>
|
||||
HandlerFixFor(CCMouse);
|
||||
|
||||
#include <Geode/modify/CCTargetedTouchHandler.hpp>
|
||||
class $modify(CCTargetedTouchHandlerTypeinfoFix, CCTargetedTouchHandler) {
|
||||
void destructor() {
|
||||
if (m_pDelegate) {
|
||||
|
@ -99,6 +100,7 @@ class $modify(CCTargetedTouchHandlerTypeinfoFix, CCTargetedTouchHandler) {
|
|||
}
|
||||
};
|
||||
|
||||
#include <Geode/modify/CCStandardTouchHandler.hpp>
|
||||
class $modify(CCStandardTouchHandlerTypeinfoFix, CCStandardTouchHandler) {
|
||||
void destructor() {
|
||||
if (m_pDelegate) {
|
||||
|
@ -140,6 +142,4 @@ class $modify(CCStandardTouchHandlerTypeinfoFix, CCStandardTouchHandler) {
|
|||
}
|
||||
};
|
||||
|
||||
} // geode::fixes
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
#include <Geode/ui/SceneManager.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
#include <Geode/modify/AchievementNotifier.hpp>
|
||||
class $modify(AchievementNotifier) {
|
||||
void willSwitchToScene(CCScene* scene) {
|
||||
AchievementNotifier::willSwitchToScene(scene);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
#include <Geode/modify/AppDelegate.hpp>
|
||||
class $modify(AppDelegate) {
|
||||
void trySaveGame() {
|
||||
log::log(Severity::Info, Loader::getInternalMod(), "Saving...");
|
||||
|
||||
auto r = Loader::get()->saveSettings();
|
||||
if (!r) {
|
||||
log::log(Severity::Error, Loader::getInternalMod(), r.error());
|
||||
log::log(Severity::Error, Loader::getInternalMod(), "{}", r.error());
|
||||
}
|
||||
|
||||
log::log(Severity::Info, Loader::getInternalMod(), "Saved");
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
#include <InternalLoader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
#include <Geode/modify/CCScheduler.hpp>
|
||||
class $modify(CCScheduler) {
|
||||
void update(float dt) {
|
||||
InternalLoader::get()->executeGDThreadQueue();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include <Geode/Modify.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
#include <Geode/modify/GameManager.hpp>
|
||||
class $modify(GameManager) {
|
||||
void reloadAllStep2() {
|
||||
GameManager::reloadAllStep2();
|
||||
|
@ -10,6 +10,7 @@ class $modify(GameManager) {
|
|||
}
|
||||
};
|
||||
|
||||
#include <Geode/modify/LoadingLayer.hpp>
|
||||
class $modify(LoadingLayer) {
|
||||
void loadAssets() {
|
||||
LoadingLayer::loadAssets();
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
#include <Geode/utils/json.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <Geode/utils/fetch.hpp>
|
||||
#include <hash.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <Geode/utils/vector.hpp>
|
||||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/general.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
|
||||
#define GITHUB_DONT_RATE_LIMIT_ME_PLS 0
|
||||
|
||||
|
@ -31,7 +40,6 @@ static PlatformID platformFromString(std::string const& str) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Index* Index::get() {
|
||||
static auto ret = new Index();
|
||||
return ret;
|
||||
|
@ -59,159 +67,17 @@ bool Index::isFeaturedItem(std::string const& item) const {
|
|||
return m_featured.count(item);
|
||||
}
|
||||
|
||||
void Index::updateIndexThread(bool force) {
|
||||
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
|
||||
// download index
|
||||
|
||||
#if GITHUB_DONT_RATE_LIMIT_ME_PLS == 0
|
||||
|
||||
indexUpdateProgress(
|
||||
UpdateStatus::Progress, "Fetching index metadata", 0
|
||||
);
|
||||
|
||||
// get all commits in index repo
|
||||
auto commit = web::fetchJSON(
|
||||
"https://api.github.com/repos/geode-sdk/mods/commits"
|
||||
);
|
||||
if (!commit) {
|
||||
return indexUpdateProgress(UpdateStatus::Failed, commit.error());
|
||||
}
|
||||
auto json = commit.value();
|
||||
if (
|
||||
json.is_object() &&
|
||||
json.contains("documentation_url") &&
|
||||
json.contains("message")
|
||||
) {
|
||||
// whoops! got rate limited
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed,
|
||||
json["message"].get<std::string>()
|
||||
);
|
||||
}
|
||||
|
||||
indexUpdateProgress(
|
||||
UpdateStatus::Progress, "Checking index status", 25
|
||||
);
|
||||
|
||||
// read sha of latest commit
|
||||
|
||||
if (!json.is_array()) {
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Fetched commits, expected 'array', got '" +
|
||||
std::string(json.type_name()) + "'. "
|
||||
"Report this bug to the Geode developers!"
|
||||
);
|
||||
}
|
||||
|
||||
if (!json.at(0).is_object()) {
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Fetched commits, expected 'array.object', got 'array." +
|
||||
std::string(json.type_name()) + "'. "
|
||||
"Report this bug to the Geode developers!"
|
||||
);
|
||||
}
|
||||
|
||||
if (!json.at(0).contains("sha")) {
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Fetched commits, missing '0.sha'. "
|
||||
"Report this bug to the Geode developers!"
|
||||
);
|
||||
}
|
||||
|
||||
auto upcomingCommitSHA = json.at(0)["sha"];
|
||||
|
||||
// read sha of currently installed commit
|
||||
std::string currentCommitSHA = "";
|
||||
if (ghc::filesystem::exists(indexDir / "current")) {
|
||||
auto data = utils::file::readString(indexDir / "current");
|
||||
if (data) {
|
||||
currentCommitSHA = data.value();
|
||||
}
|
||||
}
|
||||
|
||||
// update if forced or latest commit has
|
||||
// different sha
|
||||
if (force || currentCommitSHA != upcomingCommitSHA) {
|
||||
// save new sha in file
|
||||
utils::file::writeString(indexDir / "current", upcomingCommitSHA);
|
||||
|
||||
// download latest commit (by downloading
|
||||
// the repo as a zip)
|
||||
|
||||
indexUpdateProgress(
|
||||
UpdateStatus::Progress,
|
||||
"Downloading index",
|
||||
50
|
||||
);
|
||||
auto gotZip = web::fetchFile(
|
||||
"https://github.com/geode-sdk/mods/zipball/main",
|
||||
indexDir / "index.zip"
|
||||
);
|
||||
if (!gotZip) {
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed,
|
||||
gotZip.error()
|
||||
);
|
||||
}
|
||||
|
||||
// delete old index
|
||||
if (ghc::filesystem::exists(indexDir / "index")) {
|
||||
ghc::filesystem::remove_all(indexDir / "index");
|
||||
}
|
||||
|
||||
auto unzip = file::unzipTo(indexDir / "index.zip", indexDir);
|
||||
if (!unzip) {
|
||||
return indexUpdateProgress(
|
||||
UpdateStatus::Failed, unzip.error()
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// update index
|
||||
|
||||
indexUpdateProgress(
|
||||
UpdateStatus::Progress,
|
||||
"Updating index",
|
||||
75
|
||||
);
|
||||
this->updateIndexFromLocalCache();
|
||||
|
||||
m_upToDate = true;
|
||||
m_updating = false;
|
||||
|
||||
indexUpdateProgress(
|
||||
UpdateStatus::Finished,
|
||||
"",
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
void Index::indexUpdateProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t percentage
|
||||
) {
|
||||
Loader::get()->queueInGDThread([this, status, info, percentage]() -> void {
|
||||
m_callbacksMutex.lock();
|
||||
for (auto& d : m_callbacks) {
|
||||
d(status, info, percentage);
|
||||
}
|
||||
if (
|
||||
status == UpdateStatus::Finished ||
|
||||
status == UpdateStatus::Failed
|
||||
) {
|
||||
m_callbacks.clear();
|
||||
}
|
||||
m_callbacksMutex.unlock();
|
||||
});
|
||||
}
|
||||
|
||||
void Index::updateIndex(IndexUpdateCallback callback, bool force) {
|
||||
#define RETURN_ERROR(str) \
|
||||
std::string err__ = (str); \
|
||||
if (callback) callback( \
|
||||
UpdateStatus::Failed, \
|
||||
err__, \
|
||||
0 \
|
||||
); \
|
||||
log::info("Index update failed: {}", err__);\
|
||||
return;
|
||||
|
||||
// if already updated and no force, let
|
||||
// delegate know
|
||||
if (!force && m_upToDate) {
|
||||
|
@ -225,36 +91,134 @@ void Index::updateIndex(IndexUpdateCallback callback, bool force) {
|
|||
return;
|
||||
}
|
||||
|
||||
// add delegate thread-safely if it's not
|
||||
// added already
|
||||
if (callback) {
|
||||
m_callbacksMutex.lock();
|
||||
m_callbacks.push_back(callback);
|
||||
m_callbacksMutex.unlock();
|
||||
}
|
||||
|
||||
// if already updating, let delegate know
|
||||
// and return
|
||||
if (m_updating) {
|
||||
if (callback) {
|
||||
callback(
|
||||
UpdateStatus::Progress,
|
||||
"Waiting for update info",
|
||||
0
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_updating = true;
|
||||
|
||||
// create directory for the local clone of
|
||||
// the index
|
||||
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
ghc::filesystem::create_directories(indexDir);
|
||||
|
||||
// update index in another thread to avoid
|
||||
// pausing UI
|
||||
std::thread(&Index::updateIndexThread, this, force).detach();
|
||||
#if GITHUB_DONT_RATE_LIMIT_ME_PLS == 1
|
||||
|
||||
auto err = this->updateIndexFromLocalCache();
|
||||
if (!err) {
|
||||
RETURN_ERROR(err);
|
||||
}
|
||||
|
||||
m_upToDate = true;
|
||||
m_updating = false;
|
||||
|
||||
if (callback) callback(UpdateStatus::Finished, "", 100);
|
||||
return;
|
||||
|
||||
#endif
|
||||
|
||||
web::AsyncWebRequest()
|
||||
.join("index-update")
|
||||
.fetch("https://api.github.com/repos/geode-sdk/mods/commits")
|
||||
.json()
|
||||
.then([this, force, callback](nlohmann::json const& json) {
|
||||
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
|
||||
// check if rate-limited (returns object)
|
||||
JsonChecker checkerObj(json);
|
||||
auto obj = checkerObj.root("[geode-sdk/mods/commits]").obj();
|
||||
if (obj.has("documentation_url") && obj.has("message")) {
|
||||
RETURN_ERROR(obj.has("message").get<std::string>());
|
||||
}
|
||||
|
||||
// get sha of latest commit
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[geode-sdk/mods/commits]").array();
|
||||
|
||||
std::string upcomingCommitSHA;
|
||||
if (auto first = root.at(0).obj().needs("sha")) {
|
||||
upcomingCommitSHA = first.get<std::string>();
|
||||
} else {
|
||||
RETURN_ERROR("Unable to get hash from latest commit: " + checker.getError());
|
||||
}
|
||||
|
||||
// read sha of currently installed commit
|
||||
std::string currentCommitSHA = "";
|
||||
if (ghc::filesystem::exists(indexDir / "current")) {
|
||||
auto data = utils::file::readString(indexDir / "current");
|
||||
if (data) {
|
||||
currentCommitSHA = data.value();
|
||||
}
|
||||
}
|
||||
|
||||
// update if forced or latest commit has
|
||||
// different sha
|
||||
if (force || currentCommitSHA != upcomingCommitSHA) {
|
||||
// save new sha in file
|
||||
utils::file::writeString(indexDir / "current", upcomingCommitSHA);
|
||||
|
||||
web::AsyncWebRequest()
|
||||
.join("index-download")
|
||||
.fetch("https://github.com/geode-sdk/mods/zipball/main")
|
||||
.into(indexDir / "index.zip")
|
||||
.then([this, indexDir, callback](auto) {
|
||||
// delete old index
|
||||
try {
|
||||
if (ghc::filesystem::exists(indexDir / "index")) {
|
||||
ghc::filesystem::remove_all(indexDir / "index");
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
RETURN_ERROR("Unable to delete old index " + std::string(e.what()));
|
||||
}
|
||||
|
||||
// unzip new index
|
||||
auto unzip = file::unzipTo(indexDir / "index.zip", indexDir);
|
||||
if (!unzip) {
|
||||
RETURN_ERROR(unzip.error());
|
||||
}
|
||||
|
||||
// update index
|
||||
auto err = this->updateIndexFromLocalCache();
|
||||
if (!err) {
|
||||
RETURN_ERROR(err.error());
|
||||
}
|
||||
|
||||
m_upToDate = true;
|
||||
m_updating = false;
|
||||
|
||||
if (callback) callback(
|
||||
UpdateStatus::Finished, "", 100
|
||||
);
|
||||
})
|
||||
.expect([callback](std::string const& err) {
|
||||
RETURN_ERROR(err);
|
||||
})
|
||||
.progress([callback](web::SentAsyncWebRequest& req, double now, double total) {
|
||||
if (callback) callback(
|
||||
UpdateStatus::Progress,
|
||||
"Downloading",
|
||||
static_cast<int>(now / total * 100.0)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
auto err = this->updateIndexFromLocalCache();
|
||||
if (!err) {
|
||||
RETURN_ERROR(err.error());
|
||||
}
|
||||
|
||||
m_upToDate = true;
|
||||
m_updating = false;
|
||||
|
||||
if (callback) callback(
|
||||
UpdateStatus::Finished,
|
||||
"", 100
|
||||
);
|
||||
}
|
||||
})
|
||||
.expect([callback](std::string const& err) {
|
||||
RETURN_ERROR(err);
|
||||
})
|
||||
.progress([callback](web::SentAsyncWebRequest& req, double now, double total) {
|
||||
if (callback) callback(
|
||||
UpdateStatus::Progress,
|
||||
"Downloading",
|
||||
static_cast<int>(now / total * 100.0)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
|
||||
|
@ -271,12 +235,7 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto readModJson = readJSON<ModJson>(dir / "mod.json");
|
||||
if (!readModJson) {
|
||||
log::warn("Error reading mod.json: {}, skipping", readModJson.error());
|
||||
return;
|
||||
}
|
||||
auto info = ModInfo::create(readModJson.value());
|
||||
auto info = ModInfo::createFromFile(dir / "mod.json");
|
||||
if (!info) {
|
||||
log::warn("{}: {}, skipping", dir, info.error());
|
||||
return;
|
||||
|
@ -338,7 +297,7 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
|
|||
}
|
||||
}
|
||||
|
||||
void Index::updateIndexFromLocalCache() {
|
||||
Result<> Index::updateIndexFromLocalCache() {
|
||||
m_items.clear();
|
||||
auto baseIndexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
|
||||
|
@ -352,10 +311,19 @@ void Index::updateIndexFromLocalCache() {
|
|||
|
||||
// load index mods
|
||||
auto modsDir = baseIndexDir / "index";
|
||||
for (auto const& dir : ghc::filesystem::directory_iterator(modsDir)) {
|
||||
if (ghc::filesystem::is_directory(dir)) {
|
||||
this->addIndexItemFromFolder(dir);
|
||||
if (ghc::filesystem::exists(modsDir)) {
|
||||
for (auto const& dir : ghc::filesystem::directory_iterator(modsDir)) {
|
||||
if (ghc::filesystem::is_directory(dir)) {
|
||||
this->addIndexItemFromFolder(dir);
|
||||
}
|
||||
}
|
||||
log::info("Index updated");
|
||||
return Ok();
|
||||
} else {
|
||||
return Err(
|
||||
"Index appears not to have been "
|
||||
"downloaded, or is fully empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,9 +414,8 @@ Result<std::vector<std::string>> Index::checkDependenciesForItem(
|
|||
}
|
||||
}
|
||||
|
||||
Result<InstallTicket*> Index::installItems(
|
||||
std::vector<IndexItem> const& items,
|
||||
ItemInstallCallback progress
|
||||
Result<InstallHandle> Index::installItems(
|
||||
std::vector<IndexItem> const& items
|
||||
) {
|
||||
std::vector<std::string> ids {};
|
||||
for (auto& item : items) {
|
||||
|
@ -482,32 +449,34 @@ Result<InstallTicket*> Index::installItems(
|
|||
}
|
||||
utils::vector::push(ids, list.value());
|
||||
}
|
||||
return Ok(new InstallTicket(this, ids, progress));
|
||||
auto ret = std::make_shared<InstallItems>(
|
||||
std::unordered_set(ids.begin(), ids.end())
|
||||
);
|
||||
m_installations.insert(ret);
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
Result<InstallTicket*> Index::installItem(
|
||||
IndexItem const& item,
|
||||
ItemInstallCallback progress
|
||||
Result<InstallHandle> Index::installItem(
|
||||
IndexItem const& item
|
||||
) {
|
||||
return this->installItems({ item }, progress);
|
||||
return this->installItems({ item });
|
||||
}
|
||||
|
||||
bool Index::isUpdateAvailableForItem(std::string const& id) const {
|
||||
if (!Loader::get()->isModInstalled(id)) {
|
||||
return false;
|
||||
}
|
||||
if (!this->isKnownItem(id)) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
this->getKnownItem(id).m_info.m_version >
|
||||
Loader::get()->getInstalledMod(id)->getVersion();
|
||||
return this->isUpdateAvailableForItem(this->getKnownItem(id));
|
||||
}
|
||||
|
||||
bool Index::isUpdateAvailableForItem(IndexItem const& item) const {
|
||||
if (!Loader::get()->isModInstalled(item.m_info.m_id)) {
|
||||
return false;
|
||||
}
|
||||
// has the mod been updated (but update not yet been applied by restarting game)
|
||||
if (m_updated.count(item.m_info.m_id)) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
item.m_info.m_version >
|
||||
Loader::get()->getInstalledMod(item.m_info.m_id)->getVersion();
|
||||
|
@ -522,9 +491,7 @@ bool Index::areUpdatesAvailable() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
Result<InstallTicket*> Index::installUpdates(
|
||||
IndexUpdateCallback callback, bool force
|
||||
) {
|
||||
Result<InstallHandle> Index::installAllUpdates() {
|
||||
// find items that need updating
|
||||
std::vector<IndexItem> itemsToUpdate {};
|
||||
for (auto& item : m_items) {
|
||||
|
@ -532,52 +499,222 @@ Result<InstallTicket*> Index::installUpdates(
|
|||
itemsToUpdate.push_back(item);
|
||||
}
|
||||
}
|
||||
return this->installItems(itemsToUpdate);
|
||||
}
|
||||
|
||||
// generate ticket
|
||||
auto ticket = this->installItems(
|
||||
itemsToUpdate,
|
||||
[itemsToUpdate, callback](
|
||||
InstallTicket*,
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t progress
|
||||
) -> void {
|
||||
switch (status) {
|
||||
case UpdateStatus::Failed: {
|
||||
callback(
|
||||
UpdateStatus::Failed,
|
||||
"Updating failed: " + info,
|
||||
0
|
||||
);
|
||||
} break;
|
||||
|
||||
case UpdateStatus::Finished: {
|
||||
std::string updatedStr = "";
|
||||
for (auto& item : itemsToUpdate) {
|
||||
updatedStr += item.m_info.m_name + " (" +
|
||||
item.m_info.m_id + ")\n";
|
||||
}
|
||||
callback(
|
||||
UpdateStatus::Finished,
|
||||
"Updated the following mods: " +
|
||||
updatedStr +
|
||||
"Please restart to apply changes.",
|
||||
100
|
||||
);
|
||||
} break;
|
||||
|
||||
case UpdateStatus::Progress: {
|
||||
callback(UpdateStatus::Progress, info, progress);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
std::vector<InstallHandle> Index::getRunningInstallations() const {
|
||||
return std::vector<InstallHandle>(
|
||||
m_installations.begin(),
|
||||
m_installations.end()
|
||||
);
|
||||
if (!ticket) {
|
||||
return Err(ticket.error());
|
||||
}
|
||||
|
||||
InstallHandle Index::isInstallingItem(std::string const& id) {
|
||||
for (auto& inst : m_installations) {
|
||||
if (inst->m_toInstall.count(id)) {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> InstallItems::toInstall() const {
|
||||
return m_toInstall;
|
||||
}
|
||||
|
||||
InstallItems::CallbackID InstallItems::join(ItemInstallCallback callback) {
|
||||
// already finished?
|
||||
if (m_started && this->finished()) {
|
||||
callback(shared_from_this(), UpdateStatus::Finished, "", 100);
|
||||
return 0;
|
||||
}
|
||||
// start at one because 0 means invalid callback
|
||||
static CallbackID COUNTER = 1;
|
||||
if (callback) {
|
||||
auto id = COUNTER++;
|
||||
m_callbacks.insert({ id, callback });
|
||||
return id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InstallItems::leave(InstallItems::CallbackID id) {
|
||||
m_callbacks.erase(id);
|
||||
}
|
||||
|
||||
void InstallItems::post(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t progress
|
||||
) {
|
||||
for (auto& [_, cb] : m_callbacks) {
|
||||
cb(shared_from_this(), status, info, progress);
|
||||
}
|
||||
}
|
||||
|
||||
void InstallItems::progress(
|
||||
std::string const& info,
|
||||
uint8_t progress
|
||||
) {
|
||||
this->post(UpdateStatus::Progress, info, progress);
|
||||
}
|
||||
|
||||
void InstallItems::error(std::string const& info) {
|
||||
this->post(UpdateStatus::Failed, info, 0);
|
||||
}
|
||||
|
||||
void InstallItems::finish(bool replaceFiles) {
|
||||
// move files from temp dir to geode directory
|
||||
auto tempDir = Loader::get()->getGeodeDirectory() / "index" / "temp";
|
||||
for (auto& file : ghc::filesystem::directory_iterator(tempDir)) {
|
||||
try {
|
||||
auto modDir = Loader::get()->getGeodeDirectory() / "mods";
|
||||
auto targetFile = modDir / file.path().filename();
|
||||
auto targetName = file.path().stem();
|
||||
|
||||
if (!replaceFiles) {
|
||||
// find valid filename that doesn't exist yet
|
||||
auto filename = ghc::filesystem::path(targetName)
|
||||
.replace_extension("")
|
||||
.string();
|
||||
|
||||
size_t number = 0;
|
||||
while (ghc::filesystem::exists(targetFile)) {
|
||||
targetFile = modDir /
|
||||
(filename + std::to_string(number) + ".geode");
|
||||
number++;
|
||||
}
|
||||
}
|
||||
|
||||
// move file
|
||||
ghc::filesystem::rename(file, targetFile);
|
||||
|
||||
} catch(std::exception& e) {
|
||||
try { ghc::filesystem::remove_all(tempDir); } catch(...) {}
|
||||
return this->error(
|
||||
"Unable to move downloaded file to mods directory: \"" +
|
||||
std::string(e.what()) + " \" "
|
||||
"(This might be due to insufficient permissions to "
|
||||
"write files under SteamLibrary, try running GD as "
|
||||
"administrator)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// install updates concurrently
|
||||
ticket.value()->start(InstallMode::Concurrent);
|
||||
// load mods
|
||||
Loader::get()->refreshMods();
|
||||
|
||||
// finished
|
||||
this->post(UpdateStatus::Finished, "", 100);
|
||||
|
||||
return ticket;
|
||||
// let index know these mods have been updated
|
||||
for (auto& inst : m_toInstall) {
|
||||
Index::get()->m_updated.insert(inst);
|
||||
}
|
||||
|
||||
// if no one is listening, show a popup anyway
|
||||
if (!m_callbacks.size()) {
|
||||
FLAlertLayer::create(
|
||||
"Mods installed",
|
||||
"The following <cy>mods</c> have been installed: " +
|
||||
container::join(m_toInstall, ",") + "\n"
|
||||
"Please <cr>restart the game</c> to apply",
|
||||
"OK"
|
||||
)->show();
|
||||
}
|
||||
|
||||
// no longer need to ensure aliveness
|
||||
Index::get()->m_installations.erase(shared_from_this());
|
||||
}
|
||||
|
||||
InstallItems::CallbackID InstallItems::start(
|
||||
ItemInstallCallback callback,
|
||||
bool replaceFiles
|
||||
) {
|
||||
auto id = this->join(callback);
|
||||
|
||||
// check if started already, if so, behave like join
|
||||
if (m_started) return id;
|
||||
m_started = true;
|
||||
|
||||
for (auto& inst : m_toInstall) {
|
||||
// by virtue of running this function we know item must be valid
|
||||
auto item = Index::get()->getKnownItem(inst);
|
||||
|
||||
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
file::createDirectoryAll(indexDir / "temp");
|
||||
auto tempFile = indexDir / "temp" / item.m_download.m_filename;
|
||||
|
||||
m_downloaded.push_back(tempFile);
|
||||
|
||||
auto handle = web::AsyncWebRequest()
|
||||
.join("install_mod_" + inst)
|
||||
.fetch(item.m_download.m_url)
|
||||
.into(tempFile)
|
||||
.then([this, replaceFiles, item, inst, indexDir, tempFile](auto) {
|
||||
// check for 404
|
||||
auto notFound = utils::file::readString(tempFile);
|
||||
if (notFound && notFound.value() == "Not Found") {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->error(
|
||||
"Binary file download returned \"Not found\". Report "
|
||||
"this to the Geode development team."
|
||||
);
|
||||
}
|
||||
|
||||
// verify checksum
|
||||
this->progress("Verifying", 100);
|
||||
if (::calculateHash(tempFile) != item.m_download.m_hash) {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->error(
|
||||
"Checksum mismatch! (Downloaded file did not match what "
|
||||
"was expected. Try again, and if the download fails another time, "
|
||||
"report this to the Geode development team."
|
||||
);
|
||||
}
|
||||
|
||||
// finished() just checks if the web requests are done
|
||||
if (this->finished()) {
|
||||
this->finish(replaceFiles);
|
||||
}
|
||||
})
|
||||
.expect([this, inst](std::string const& error) {
|
||||
this->error(error);
|
||||
this->cancel();
|
||||
})
|
||||
.cancelled([this, item](auto&) {
|
||||
this->cancel();
|
||||
})
|
||||
.progress([this, inst](web::SentAsyncWebRequest&, double now, double total) {
|
||||
this->progress(
|
||||
"Downloading binary",
|
||||
static_cast<uint8_t>(now / total * 100.0)
|
||||
);
|
||||
})
|
||||
.send();
|
||||
|
||||
m_handles.push_back(handle);
|
||||
}
|
||||
// manage installation in the index until it's finished so
|
||||
// even if no one listens to it it doesn't get freed from
|
||||
// memory
|
||||
Index::get()->m_installations.insert(shared_from_this());
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
bool InstallItems::finished() const {
|
||||
for (auto& inst : m_handles) {
|
||||
if (!inst->finished()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstallItems::cancel() {
|
||||
for (auto& inst : m_handles) {
|
||||
inst->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <Geode/utils/fetch.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
class Index;
|
||||
struct ModInstallUpdate;
|
||||
class InstallTicket;
|
||||
struct InstallItems;
|
||||
|
||||
using InstallHandle = std::shared_ptr<InstallItems>;
|
||||
|
||||
// todo: make index use events
|
||||
|
||||
|
@ -19,7 +22,7 @@ enum class UpdateStatus {
|
|||
};
|
||||
|
||||
using ItemInstallCallback = std::function<void(
|
||||
InstallTicket*, UpdateStatus, std::string const&, uint8_t
|
||||
InstallHandle, UpdateStatus, std::string const&, uint8_t
|
||||
)>;
|
||||
using IndexUpdateCallback = std::function<void(
|
||||
UpdateStatus, std::string const&, uint8_t
|
||||
|
@ -39,73 +42,38 @@ struct IndexItem {
|
|||
std::unordered_set<std::string> m_categories;
|
||||
};
|
||||
|
||||
enum class InstallMode {
|
||||
Order, // download & install one-by-one
|
||||
Concurrent, // download & install all simultaneously
|
||||
};
|
||||
struct InstallItems final : public std::enable_shared_from_this<InstallItems> {
|
||||
public:
|
||||
using CallbackID = size_t;
|
||||
|
||||
/**
|
||||
* Used for working with a currently
|
||||
* happening mod installation from
|
||||
* the index. Note that once the
|
||||
* installation is finished / failed,
|
||||
* the ticket will free its own memory,
|
||||
* so make sure to let go of any
|
||||
* pointers you may have to it.
|
||||
*/
|
||||
class InstallTicket {
|
||||
protected:
|
||||
ItemInstallCallback m_progress;
|
||||
const std::vector<std::string> m_installList;
|
||||
mutable std::mutex m_cancelMutex;
|
||||
bool m_cancelling = false;
|
||||
bool m_installing = false;
|
||||
bool m_replaceFiles = true;
|
||||
Index* m_index;
|
||||
private:
|
||||
bool m_started = false;
|
||||
std::unordered_set<std::string> m_toInstall;
|
||||
std::vector<web::SentAsyncWebRequestHandle> m_handles;
|
||||
std::unordered_map<CallbackID, ItemInstallCallback> m_callbacks;
|
||||
std::vector<ghc::filesystem::path> m_downloaded;
|
||||
|
||||
void postProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info = "",
|
||||
uint8_t progress = 0
|
||||
);
|
||||
void install(std::string const& id);
|
||||
void post(UpdateStatus status, std::string const& info, uint8_t progress);
|
||||
void progress(std::string const& info, uint8_t progress);
|
||||
void error(std::string const& info);
|
||||
void finish(bool replaceFiles);
|
||||
|
||||
friend class Index;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new ticket for installing a list of mods. This method
|
||||
* should not be called manually; instead, you should always use
|
||||
* `Index::installItem`. Note that once the installation is
|
||||
* finished / failed, the ticket will free its own memory, so make
|
||||
* sure to let go of any pointers you may have to it.
|
||||
*/
|
||||
InstallTicket(
|
||||
Index* index,
|
||||
std::vector<std::string> const& list,
|
||||
ItemInstallCallback progress
|
||||
);
|
||||
std::unordered_set<std::string> toInstall() const;
|
||||
|
||||
/**
|
||||
* Get list of mods to install
|
||||
*/
|
||||
std::vector<std::string> getInstallList() const;
|
||||
inline InstallItems(
|
||||
std::unordered_set<std::string> const& toInstall
|
||||
) : m_toInstall(toInstall) {}
|
||||
|
||||
/**
|
||||
* Cancel all pending installations and revert finished ones. This
|
||||
* function is thread-safe
|
||||
*/
|
||||
void cancel();
|
||||
bool finished() const;
|
||||
|
||||
/**
|
||||
* Begin installation. Note that this function is *not*
|
||||
* thread-safe
|
||||
* @param mode Whether to install the list of mods
|
||||
* provided concurrently or in order
|
||||
* @note Use InstallTicket::cancel to cancel the
|
||||
* installation
|
||||
*/
|
||||
void start(InstallMode mode = InstallMode::Concurrent);
|
||||
CallbackID join(ItemInstallCallback callback);
|
||||
void leave(CallbackID id);
|
||||
|
||||
CallbackID start(ItemInstallCallback callback, bool replaceFiles = true);
|
||||
};
|
||||
|
||||
class Index {
|
||||
|
@ -113,50 +81,43 @@ protected:
|
|||
bool m_upToDate = false;
|
||||
bool m_updating = false;
|
||||
mutable std::mutex m_callbacksMutex;
|
||||
std::vector<IndexUpdateCallback> m_callbacks;
|
||||
std::vector<IndexItem> m_items;
|
||||
std::unordered_set<InstallHandle> m_installations;
|
||||
mutable std::mutex m_ticketsMutex;
|
||||
std::unordered_set<std::string> m_featured;
|
||||
std::unordered_set<std::string> m_categories;
|
||||
std::unordered_set<std::string> m_updated;
|
||||
|
||||
void indexUpdateProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info = "",
|
||||
uint8_t percentage = 0
|
||||
);
|
||||
|
||||
void updateIndexThread(bool force);
|
||||
void addIndexItemFromFolder(ghc::filesystem::path const& dir);
|
||||
void updateIndexFromLocalCache();
|
||||
Result<> updateIndexFromLocalCache();
|
||||
|
||||
Result<std::vector<std::string>> checkDependenciesForItem(
|
||||
IndexItem const& item
|
||||
);
|
||||
|
||||
friend struct InstallItems;
|
||||
|
||||
public:
|
||||
static Index* get();
|
||||
|
||||
std::vector<IndexItem> getItems() const;
|
||||
bool isKnownItem(std::string const& id) const;
|
||||
IndexItem getKnownItem(std::string const& id) const;
|
||||
Result<InstallTicket*> installItems(
|
||||
std::vector<IndexItem> const& item,
|
||||
ItemInstallCallback progress = nullptr
|
||||
);
|
||||
Result<InstallTicket*> installItem(
|
||||
IndexItem const& item,
|
||||
ItemInstallCallback progress = nullptr
|
||||
);
|
||||
bool isUpdateAvailableForItem(std::string const& id) const;
|
||||
bool isUpdateAvailableForItem(IndexItem const& item) const;
|
||||
bool areUpdatesAvailable() const;
|
||||
Result<InstallTicket*> installUpdates(
|
||||
IndexUpdateCallback callback = nullptr,
|
||||
bool force = false
|
||||
);
|
||||
|
||||
std::unordered_set<std::string> getCategories() const;
|
||||
std::vector<IndexItem> getFeaturedItems() const;
|
||||
bool isFeaturedItem(std::string const& item) const;
|
||||
|
||||
Result<InstallHandle> installItems(std::vector<IndexItem> const& item);
|
||||
Result<InstallHandle> installItem(IndexItem const& item);
|
||||
std::vector<InstallHandle> getRunningInstallations() const;
|
||||
InstallHandle isInstallingItem(std::string const& id);
|
||||
|
||||
bool isUpdateAvailableForItem(std::string const& id) const;
|
||||
bool isUpdateAvailableForItem(IndexItem const& item) const;
|
||||
bool areUpdatesAvailable() const;
|
||||
Result<InstallHandle> installAllUpdates();
|
||||
|
||||
bool isIndexUpdated() const;
|
||||
void updateIndex(IndexUpdateCallback callback, bool force = false);
|
||||
};
|
||||
|
|
|
@ -1,201 +0,0 @@
|
|||
#include "Index.hpp"
|
||||
#include <thread>
|
||||
#include <Geode/utils/json.hpp>
|
||||
#include <hash.hpp>
|
||||
#include <Geode/utils/fetch.hpp>
|
||||
|
||||
void InstallTicket::postProgress(
|
||||
UpdateStatus status,
|
||||
std::string const& info,
|
||||
uint8_t percentage
|
||||
) {
|
||||
if (m_progress) {
|
||||
Loader::get()->queueInGDThread([this, status, info, percentage]() -> void {
|
||||
m_progress(this, status, info, percentage);
|
||||
});
|
||||
}
|
||||
if (status == UpdateStatus::Failed || status == UpdateStatus::Finished) {
|
||||
log::info("Deleting InstallTicket at {}", this);
|
||||
// clean up own memory
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
InstallTicket::InstallTicket(
|
||||
Index* index,
|
||||
std::vector<std::string> const& list,
|
||||
ItemInstallCallback progress
|
||||
) : m_index(index),
|
||||
m_installList(list),
|
||||
m_progress(progress) {}
|
||||
|
||||
std::vector<std::string> InstallTicket::getInstallList() const {
|
||||
return m_installList;
|
||||
}
|
||||
|
||||
void InstallTicket::install(std::string const& id) {
|
||||
// run installing in another thread in order
|
||||
// to render progress on screen while installing
|
||||
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
|
||||
|
||||
auto item = m_index->getKnownItem(id);
|
||||
|
||||
this->postProgress(UpdateStatus::Progress, "Checking status", 0);
|
||||
|
||||
// download to temp file in index dir
|
||||
auto tempFile = indexDir / item.m_download.m_filename;
|
||||
|
||||
this->postProgress(UpdateStatus::Progress, "Fetching binary", 0);
|
||||
auto res = web::fetchFile(
|
||||
item.m_download.m_url,
|
||||
tempFile,
|
||||
[this, tempFile](double now, double total) -> int {
|
||||
// check if cancelled
|
||||
std::lock_guard cancelLock(m_cancelMutex);
|
||||
if (m_cancelling) {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
// no need to scope the lock guard more as this
|
||||
// function is going to exit right after anyway
|
||||
|
||||
this->postProgress(
|
||||
UpdateStatus::Progress,
|
||||
"Downloading binary",
|
||||
static_cast<uint8_t>(now / total * 100.0)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
if (!res) {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->postProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Downloading failed: " + res.error()
|
||||
);
|
||||
}
|
||||
|
||||
// check if cancelled
|
||||
{
|
||||
std::lock_guard cancelLock(m_cancelMutex);
|
||||
if (m_cancelling) {
|
||||
ghc::filesystem::remove(tempFile);
|
||||
return;
|
||||
}
|
||||
// scope ends here since we don't need to
|
||||
// access m_cancelling anymore
|
||||
}
|
||||
|
||||
// check for 404
|
||||
auto notFound = utils::file::readString(tempFile);
|
||||
if (notFound && notFound.value() == "Not Found") {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->postProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Binary file download returned \"Not found\". Report "
|
||||
"this to the Geode development team."
|
||||
);
|
||||
}
|
||||
|
||||
// verify checksum
|
||||
this->postProgress(UpdateStatus::Progress, "Verifying", 100);
|
||||
|
||||
auto checksum = ::calculateHash(tempFile.string());
|
||||
|
||||
if (checksum != item.m_download.m_hash) {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->postProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Checksum mismatch! (Downloaded file did not match what "
|
||||
"was expected. Try again, and if the download fails another time, "
|
||||
"report this to the Geode development team."
|
||||
);
|
||||
}
|
||||
|
||||
// move temp file to geode directory
|
||||
try {
|
||||
auto modDir = Loader::get()->getGeodeDirectory() / "mods";
|
||||
auto targetFile = modDir / item.m_download.m_filename;
|
||||
|
||||
// find valid filename that doesn't exist yet
|
||||
if (!m_replaceFiles) {
|
||||
auto filename = ghc::filesystem::path(
|
||||
item.m_download.m_filename
|
||||
).replace_extension("").string();
|
||||
|
||||
size_t number = 0;
|
||||
while (ghc::filesystem::exists(targetFile)) {
|
||||
targetFile = modDir /
|
||||
(filename + std::to_string(number) + ".geode");
|
||||
number++;
|
||||
}
|
||||
}
|
||||
|
||||
// move file
|
||||
ghc::filesystem::rename(tempFile, targetFile);
|
||||
} catch(std::exception& e) {
|
||||
try { ghc::filesystem::remove(tempFile); } catch(...) {}
|
||||
return this->postProgress(
|
||||
UpdateStatus::Failed,
|
||||
"Unable to move downloaded file to mods directory: \"" +
|
||||
std::string(e.what()) + " \" "
|
||||
"(This might be due to insufficient permissions to "
|
||||
"write files under SteamLibrary, try running GD as "
|
||||
"administrator)"
|
||||
);
|
||||
}
|
||||
|
||||
// call next in queue or post finish message
|
||||
Loader::get()->queueInGDThread([this, id]() -> void {
|
||||
// todo: Loader::get()->refreshMods(m_updateMod);
|
||||
// where the Loader unloads the mod binary and
|
||||
// reloads it from disk (this should prolly be
|
||||
// done only for the installed mods)
|
||||
Loader::get()->refreshMods();
|
||||
// already in GD thread, so might aswell do the
|
||||
// progress posting manually
|
||||
if (m_progress) {
|
||||
(m_progress)(
|
||||
this, UpdateStatus::Finished, "", 100
|
||||
);
|
||||
}
|
||||
// clean up memory
|
||||
delete this;
|
||||
});
|
||||
}
|
||||
|
||||
void InstallTicket::cancel() {
|
||||
// really no point in using std::lock_guard here
|
||||
// since just plain locking and unlocking the mutex
|
||||
// will do the job just fine with the same legibility
|
||||
m_cancelMutex.lock();
|
||||
m_cancelling = true;
|
||||
m_cancelMutex.unlock();
|
||||
}
|
||||
|
||||
void InstallTicket::start(InstallMode mode) {
|
||||
if (m_installing) return;
|
||||
// make sure we have stuff to install
|
||||
if (!m_installList.size()) {
|
||||
return this->postProgress(
|
||||
UpdateStatus::Failed, "Nothing to install", 0
|
||||
);
|
||||
}
|
||||
m_installing = true;
|
||||
switch (mode) {
|
||||
case InstallMode::Concurrent: {
|
||||
for (auto& id : m_installList) {
|
||||
std::thread(&InstallTicket::install, this, id).detach();
|
||||
}
|
||||
} break;
|
||||
|
||||
case InstallMode::Order: {
|
||||
std::thread([this]() -> void {
|
||||
for (auto& id : m_installList) {
|
||||
this->install(id);
|
||||
}
|
||||
}).detach();
|
||||
} break;
|
||||
}
|
||||
}
|
|
@ -6,9 +6,11 @@
|
|||
#include "InternalMod.hpp"
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/utils/fetch.hpp>
|
||||
#include <thread>
|
||||
#include "resources.hpp"
|
||||
#include <hash.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
|
||||
InternalLoader::InternalLoader() : Loader() {}
|
||||
|
||||
|
@ -40,19 +42,21 @@ bool InternalLoader::setup() {
|
|||
|
||||
void InternalLoader::queueInGDThread(ScheduledFunction func) {
|
||||
std::lock_guard<std::mutex> lock(m_gdThreadMutex);
|
||||
this->m_gdThreadQueue.push_back(func);
|
||||
m_gdThreadQueue.push_back(func);
|
||||
}
|
||||
|
||||
void InternalLoader::executeGDThreadQueue() {
|
||||
// copy queue to avoid locking mutex if someone is
|
||||
// running addToGDThread inside their function
|
||||
m_gdThreadMutex.lock();
|
||||
auto queue = std::move(m_gdThreadQueue);
|
||||
auto queue = m_gdThreadQueue;
|
||||
m_gdThreadQueue.clear();
|
||||
m_gdThreadMutex.unlock();
|
||||
|
||||
// call queue
|
||||
for (auto const& func : queue) {
|
||||
func();
|
||||
}
|
||||
m_gdThreadMutex.lock();
|
||||
m_gdThreadQueue.clear();
|
||||
m_gdThreadMutex.unlock();
|
||||
}
|
||||
|
||||
void InternalLoader::logConsoleMessage(std::string const& msg) {
|
||||
|
@ -82,6 +86,121 @@ void InternalLoader::loadInfoAlerts(nlohmann::json& json) {
|
|||
m_shownInfoAlerts = json["alerts"].get<std::unordered_set<std::string>>();
|
||||
}
|
||||
|
||||
void InternalLoader::downloadLoaderResources(IndexUpdateCallback callback) {
|
||||
auto version = this->getVersion().toString();
|
||||
auto tempResourcesZip = this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY / "new.zip";
|
||||
auto resourcesDir = this->getGeodeDirectory() /
|
||||
GEODE_RESOURCE_DIRECTORY / InternalMod::get()->getID();
|
||||
|
||||
web::AsyncWebRequest()
|
||||
.join("update-geode-loader-resources")
|
||||
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/tags/" + version)
|
||||
.json()
|
||||
.then([tempResourcesZip, resourcesDir, callback](nlohmann::json const& json) {
|
||||
auto checker = JsonChecker(json);
|
||||
auto root = checker.root("[matching geode release]").obj();
|
||||
|
||||
// find resources.zip and download it
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").get<std::string>() == "resources.zip") {
|
||||
auto url = obj.needs("browser_download_url").get<std::string>();
|
||||
if (url.size()) {
|
||||
web::AsyncWebRequest()
|
||||
.fetch(url)
|
||||
.into(tempResourcesZip)
|
||||
.then([tempResourcesZip, resourcesDir, callback](auto) {
|
||||
// unzip resources zip
|
||||
auto unzip = file::unzipTo(tempResourcesZip, resourcesDir);
|
||||
if (!unzip) {
|
||||
if (callback) callback(
|
||||
UpdateStatus::Failed,
|
||||
"Unable to unzip new resources: " + unzip.error(),
|
||||
0
|
||||
);
|
||||
return;
|
||||
}
|
||||
// delete resources zip
|
||||
try {
|
||||
ghc::filesystem::remove(tempResourcesZip);
|
||||
} catch(...) {}
|
||||
|
||||
if (callback) callback(
|
||||
UpdateStatus::Finished,
|
||||
"Resources updated",
|
||||
100
|
||||
);
|
||||
})
|
||||
.expect([callback](std::string const& info) {
|
||||
if (callback) callback(UpdateStatus::Failed, info, 0);
|
||||
})
|
||||
.progress([callback](auto&, double now, double total) {
|
||||
if (callback) callback(
|
||||
UpdateStatus::Progress,
|
||||
"Downloading resources",
|
||||
static_cast<uint8_t>(now / total * 100.0)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checker.isError()) {
|
||||
if (callback) callback(UpdateStatus::Failed, checker.getError(), 0);
|
||||
}
|
||||
})
|
||||
.expect([callback](std::string const& info) {
|
||||
if (callback) callback(UpdateStatus::Failed, info, 0);
|
||||
});
|
||||
}
|
||||
|
||||
bool InternalLoader::verifyLoaderResources(IndexUpdateCallback callback) {
|
||||
static std::optional<bool> CACHED = std::nullopt;
|
||||
if (CACHED.has_value()) {
|
||||
return CACHED.value();
|
||||
}
|
||||
|
||||
// geode/resources/geode.loader
|
||||
auto resourcesDir = this->getGeodeDirectory() /
|
||||
GEODE_RESOURCE_DIRECTORY / InternalMod::get()->getID();
|
||||
|
||||
// if the resources dir doesn't exist, then it's probably incorrect
|
||||
if (!(
|
||||
ghc::filesystem::exists(resourcesDir) &&
|
||||
ghc::filesystem::is_directory(resourcesDir)
|
||||
)) {
|
||||
this->downloadLoaderResources(callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure every file was covered
|
||||
size_t coverage = 0;
|
||||
|
||||
// verify hashes
|
||||
for (auto& file : ghc::filesystem::directory_iterator(resourcesDir)) {
|
||||
auto name = file.path().filename().string();
|
||||
// skip unknown files
|
||||
if (!LOADER_RESOURCE_HASHES.count(name)) {
|
||||
continue;
|
||||
}
|
||||
// verify hash
|
||||
auto hash = calculateSHA256(file.path());
|
||||
if (hash != LOADER_RESOURCE_HASHES.at(name)) {
|
||||
this->downloadLoaderResources(callback);
|
||||
return false;
|
||||
}
|
||||
coverage += 1;
|
||||
}
|
||||
|
||||
// make sure every file was found
|
||||
if (coverage != LOADER_RESOURCE_HASHES.size()) {
|
||||
this->downloadLoaderResources(callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(GEODE_IS_WINDOWS)
|
||||
void InternalLoader::platformMessageBox(const char* title, std::string const& info) {
|
||||
MessageBoxA(nullptr, info.c_str(), title, MB_ICONERROR);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <unordered_set>
|
||||
#include <Geode/utils/json.hpp>
|
||||
#include <optional>
|
||||
#include "../index/Index.hpp"
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -27,6 +28,8 @@ protected:
|
|||
void saveInfoAlerts(nlohmann::json& json);
|
||||
void loadInfoAlerts(nlohmann::json& json);
|
||||
|
||||
void downloadLoaderResources(IndexUpdateCallback callback);
|
||||
|
||||
InternalLoader();
|
||||
~InternalLoader();
|
||||
|
||||
|
@ -54,6 +57,8 @@ public:
|
|||
void openPlatformConsole();
|
||||
void closePlatformConsole();
|
||||
static void platformMessageBox(const char* title, std::string const& info);
|
||||
|
||||
bool verifyLoaderResources(IndexUpdateCallback callback);
|
||||
|
||||
friend int geodeEntry(void* platformData);
|
||||
};
|
||||
|
|
|
@ -42,7 +42,7 @@ InternalMod::InternalMod() : Mod(getInternalModInfo()) {
|
|||
|
||||
auto sett = this->loadSettings();
|
||||
if (!sett) {
|
||||
log::log(Severity::Error, this, sett.error());
|
||||
log::log(Severity::Error, this, "{}", sett.error());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/utils/casts.hpp>
|
||||
#include <Geode/utils/file.hpp>
|
||||
|
||||
#include "../crashlog.hpp"
|
||||
#include <Windows.h>
|
||||
|
@ -266,11 +267,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
|
|||
|
||||
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
|
||||
|
||||
std::ofstream file;
|
||||
file.open(
|
||||
crashlog::getCrashLogDirectory() + "/" + getDateString(true) + ".log",
|
||||
std::ios::app
|
||||
);
|
||||
std::stringstream file;
|
||||
|
||||
// init symbols so we can get some juicy debug info
|
||||
g_symbolsInitialized = SymInitialize(
|
||||
|
@ -293,7 +290,7 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
|
|||
<< "Please submit this crash report to its developer ("
|
||||
<< faultyMod->getDeveloper() << ") for assistance.\n";
|
||||
}
|
||||
|
||||
|
||||
// geode info
|
||||
file << "\n== Geode Information ==\n";
|
||||
printGeodeInfo(file);
|
||||
|
@ -314,6 +311,20 @@ static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
|
|||
file << "\n== Installed Mods ==\n";
|
||||
printMods(file);
|
||||
|
||||
// show message box on debug mode
|
||||
#ifdef GEODE_DEBUG
|
||||
MessageBoxA(nullptr, file.str().c_str(), "Geode Crashed", MB_ICONERROR);
|
||||
#endif
|
||||
|
||||
// save actual file
|
||||
std::ofstream actualFile;
|
||||
actualFile.open(
|
||||
crashlog::getCrashLogDirectory() + "/" + getDateString(true) + ".log",
|
||||
std::ios::app
|
||||
);
|
||||
actualFile << file.rdbuf() << std::flush;
|
||||
actualFile.close();
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ bool InternalLoader::loadHooks() {
|
|||
for (auto const& hook : internalHooks()) {
|
||||
auto res = hook.mod->addHook(hook.hook);
|
||||
if (!res) {
|
||||
log::log(Severity::Error, hook.mod, res.error());
|
||||
log::log(Severity::Error, hook.mod, "{}", res.error());
|
||||
thereWereErrors = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/types.hpp>
|
||||
#include <mutex>
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <about.hpp>
|
||||
#include <crashlog.hpp>
|
||||
|
||||
|
@ -51,6 +50,8 @@ void Loader::createDirectories() {
|
|||
}
|
||||
|
||||
void Loader::updateResourcePaths() {
|
||||
log::debug("Updating resources paths");
|
||||
|
||||
// add own geode/resources directory
|
||||
CCFileUtils::sharedFileUtils()->addSearchPath(
|
||||
(this->getGeodeDirectory() / GEODE_RESOURCE_DIRECTORY).string().c_str()
|
||||
|
@ -62,11 +63,20 @@ void Loader::updateResourcePaths() {
|
|||
|
||||
// add geode/temp/mod.id/resources for accessing additional resources in mods
|
||||
for (auto& [_, mod] : m_mods) {
|
||||
if (mod->m_addResourcesToSearchPath) {
|
||||
CCFileUtils::sharedFileUtils()->addSearchPath(
|
||||
(tempDir / mod->getID() / "resources").string().c_str()
|
||||
);
|
||||
}
|
||||
this->updateModResourcePaths(mod);
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::updateModResourcePaths(Mod* mod) {
|
||||
if (mod->m_addResourcesToSearchPath) {
|
||||
CCFileUtils::sharedFileUtils()->addSearchPath(
|
||||
(this->getGeodeDirectory() /
|
||||
GEODE_TEMP_DIRECTORY /
|
||||
mod->getID() /
|
||||
"resources"
|
||||
).string().c_str()
|
||||
);
|
||||
log::debug("Added resources path for {}", mod->getID());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,11 +99,15 @@ void Loader::updateModResources(Mod* mod) {
|
|||
CCTextureCache::sharedTextureCache()->addImage(png.c_str(), false);
|
||||
CCSpriteFrameCache::sharedSpriteFrameCache()
|
||||
->addSpriteFramesWithFile(plist.c_str());
|
||||
|
||||
log::debug("Added resources for {}", mod->getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::updateResources() {
|
||||
log::debug("Adding mod resources");
|
||||
|
||||
// add own spritesheets
|
||||
this->updateModResources(InternalMod::get());
|
||||
|
||||
|
@ -327,6 +341,12 @@ bool Loader::setup() {
|
|||
if (m_isSetup)
|
||||
return true;
|
||||
|
||||
if (crashlog::setupPlatformHandler()) {
|
||||
log::debug("Set up platform crash logger");
|
||||
} else {
|
||||
log::debug("Unable to set up platform crash logger");
|
||||
}
|
||||
|
||||
log::debug("Setting up Loader...");
|
||||
|
||||
this->createDirectories();
|
||||
|
@ -336,14 +356,9 @@ bool Loader::setup() {
|
|||
// add resources on startup
|
||||
this->queueInGDThread([]() {
|
||||
Loader::get()->updateResourcePaths();
|
||||
Loader::get()->updateResources();
|
||||
});
|
||||
|
||||
if (crashlog::setupPlatformHandler()) {
|
||||
log::debug("Set up platform crash logger");
|
||||
} else {
|
||||
log::debug("Unable to set up platform crash logger");
|
||||
}
|
||||
|
||||
m_isSetup = true;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
|
|
@ -78,7 +78,8 @@ Result<> Mod::loadSettings() {
|
|||
log::log(
|
||||
Severity::Warning,
|
||||
this,
|
||||
"Encountered unknown setting \"" + key + "\" while loading settings"
|
||||
"Encountered unknown setting \"{}\" while loading settings",
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -382,13 +383,13 @@ bool Mod::updateDependencyStates() {
|
|||
auto r = dep.m_mod->load();
|
||||
if (!r) {
|
||||
dep.m_state = ModResolveState::Unloaded;
|
||||
log::log(Severity::Error, dep.m_mod, r.error());
|
||||
log::log(Severity::Error, dep.m_mod, "{}", r.error());
|
||||
}
|
||||
else {
|
||||
auto r = dep.m_mod->enable();
|
||||
if (!r) {
|
||||
dep.m_state = ModResolveState::Disabled;
|
||||
log::log(Severity::Error, dep.m_mod, r.error());
|
||||
log::log(Severity::Error, dep.m_mod, "{}", r.error());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
static std::string sanitizeDetailsData(unsigned char* start, unsigned char* end) {
|
||||
static std::string sanitizeDetailsData(std::string const& str) {
|
||||
// delete CRLF
|
||||
return utils::string::replace(std::string(start, end), "\r", "");
|
||||
return utils::string::replace(str, "\r", "");
|
||||
}
|
||||
|
||||
Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
|
||||
|
@ -194,6 +194,9 @@ Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
|
|||
if (!res) return res;
|
||||
auto info = res.value();
|
||||
info.m_path = path;
|
||||
if (path.has_parent_path()) {
|
||||
info.addSpecialFiles(path.parent_path());
|
||||
}
|
||||
return Ok(info);
|
||||
} catch(std::exception& e) {
|
||||
return Err("Unable to parse mod.json: " + std::string(e.what()));
|
||||
|
@ -241,24 +244,51 @@ Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path)
|
|||
}
|
||||
auto info = res.value();
|
||||
info.m_path = path;
|
||||
|
||||
info.addSpecialFiles(unzip);
|
||||
|
||||
return Ok(info);
|
||||
}
|
||||
|
||||
Result<> ModInfo::addSpecialFiles(ZipFile& unzip) {
|
||||
// unzip known MD files
|
||||
using God = std::initializer_list<std::pair<std::string, std::optional<std::string>*>>;
|
||||
for (auto [file, target] : God {
|
||||
{ "about.md", &info.m_details },
|
||||
{ "changelog.md", &info.m_changelog },
|
||||
{ "support.md", &info.m_supportInfo },
|
||||
}) {
|
||||
for (auto& [file, target] : getSpecialFiles()) {
|
||||
if (unzip.fileExists(file)) {
|
||||
unsigned long readSize = 0;
|
||||
auto fileData = unzip.getFileData(file, &readSize);
|
||||
if (!fileData || !readSize) {
|
||||
return Err("Unable to read \"" + path.string() + "\"/" + file);
|
||||
return Err("Unable to read \"" + file + "\"");
|
||||
} else {
|
||||
*target = sanitizeDetailsData(fileData, fileData + readSize);
|
||||
*target = sanitizeDetailsData(
|
||||
std::string(fileData, fileData + readSize)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(info);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> ModInfo::addSpecialFiles(ghc::filesystem::path const& dir) {
|
||||
// unzip known MD files
|
||||
for (auto& [file, target] : getSpecialFiles()) {
|
||||
if (ghc::filesystem::exists(dir / file)) {
|
||||
auto data = file::readString(dir / file);
|
||||
if (!data) {
|
||||
return Err("Unable to read \"" + file + "\": " + data.error());
|
||||
}
|
||||
*target = sanitizeDetailsData(data.value());
|
||||
}
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
std::vector<std::pair<
|
||||
std::string,
|
||||
std::optional<std::string>*
|
||||
>> ModInfo::getSpecialFiles() {
|
||||
return {
|
||||
{ "about.md", &m_details },
|
||||
{ "changelog.md", &m_changelog },
|
||||
{ "support.md", &m_supportInfo },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ std::string g_lastError = "";
|
|||
|
||||
void geode_mod_log(void* cmod, const char* message) {
|
||||
auto mod = reinterpret_cast<Mod*>(cmod);
|
||||
log::log(Severity::Debug, mod, message);
|
||||
log::log(Severity::Debug, mod, "{}", message);
|
||||
}
|
||||
|
||||
bool geode_mod_add_hook(void* cmod, void* address, void* detour) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
|
|
@ -41,13 +41,19 @@ Result<Mod*> Loader::loadModFromFile(std::string const& path) {
|
|||
|
||||
auto sett = mod->loadSettings();
|
||||
if (!sett) {
|
||||
log::log(Severity::Error, mod, sett.error());
|
||||
log::log(Severity::Error, mod, "{}", sett.error());
|
||||
}
|
||||
|
||||
// enable mod if needed
|
||||
mod->m_enabled = Loader::get()->shouldLoadMod(mod->m_info.m_id);
|
||||
this->m_mods.insert({ res.value().m_id, mod });
|
||||
m_mods.insert({ res.value().m_id, mod });
|
||||
mod->updateDependencyStates();
|
||||
|
||||
return Ok<>(mod);
|
||||
// add mod resources
|
||||
this->queueInGDThread([this, mod]() {
|
||||
this->updateModResourcePaths(mod);
|
||||
this->updateModResources(mod);
|
||||
});
|
||||
|
||||
return Ok(mod);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <Geode/Geode.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "HookListLayer.hpp"
|
||||
#include <Geode/binding/GJListLayer.hpp>
|
||||
|
||||
bool HookListLayer::init(Mod* mod) {
|
||||
if (!GJDropDownLayer::init("Hooks", 220.f))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "HookListView.hpp"
|
||||
#include <Geode/binding/GJDropDownLayer.hpp>
|
||||
|
||||
class HookListLayer : public GJDropDownLayer {
|
||||
protected:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue