├── .clang-format
├── .clang-tidy
├── .clangd
├── .github
├── actions
│ └── canary-ndk
│ │ └── action.yml
└── workflows
│ ├── build-ndk.yml
│ └── publish.yml
├── .gitignore
├── .idea
└── cmake.xml
├── .vscode
├── c_cpp_properties.json
├── launch.json
├── settings.json
└── tasks.json
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include
├── CustomJSONDataHooks.h
└── HookUtils.hpp
├── mod.template.json
├── qpm.json
├── qpm.shared.json
├── scripts
├── build.ps1
├── copy.ps1
├── createqmod.ps1
├── decompile.sh
├── ndk-stack.ps1
├── pull-tombstone.ps1
├── restart-game.ps1
├── start-logging.ps1
└── validate-modjson.ps1
├── shared
├── CJDLogger.h
├── CustomBeatmapData.h
├── CustomBeatmapSaveDatav2.h
├── CustomBeatmapSaveDatav3.h
├── CustomEventData.h
├── JSONWrapper.h
├── JsonUtils.h
├── LowLevelUtils.hpp
├── VList.h
├── _config.hpp
└── misc
│ ├── BeatmapDataLoaderUtils.hpp
│ └── BeatmapFieldUtils.hpp
└── src
├── CustomBeatmapData.cpp
├── CustomBeatmapSaveDatav2.cpp
├── CustomBeatmapSaveDatav3.cpp
├── CustomEventData.cpp
├── JSONWrapper.cpp
├── hooks
├── BeatmapHooks.cpp
├── CustomJSONDataHooks.cpp
├── V2Hooks.cpp
└── V3Hooks.cpp
└── main.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: LLVM
2 |
3 | #Stolen from scad
4 | AllowShortBlocksOnASingleLine: false
5 | AllowShortFunctionsOnASingleLine: Empty
6 | AllowShortIfStatementsOnASingleLine: true
7 | CommentPragmas: NOLINT:.*
8 | DerivePointerAlignment: false
9 | IncludeBlocks: Preserve
10 | PointerAlignment: Left
11 | UseTab: Never
12 | Cpp11BracedListStyle: false
13 | QualifierAlignment: Right
14 | SortIncludes: Never
15 | ColumnLimit: 120
16 |
--------------------------------------------------------------------------------
/.clang-tidy:
--------------------------------------------------------------------------------
1 | Checks: "*,-llvm*,
2 | -google-readability-namespace,
3 | -llvmlib,
4 | -llvmlibc,
5 | -fuchsi,
6 | -fuchsia,
7 | -alter,
8 | -modernize-use-trailing-return-type,
9 | -modernize-use-trailing-return,
10 | -readability-avoid-const-params-in,
11 | -cppcoreguidelines-macro-usage,
12 | -readability-identifier-length,
13 | -google-readability-todo,
14 | -fuchsia-default-arguments-calls,
15 | -altera-unroll-loops,
16 | -altera*,
17 | -readability-implicit-bool-conversion,
18 | -cert-err58-cpp,
19 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
20 | -hicpp-avoid-c-arrays,
21 |
22 | -bugprone-undefined-memory-manipulation,
23 | -misc-use-anonymous-namespace,
24 | -readability-magic-numbers,
25 | -cppcoreguidelines-pro-type-reinterpret-cast,
26 | -cppcoreguidelines-virtual-class-destructor,
27 | -cppcoreguidelines-pro-type-union-access,
28 | -cppcoreguidelines-pro-type-vararg,
29 | -cppcoreguidelines-pro-type-static-cast-downcast,
30 | -misc-non-private-member-variables-in-classes,
31 | -fuchsia-statically-constructed-objects,
32 | -bugprone-easily-swappable-parameters,
33 | -cppcoreguidelines-avoid-magic-numbers,
34 | -hicpp-vararg,
35 | -cppcoreguidelines-pro-bounds-constant-array-index,
36 | -google-build-using-namespace,
37 | -abseil-string-find-str-contains,
38 | clang-diagnostic-*,
39 | clang-analyzer-*,
40 | -bugprone-unchecked-optional-access,
41 | -llvmlibc-callee-namespace,
42 | -readability-identifier-length,
43 | -llvmlibc-implementation-in-namespace,
44 | -llvmlibc-restrict-system-libc-headers,
45 | -llvm-namespace-comment,
46 | -llvm-include-order,
47 | -altera-*,
48 | -fuchsia-*,
49 | -modernize-use-trailing-return-type,
50 | -cppcoreguidelines-avoid-non-const-global-variables,
51 | -llvm-header-guard,
52 | -cppcoreguidelines-macro-usage,
53 | -cppcoreguidelines-pro-type-vararg,
54 | -cppcoreguidelines-avoid-do-while,
55 | -hicpp-vararg,
56 | -concurrency-mt-unsafe,
57 | -abseil-*
58 | "
59 | FormatStyle: file
60 | HeaderFilterRegex: ""
61 | AnalyzeTemporaryDtors: false
62 |
63 |
--------------------------------------------------------------------------------
/.clangd:
--------------------------------------------------------------------------------
1 | Diagnostics:
2 | UnusedIncludes: None
3 |
4 | If:
5 | # Note: This is a regexp, notice '.*' at the end of PathMatch string.
6 | PathMatch: ./extern/.*
7 | Index:
8 | # Disable slow background indexing of these files.
9 | Background: Skip
--------------------------------------------------------------------------------
/.github/actions/canary-ndk/action.yml:
--------------------------------------------------------------------------------
1 | name: "Setup canary ndk"
2 | description: "Sets up canary ndk"
3 | outputs:
4 | ndk-path:
5 | value: ${{ steps.path.outputs.path }}
6 | description: "Output path of the ndk"
7 | cache-hit:
8 | value: ${{ steps.cache.outputs.cache-hit }}
9 | description: "Whether a cache hit occurred for the ndk"
10 | runs:
11 | using: "composite"
12 | steps:
13 | - name: Get Home dir
14 | id: home-dir
15 | run: echo "path=${HOME}" >> ${GITHUB_OUTPUT}
16 | shell: bash
17 |
18 | - name: NDK cache
19 | id: cache
20 | uses: actions/cache@v3
21 | with:
22 | path: ${{ steps.home-dir.outputs.path }}/android-ndk-r27-canary/
23 | key: ${{ runner.os }}-ndk-r27-canary
24 |
25 | - name: Download canary ndk
26 | if: ${{ !steps.cache.outputs.cache-hit }}
27 | env:
28 | CANARY_URL: https://github.com/QuestPackageManager/ndk-canary-archive/releases/download/27.0.1/android-ndk-10883340-linux-x86_64.zip
29 | run: wget ${CANARY_URL} -O ${HOME}/ndk.zip
30 | shell: bash
31 |
32 | - name: Unzip ndk
33 | if: ${{ !steps.cache.outputs.cache-hit }}
34 | run: 7z x "${HOME}/ndk.zip" -o"${HOME}/"
35 | shell: bash
36 |
37 | - name: Set output
38 | id: path
39 | shell: bash
40 | run: echo "path=${HOME}/android-ndk-r27-canary" >> ${GITHUB_OUTPUT}
--------------------------------------------------------------------------------
/.github/workflows/build-ndk.yml:
--------------------------------------------------------------------------------
1 | name: NDK build
2 |
3 | env:
4 | module_id: custom-json-data
5 | qmodName: custom-json-data
6 |
7 | on:
8 | push:
9 | branches: [ main ]
10 | pull_request:
11 | branches: [ main ]
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | name: Checkout
20 | with:
21 | submodules: true
22 | lfs: true
23 |
24 | - uses: seanmiddleditch/gha-setup-ninja@v3
25 |
26 |
27 |
28 | # - name: Create ndkpath.txt
29 | # run: |
30 | # echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt
31 | # cat ${GITHUB_WORKSPACE}/ndkpath.txt
32 |
33 | - name: QPM Rust Action
34 | uses: Fernthedev/qpm-rust-action@v1
35 | with:
36 | #required
37 | workflow_token: ${{secrets.GITHUB_TOKEN}}
38 |
39 | restore: true # will run restore on download
40 | cache: true #will cache dependencies
41 |
42 | # Name of qmod in release asset. Assumes exists, same as prior
43 | qpm_qmod: ${{env.qmodName}}.qmod
44 |
45 | - name: QPM Collapse
46 | run: |
47 | qpm-rust collapse
48 |
49 | - name: Build
50 | run: |
51 | cd ${GITHUB_WORKSPACE}
52 | qpm-rust s build
53 |
54 | - name: Create Qmod
55 | run: |
56 | qpm-rust qmod zip
57 |
58 | - name: Get Library Name
59 | id: libname
60 | run: |
61 | cd ./build/
62 | pattern="lib${module_id}*.so"
63 | files=( $pattern )
64 | echo ::set-output name=NAME::"${files[0]}"
65 | - name: Upload non-debug artifact
66 | uses: actions/upload-artifact@v4
67 | with:
68 | name: ${{ steps.libname.outputs.NAME }}
69 | path: ./build/${{ steps.libname.outputs.NAME }}
70 | if-no-files-found: error
71 |
72 | - name: Upload qmod artifact
73 | uses: actions/upload-artifact@v4
74 | with:
75 | name: ${{env.qmodName}}.qmod
76 | path: ./${{ env.qmodName }}.qmod
77 | if-no-files-found: error
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish QPM Package
2 |
3 | env:
4 | module_id: custom-json-data
5 | qmodName: custom-json-data
6 | cache-name: custom-json-data_cache
7 |
8 | on:
9 | push:
10 | tags:
11 | - 'v*'
12 |
13 | permissions:
14 | pull-requests: write
15 | contents: write
16 |
17 | jobs:
18 | publish:
19 | runs-on: ubuntu-latest
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | name: Checkout
24 | with:
25 | submodules: true
26 | lfs: true
27 |
28 | - uses: seanmiddleditch/gha-setup-ninja@v3
29 |
30 |
31 | # - name: Create ndkpath.txt
32 | # run: |
33 | # echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt
34 | # cat ${GITHUB_WORKSPACE}/ndkpath.txt
35 |
36 | - name: Get Tag Version
37 | id: get_tag_version
38 | run: |
39 | echo ${GITHUB_REF#refs/tags/}
40 | echo ::set-output name=TAG::${GITHUB_REF#refs/tags/}
41 | echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
42 |
43 | - name: QPM Rust Action
44 | uses: Fernthedev/qpm-rust-action@v1
45 | with:
46 | #required
47 | workflow_token: ${{secrets.GITHUB_TOKEN}}
48 |
49 | restore: true # will run restore on download
50 | cache: true #will cache dependencies
51 |
52 | publish: "late"
53 | publish_token: ${{secrets.QPM_TOKEN}}
54 |
55 | version: ${{ steps.get_tag_version.outputs.VERSION }}
56 | tag: ${{ steps.get_tag_version.outputs.TAG }}
57 |
58 | # set to true if applicable, ASSUMES the file is already a release asset
59 | qpm_release_bin: true
60 | qpm_debug_bin: true
61 |
62 | # Name of qmod in release asset. Assumes exists, same as prior
63 | qpm_qmod: ${{env.qmodName}}.qmod
64 |
65 | - name: Build
66 | run: |
67 | cd ${GITHUB_WORKSPACE}
68 | qpm s build
69 |
70 | - name: Create Qmod
71 | run: |
72 | qpm qmod zip
73 |
74 | - name: Get Library Name
75 | id: libname
76 | run: |
77 | cd ./build/
78 | pattern="lib${module_id}*.so"
79 | files=( $pattern )
80 | echo ::set-output name=NAME::"${files[0]}"
81 |
82 | - name: Rename debug
83 | run: |
84 | mv ./build/debug/${{ steps.libname.outputs.NAME }} ./build/debug/debug_${{ steps.libname.outputs.NAME }}
85 |
86 |
87 | - name: Create Qmod
88 | run: |
89 | pwsh -Command ./scripts/createqmod.ps1 ${{env.qmodName}}
90 |
91 | - name: Upload to Release
92 | id: upload_file_release
93 | uses: softprops/action-gh-release@v0.1.15
94 | with:
95 | name: ${{ github.event.inputs.release_msg }}
96 | tag_name: ${{ github.event.inputs.version }}
97 | files: |
98 | ./${{ env.qmodName }}.qmod
99 | ./build/${{ steps.libname.outputs.NAME }}
100 | ./build/debug/debug_${{ steps.libname.outputs.NAME }}
101 | env:
102 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
103 |
104 | - name: Make PR to QeatMods3
105 | id: qmod-release
106 | uses: QuestPackageManager/qmod-repo-publish-action@main
107 | # TODO: Make this work!
108 | continue-on-error: true
109 | with:
110 | token: ${{secrets.GITHUB_TOKEN}}
111 | # first asset URL
112 | qmod_url: ${{ fromJSON(steps.upload_file_release.outputs.assets)[0].browser_download_url }}
113 | qmod_repo_owner: 'dantheman827'
114 | qmod_repo_name: 'bsqmods'
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache/
2 |
3 | # Prerequisites
4 | *.d
5 |
6 | # Compiled Object files
7 | *.slo
8 | *.lo
9 | *.o
10 | *.obj
11 |
12 | # Precompiled Headers
13 | *.gch
14 | *.pch
15 |
16 | # Compiled Dynamic libraries
17 | *.so
18 | *.dylib
19 | *.dll
20 |
21 | # Fortran module files
22 | *.mod
23 | *.smod
24 |
25 | # Compiled Static libraries
26 | *.lai
27 | *.la
28 | *.a
29 | *.lib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 |
36 | # VSCode config stuff
37 | !.vscode/c_cpp_properties.json
38 | !.vscode/tasks.json
39 | !.vscode/settings.json
40 |
41 | # Jetbrains IDEs
42 | .idea/
43 |
44 | # NDK stuff
45 | out/
46 | [Ll]ib/
47 | [Ll]ibs/
48 | [Oo]bj/
49 | [Oo]bjs/
50 | ndkpath.txt
51 | *.zip
52 | *.txt
53 | *.log
54 | Android.mk.backup
55 |
56 | # QPM stuff
57 | [Ee]xtern/
58 | *.qmod
59 | mod.json
60 | qpm_defines.cmake
61 | ![Cc][Mm]ake[Ll]ists.txt
62 |
63 | # CMake stuff
64 | [Bb]uild/
65 | cmake-build-*/
66 | extern.cmake
67 |
68 | # QMOD Schema
69 | mod.json.schema
--------------------------------------------------------------------------------
/.idea/cmake.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "defines": [
5 | "MOD_ID=\"custom-json-data\"",
6 | "VERSION=\"0.1.0\"",
7 | "__GNUC__",
8 | "__aarch64__"
9 | ],
10 | "includePath": [
11 | "${workspaceFolder}/extern/includes/libil2cpp/il2cpp/libil2cpp",
12 | "${workspaceFolder}/extern/includes/codegen/include",
13 | "${workspaceFolder}/extern/includes",
14 | "${workspaceFolder}/include",
15 | "${workspaceFolder}/shared",
16 | "${workspaceFolder}",
17 |
18 | "D:\DevTools\android-ndk-r27\android-ndk-r27-canary/**",
19 | "${default}"
20 | ],
21 | "name": "Quest",
22 | "cStandard": "c11",
23 | "cppStandard": "c++20",
24 | "intelliSenseMode": "clang-x64",
25 | "compileCommands": "${workspaceFolder}/build/compile_commands.json"
26 | }
27 | ],
28 | "version": 4
29 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Beat Saber",
6 | "type": "fb-lldb",
7 | "request": "launch",
8 | "preLaunchTask": "Powershell Build and Copy",
9 | "android": {
10 | "application": {
11 | "package": "com.beatgames.beatsaber",
12 | "activity": "com.unity3d.player.UnityPlayerActivity"
13 | },
14 | "lldbConfig": {
15 | "sourceMaps": [],
16 | "librarySearchPaths": [
17 | "${workspaceFolder}/build/debug/",
18 | "${workspaceFolder}/extern/libs/"
19 | ]
20 | }
21 | }
22 | },
23 | {
24 | "name": "Attach to running Beat Saber Instance",
25 | "type": "fb-lldb",
26 | "request": "attach",
27 | "android": {
28 | "application": {
29 | "package": "com.beatgames.beatsaber",
30 | "activity": "com.unity3d.player.UnityPlayerActivity"
31 | },
32 | "lldbConfig": {
33 | "sourceMaps": [],
34 | "librarySearchPaths": [
35 | "${workspaceFolder}/build/debug/",
36 | "${workspaceFolder}/extern/libs/",
37 | ]
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.associations": {
3 | "*.bsml": "xml",
4 | "iosfwd": "cpp",
5 | "__config": "cpp",
6 | "__nullptr": "cpp",
7 | "thread": "cpp",
8 | "any": "cpp",
9 | "deque": "cpp",
10 | "list": "cpp",
11 | "map": "cpp",
12 | "optional": "cpp",
13 | "queue": "cpp",
14 | "set": "cpp",
15 | "stack": "cpp",
16 | "unordered_map": "cpp",
17 | "unordered_set": "cpp",
18 | "variant": "cpp",
19 | "vector": "cpp",
20 | "__bit_reference": "cpp",
21 | "__debug": "cpp",
22 | "__errc": "cpp",
23 | "__functional_base": "cpp",
24 | "__hash_table": "cpp",
25 | "__locale": "cpp",
26 | "__mutex_base": "cpp",
27 | "__node_handle": "cpp",
28 | "__split_buffer": "cpp",
29 | "__string": "cpp",
30 | "__threading_support": "cpp",
31 | "__tree": "cpp",
32 | "__tuple": "cpp",
33 | "algorithm": "cpp",
34 | "array": "cpp",
35 | "atomic": "cpp",
36 | "bit": "cpp",
37 | "bitset": "cpp",
38 | "cctype": "cpp",
39 | "cfenv": "cpp",
40 | "charconv": "cpp",
41 | "chrono": "cpp",
42 | "cinttypes": "cpp",
43 | "clocale": "cpp",
44 | "cmath": "cpp",
45 | "codecvt": "cpp",
46 | "compare": "cpp",
47 | "complex": "cpp",
48 | "condition_variable": "cpp",
49 | "csetjmp": "cpp",
50 | "csignal": "cpp",
51 | "cstdarg": "cpp",
52 | "cstddef": "cpp",
53 | "cstdint": "cpp",
54 | "cstdio": "cpp",
55 | "cstdlib": "cpp",
56 | "cstring": "cpp",
57 | "ctime": "cpp",
58 | "cwchar": "cpp",
59 | "cwctype": "cpp",
60 | "exception": "cpp",
61 | "coroutine": "cpp",
62 | "propagate_const": "cpp",
63 | "forward_list": "cpp",
64 | "fstream": "cpp",
65 | "functional": "cpp",
66 | "future": "cpp",
67 | "initializer_list": "cpp",
68 | "iomanip": "cpp",
69 | "ios": "cpp",
70 | "iostream": "cpp",
71 | "istream": "cpp",
72 | "iterator": "cpp",
73 | "limits": "cpp",
74 | "locale": "cpp",
75 | "memory": "cpp",
76 | "mutex": "cpp",
77 | "new": "cpp",
78 | "numeric": "cpp",
79 | "ostream": "cpp",
80 | "random": "cpp",
81 | "ratio": "cpp",
82 | "regex": "cpp",
83 | "scoped_allocator": "cpp",
84 | "span": "cpp",
85 | "sstream": "cpp",
86 | "stdexcept": "cpp",
87 | "streambuf": "cpp",
88 | "string": "cpp",
89 | "string_view": "cpp",
90 | "strstream": "cpp",
91 | "system_error": "cpp",
92 | "tuple": "cpp",
93 | "type_traits": "cpp",
94 | "typeindex": "cpp",
95 | "typeinfo": "cpp",
96 | "utility": "cpp",
97 | "valarray": "cpp",
98 | "xstring": "cpp",
99 | "xlocale": "cpp",
100 | "xlocbuf": "cpp",
101 | "concepts": "cpp",
102 | "filesystem": "cpp",
103 | "shared_mutex": "cpp",
104 | "xfacet": "cpp",
105 | "xhash": "cpp",
106 | "xiosbase": "cpp",
107 | "xlocinfo": "cpp",
108 | "xlocmes": "cpp",
109 | "xlocmon": "cpp",
110 | "xlocnum": "cpp",
111 | "xloctime": "cpp",
112 | "xmemory": "cpp",
113 | "xstddef": "cpp",
114 | "xtr1common": "cpp",
115 | "xtree": "cpp",
116 | "xutility": "cpp",
117 | "format": "cpp",
118 | "ranges": "cpp",
119 | "stop_token": "cpp"
120 | }
121 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "NDK Build",
6 | "detail": "Builds the library using ndk-build.cmd",
7 | "type": "shell",
8 | "command": "ndk-build",
9 | "windows": {
10 | "command": "ndk-build.cmd"
11 | },
12 | "args": ["NDK_PROJECT_PATH=.", "APP_BUILD_SCRIPT=./Android.mk", "NDK_APPLICATION_MK=./Application.mk"],
13 | "group": "build",
14 | "options": {
15 | "env": {}
16 | }
17 | },
18 | {
19 | "label": "Powershell Build",
20 | "detail": "Builds the library using Powershell (recommended)",
21 | "type": "shell",
22 | "command": "./build.ps1",
23 | "windows": {
24 | "command": "./build.ps1"
25 | },
26 | "group": {
27 | "kind": "build",
28 | "isDefault": true
29 | },
30 | "options": {
31 | "env": {}
32 | }
33 | },
34 | {
35 | "label": "Powershell Build and Copy",
36 | "detail": "Builds and copies the library to the Quest using ADB and force-quits Beat Saber",
37 | "type": "shell",
38 | "command": "./copy.ps1",
39 | "windows": {
40 | "command": "./copy.ps1"
41 | },
42 | "group": "build",
43 | "options": {
44 | "env": {}
45 | }
46 | },
47 | {
48 | "label": "QMOD Build",
49 | "detail": "Builds a .qmod to be installed into BMBF or QuestPatcher",
50 | "type": "shell",
51 | "command": "./buildQMOD.ps1",
52 | "windows": {
53 | "command": "./buildQMOD.ps1"
54 | },
55 | "args": [],
56 | "group": "build",
57 | "options": {
58 | "env": {}
59 | }
60 | },
61 | {
62 | "label": "Start logging",
63 | "detail": "Begin logging from the Quest to the console",
64 | "type": "shell",
65 | "command": "./start-logging.ps1",
66 | "windows": {
67 | "command": "./start-logging.ps1"
68 | }
69 | },
70 | {
71 | "label": "Start logging to file",
72 | "detail": "Begin logging from the Quest to the console and saving output to a file 'logcat.log'",
73 | "type": "shell",
74 | "command": "./start-logging.ps1 --file",
75 | "windows": {
76 | "command": "./start-logging.ps1 --file"
77 | }
78 | },
79 | {
80 | "label": "Restart Beat Saber",
81 | "detail": "Force-quits and restarts Beat Saber on the Quest",
82 | "type": "shell",
83 | "command": "./start-logging.ps1",
84 | "windows": {
85 | "command": "./start-logging.ps1"
86 | }
87 | }
88 | ]
89 | }
90 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # include some defines automatically made by qpm
2 | include(qpm_defines.cmake)
3 |
4 | # override mod id
5 | set(MOD_ID "custom-json-data")
6 |
7 | # Enable link time optimization
8 | # In my experience, this can be highly unstable but it nets a huge size optimization and likely performance
9 | # However, the instability was seen using Android.mk/ndk-build builds. With Ninja + CMake, this problem seems to have been solved.
10 | # As always, test thoroughly
11 | # - Fern
12 | # set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
13 |
14 | cmake_minimum_required(VERSION 3.21)
15 | project(${COMPILE_ID})
16 |
17 | # export compile commands for significantly better intellisense
18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
19 |
20 | # c++ standard
21 | set(CMAKE_CXX_STANDARD 20)
22 | set(CMAKE_CXX_STANDARD_REQUIRED 20)
23 |
24 | # define that stores the actual source directory
25 | set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
26 | set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
27 |
28 | # compile options used
29 | add_compile_options(-frtti -fexceptions)
30 | add_compile_options(-O3)
31 | # compile definitions used
32 | add_compile_definitions(VERSION=\"${MOD_VERSION}\")
33 | add_compile_definitions(MOD_ID=\"${MOD_ID}\")
34 |
35 | # recursively get all src files
36 | RECURSE_FILES(cpp_file_list ${SOURCE_DIR}/*.cpp)
37 | RECURSE_FILES(c_file_list ${SOURCE_DIR}/*.c)
38 |
39 | RECURSE_FILES(inline_hook_c ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.c)
40 | RECURSE_FILES(inline_hook_cpp ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.cpp)
41 |
42 | # add all src files to compile
43 | add_library(
44 | ${COMPILE_ID}
45 | SHARED
46 | ${cpp_file_list}
47 | ${c_file_list}
48 | ${inline_hook_c}
49 | ${inline_hook_cpp}
50 | )
51 |
52 | target_include_directories(${COMPILE_ID} PRIVATE .)
53 |
54 | # add src dir as include dir
55 | target_include_directories(${COMPILE_ID} PRIVATE ${SOURCE_DIR})
56 | # add include dir as include dir
57 | target_include_directories(${COMPILE_ID} PRIVATE ${INCLUDE_DIR})
58 | # add shared dir as include dir
59 | target_include_directories(${COMPILE_ID} PUBLIC ${SHARED_DIR})
60 | # codegen includes
61 | target_include_directories(${COMPILE_ID} PRIVATE ${EXTERN_DIR}/includes/${CODEGEN_ID}/include)
62 |
63 | target_link_libraries(${COMPILE_ID} PRIVATE -llog)
64 | # add extern stuff like libs and other includes
65 | include(extern.cmake)
66 |
67 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
68 | COMMAND ${CMAKE_STRIP} -d --strip-all
69 | "lib${COMPILE_ID}.so" -o "stripped_lib${COMPILE_ID}.so"
70 | COMMENT "Strip debug symbols done on final binary.")
71 |
72 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
73 | COMMAND ${CMAKE_COMMAND} -E make_directory debug
74 | COMMENT "Make directory for debug symbols"
75 | )
76 |
77 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
78 | COMMAND ${CMAKE_COMMAND} -E rename lib${COMPILE_ID}.so debug/lib${COMPILE_ID}.so
79 | COMMENT "Rename the lib to debug_ since it has debug symbols"
80 | )
81 |
82 | # strip debug symbols from the .so and all dependencies
83 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
84 | COMMAND ${CMAKE_COMMAND} -E rename stripped_lib${COMPILE_ID}.so lib${COMPILE_ID}.so
85 | COMMENT "Rename the stripped lib to regular"
86 | )
87 | foreach(so_file ${so_list})
88 | cmake_path(GET so_file FILENAME file)
89 |
90 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
91 | COMMAND ${CMAKE_COMMAND} -E copy ${so_file} debug/${file}
92 | COMMENT "Copy so files for ndk stack"
93 | )
94 |
95 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
96 | COMMAND ${CMAKE_STRIP} -g -S -d --strip-all ${so_file} -o ${file}
97 | COMMENT "Strip debug symbols from the dependencies")
98 | endforeach()
99 |
100 | foreach(a_file ${a_list})
101 | cmake_path(GET a_file FILENAME file)
102 |
103 | add_custom_command(TARGET ${COMPILE_ID} POST_BUILD
104 | COMMAND ${CMAKE_COMMAND} -E copy ${a_file} debug/${file}
105 | COMMENT "Copy a files for ndk stack")
106 | endforeach()
107 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 StackDoubleFlow
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CustomJSONData
2 |
3 | Funny maps go brrr
4 |
5 | Use `qpm-rust s build` to build
6 | Same goes for `qpm-rust s copy` and `qpm-rust s qmod`
7 |
8 | ## Credits
9 |
10 | * [zoller27osu](https://github.com/zoller27osu), [Sc2ad](https://github.com/Sc2ad) and [jakibaki](https://github.com/jakibaki) - [beatsaber-hook](https://github.com/sc2ad/beatsaber-hook)
11 | * [raftario](https://github.com/raftario)
12 | * [Lauriethefish](https://github.com/Lauriethefish), [danrouse](https://github.com/danrouse) and [Bobby Shmurner](https://github.com/BobbyShmurner) for [this template](https://github.com/Lauriethefish/quest-mod-template)
13 |
--------------------------------------------------------------------------------
/include/CustomJSONDataHooks.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "beatsaber-hook/shared/utils/logging.hpp"
4 | #include "beatsaber-hook/shared/utils/hooking.hpp"
5 | #include "CJDLogger.h"
6 |
7 | namespace CustomJSONData {
8 |
9 | void InstallHooks();
10 |
11 | void InstallBeatmapHooks();
12 |
13 | namespace v2 {
14 | void InstallHooks();
15 | }
16 |
17 | namespace v3 {
18 | void InstallHooks();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/include/HookUtils.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
6 |
7 | #include "misc/BeatmapDataLoaderUtils.hpp"
8 | #include "misc/BeatmapFieldUtils.hpp"
9 |
10 | // for rapidjson error parsing
11 | #include "beatsaber-hook/shared/rapidjson/include/rapidjson/error/en.h"
12 |
13 | namespace CustomJSONData {
14 |
15 | template constexpr void addAllToVector(std::vector& vec, auto const& listPtr) {
16 | ListW::value_type> vList(listPtr);
17 |
18 | std::copy(vList.begin(), vList.end(), std::back_inserter(vec));
19 | };
20 |
21 | template constexpr void sortInPlace(std::span vec) {
22 | std::stable_sort(vec.begin(), vec.end(), TimeCompare::const_reference>);
23 | };
24 |
25 | template constexpr void cleanAndSort(std::vector& vec) {
26 | // remove nulls
27 | for (auto it = vec.begin(); it != vec.end();) {
28 | auto const& v = *it;
29 | if (!v) {
30 | it = vec.erase(it);
31 | continue;
32 | }
33 |
34 | it++;
35 | }
36 |
37 | sortInPlace({vec.begin(), vec.end()});
38 | };
39 |
40 | static std::optional> parseDocument(std::string_view stringData) {
41 | auto sharedDoc = std::make_shared();
42 | rapidjson::Document& doc = *sharedDoc;
43 | rapidjson::ParseResult result = doc.Parse(stringData.data());
44 |
45 | if (!result || doc.IsNull() || doc.HasParseError()) {
46 | std::string errorCodeStr(rapidjson::GetParseError_En(result.Code()));
47 | CJDLogger::Logger.fmtLog("Unable to parse json due to {}", errorCodeStr);
48 | return std::nullopt;
49 | }
50 |
51 | CJDLogger::Logger.fmtLog("Parsing json success");
52 |
53 | return sharedDoc;
54 | }
55 |
56 | static std::string GetVersionFromPath(std::string_view path) {
57 | // SongCore has a fallback so i guess i do too
58 | static std::string_view const fallback = "2.0.0";
59 |
60 | auto truncatedText = path.substr(0, 50);
61 | static std::regex const versionRegex(R"("_?version"\s*:\s*"[0-9]+\.[0-9]+\.?[0-9]?")");
62 | std::match_results matches;
63 | if (std::regex_search(truncatedText.begin(), truncatedText.end(), matches, versionRegex)) {
64 | if (!matches.empty()) {
65 | auto version = matches[0].str();
66 | version = version.substr(0, version.length() - 1);
67 | version = version.substr(version.find_last_of('\"') + 1, version.length());
68 |
69 | return version;
70 | }
71 | }
72 |
73 | return std::string(fallback);
74 | }
75 | } // namespace CustomJSONData
--------------------------------------------------------------------------------
/mod.template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json",
3 | "_QPVersion": "0.1.2",
4 | "name": "${mod_name}",
5 | "id": "${mod_id}",
6 | "author": "StackDoubleFlow, Fernthedev",
7 | "version": "${version}",
8 | "packageId": "com.beatgames.beatsaber",
9 | "packageVersion": "1.40.4_5283",
10 | "description": "Funny maps go brrr",
11 | "dependencies": [],
12 | "modFiles": [],
13 | "lateModFiles": ["${binary}"],
14 | "libraryFiles": [],
15 | "fileCopies": []
16 | }
17 |
--------------------------------------------------------------------------------
/qpm.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.4.0",
3 | "sharedDir": "shared",
4 | "dependenciesDir": "extern",
5 | "info": {
6 | "name": "CustomJSONData",
7 | "id": "custom-json-data",
8 | "version": "0.22.0",
9 | "url": "https://github.com/StackDoubleFlow/CustomJSONData",
10 | "additionalData": {
11 | "overrideSoName": "libcustom-json-data.so",
12 | "compileOptions": {
13 | "cppFlags": [
14 | "-DRAPIDJSON_NEON"
15 | ]
16 | },
17 | "cmake": true
18 | }
19 | },
20 | "workspace": {
21 | "scripts": {
22 | "build": [
23 | "pwsh ./scripts/build.ps1"
24 | ],
25 | "copy": [
26 | "pwsh ./scripts/copy.ps1"
27 | ],
28 | "log": [
29 | "pwsh ./scripts/copy.ps1 -log"
30 | ],
31 | "ndk-stack": [
32 | "pwsh ./scripts/ndk-stack.ps1"
33 | ]
34 | },
35 | "qmodIncludeDirs": [
36 | "./build",
37 | "./extern/libs"
38 | ],
39 | "qmodIncludeFiles": [],
40 | "qmodOutput": "custom-json-data.qmod"
41 | },
42 | "dependencies": [
43 | {
44 | "id": "beatsaber-hook",
45 | "versionRange": "^6.1.7",
46 | "additionalData": {}
47 | },
48 | {
49 | "id": "bs-cordl",
50 | "versionRange": "^4004.0.0",
51 | "additionalData": {}
52 | },
53 | {
54 | "id": "custom-types",
55 | "versionRange": "^0.18.0",
56 | "additionalData": {
57 | "includeQmod": true
58 | }
59 | },
60 | {
61 | "id": "songcore",
62 | "versionRange": "^1.1.20",
63 | "additionalData": {
64 | "includeQmod": true,
65 | "private": true
66 | }
67 | },
68 | {
69 | "id": "scotland2",
70 | "versionRange": "^0.1.6",
71 | "additionalData": {
72 | "includeQmod": false
73 | }
74 | },
75 | {
76 | "id": "paper2_scotland2",
77 | "versionRange": "^4.6.4",
78 | "additionalData": {}
79 | },
80 | {
81 | "id": "cpp-semver",
82 | "versionRange": "^0.1.1",
83 | "additionalData": {
84 | "private": true
85 | }
86 | },
87 | {
88 | "id": "sombrero",
89 | "versionRange": "^0.1.43",
90 | "additionalData": {
91 | "private": true
92 | }
93 | }
94 | ]
95 | }
--------------------------------------------------------------------------------
/qpm.shared.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/QuestPackageManager/QPM.Package/refs/heads/main/qpm.shared.schema.json",
3 | "config": {
4 | "version": "0.4.0",
5 | "sharedDir": "shared",
6 | "dependenciesDir": "extern",
7 | "info": {
8 | "name": "CustomJSONData",
9 | "id": "custom-json-data",
10 | "version": "0.22.0",
11 | "url": "https://github.com/StackDoubleFlow/CustomJSONData",
12 | "additionalData": {
13 | "overrideSoName": "libcustom-json-data.so",
14 | "compileOptions": {
15 | "cppFlags": [
16 | "-DRAPIDJSON_NEON"
17 | ]
18 | },
19 | "cmake": true
20 | }
21 | },
22 | "workspace": {
23 | "scripts": {
24 | "build": [
25 | "pwsh ./scripts/build.ps1"
26 | ],
27 | "copy": [
28 | "pwsh ./scripts/copy.ps1"
29 | ],
30 | "log": [
31 | "pwsh ./scripts/copy.ps1 -log"
32 | ],
33 | "ndk-stack": [
34 | "pwsh ./scripts/ndk-stack.ps1"
35 | ]
36 | },
37 | "qmodIncludeDirs": [
38 | "./build",
39 | "./extern/libs"
40 | ],
41 | "qmodIncludeFiles": [],
42 | "qmodOutput": "custom-json-data.qmod"
43 | },
44 | "dependencies": [
45 | {
46 | "id": "beatsaber-hook",
47 | "versionRange": "^6.1.7",
48 | "additionalData": {}
49 | },
50 | {
51 | "id": "bs-cordl",
52 | "versionRange": "^4004.0.0",
53 | "additionalData": {}
54 | },
55 | {
56 | "id": "custom-types",
57 | "versionRange": "^0.18.0",
58 | "additionalData": {
59 | "includeQmod": true
60 | }
61 | },
62 | {
63 | "id": "songcore",
64 | "versionRange": "^1.1.20",
65 | "additionalData": {
66 | "includeQmod": true,
67 | "private": true
68 | }
69 | },
70 | {
71 | "id": "scotland2",
72 | "versionRange": "^0.1.6",
73 | "additionalData": {
74 | "includeQmod": false
75 | }
76 | },
77 | {
78 | "id": "paper2_scotland2",
79 | "versionRange": "^4.6.4",
80 | "additionalData": {}
81 | },
82 | {
83 | "id": "cpp-semver",
84 | "versionRange": "^0.1.1",
85 | "additionalData": {
86 | "private": true
87 | }
88 | },
89 | {
90 | "id": "sombrero",
91 | "versionRange": "^0.1.43",
92 | "additionalData": {
93 | "private": true
94 | }
95 | }
96 | ]
97 | },
98 | "restoredDependencies": [
99 | {
100 | "dependency": {
101 | "id": "custom-types",
102 | "versionRange": "=0.18.2",
103 | "additionalData": {
104 | "soLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.2/libcustom-types.so",
105 | "debugSoLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.2/debug_libcustom-types.so",
106 | "overrideSoName": "libcustom-types.so",
107 | "modLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.2/CustomTypes.qmod",
108 | "branchName": "version/v0_18_2",
109 | "compileOptions": {
110 | "cppFlags": [
111 | "-Wno-invalid-offsetof"
112 | ]
113 | },
114 | "cmake": true
115 | }
116 | },
117 | "version": "0.18.2"
118 | },
119 | {
120 | "dependency": {
121 | "id": "sombrero",
122 | "versionRange": "=0.1.43",
123 | "additionalData": {
124 | "headersOnly": true,
125 | "branchName": "version/v0_1_43"
126 | }
127 | },
128 | "version": "0.1.43"
129 | },
130 | {
131 | "dependency": {
132 | "id": "bs-cordl",
133 | "versionRange": "=4004.0.0",
134 | "additionalData": {
135 | "headersOnly": true,
136 | "branchName": "version/v4004_0_0",
137 | "compileOptions": {
138 | "includePaths": [
139 | "include"
140 | ],
141 | "cppFeatures": [],
142 | "cppFlags": [
143 | "-DNEED_UNSAFE_CSHARP",
144 | "-fdeclspec",
145 | "-DUNITY_2021",
146 | "-DHAS_CODEGEN",
147 | "-Wno-invalid-offsetof"
148 | ]
149 | }
150 | }
151 | },
152 | "version": "4004.0.0"
153 | },
154 | {
155 | "dependency": {
156 | "id": "paper2_scotland2",
157 | "versionRange": "=4.6.4",
158 | "additionalData": {
159 | "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.6.4/libpaper2_scotland2.so",
160 | "overrideSoName": "libpaper2_scotland2.so",
161 | "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.6.4/paper2_scotland2.qmod",
162 | "branchName": "version/v4_6_4",
163 | "compileOptions": {
164 | "systemIncludes": [
165 | "shared/utfcpp/source"
166 | ]
167 | },
168 | "cmake": false
169 | }
170 | },
171 | "version": "4.6.4"
172 | },
173 | {
174 | "dependency": {
175 | "id": "cpp-semver",
176 | "versionRange": "=0.1.2",
177 | "additionalData": {
178 | "headersOnly": true,
179 | "branchName": "version-v0.1.2"
180 | }
181 | },
182 | "version": "0.1.2"
183 | },
184 | {
185 | "dependency": {
186 | "id": "fmt",
187 | "versionRange": "=11.0.2",
188 | "additionalData": {
189 | "headersOnly": true,
190 | "branchName": "version/v11_0_2",
191 | "compileOptions": {
192 | "systemIncludes": [
193 | "fmt/include/"
194 | ],
195 | "cppFlags": [
196 | "-DFMT_HEADER_ONLY"
197 | ]
198 | }
199 | }
200 | },
201 | "version": "11.0.2"
202 | },
203 | {
204 | "dependency": {
205 | "id": "beatsaber-hook",
206 | "versionRange": "=6.4.1",
207 | "additionalData": {
208 | "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.1/libbeatsaber-hook.so",
209 | "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.1/debug_libbeatsaber-hook.so",
210 | "overrideSoName": "libbeatsaber-hook.so",
211 | "modLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.1/beatsaber-hook.qmod",
212 | "branchName": "version/v6_4_1",
213 | "cmake": true
214 | }
215 | },
216 | "version": "6.4.1"
217 | },
218 | {
219 | "dependency": {
220 | "id": "songcore",
221 | "versionRange": "=1.1.20",
222 | "additionalData": {
223 | "soLink": "https://github.com/raineaeternal/Quest-SongCore/releases/download/v1.1.20/libsongcore.so",
224 | "debugSoLink": "https://github.com/raineaeternal/Quest-SongCore/releases/download/v1.1.20/debug_libsongcore.so",
225 | "overrideSoName": "libsongcore.so",
226 | "modLink": "https://github.com/raineaeternal/Quest-SongCore/releases/download/v1.1.20/SongCore.qmod",
227 | "branchName": "version/v1_1_20",
228 | "cmake": true
229 | }
230 | },
231 | "version": "1.1.20"
232 | },
233 | {
234 | "dependency": {
235 | "id": "scotland2",
236 | "versionRange": "=0.1.6",
237 | "additionalData": {
238 | "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/libsl2.so",
239 | "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/debug_libsl2.so",
240 | "overrideSoName": "libsl2.so",
241 | "branchName": "version/v0_1_6"
242 | }
243 | },
244 | "version": "0.1.6"
245 | },
246 | {
247 | "dependency": {
248 | "id": "libil2cpp",
249 | "versionRange": "=0.4.0",
250 | "additionalData": {
251 | "headersOnly": true,
252 | "compileOptions": {
253 | "systemIncludes": [
254 | "il2cpp/external/baselib/Include",
255 | "il2cpp/external/baselib/Platforms/Android/Include"
256 | ]
257 | }
258 | }
259 | },
260 | "version": "0.4.0"
261 | }
262 | ]
263 | }
--------------------------------------------------------------------------------
/scripts/build.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$false)]
3 | [Switch] $clean,
4 |
5 | [Parameter(Mandatory=$false)]
6 | [Switch] $help
7 | )
8 |
9 | if ($help -eq $true) {
10 | Write-Output "`"Build`" - Copiles your mod into a `".so`" or a `".a`" library"
11 | Write-Output "`n-- Arguments --`n"
12 |
13 | Write-Output "-Clean `t`t Deletes the `"build`" folder, so that the entire library is rebuilt"
14 |
15 | exit
16 | }
17 |
18 | # if user specified clean, remove all build files
19 | if ($clean.IsPresent) {
20 | if (Test-Path -Path "build") {
21 | remove-item build -R
22 | }
23 | }
24 |
25 |
26 | if (($clean.IsPresent) -or (-not (Test-Path -Path "build"))) {
27 | new-item -Path build -ItemType Directory
28 | }
29 |
30 | & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build
31 | & cmake --build ./build
32 |
--------------------------------------------------------------------------------
/scripts/copy.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$false)]
3 | [Switch] $clean,
4 |
5 | [Parameter(Mandatory=$false)]
6 | [Switch] $log,
7 |
8 | [Parameter(Mandatory=$false)]
9 | [Switch] $useDebug,
10 |
11 | [Parameter(Mandatory=$false)]
12 | [Switch] $self,
13 |
14 | [Parameter(Mandatory=$false)]
15 | [Switch] $all,
16 |
17 | [Parameter(Mandatory=$false)]
18 | [String] $custom="",
19 |
20 | [Parameter(Mandatory=$false)]
21 | [String] $file="",
22 |
23 | [Parameter(Mandatory=$false)]
24 | [Switch] $help
25 | )
26 |
27 | if ($help -eq $true) {
28 | Write-Output "`"Copy`" - Builds and copies your mod to your quest, and also starts Beat Saber with optional logging"
29 | Write-Output "`n-- Arguments --`n"
30 |
31 | Write-Output "-Clean `t`t Performs a clean build (equvilant to running `"build -clean`")"
32 | Write-Output "-UseDebug `t Copies the debug version of the mod to your quest"
33 | Write-Output "-Log `t`t Logs Beat Saber using the `"Start-Logging`" command"
34 |
35 | Write-Output "`n-- Logging Arguments --`n"
36 |
37 | & $PSScriptRoot/start-logging.ps1 -help -excludeHeader
38 |
39 | exit
40 | }
41 |
42 | & $PSScriptRoot/build.ps1 -clean:$clean
43 |
44 | if ($LASTEXITCODE -ne 0) {
45 | Write-Output "Failed to build, exiting..."
46 | exit $LASTEXITCODE
47 | }
48 |
49 | & $PSScriptRoot/validate-modjson.ps1
50 | if ($LASTEXITCODE -ne 0) {
51 | exit $LASTEXITCODE
52 | }
53 | & qpm qmod manifest
54 | $modJson = Get-Content "./mod.json" -Raw | ConvertFrom-Json
55 |
56 | $modFiles = $modJson.lateModFiles
57 | foreach ($fileName in $modFiles) {
58 | if ($useDebug -eq $true) {
59 | & adb push build/debug/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName
60 | } else {
61 | & adb push build/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName
62 | }
63 | }
64 |
65 | & $PSScriptRoot/restart-game.ps1
66 |
67 | if ($log -eq $true) {
68 | & adb logcat -c
69 | & adb logcat > log.log
70 | # & $PSScriptRoot/start-logging.ps1 -self:$self -all:$all -custom:$custom -file:$file
71 | }
72 |
--------------------------------------------------------------------------------
/scripts/createqmod.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$false)]
3 | [String] $qmodName="",
4 |
5 | [Parameter(Mandatory=$false)]
6 | [Switch] $help
7 | )
8 |
9 | if ($help -eq $true) {
10 | Write-Output "`"createqmod`" - Creates a .qmod file with your compiled libraries and mod.json."
11 | Write-Output "`n-- Arguments --`n"
12 |
13 | Write-Output "-QmodName `t The file name of your qmod"
14 |
15 | exit
16 | }
17 |
18 | $mod = "./mod.json"
19 |
20 | & $PSScriptRoot/validate-modjson.ps1
21 | if ($LASTEXITCODE -ne 0) {
22 | exit $LASTEXITCODE
23 | }
24 | $modJson = Get-Content $mod -Raw | ConvertFrom-Json
25 |
26 | if ($qmodName -eq "") {
27 | $qmodName = $modJson.name
28 | }
29 |
30 | $filelist = @($mod)
31 |
32 | $cover = "./" + $modJson.coverImage
33 | if ((-not ($cover -eq "./")) -and (Test-Path $cover)) {
34 | $filelist += ,$cover
35 | }
36 |
37 | foreach ($mod in $modJson.modFiles) {
38 | $path = "./build/" + $mod
39 | if (-not (Test-Path $path)) {
40 | $path = "./extern/libs/" + $mod
41 | }
42 | if (-not (Test-Path $path)) {
43 | Write-Output "Error: could not find dependency: $path"
44 | exit 1
45 | }
46 | $filelist += $path
47 | }
48 |
49 | foreach ($lib in $modJson.libraryFiles) {
50 | $path = "./build/" + $lib
51 | if (-not (Test-Path $path)) {
52 | $path = "./extern/libs/" + $lib
53 | }
54 | if (-not (Test-Path $path)) {
55 | Write-Output "Error: could not find dependency: $path"
56 | exit 1
57 | }
58 | $filelist += $path
59 | }
60 |
61 | $zip = $qmodName + ".zip"
62 | $qmod = $qmodName + ".qmod"
63 |
64 | Compress-Archive -Path $filelist -DestinationPath $zip -Update
65 | Move-Item $zip $qmod -Force
66 |
--------------------------------------------------------------------------------
/scripts/decompile.sh:
--------------------------------------------------------------------------------
1 | ilspycmd -r "/home/stack/.local/share/Steam/steamapps/common/Beat Saber/Beat Saber_Data/Managed" -t ${1} "/home/stack/.local/share/Steam/steamapps/common/Beat Saber/Beat Saber_Data/Managed/Main.dll"
--------------------------------------------------------------------------------
/scripts/ndk-stack.ps1:
--------------------------------------------------------------------------------
1 | if (Test-Path "./ndkpath.txt")
2 | {
3 | $NDKPath = Get-Content ./ndkpath.txt
4 | } else {
5 | $NDKPath = $ENV:ANDROID_NDK_HOME
6 | }
7 |
8 | $stackScript = "$NDKPath/ndk-stack"
9 | if (-not ($PSVersionTable.PSEdition -eq "Core")) {
10 | $stackScript += ".cmd"
11 | }
12 |
13 |
14 | Get-Content ./log.log | & $stackScript -sym ./build/debug/ > log_unstripped.log
--------------------------------------------------------------------------------
/scripts/pull-tombstone.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$false)]
3 | [String] $fileName = "RecentCrash.log",
4 |
5 | [Parameter(Mandatory=$false)]
6 | [Switch] $analyze,
7 |
8 | [Parameter(Mandatory=$false)]
9 | [Switch] $help
10 | )
11 |
12 | if ($help -eq $true) {
13 | Write-Output "`"Pull-Tombstone`" - Finds and pulls the most recent tombstone from your quest, optionally analyzing it with ndk-stack"
14 | Write-Output "`n-- Arguments --`n"
15 |
16 | Write-Output "-FileName `t The name for the output file, defaulting to RecentCrash.log"
17 | Write-Output "-Analyze `t Runs ndk-stack on the file after pulling"
18 |
19 | exit
20 | }
21 |
22 | $global:currentDate = get-date
23 | $global:recentDate = $Null
24 | $global:recentTombstone = $Null
25 |
26 | for ($i = 0; $i -lt 3; $i++) {
27 | $stats = & adb shell stat /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$i
28 | $date = (Select-String -Input $stats -Pattern "(?<=Modify: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?=.\d{9})").Matches.Value
29 | if([string]::IsNullOrEmpty($date)) {
30 | Write-Output "Failed to pull tombstone, exiting..."
31 | exit 1;
32 | }
33 | $dateObj = [datetime]::ParseExact($date, "yyyy-MM-dd HH:mm:ss", $Null)
34 | $difference = [math]::Round(($currentDate - $dateObj).TotalMinutes)
35 | if ($difference -eq 1) {
36 | Write-Output "Found tombstone_0$i $difference minute ago"
37 | } else {
38 | Write-Output "Found tombstone_0$i $difference minutes ago"
39 | }
40 | if (-not $recentDate -or $recentDate -lt $dateObj) {
41 | $recentDate = $dateObj
42 | $recentTombstone = $i
43 | }
44 | }
45 |
46 | Write-Output "Latest tombstone was tombstone_0$recentTombstone"
47 |
48 | & adb pull /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$recentTombstone $fileName
49 |
50 | if ($analyze) {
51 | & $PSScriptRoot/ndk-stack.ps1 -logName:$fileName
52 | }
53 |
--------------------------------------------------------------------------------
/scripts/restart-game.ps1:
--------------------------------------------------------------------------------
1 | adb shell am force-stop com.beatgames.beatsaber
2 | adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity
3 |
--------------------------------------------------------------------------------
/scripts/start-logging.ps1:
--------------------------------------------------------------------------------
1 | Param(
2 | [Parameter(Mandatory=$false)]
3 | [Switch] $self,
4 |
5 | [Parameter(Mandatory=$false)]
6 | [Switch] $all,
7 |
8 | [Parameter(Mandatory=$false)]
9 | [String] $custom="",
10 |
11 | [Parameter(Mandatory=$false)]
12 | [String] $file="",
13 |
14 | [Parameter(Mandatory=$false)]
15 | [Switch] $help,
16 |
17 | [Parameter(Mandatory=$false)]
18 | [Switch] $excludeHeader
19 | )
20 |
21 | if ($help -eq $true) {
22 | if ($excludeHeader -eq $false) {
23 | Write-Output "`"Start-Logging`" - Logs Beat Saber using `"adb logcat`""
24 | Write-Output "`n-- Arguments --`n"
25 | }
26 |
27 | Write-Output "-Self `t`t Only Logs your mod and Crashes"
28 | Write-Output "-All `t`t Logs everything, including logs made by the Quest itself"
29 | Write-Output "-Custom `t Specify a specific logging pattern, e.g `"custom-types|questui`""
30 | Write-Output "`t`t NOTE: The paterent `"AndriodRuntime|CRASH`" is always appended to a custom pattern"
31 | Write-Output "-File `t`t Saves the output of the log to the file name given"
32 |
33 | exit
34 | }
35 |
36 | $bspid = adb shell pidof com.beatgames.beatsaber
37 | $command = "adb logcat "
38 |
39 | if ($all -eq $false) {
40 | $loops = 0
41 | while ([string]::IsNullOrEmpty($bspid) -and $loops -lt 3) {
42 | Start-Sleep -Milliseconds 100
43 | $bspid = adb shell pidof com.beatgames.beatsaber
44 | $loops += 1
45 | }
46 |
47 | if ([string]::IsNullOrEmpty($bspid)) {
48 | Write-Output "Could not connect to adb, exiting..."
49 | exit 1
50 | }
51 |
52 | $command += "--pid $bspid"
53 | }
54 |
55 | if ($all -eq $false) {
56 | $pattern = "("
57 | if ($self -eq $true) {
58 | $modID = (Get-Content "./mod.json" -Raw | ConvertFrom-Json).id
59 | $pattern += "$modID|"
60 | }
61 | if (![string]::IsNullOrEmpty($custom)) {
62 | $pattern += "$custom|"
63 | }
64 | if ($pattern -eq "(") {
65 | $pattern = "(QuestHook|modloader|"
66 | }
67 | $pattern += "AndroidRuntime|CRASH)"
68 | $command += " | Select-String -pattern `"$pattern`""
69 | }
70 |
71 | if (![string]::IsNullOrEmpty($file)) {
72 | $command += " | Out-File -FilePath $PSScriptRoot\$file"
73 | }
74 |
75 | Write-Output "Logging using Command `"$command`""
76 | Invoke-Expression $command
77 |
--------------------------------------------------------------------------------
/scripts/validate-modjson.ps1:
--------------------------------------------------------------------------------
1 | return
2 |
3 | $mod = "./mod.json"
4 |
5 | if (-not (Test-Path -Path $mod)) {
6 | if (Test-Path -Path ".\mod.template.json") {
7 | & qpm-rust qmod build
8 | if ($LASTEXITCODE -ne 0) {
9 | exit $LASTEXITCODE
10 | }
11 | }
12 | else {
13 | Write-Output "Error: mod.json and mod.template.json were not present"
14 | exit 1
15 | }
16 | }
17 |
18 | Write-Output "Creating qmod from mod.json"
19 |
20 | $psVersion = $PSVersionTable.PSVersion.Major
21 | if ($psVersion -ge 6) {
22 | $schemaUrl = "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json"
23 | Invoke-WebRequest $schemaUrl -OutFile ./mod.schema.json
24 |
25 | $schema = "./mod.schema.json"
26 | $modJsonRaw = Get-Content $mod -Raw
27 | $modSchemaRaw = Get-Content $schema -Raw
28 |
29 | Remove-Item $schema
30 |
31 | Write-Output "Validating mod.json..."
32 | if (-not ($modJsonRaw | Test-Json -Schema $modSchemaRaw)) {
33 | Write-Output "Error: mod.json is not valid"
34 | exit 1
35 | }
36 | }
37 | else {
38 | Write-Output "Could not validate mod.json with schema: powershell version was too low (< 6)"
39 | }
40 | exit
41 |
--------------------------------------------------------------------------------
/shared/CJDLogger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "beatsaber-hook/shared/utils/logging.hpp"
3 | #include "beatsaber-hook/shared/utils/il2cpp-utils.hpp"
4 |
5 | #include "scotland2/shared/loader.hpp"
6 |
7 | #include "paper2_scotland2/shared/logger.hpp"
8 |
9 | using LogLevel = Paper::LogLevel;
10 |
11 | namespace CustomJSONData {
12 | static modloader::ModInfo const modInfo{"CustomJSONData", VERSION, 1};
13 | }
14 |
15 | class CJDLogger {
16 | public:
17 | // Register file log in main.cpp
18 | static constexpr auto Logger =
19 | Paper::ConstLoggerContext("CustomJSONData"); // Paper::Logger::WithContext<"CustomJSONData", false>();
20 | };
21 |
22 | // Implements a try-catch handler which will first attempt to run the provided body.
23 | // If there is an uncaught RunMethodException, it will first attempt to log the backtrace.
24 | // If it holds a valid C# exception, it will attempt to raise it, such that it is caught in the il2cpp domain.
25 | // If an exception is thrown that is otherwise what-able is caught, it will attempt to call the what() method
26 | // and then rethrow the exception to the il2cpp domain.
27 | // If an unknown exception is caught, it will terminate explicitly, as opposed to letting it be thrown across the il2cpp
28 | // domain. All logs that occur as a result of this function will be under the core beatsaber-hook global logger.
29 | #define PAPER_IL2CPP_CATCH_HANDLER(...) \
30 | try { \
31 | __VA_ARGS__ \
32 | } catch (::il2cpp_utils::RunMethodException const& exc) { \
33 | CJDLogger::Logger.fmtLog("Uncaught RunMethodException! what(): {}", exc.what()); \
34 | Paper::Logger::WaitForFlush(); \
35 | CJDLogger::Logger.error("Uncaught RunMethodException! what(): {}", exc.what()); \
36 | exc.log_backtrace(); \
37 | CJDLogger::Logger.Backtrace(100); \
38 | if (exc.ex) { \
39 | exc.rethrow(); \
40 | } \
41 | SAFE_ABORT(); \
42 | } catch (::il2cpp_utils::exceptions::StackTraceException const& exc) { \
43 | CJDLogger::Logger.error("Uncaught StackTraceException! what(): {}", exc.what()); \
44 | CJDLogger::Logger.fmtLog("Uncaught StackTraceException! what(): {}", exc.what()); \
45 | exc.log_backtrace(); \
46 | CJDLogger::Logger.Backtrace(100); \
47 | SAFE_ABORT(); \
48 | } catch (::std::exception const& exc) { \
49 | CJDLogger::Logger.error("Uncaught C++ exception! type name: {}, what(): {}", typeid(exc).name(), exc.what()); \
50 | CJDLogger::Logger.fmtLog("Uncaught C++ exception! type name: {}, what(): {}", typeid(exc).name(), \
51 | exc.what()); \
52 | Paper::Logger::WaitForFlush(); \
53 | CJDLogger::Logger.Backtrace(100); \
54 | ::il2cpp_utils::raise(exc); \
55 | } catch (...) { \
56 | CJDLogger::Logger.fmtLog( \
57 | "Uncaught, unknown C++ exception (not std::exception) with no known what() method!"); \
58 | Paper::Logger::WaitForFlush(); \
59 | CJDLogger::Logger.error("Uncaught, unknown C++ exception (not std::exception) with no known what() method!"); \
60 | CJDLogger::Logger.Backtrace(100); \
61 | SAFE_ABORT(); \
62 | }
63 |
64 | template
65 | /// @brief Exposes a static wrapper method that forwards to the provided function pointer, wrapping it in
66 | /// IL2CPP_CATCH_HANDLER.
67 | struct PaperHookCatchWrapper;
68 |
69 | template struct PaperHookCatchWrapper {
70 | static R wrapper(TArgs... args) {
71 | PAPER_IL2CPP_CATCH_HANDLER(return Func(args...);)
72 | }
73 | };
74 |
75 | // Make a hook that uses the provided method pointer in a match an ensures the signature matches.
76 | // This should be your go-to hook macro when hooking anything that has a codegen equivalent.
77 | // Also includes a catch handler.
78 | #define MAKE_PAPER_HOOK_MATCH(name_, mPtr, retval, ...) \
79 | struct Hook_##name_ { \
80 | using funcType = retval (*)(__VA_ARGS__); \
81 | static_assert(std::is_same_v::funcType>, \
82 | "Hook method signature does not match!"); \
83 | constexpr static char const* name() { \
84 | return #name_; \
85 | } \
86 | static MethodInfo const* getInfo() { \
87 | return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); \
88 | } \
89 | static funcType* trampoline() { \
90 | return &name_; \
91 | } \
92 | static inline retval (*name_)(__VA_ARGS__) = nullptr; \
93 | static funcType hook() { \
94 | return &::PaperHookCatchWrapper<&hook_##name_, funcType>::wrapper; \
95 | } \
96 | static retval hook_##name_(__VA_ARGS__); \
97 | }; \
98 | retval Hook_##name_::hook_##name_(__VA_ARGS__)
99 |
100 | // Same as MAKE_PAPER_HOOK_MATCH, but for manual MethodInfo* usage.
101 | #define MAKE_PAPER_HOOK_FIND(name_, infoGet, retval, ...) \
102 | struct Hook_##name_ { \
103 | using funcType = retval (*)(__VA_ARGS__); \
104 | constexpr static char const* name() { \
105 | return #name_; \
106 | } \
107 | static MethodInfo const* getInfo() { \
108 | return infoGet; \
109 | } \
110 | static funcType* trampoline() { \
111 | return &name_; \
112 | } \
113 | static inline retval (*name_)(__VA_ARGS__) = nullptr; \
114 | static funcType hook() { \
115 | return &::PaperHookCatchWrapper<&hook_##name_, funcType>::wrapper; \
116 | } \
117 | static retval hook_##name_(__VA_ARGS__); \
118 | }; \
119 | retval Hook_##name_::hook_##name_(__VA_ARGS__)
--------------------------------------------------------------------------------
/shared/CustomBeatmapData.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "custom-types/shared/macros.hpp"
4 |
5 | #include "System/Func_2.hpp"
6 |
7 | #include "GlobalNamespace/BeatmapData.hpp"
8 | #include "GlobalNamespace/BeatmapEventData.hpp"
9 | #include "GlobalNamespace/ObstacleData.hpp"
10 | #include "GlobalNamespace/NoteData.hpp"
11 | #include "GlobalNamespace/WaypointData.hpp"
12 | #include "GlobalNamespace/BasicBeatmapEventData.hpp"
13 | #include "GlobalNamespace/SliderData.hpp"
14 | #include "GlobalNamespace/BeatmapDataSortedListForTypeAndIds_1.hpp"
15 | #include "GlobalNamespace/ISortedList_1.hpp"
16 | #include "System/Object.hpp"
17 | #include "System/Collections/IEnumerable.hpp"
18 | #include "System/Collections/IEnumerator.hpp"
19 | #include "System/Collections/Generic/IEnumerator_1.hpp"
20 | #include "System/Collections/Generic/IEnumerable_1.hpp"
21 | #include "System/Collections/Generic/LinkedList_1.hpp"
22 | #include "System/Collections/Generic/LinkedListNode_1.hpp"
23 |
24 | #include "LowLevelUtils.hpp"
25 | #include "CustomEventData.h"
26 | #include "JSONWrapper.h"
27 | #include "CJDLogger.h"
28 |
29 | namespace CustomJSONData {
30 | template
31 | static constexpr System::Collections::Generic::LinkedListNode_1*
32 | LinkedListNode_1_get_Next(System::Collections::Generic::LinkedListNode_1* self) {
33 | if (self->next != nullptr && self->next != self->list->head) {
34 | return self->next;
35 | }
36 | return nullptr;
37 | }
38 | } // namespace CustomJSONData
39 |
40 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomBeatmapData, GlobalNamespace::BeatmapData) {
41 | DECLARE_FASTER_CTOR(ctor, int numberOfLines);
42 | DECLARE_SIMPLE_DTOR();
43 |
44 | DECLARE_INSTANCE_METHOD(CustomBeatmapData*, BaseCopy);
45 |
46 | public:
47 | void AddBeatmapObjectDataOverride(GlobalNamespace::BeatmapObjectData * beatmapObjectData);
48 | void AddBeatmapObjectDataInOrderOverride(GlobalNamespace::BeatmapObjectData * beatmapObjectData);
49 | void InsertBeatmapEventDataOverride(GlobalNamespace::BeatmapEventData * beatmapObjectData);
50 | void InsertBeatmapEventDataInOrderOverride(GlobalNamespace::BeatmapEventData * beatmapObjectData);
51 |
52 | inline CustomBeatmapData* GetCopyOverride() {
53 | return GetFilteredCopyOverride([](auto i) constexpr { return i; });
54 | }
55 |
56 | template CustomBeatmapData* GetFilteredCopyOverride(F && filter) {
57 | _isCreatingFilteredCopy = true;
58 |
59 | CustomBeatmapData* copy = BaseCopy();
60 |
61 | auto linkedList = _allBeatmapData->get_items();
62 |
63 | for (auto node = linkedList->get_First(); node != nullptr; node = LinkedListNode_1_get_Next(node)) {
64 | auto beatmapDataItem = node->item;
65 |
66 | if (!beatmapDataItem) continue;
67 |
68 | beatmapDataItem = filter(beatmapDataItem->GetCopy());
69 |
70 | if (!beatmapDataItem) continue;
71 |
72 | if (auto event = il2cpp_utils::try_cast(beatmapDataItem)) {
73 | copy->InsertBeatmapEventDataInOrder(*event);
74 | }
75 |
76 | if (auto object = il2cpp_utils::try_cast(beatmapDataItem)) {
77 | copy->AddBeatmapObjectDataInOrder(*object);
78 | }
79 |
80 | if (auto customEvent = il2cpp_utils::try_cast(beatmapDataItem)) {
81 | copy->InsertCustomEventDataInOrder(*customEvent);
82 | }
83 | }
84 |
85 | _isCreatingFilteredCopy = false;
86 |
87 | return copy;
88 | }
89 |
90 | static System::Type* GetCustomType(Il2CppObject * obj);
91 | static System::Type* GetCustomType(Il2CppClass * obj);
92 |
93 | void InsertCustomEventData(CustomEventData * customEventData);
94 | void InsertCustomEventDataInOrder(CustomEventData * customEventData);
95 |
96 | template std::vector GetBeatmapItemsCpp(GlobalNamespace::BeatmapDataItem::BeatmapDataItemType type) {
97 | auto* list = reinterpret_cast*>(
98 | _beatmapDataItemsPerTypeAndId->GetList(GetCustomType(classof(T)), type.value__));
99 |
100 | if (!list) return {};
101 |
102 | auto linkedItems = list->get_items();
103 |
104 | std::vector items;
105 | items.reserve(linkedItems->get_Count());
106 |
107 | for (auto node = linkedItems->get_First(); node != nullptr; node = LinkedListNode_1_get_Next(node)) {
108 | auto val = node->item;
109 | if (!val) continue;
110 |
111 | items.template emplace_back(reinterpret_cast(val));
112 | }
113 |
114 | return items;
115 | }
116 |
117 | std::vector GetAllBeatmapItemsCpp() {
118 | if (!_allBeatmapData) return {};
119 |
120 | auto linkedItems = _allBeatmapData->get_items();
121 |
122 | std::vector items;
123 | items.reserve(linkedItems->get_Count());
124 |
125 | for (auto node = linkedItems->get_First(); node != nullptr; node = LinkedListNode_1_get_Next(node)) {
126 | auto val = node->item;
127 | if (!val) continue;
128 |
129 | items.template emplace_back(val);
130 | }
131 |
132 | return items;
133 | }
134 |
135 | std::vector beatmapObjectDatas;
136 | //
137 | std::vector beatmapEventDatas;
138 | //
139 | std::vector customEventDatas;
140 |
141 | DECLARE_INSTANCE_FIELD(bool, v2orEarlier);
142 | // optional
143 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapper*, customData);
144 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapperUTF16*, beatmapCustomData);
145 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapperUTF16*, levelCustomData);
146 | DECLARE_INSTANCE_FIELD(CustomJSONData::DocumentWrapper*, doc);
147 | };
148 |
149 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomBeatmapEventData, GlobalNamespace::BasicBeatmapEventData) {
150 | DECLARE_FASTER_CTOR(ctor, float time,
151 | ::GlobalNamespace::BasicBeatmapEventType basicBeatmapEventType, int value, float floatValue);
152 |
153 | DECLARE_OVERRIDE_METHOD(CustomBeatmapEventData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
154 |
155 | // optional
156 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapper*, customData);
157 | };
158 |
159 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomObstacleData, GlobalNamespace::ObstacleData) {
160 | DECLARE_FASTER_CTOR(ctor, float time, float endBeat, float beat, int rotation, int lineIndex,
161 | ::GlobalNamespace::NoteLineLayer lineLayer, float duration, int width, int height);
162 |
163 | DECLARE_OVERRIDE_METHOD(CustomObstacleData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
164 |
165 | // optional
166 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapper*, customData);
167 | // Used for Noodle Extensions
168 | DECLARE_INSTANCE_FIELD(float, bpm);
169 | DECLARE_INSTANCE_FIELD(float, aheadTimeNoodle);
170 | };
171 |
172 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomNoteData, GlobalNamespace::NoteData) {
173 | DECLARE_FASTER_CTOR(
174 | ctor, float time, float beat, int rotation, int lineIndex, ::GlobalNamespace::NoteLineLayer noteLineLayer,
175 | ::GlobalNamespace::NoteLineLayer beforeJumpNoteLineLayer, ::GlobalNamespace::NoteData::GameplayType gameplayType,
176 | ::GlobalNamespace::NoteData::ScoringType scoringType, ::GlobalNamespace::ColorType colorType,
177 | ::GlobalNamespace::NoteCutDirection cutDirection, float timeToNextColorNote, float timeToPrevColorNote,
178 | int flipLineIndex, float flipYSide, float cutDirectionAngleOffset, float cutSfxVolumeMultiplier);
179 |
180 | DECLARE_OVERRIDE_METHOD(CustomNoteData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
181 |
182 | // optional
183 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapper*, customData);
184 | // Used for Noodle Extensions
185 | DECLARE_INSTANCE_FIELD(float, bpm);
186 | DECLARE_INSTANCE_FIELD(float, aheadTimeNoodle);
187 | };
188 |
189 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomSliderData, GlobalNamespace::SliderData) {
190 | DECLARE_FASTER_CTOR(ctor, GlobalNamespace::SliderData::Type sliderType, ::GlobalNamespace::ColorType colorType,
191 | bool hasHeadNote, float headTime, float headBeat, int headRotation, int headLineIndex,
192 | ::GlobalNamespace::NoteLineLayer headLineLayer,
193 | ::GlobalNamespace::NoteLineLayer headBeforeJumpLineLayer, float headControlPointLengthMultiplier,
194 | ::GlobalNamespace::NoteCutDirection headCutDirection, float headCutDirectionAngleOffset,
195 | bool hasTailNote, float tailTime, int tailRotation, int tailLineIndex,
196 | ::GlobalNamespace::NoteLineLayer tailLineLayer,
197 | ::GlobalNamespace::NoteLineLayer tailBeforeJumpLineLayer, float tailControlPointLengthMultiplier,
198 | ::GlobalNamespace::NoteCutDirection tailCutDirection, float tailCutDirectionAngleOffset,
199 | ::GlobalNamespace::SliderMidAnchorMode midAnchorMode, int sliceCount, float squishAmount);
200 |
201 | DECLARE_OVERRIDE_METHOD(CustomSliderData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
202 |
203 | // optional
204 | DECLARE_INSTANCE_FIELD(CustomJSONData::JSONWrapper*, customData);
205 | // Used for Noodle Extensions
206 | DECLARE_INSTANCE_FIELD(float, bpm);
207 | };
208 |
209 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomWaypointData, GlobalNamespace::WaypointData) {
210 | DECLARE_FASTER_CTOR(ctor, float time, float beat, int rotation, int lineIndex,
211 | ::GlobalNamespace::NoteLineLayer lineLayer, ::GlobalNamespace::OffsetDirection offsetDirection);
212 |
213 | DECLARE_OVERRIDE_METHOD(CustomWaypointData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
214 |
215 | // Used for Noodle Extensions
216 | DECLARE_INSTANCE_FIELD(float, bpm);
217 | };
--------------------------------------------------------------------------------
/shared/CustomBeatmapSaveDatav2.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "custom-types/shared/macros.hpp"
4 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
5 |
6 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/BeatmapSaveData.hpp"
7 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/SpecialEventKeywordFiltersData.hpp"
8 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/SpecialEventsForKeyword.hpp"
9 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/NoteData.hpp"
10 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/SliderData.hpp"
11 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/EventData.hpp"
12 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/ObstacleData.hpp"
13 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/WaypointData.hpp"
14 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/ObstacleType.hpp"
15 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/ColorType.hpp"
16 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/NoteType.hpp"
17 |
18 | #include "BeatmapSaveDataCommon/BeatmapEventType.hpp"
19 | #include "BeatmapSaveDataCommon/NoteCutDirection.hpp"
20 | #include "BeatmapSaveDataCommon/SliderMidAnchorMode.hpp"
21 | #include "BeatmapSaveDataCommon/NoteLineLayer.hpp"
22 | #include "BeatmapSaveDataCommon/OffsetDirection.hpp"
23 | #include "BeatmapSaveDataCommon/Axis.hpp"
24 | #include "BeatmapSaveDataCommon/BasicEventTypesWithKeywords.hpp"
25 | #include "BeatmapSaveDataCommon/ExecutionTime.hpp"
26 | #include "BeatmapSaveDataCommon/BasicEventTypesWithKeywords.hpp"
27 |
28 | #include "GlobalNamespace/StandardLevelInfoSaveData.hpp"
29 |
30 | #include "JSONWrapper.h"
31 |
32 | #include "LowLevelUtils.hpp"
33 | #include "CustomEventData.h"
34 | #include "CJDLogger.h"
35 |
36 | namespace CustomJSONData::v2 {
37 | using CustomDataOpt = std::optional>;
38 | using CustomDataOptUTF16 = std::optional>;
39 |
40 | } // namespace CustomJSONData::v2
41 | DECLARE_CLASS_CODEGEN(CustomJSONData::v2, CustomBeatmapSaveData,
42 | BeatmapSaveDataVersion2_6_0AndEarlier::BeatmapSaveData) {
43 | DECLARE_FASTER_CTOR(
44 | ctor, System::Collections::Generic::List_1 * events,
45 | System::Collections::Generic::List_1 * notes,
46 | System::Collections::Generic::List_1 * sliders,
47 | System::Collections::Generic::List_1 * waypoints,
48 | System::Collections::Generic::List_1 * obstacles,
49 | BeatmapSaveDataVersion2_6_0AndEarlier::SpecialEventKeywordFiltersData * specialEventsKeywordFilters);
50 |
51 | DECLARE_SIMPLE_DTOR();
52 |
53 | public:
54 | static CustomBeatmapSaveData* Deserialize(std::shared_ptr const& sharedDoc);
55 |
56 | std::shared_ptr> customEventsData;
57 | std::shared_ptr doc;
58 | CustomDataOpt customData;
59 | CustomDataOptUTF16 beatmapCustomData;
60 | CustomDataOptUTF16 levelCustomData;
61 | };
62 |
63 | DECLARE_CLASS_CODEGEN(CustomJSONData::v2, CustomBeatmapSaveData_NoteData,
64 | BeatmapSaveDataVersion2_6_0AndEarlier::NoteData) {
65 | DECLARE_FASTER_CTOR(ctor, float time, int lineIndex, BeatmapSaveDataCommon::NoteLineLayer lineLayer,
66 | BeatmapSaveDataVersion2_6_0AndEarlier::NoteType type,
67 | BeatmapSaveDataCommon::NoteCutDirection cutDirection);
68 |
69 | DECLARE_SIMPLE_DTOR();
70 |
71 | public:
72 | CustomDataOpt customData;
73 | };
74 |
75 | DECLARE_CLASS_CODEGEN(CustomJSONData::v2, CustomBeatmapSaveData_SliderData,
76 | BeatmapSaveDataVersion2_6_0AndEarlier::SliderData) {
77 | DECLARE_FASTER_CTOR(ctor, BeatmapSaveDataVersion2_6_0AndEarlier::ColorType colorType, float headTime,
78 | int headLineIndex, BeatmapSaveDataCommon::NoteLineLayer headLineLayer,
79 | float headControlPointLengthMultiplier, BeatmapSaveDataCommon::NoteCutDirection headCutDirection,
80 | float tailTime, int tailLineIndex, BeatmapSaveDataCommon::NoteLineLayer tailLineLayer,
81 | float tailControlPointLengthMultiplier, BeatmapSaveDataCommon::NoteCutDirection tailCutDirection,
82 | BeatmapSaveDataCommon::SliderMidAnchorMode sliderMidAnchorMode);
83 |
84 | DECLARE_SIMPLE_DTOR();
85 |
86 | public:
87 | CustomDataOpt customData;
88 | };
89 |
90 | DECLARE_CLASS_CODEGEN(CustomJSONData::v2, CustomBeatmapSaveData_ObstacleData,
91 | BeatmapSaveDataVersion2_6_0AndEarlier::ObstacleData) {
92 | DECLARE_FASTER_CTOR(ctor, float time, int lineIndex, BeatmapSaveDataVersion2_6_0AndEarlier::ObstacleType type,
93 | float duration, int width);
94 |
95 | DECLARE_SIMPLE_DTOR();
96 |
97 | public:
98 | CustomDataOpt customData;
99 | };
100 |
101 | DECLARE_CLASS_CODEGEN(CustomJSONData::v2, CustomBeatmapSaveData_EventData,
102 | BeatmapSaveDataVersion2_6_0AndEarlier::EventData) {
103 | DECLARE_FASTER_CTOR(ctor, float time, BeatmapSaveDataCommon::BeatmapEventType type, int value, float floatValue);
104 |
105 | DECLARE_SIMPLE_DTOR();
106 |
107 | public:
108 | CustomDataOpt customData;
109 | };
--------------------------------------------------------------------------------
/shared/CustomBeatmapSaveDatav3.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "custom-types/shared/macros.hpp"
4 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
5 |
6 | #include "BeatmapSaveDataVersion3/ColorNoteData.hpp"
7 | #include "BeatmapSaveDataVersion3/BurstSliderData.hpp"
8 | #include "BeatmapSaveDataVersion3/BombNoteData.hpp"
9 | #include "BeatmapSaveDataVersion3/BurstSliderData.hpp"
10 | #include "BeatmapSaveDataVersion3/SliderData.hpp"
11 | #include "BeatmapSaveDataVersion3/SliderType.hpp"
12 | #include "BeatmapSaveDataVersion3/ObstacleData.hpp"
13 | #include "BeatmapSaveDataVersion3/WaypointData.hpp"
14 | #include "BeatmapSaveDataVersion3/BasicEventData.hpp"
15 | #include "BeatmapSaveDataVersion3/BpmChangeEventData.hpp"
16 | #include "BeatmapSaveDataVersion3/BeatmapSaveData.hpp"
17 | #include "BeatmapSaveDataVersion3/BeatmapSaveDataItem.hpp"
18 | #include "BeatmapSaveDataVersion3/LightColorBaseData.hpp"
19 | #include "BeatmapSaveDataVersion3/IndexFilter.hpp"
20 | #include "BeatmapSaveDataVersion3/EventBox.hpp"
21 | #include "BeatmapSaveDataVersion3/LightRotationEventBox.hpp"
22 | #include "BeatmapSaveDataVersion3/LightTranslationEventBox.hpp"
23 | #include "BeatmapSaveDataVersion3/LightColorEventBox.hpp"
24 | #include "BeatmapSaveDataVersion3/LightRotationBaseData.hpp"
25 | #include "BeatmapSaveDataVersion3/LightColorBaseData.hpp"
26 | #include "BeatmapSaveDataVersion3/LightTranslationBaseData.hpp"
27 | #include "BeatmapSaveDataVersion3/IntFxEventBaseData.hpp"
28 | #include "BeatmapSaveDataVersion3/FloatFxEventBaseData.hpp"
29 | #include "BeatmapSaveDataVersion3/FxEventsCollection.hpp"
30 | #include "BeatmapSaveDataVersion3/FxEventBox.hpp"
31 | #include "BeatmapSaveDataVersion3/FxEventBoxGroup.hpp"
32 | #include "BeatmapSaveDataVersion3/FxEventType.hpp"
33 | #include "BeatmapSaveDataVersion3/ColorBoostEventData.hpp"
34 | #include "BeatmapSaveDataVersion3/RotationEventData.hpp"
35 | #include "BeatmapSaveDataVersion3/RotationEventData.hpp"
36 |
37 | #include "GlobalNamespace/StandardLevelInfoSaveData.hpp"
38 |
39 | #include "LowLevelUtils.hpp"
40 | #include "CustomEventData.h"
41 | #include "CJDLogger.h"
42 | #include "JSONWrapper.h"
43 | #include "songcore/shared/CustomJSONData.hpp"
44 |
45 | namespace CustomJSONData::v2 {
46 | class CustomBeatmapSaveData;
47 | }
48 |
49 | namespace CustomJSONData::v3::Constants {
50 | // worst naming scheme ever
51 | static inline auto const beat = "b";
52 | static inline auto const colorType = "c";
53 | static inline auto const line = "x";
54 | static inline auto const layer = "y";
55 | static inline auto const cutDirection = "d";
56 | static inline auto const tailBeat = "tb";
57 | static inline auto const tailLine = "tx";
58 | static inline auto const tailLayer = "ty";
59 | static inline auto const eventBoxes = "e";
60 | static inline auto const groupId = "g";
61 | static inline auto const indexFilter = "f";
62 | static inline auto const beatDistributionParam = "w";
63 | static inline auto const beatDistributionParamType = "d";
64 |
65 | static inline auto const customData = "customData";
66 | } // namespace CustomJSONData::v3::Constants
67 |
68 | namespace CustomJSONData::v3 {
69 | using CustomDataOpt = std::optional>;
70 | using CustomDataOptUTF16 = std::optional>;
71 |
72 | } // namespace CustomJSONData::v3
73 |
74 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData, BeatmapSaveDataVersion3::BeatmapSaveData) {
75 | DECLARE_FASTER_CTOR(
76 | ctor, System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::BpmChangeEventData*> * bpmEvents,
77 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::RotationEventData*> * rotationEvents,
78 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::ColorNoteData*> * colorNotes,
79 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::BombNoteData*> * bombNotes,
80 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::ObstacleData*> * obstacles,
81 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::SliderData*> * sliders,
82 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::BurstSliderData*> * burstSliders,
83 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::WaypointData*> * waypoints,
84 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::BasicEventData*> * basicBeatmapEvents,
85 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::ColorBoostEventData*> * colorBoostBeatmapEvents,
86 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::LightColorEventBoxGroup*> *
87 | lightColorEventBoxGroups,
88 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::LightRotationEventBoxGroup*> *
89 | lightRotationEventBoxGroups,
90 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::LightTranslationEventBoxGroup*> *
91 | lightTranslationEventBoxGroups,
92 | ::System::Collections::Generic::List_1<::BeatmapSaveDataVersion3::FxEventBoxGroup*> * lightFxEventBoxGroups,
93 | BeatmapSaveDataVersion3::FxEventsCollection * eventsCollection,
94 | BeatmapSaveDataCommon::BasicEventTypesWithKeywords * basicEventTypesWithKeywords,
95 | bool useNormalEventsAsCompatibleEvents);
96 |
97 | DECLARE_SIMPLE_DTOR();
98 |
99 | public:
100 | static CustomBeatmapSaveData* Deserialize(std::shared_ptr const& sharedDoc);
101 | static CustomBeatmapSaveData* Convert2_6_0(CustomJSONData::v2::CustomBeatmapSaveData * beatmap);
102 |
103 | std::shared_ptr> customEventsData;
104 | std::shared_ptr doc;
105 | CustomDataOpt customData;
106 | CustomDataOptUTF16 beatmapCustomData;
107 | CustomDataOptUTF16 levelCustomData;
108 |
109 | DECLARE_INSTANCE_FIELD(bool, isV2);
110 | };
111 |
112 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_ColorNoteData, BeatmapSaveDataVersion3::ColorNoteData) {
113 | DECLARE_FASTER_CTOR(ctor, float beat, int line, int layer, BeatmapSaveDataCommon::NoteColorType color,
114 | BeatmapSaveDataCommon::NoteCutDirection cutDirection, int angleOffset);
115 | DECLARE_SIMPLE_DTOR();
116 |
117 | public:
118 | CustomDataOpt customData;
119 | };
120 |
121 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_BombNoteData, BeatmapSaveDataVersion3::BombNoteData) {
122 | DECLARE_FASTER_CTOR(ctor, float beat, int line, int layer);
123 | DECLARE_SIMPLE_DTOR();
124 |
125 | public:
126 | CustomDataOpt customData;
127 | };
128 |
129 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_SliderData, BeatmapSaveDataVersion3::SliderData) {
130 | DECLARE_FASTER_CTOR(ctor, BeatmapSaveDataCommon::NoteColorType colorType, float headBeat, int headLine, int headLayer,
131 | float headControlPointLengthMultiplier, BeatmapSaveDataCommon::NoteCutDirection headCutDirection,
132 | float tailBeat, int tailLine, int tailLayer, float tailControlPointLengthMultiplier,
133 | BeatmapSaveDataCommon::NoteCutDirection tailCutDirection,
134 | BeatmapSaveDataCommon::SliderMidAnchorMode sliderMidAnchorMode);
135 | DECLARE_SIMPLE_DTOR();
136 |
137 | public:
138 | CustomDataOpt customData;
139 | };
140 |
141 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_BurstSliderData,
142 | BeatmapSaveDataVersion3::BurstSliderData) {
143 | DECLARE_FASTER_CTOR(ctor, BeatmapSaveDataCommon::NoteColorType colorType, float headBeat, int headLine, int headLayer,
144 | BeatmapSaveDataCommon::NoteCutDirection headCutDirection, float tailBeat, int tailLine,
145 | int tailLayer, int sliceCount, float squishAmount);
146 | DECLARE_SIMPLE_DTOR();
147 |
148 | public:
149 | CustomDataOpt customData;
150 | };
151 |
152 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_ObstacleData, BeatmapSaveDataVersion3::ObstacleData) {
153 | DECLARE_FASTER_CTOR(ctor, float beat, int line, int layer, float duration, int width, int height);
154 |
155 | DECLARE_SIMPLE_DTOR();
156 |
157 | public:
158 | CustomDataOpt customData;
159 | };
160 |
161 | DECLARE_CLASS_CODEGEN(CustomJSONData::v3, CustomBeatmapSaveData_BasicEventData,
162 | BeatmapSaveDataVersion3::BasicEventData) {
163 | DECLARE_FASTER_CTOR(ctor, float beat, BeatmapSaveDataCommon::BeatmapEventType eventType, int value, float floatValue);
164 |
165 | DECLARE_SIMPLE_DTOR();
166 |
167 | public:
168 | CustomDataOpt customData;
169 | };
170 |
171 | // clang-format on
172 |
173 | namespace CustomJSONData::v3::Parser {
174 | CustomBeatmapSaveData_BurstSliderData* DeserializeBurstSlider(rapidjson::Value const& val);
175 | CustomBeatmapSaveData_SliderData* DeserializeSlider(rapidjson::Value const& val);
176 |
177 | CustomBeatmapSaveData_ObstacleData* DeserializeObstacle(rapidjson::Value const& val);
178 |
179 | CustomBeatmapSaveData_BombNoteData* DeserializeBombNote(rapidjson::Value const& val);
180 | CustomBeatmapSaveData_ColorNoteData* DeserializeColorNote(rapidjson::Value const& val);
181 |
182 | extern UnorderedEventCallback ParsedEvent;
183 | } // namespace CustomJSONData::v3::Parser
184 |
--------------------------------------------------------------------------------
/shared/CustomEventData.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "_config.hpp"
4 |
5 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
6 | #include "custom-types/shared/macros.hpp"
7 |
8 | #include "CJDLogger.h"
9 | #include "GlobalNamespace/BeatmapDataItem.hpp"
10 | #include "GlobalNamespace/BeatmapDataCallbackWrapper.hpp"
11 | #include "GlobalNamespace/BeatmapCallbacksController.hpp"
12 |
13 | #include "System/Collections/Generic/LinkedListNode_1.hpp"
14 |
15 | #include "UnityEngine/Object.hpp"
16 |
17 | #include "LowLevelUtils.hpp"
18 |
19 | namespace GlobalNamespace {
20 | class BeatmapCallbacksController;
21 | }
22 |
23 | #define FindMethodGetter(methodName) ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo()
24 |
25 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomEventData, GlobalNamespace::BeatmapDataItem) {
26 |
27 | private:
28 | DECLARE_FASTER_CTOR(ctor, float time);
29 |
30 | public:
31 | static CustomEventData* New(float time, std::string_view type, size_t typeHash, rapidjson::Value const* data);
32 |
33 | DECLARE_OVERRIDE_METHOD(CustomEventData*, GetCopy, il2cpp_utils::FindMethod("", "BeatmapDataItem", "GetCopy"));
34 |
35 | public:
36 | std::string_view type;
37 | size_t typeHash;
38 | rapidjson::Value const* data;
39 | };
40 |
41 | DECLARE_CLASS_CODEGEN(CustomJSONData, CustomBeatmapDataCallbackWrapper, GlobalNamespace::BeatmapDataCallbackWrapper) {
42 | DECLARE_FASTER_CTOR(ctor);
43 |
44 | DECLARE_SIMPLE_DTOR();
45 |
46 | DECLARE_OVERRIDE_METHOD(void, CallCallback,
47 | FindMethodGetter(&GlobalNamespace::BeatmapDataCallbackWrapper::CallCallback),
48 | GlobalNamespace::BeatmapDataItem* item);
49 |
50 | DECLARE_INSTANCE_FIELD(GlobalNamespace::BeatmapCallbacksController*, controller);
51 |
52 | std::function
53 | redirectEvent;
54 | };
55 |
56 | namespace CustomJSONData {
57 |
58 | class CustomEventSaveData {
59 | public:
60 | std::string_view type;
61 | size_t typeHash;
62 | float time;
63 | rapidjson::Value const* data;
64 |
65 | constexpr CustomEventSaveData(std::string_view type, size_t typeHash, float time, rapidjson::Value const* data)
66 | : type(type), typeHash(typeHash), time(time), data(data) {}
67 |
68 | constexpr CustomEventSaveData(std::string_view type, float time, rapidjson::Value const* data)
69 | : type(type), time(time), data(data) {
70 | typeHash = std::hash()(type);
71 | }
72 | };
73 |
74 | struct CustomEventCallbackData {
75 | void (*callback)(GlobalNamespace::BeatmapCallbacksController* callbackController, CustomJSONData::CustomEventData*);
76 |
77 | constexpr explicit CustomEventCallbackData(void (*callback)(GlobalNamespace::BeatmapCallbacksController*,
78 | CustomEventData*))
79 | : callback(callback) {}
80 | };
81 |
82 | class CJD_MOD_EXPORT CustomEventCallbacks {
83 | public:
84 | static std::vector customEventCallbacks;
85 | // For Noodle
86 | static SafePtr> firstNode;
87 |
88 | static void AddCustomEventCallback(void (*callback)(GlobalNamespace::BeatmapCallbacksController* callbackController,
89 | CustomJSONData::CustomEventData*));
90 |
91 | static void RegisterCallbacks(GlobalNamespace::BeatmapCallbacksController* callbackController);
92 | };
93 |
94 | } // end namespace CustomJSONData
--------------------------------------------------------------------------------
/shared/JSONWrapper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "custom-types/shared/macros.hpp"
4 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
5 |
6 | #include "System/Object.hpp"
7 |
8 | #include "CJDLogger.h"
9 |
10 | #include
11 | #include
12 |
13 | #include "songcore/shared/CustomJSONData.hpp"
14 | #include "LowLevelUtils.hpp"
15 |
16 | DECLARE_CLASS_CODEGEN(CustomJSONData, DocumentWrapper, Il2CppObject) {
17 |
18 | DECLARE_DEFAULT_CTOR();
19 | DECLARE_SIMPLE_DTOR();
20 |
21 | public:
22 | std::shared_ptr doc;
23 | };
24 |
25 | DECLARE_CLASS_CODEGEN(CustomJSONData, JSONWrapper, Il2CppObject) {
26 | DECLARE_FASTER_CTOR(ctor);
27 | DECLARE_SIMPLE_DTOR();
28 |
29 | DECLARE_INSTANCE_METHOD(JSONWrapper*, GetCopy);
30 |
31 | public:
32 | std::optional> value;
33 | std::unordered_map associatedData;
34 | };
35 |
36 | DECLARE_CLASS_CODEGEN(CustomJSONData, JSONWrapperUTF16, Il2CppObject) {
37 |
38 | DECLARE_FASTER_CTOR(ctor);
39 | DECLARE_SIMPLE_DTOR();
40 |
41 | public:
42 | std::optional> value;
43 | std::unordered_map associatedData;
44 |
45 | DECLARE_INSTANCE_METHOD(JSONWrapperUTF16*, GetCopy);
46 | };
47 |
--------------------------------------------------------------------------------
/shared/JsonUtils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "sombrero/shared/FastVector3.hpp"
7 | #include "sombrero/shared/FastVector2.hpp"
8 | #include "sombrero/shared/FastQuaternion.hpp"
9 | #include "sombrero/shared/FastColor.hpp"
10 |
11 | #ifndef RAPIDJSON_NEON
12 | #define RAPIDJSON_NEON
13 | #endif
14 |
15 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
16 | #include "CJDLogger.h"
17 |
18 | namespace NEJSON {
19 |
20 | static std::optional ReadOptionalBool(rapidjson::Value const& object, std::string_view const key) {
21 | auto itr = object.FindMember(key.data());
22 | if (itr != object.MemberEnd()) {
23 | if (itr->value.IsString()) {
24 | std::string boolS = itr->value.GetString();
25 | CJDLogger::Logger.fmtLog("ReadOptionalBool: THE VALUE IS A STRING WHY! value: \"{}\"", boolS);
26 |
27 | if (boolS == "true") {
28 | return true;
29 | }
30 |
31 | return false;
32 | }
33 |
34 | if (itr->value.IsNumber()) {
35 | return itr->value.GetInt() != 0;
36 | }
37 |
38 | return itr->value.GetBool();
39 | }
40 | return std::nullopt;
41 | }
42 |
43 | static std::optional ReadOptionalString(rapidjson::Value const& object, std::string_view const key) {
44 | auto itr = object.FindMember(key.data());
45 | if (itr != object.MemberEnd() && itr->value.IsString()) {
46 | return itr->value.GetString();
47 | }
48 | return std::nullopt;
49 | }
50 |
51 | static std::optional ReadOptionalFloat(rapidjson::Value const& object, std::string_view const key) {
52 | auto itr = object.FindMember(key.data());
53 | if (itr != object.MemberEnd()) {
54 | return itr->value.GetFloat();
55 | }
56 | return std::nullopt;
57 | }
58 |
59 | static std::optional ReadOptionalInt(rapidjson::Value const& object, std::string_view const key) {
60 | auto itr = object.FindMember(key.data());
61 | if (itr != object.MemberEnd()) {
62 | return itr->value.GetInt();
63 | }
64 | return std::nullopt;
65 | }
66 |
67 | static std::optional ReadOptionalVector2(rapidjson::Value const& object,
68 | std::string_view const key) {
69 | auto itr = object.FindMember(key.data());
70 | if (itr != object.MemberEnd() && itr->value.Size() >= 2) {
71 | float x = itr->value[0].GetFloat();
72 | float y = itr->value[1].GetFloat();
73 | return Sombrero::FastVector2(x, y);
74 | }
75 | return std::nullopt;
76 | }
77 |
78 | // Used for note flip
79 | static std::optional ReadOptionalVector2_emptyY(rapidjson::Value const& object,
80 | std::string_view const key) {
81 | auto itr = object.FindMember(key.data());
82 |
83 | if (itr != object.MemberEnd() && itr->value.Size() >= 1) {
84 | float x = itr->value[0].GetFloat();
85 | float y = 0;
86 |
87 | if (itr->value.Size() > 1) {
88 | y = itr->value[1].GetFloat();
89 | }
90 | return Sombrero::FastVector2(x, y);
91 | }
92 | return std::nullopt;
93 | }
94 |
95 | using OptPair = std::pair, std::optional>;
96 |
97 | static OptPair ReadOptionalPair(rapidjson::Value const& object, std::string_view const key) {
98 | auto itr = object.FindMember(key.data());
99 |
100 | if (itr != object.MemberEnd() && itr->value.Size() >= 1) {
101 | float x = itr->value[0].GetFloat();
102 | float y = 0;
103 |
104 | if (itr->value.Size() >= 2) {
105 | y = itr->value[1].GetFloat();
106 | return { std::optional(x), std::optional(y) };
107 | }
108 | return { std::optional(x), std::nullopt };
109 | }
110 | return { std::nullopt, std::nullopt };
111 | }
112 |
113 | static std::optional ReadOptionalRotation(rapidjson::Value const& object,
114 | std::string_view const key) {
115 | auto itr = object.FindMember(key.data());
116 | if (itr != object.MemberEnd()) {
117 | Sombrero::FastVector3 rot;
118 | if (itr->value.IsArray() && itr->value.Size() >= 3) {
119 | float x = itr->value[0].GetFloat();
120 | float y = itr->value[1].GetFloat();
121 | float z = itr->value[2].GetFloat();
122 | rot = Sombrero::FastVector3(x, y, z);
123 | } else if (itr->value.IsNumber()) {
124 | rot = Sombrero::FastVector3(0, itr->value.GetFloat(), 0);
125 | }
126 |
127 | return Sombrero::FastQuaternion::Euler(rot);
128 | }
129 | return std::nullopt;
130 | }
131 |
132 | static std::optional ReadOptionalVector3(rapidjson::Value const& object,
133 | std::string_view const key) {
134 | auto itr = object.FindMember(key.data());
135 | if (itr != object.MemberEnd() && itr->value.Size() >= 3) {
136 | float x = itr->value[0].GetFloat();
137 | float y = itr->value[1].GetFloat();
138 | float z = itr->value[2].GetFloat();
139 | return Sombrero::FastVector3(x, y, z);
140 | }
141 | return std::nullopt;
142 | }
143 |
144 | static std::optional, 3>> ReadOptionalScale(rapidjson::Value const& object,
145 | std::string_view const key) {
146 | auto itr = object.FindMember(key.data());
147 | if (itr != object.MemberEnd() && itr->value.IsArray()) {
148 | rapidjson::SizeType size = itr->value.Size();
149 | std::optional x = size >= 1 ? std::optional{ itr->value[0].GetFloat() } : std::nullopt;
150 | std::optional y = size >= 2 ? std::optional{ itr->value[1].GetFloat() } : std::nullopt;
151 | std::optional z = size >= 3 ? std::optional{ itr->value[2].GetFloat() } : std::nullopt;
152 | return { { x, y, z } };
153 | }
154 | return std::nullopt;
155 | }
156 |
157 | static std::optional ReadOptionalObject(rapidjson::Value const& object,
158 | std::string_view const key) {
159 | auto itr = object.FindMember(key.data());
160 | if (itr != object.MemberEnd()) {
161 | return itr->value.GetObject();
162 | }
163 | return std::nullopt;
164 | }
165 | static std::optional ReadOptionalValuePtr(rapidjson::Value const& object,
166 | std::string_view const key) {
167 | auto itr = object.FindMember(key.data());
168 | if (itr != object.MemberEnd()) {
169 | return &itr->value;
170 | }
171 | return std::nullopt;
172 | }
173 | static std::optional> ReadOptionalValue(rapidjson::Value const& object,
174 | std::string_view const key) {
175 | auto itr = object.FindMember(key.data());
176 | if (itr != object.MemberEnd()) {
177 | return std::ref(itr->value);
178 | }
179 | return std::nullopt;
180 | }
181 |
182 | template
183 | static std::optional> ReadOptionalArray(rapidjson::Value const& object, std::string_view const key,
184 | F&& func) {
185 | auto itr = object.FindMember(key.data());
186 | if (itr == object.MemberEnd()) {
187 | return std::nullopt;
188 | }
189 |
190 | auto jsonArray = itr->value.GetArray();
191 |
192 | std::vector vec;
193 | vec.reserve(jsonArray.Size());
194 |
195 | for (auto const& v : jsonArray) {
196 | vec.emplace_back(func(v));
197 | }
198 |
199 | return vec;
200 | }
201 |
202 | template
203 | static inline std::vector ReadArrayOrEmpty(rapidjson::Value const& object, std::string_view const key, F&& func) {
204 | auto opt = ReadOptionalArray(object, key, std::forward(func));
205 |
206 | if (!opt) {
207 | return {};
208 | }
209 |
210 | return *opt;
211 | }
212 |
213 | template
214 | static std::optional ReadOptionalType(rapidjson::Value const& object, std::string_view const key) {
215 | auto itr = object.FindMember(key.data());
216 | if (itr != object.MemberEnd()) {
217 | return itr->value.Get();
218 | }
219 | return std::nullopt;
220 | }
221 | template
222 | static std::optional ReadOptionalType(rapidjson::Value const& object, std::string_view const key, F&& func) {
223 | auto itr = object.FindMember(key.data());
224 | if (itr != object.MemberEnd()) {
225 | return func(itr->value);
226 | }
227 | return std::nullopt;
228 | }
229 |
230 | static std::string ToStringJSONValue(rapidjson::Value const& json) {
231 | using namespace rapidjson;
232 |
233 | StringBuffer sb;
234 | PrettyWriter writer(sb);
235 | json.Accept(writer);
236 | return sb.GetString();
237 | }
238 |
239 | } // namespace NEJSON
--------------------------------------------------------------------------------
/shared/LowLevelUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "beatsaber-hook/shared/utils/il2cpp-utils-methods.hpp"
5 |
6 | /// Collection of methods that are intended to be low level performance optimizations for
7 | /// niche use cases
8 | /// Might PR to bs-hooks sometime later
9 | namespace CustomJSONData {
10 |
11 | template
12 | concept CtorArgs = requires(T t, TArgs&&... args) {
13 | { T::New_ctor(std::forward(args)...) };
14 | };
15 |
16 | template
17 | requires(CtorArgs, TArgs...>)
18 | constexpr T NewFast(TArgs&&... args) {
19 | return il2cpp_utils::NewSpecificUnsafe(std::forward(args)...);
20 | }
21 |
22 | // // Faster allocation with method cache
23 | // template
24 | // // Bonus points for a requires clause here for classof(T)
25 | // T NewFastKlass(Il2CppClass* klass, TArgs&&... args) {
26 | // // return CRASH_UNLESS(il2cpp_utils::New(klass,
27 | // // std::forward(args)...));
28 | // static auto ctor = CRASH_UNLESS(il2cpp_utils::FindMethod(
29 | // klass, ".ctor", std::array{ il2cpp_utils::ExtractIndependentType()... }));
30 | // auto* obj = CRASH_UNLESS(il2cpp_functions::object_new(klass));
31 | // il2cpp_utils::RunMethodRethrow(obj, ctor, std::forward(args)...);
32 | // return reinterpret_cast(obj);
33 | // }
34 |
35 | // // Faster allocation with method cache
36 | // template
37 | // // Bonus points for a requires clause here for classof(T)
38 | // T NewFastUnsafe(TArgs&&... args) {
39 | // static auto klass = classof(T);
40 | // return NewFastKlass(klass, std::forward(args)...);
41 | // }
42 |
43 | // // Faster allocation with method cache
44 | // template
45 | // requires(CtorArgs, TArgs...>)
46 | // // Bonus points for a requires clause here for classof(T)
47 | // T NewFast(TArgs&&... args) {
48 | // static auto klass = classof(T);
49 | // return NewFastKlass(klass, std::forward(args)...);
50 | // }
51 |
52 | // Declare a method with name that will be called on construction.
53 | //#define DECLARE_FASTER_CTOR(name, ...) DECLARE_CTOR(name, __VA_ARGS__)
54 |
55 | #define DECLARE_FASTER_CTOR(name, ...) \
56 | public: \
57 | void name(__VA_ARGS__); \
58 | template <::il2cpp_utils::CreationType creationType = ::il2cpp_utils::CreationType::Temporary, class... TArgs> \
59 | static ___TargetType* New_ctor(TArgs&&... args) { \
60 | static_assert(::custom_types::Decomposer::convertible(), \
61 | "Arguments provided to New_ctor must be convertible to the constructor!"); \
62 | ___TargetType* obj; \
63 | if constexpr (creationType == ::il2cpp_utils::CreationType::Temporary) { \
64 | obj = reinterpret_cast<___TargetType*>(::il2cpp_functions::object_new(___TypeRegistration::klass_ptr)); \
65 | } else { \
66 | obj = reinterpret_cast<___TargetType*>(::il2cpp_utils::createManual(___TypeRegistration::klass_ptr)); \
67 | } \
68 | obj->name(std::forward(args)...); \
69 | return obj; \
70 | } \
71 | ___CREATE_INSTANCE_METHOD(name, ".ctor", \
72 | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_SPECIAL_NAME | \
73 | METHOD_ATTRIBUTE_RT_SPECIAL_NAME, \
74 | nullptr)
75 | } // namespace CustomJSONData
--------------------------------------------------------------------------------
/shared/VList.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "System/Collections/Generic/List_1.hpp"
3 | #include "CJDLogger.h"
4 |
5 | // template
6 | // class ListIterator {
7 | // public:
8 | // using iterator_category = random_access_iterator_tag;
9 | // using difference_type = std::ptrdiff_t;
10 | // using value_type = ListTy::value_type;
11 | // using pointer = ListTy::pointer;
12 | // using reference = ListTy::reference;
13 |
14 | // ListIterator(pointer ptr_) : ptr(ptr_) {}
15 |
16 | // reference operator*() const {
17 | // return *ptr;
18 | // }
19 |
20 | // pointer operator->() {
21 | // return ptr;
22 | // }
23 |
24 | // ListIterator& operator++() {
25 | // ptr++;
26 | // return *this;
27 | // }
28 |
29 | // ListIterator operator++(int) {
30 | // ListIterator tmp = *this;
31 | // (*this)++;
32 | // return tmp;
33 | // }
34 |
35 | // bool operator==(const Iterator& other) const {
36 | // return ptr == other.ptr;
37 | // };
38 |
39 | // bool operator!=(const Iterator& other) const {
40 | // return ptr != other.ptr;
41 | // };
42 | // private:
43 | // pointer ptr;
44 | // }
45 |
46 | namespace CustomJSONData {
47 | template
48 | inline ListW*> SpanToSystemList(std::span const span) {
49 | auto inner = System::Collections::Generic::List_1::New_ctor(span.size());
50 | // update size field, otherwise will be "empty"
51 | inner->_size = span.size();
52 |
53 | std::copy(span.begin(), span.end(), inner->_items.begin());
54 |
55 |
56 | return {inner};
57 | }
58 |
59 | template
60 | inline auto SpanToSystemList(std::vector const& list) {
61 | return SpanToSystemList(std::span(list));
62 | }
63 | } // namespace CustomJSONData
64 |
65 | template
66 | using VList = ListW*>;
67 |
68 | // template
69 | // class VList {
70 | // private:
71 | // using InnerTy = System::Collections::Generic::List_1;
72 | // InnerTy* inner;
73 |
74 | // public:
75 | // using value_type = Ty;
76 | // using pointer = Ty*;
77 | // using const_pointer = Ty const*;
78 | // using reference = Ty&;
79 |
80 | // // Maybe I'll use my own iterator type if needed
81 | // using iterator = pointer;
82 | // using const_iterator = const_pointer;
83 |
84 | // public:
85 | // constexpr VList() : inner(InnerTy::New_ctor()) {}
86 |
87 | // constexpr VList(int size) : inner(InnerTy::New_ctor(size)) {}
88 |
89 | // constexpr VList(InnerTy* list) : inner(list) {}
90 | // constexpr VList(void* list) : inner(static_cast(list)) {}
91 |
92 | // constexpr InnerTy* operator*() const {
93 | // return inner;
94 | // }
95 |
96 | // constexpr operator InnerTy*() const {
97 | // return inner;
98 | // }
99 |
100 | // constexpr Ty& operator[](const size_t pos) const {
101 | // return inner->items.get(pos);
102 | // }
103 |
104 | // [[nodiscard]] constexpr int size() const {
105 | // return inner->size;
106 | // }
107 |
108 | // constexpr auto resize(const size_t cap) {
109 | // return inner->EnsureCapacity(cap);
110 | // }
111 |
112 | // constexpr void trim() const {
113 | // return inner->TrimExcess();
114 | // }
115 |
116 | // constexpr void insert_at(int index, Ty const& val) {
117 | // // TODO: C++ impl
118 | // return inner->Insert(index, val);
119 | // }
120 |
121 | // constexpr void push_back(Ty const& val) {
122 | // // TODO: C++ impl
123 | // return inner->Add(val);
124 | // }
125 |
126 | // iterator constexpr begin() {
127 | // return inner->items.begin();
128 | // }
129 |
130 | // iterator constexpr end() {
131 | // return inner->items.begin() + size();
132 | // }
133 |
134 | // [[nodiscard]] constexpr InnerTy* getInner() const {
135 | // return inner;
136 | // }
137 |
138 | // [[nodiscard]] constexpr void* convert() const noexcept {
139 | // return inner;
140 | // }
141 |
142 | // constexpr operator std::span() {
143 | // return std::span(begin(), size());
144 | // }
145 |
146 | // constexpr std::span toSpan() {
147 | // return std::span(begin(), size());
148 | // }
149 | // };
150 |
151 | // If it's not just a pointer then bad things will happen
152 | // static_assert(sizeof(VList) == 0x8);
153 |
154 | // template struct il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class> {
155 | // static inline Il2CppClass* get() {
156 | // return classof(System::Collections::Generic::List_1*);
157 | // }
158 | // };
159 |
160 | // static_assert(il2cpp_utils::has_il2cpp_conversion>);
161 |
162 | // template struct ::il2cpp_utils::il2cpp_type_check::need_box> {
163 | // constexpr static bool value = false;
164 | // };
--------------------------------------------------------------------------------
/shared/_config.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define CJD_MOD_EXPORT __attribute__((visibility("default")))
4 | #define CJD_MOD_EXTERN_FUNC extern "C" CJD_MOD_EXPORT
--------------------------------------------------------------------------------
/shared/misc/BeatmapFieldUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/BeatmapSaveDataItem.hpp"
4 | #include "../CustomBeatmapSaveDatav3.h"
5 | #include "../JSONWrapper.h"
6 |
7 | namespace CustomJSONData {
8 |
9 | static v3::CustomDataOpt JSONObjectOrNull(v3::CustomDataOpt const& val) {
10 | if (!val || !val->get().IsObject()) {
11 | return std::nullopt;
12 | }
13 |
14 | return val;
15 | }
16 | static JSONWrapper* JSONWrapperOrNull(v3::CustomDataOpt const& val) {
17 | auto* wrapper = JSONWrapper::New_ctor();
18 |
19 | if (!val || !val->get().IsObject()) {
20 | return wrapper;
21 | }
22 |
23 | wrapper->value = val;
24 |
25 | return wrapper;
26 | }
27 |
28 | static v3::CustomDataOptUTF16 JSONObjectOrNull(v3::CustomDataOptUTF16 const& val) {
29 | if (!val || !val->get().IsObject()) {
30 | return std::nullopt;
31 | }
32 |
33 | return val;
34 | }
35 | static JSONWrapperUTF16* JSONWrapperOrNull(v3::CustomDataOptUTF16 const& val) {
36 | auto* wrapper = JSONWrapperUTF16::New_ctor();
37 |
38 | if (!val || !val->get().IsObject()) {
39 | return wrapper;
40 | }
41 |
42 | wrapper->value = val;
43 |
44 | return wrapper;
45 | }
46 |
47 | constexpr float BeatmapSaveDataItem_GetBeat(GlobalNamespace::BeatmapDataItem const* item) {
48 | if (!item) return 0;
49 | return const_cast(item)->time;
50 | }
51 |
52 | constexpr float
53 | BeatmapSaveDataItem_GetBeat(BeatmapSaveDataVersion2_6_0AndEarlier::BeatmapSaveDataItem const* const item) {
54 | if (!item) return 0;
55 |
56 | return const_cast < BeatmapSaveDataVersion2_6_0AndEarlier::BeatmapSaveDataItem*>(item)->time;
57 | }
58 |
59 | constexpr float BeatmapSaveDataItem_GetBeat(BeatmapSaveDataVersion3::BeatmapSaveDataItem const* const item) {
60 | if (!item) return 0;
61 |
62 | return item->b;
63 | }
64 |
65 |
66 | constexpr float BeatmapSaveDataItem_GetBeat(BeatmapSaveDataVersion3::LightColorBaseData const* const item) {
67 | if (!item) return 0;
68 |
69 | return item->b;
70 | }
71 |
72 | //
73 |
74 | constexpr auto&
75 | LightColorBaseData_GetTransitionType(BeatmapSaveDataVersion3::LightColorBaseData const* item) {
76 | return item->i;
77 | }
78 |
79 | constexpr auto&
80 | LightColorBaseData_GetColorType(BeatmapSaveDataVersion3::LightColorBaseData const* item) {
81 | return item->c;
82 | }
83 |
84 | constexpr auto&
85 | LightColorBaseData_GetBrightness(BeatmapSaveDataVersion3::LightColorBaseData const* item) {
86 | return item->s;
87 | }
88 |
89 | constexpr auto&
90 | LightColorBaseData_GetStrobeFrequency(BeatmapSaveDataVersion3::LightColorBaseData const* item) {
91 | return item->f;
92 | }
93 |
94 | //
95 |
96 | constexpr auto
97 | LightRotationBaseData_GetStrobeFrequency(BeatmapSaveDataVersion3::LightRotationBaseData const* item) {
98 | return item->p == 1;
99 | }
100 |
101 | constexpr auto&
102 | LightRotationBaseData_GetRotation(BeatmapSaveDataVersion3::LightRotationBaseData const* item) {
103 | return item->r;
104 | }
105 |
106 | constexpr auto&
107 | LightRotationBaseData_GetLoopsCount(BeatmapSaveDataVersion3::LightRotationBaseData const* item) {
108 | return item->l;
109 | }
110 |
111 | //
112 |
113 | constexpr auto& EventBox_GetBeatDistributionParam(BeatmapSaveDataVersion3::EventBox const* item) {
114 | return item->w;
115 | }
116 |
117 | constexpr auto& EventBox_GetBeatDistributionParamType(BeatmapSaveDataVersion3::EventBox const* item) {
118 | return item->d;
119 | }
120 |
121 | //
122 |
123 | constexpr auto& IndexFilter_GetParam0(BeatmapSaveDataVersion3::IndexFilter const* filter) {
124 | return filter->p;
125 | }
126 |
127 | constexpr auto& IndexFilter_GetParam1(BeatmapSaveDataVersion3::IndexFilter const* filter) {
128 | return filter->t;
129 | }
130 |
131 | constexpr auto IndexFilter_GetReversed(BeatmapSaveDataVersion3::IndexFilter const* filter) {
132 | return filter->r != 0;
133 | }
134 |
135 | constexpr auto& IndexFilter_GetFilterType(BeatmapSaveDataVersion3::IndexFilter const* filter) {
136 | return filter->f;
137 | }
138 | //
139 |
140 | } // namespace CustomJSONData
--------------------------------------------------------------------------------
/src/CustomBeatmapData.cpp:
--------------------------------------------------------------------------------
1 | #include "CustomBeatmapData.h"
2 |
3 | #include "GlobalNamespace/BeatmapDataSortedListForTypeAndIds_1.hpp"
4 | #include "GlobalNamespace/ISortedList_1.hpp"
5 | #include "GlobalNamespace/SortedList_2.hpp"
6 | #include "GlobalNamespace/BeatmapDataItem.hpp"
7 | #include "GlobalNamespace/ISortedListItemProcessor_1.hpp"
8 | #include "System/Collections/Generic/Dictionary_2.hpp"
9 | #include "CustomJSONDataHooks.h"
10 |
11 | #include "System/Collections/Generic/HashSet_1.hpp"
12 |
13 | using namespace GlobalNamespace;
14 |
15 | DEFINE_TYPE(CustomJSONData, CustomBeatmapData);
16 | DEFINE_TYPE(CustomJSONData, CustomBeatmapEventData);
17 | DEFINE_TYPE(CustomJSONData, CustomObstacleData);
18 | DEFINE_TYPE(CustomJSONData, CustomNoteData);
19 | DEFINE_TYPE(CustomJSONData, CustomWaypointData);
20 | DEFINE_TYPE(CustomJSONData, CustomSliderData);
21 |
22 | void CustomJSONData::CustomBeatmapData::ctor(int numberOfLines) {
23 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe("", "BeatmapData", ".ctor", 1);
24 | PAPER_IL2CPP_CATCH_HANDLER(il2cpp_utils::RunMethodRethrow(this, ctor, numberOfLines);)
25 |
26 | INVOKE_CTOR();
27 |
28 | ____beatmapDataItemsPerTypeAndId->_sortedListsDataProcessors->Add(csTypeOf(CustomEventData*), nullptr);
29 | // _beatmapDataItemsPerTypeAndId->_items->Add(csTypeOf(CustomEventData*),
30 | // reinterpret_cast*>(
31 | // SortedList_2::New_ctor(nullptr)));
33 | }
34 |
35 | [[deprecated("to remove")]]
36 | void CustomJSONData::CustomBeatmapData::AddBeatmapObjectDataOverride(
37 | GlobalNamespace::BeatmapObjectData* beatmapObjectData) {
38 | static auto const* base = il2cpp_utils::FindMethodUnsafe("", "BeatmapData", "AddBeatmapObjectData", 1);
39 |
40 | il2cpp_utils::RunMethodRethrow(this, base, beatmapObjectData);
41 | }
42 |
43 | [[deprecated("To remove")]]
44 | void CustomJSONData::CustomBeatmapData::AddBeatmapObjectDataInOrderOverride(
45 | GlobalNamespace::BeatmapObjectData* beatmapObjectData) {
46 | static auto const* base = il2cpp_utils::FindMethodUnsafe("", "BeatmapData", "AddBeatmapObjectDataInOrder", 1);
47 |
48 | PAPER_IL2CPP_CATCH_HANDLER(il2cpp_utils::RunMethodRethrow(this, base, beatmapObjectData);)
49 | }
50 |
51 | [[deprecated("to remove")]]
52 | void CustomJSONData::CustomBeatmapData::InsertBeatmapEventDataOverride(
53 | GlobalNamespace::BeatmapEventData* beatmapObjectData) {
54 | static auto const* base = il2cpp_utils::FindMethodUnsafe("", "BeatmapData", "InsertBeatmapEventData", 1);
55 |
56 | PAPER_IL2CPP_CATCH_HANDLER(il2cpp_utils::RunMethodRethrow(this, base, beatmapObjectData);)
57 | }
58 |
59 | [[deprecated("To remove")]]
60 | void CustomJSONData::CustomBeatmapData::InsertBeatmapEventDataInOrderOverride(
61 | GlobalNamespace::BeatmapEventData* beatmapEventData) {
62 | static auto const* base = il2cpp_utils::FindMethodUnsafe("", "BeatmapData", "InsertBeatmapEventDataInOrder", 1);
63 |
64 | PAPER_IL2CPP_CATCH_HANDLER(il2cpp_utils::RunMethodRethrow(this, base, beatmapEventData);)
65 | }
66 |
67 | void CustomJSONData::CustomBeatmapData::InsertCustomEventData(CustomJSONData::CustomEventData* customEventData) {
68 | customEventDatas.emplace_back(customEventData);
69 | auto* node = _beatmapDataItemsPerTypeAndId->InsertItem(customEventData);
70 | if (updateAllBeatmapDataOnInsert) {
71 | InsertToAllBeatmapData(customEventData, node);
72 | }
73 | }
74 |
75 | void CustomJSONData::CustomBeatmapData::InsertCustomEventDataInOrder(CustomEventData* customEventData) {
76 | InsertCustomEventData(customEventData);
77 | InsertToAllBeatmapData(customEventData, nullptr); // default is null
78 | }
79 |
80 | System::Type* CustomJSONData::CustomBeatmapData::GetCustomType(Il2CppObject* obj) {
81 | return GetCustomType(obj->klass);
82 | }
83 |
84 | System::Type* CustomJSONData::CustomBeatmapData::GetCustomType(Il2CppClass* obj) {
85 | static auto* CustomKlass = classof(CustomEventData*);
86 |
87 | if (obj == nullptr) {
88 | return nullptr;
89 | }
90 |
91 | static std::unordered_map typeMap;
92 |
93 | auto& typePtr = typeMap[obj];
94 |
95 | if (typePtr == nullptr) {
96 | CJDLogger::Logger.fmtLog(
97 | "Checking if {} is equal to ref {} for obj {} vs ref {}", obj->namespaze, CustomKlass->namespaze,
98 | il2cpp_utils::ClassStandardName(obj), il2cpp_utils::ClassStandardName(CustomKlass));
99 | if (std::string_view(obj->namespaze) == std::string_view(CustomKlass->namespaze) && obj != CustomKlass) {
100 | CJDLogger::Logger.fmtLog("They are custom, using parent type {}",
101 | il2cpp_utils::ClassStandardName(obj->parent));
102 |
103 | typePtr = il2cpp_utils::GetSystemType(obj->parent);
104 | } else {
105 | CJDLogger::Logger.fmtLog("They are not custom (basegame), using self type {}",
106 | il2cpp_utils::ClassStandardName(obj));
107 |
108 | typePtr = il2cpp_utils::GetSystemType(obj);
109 | }
110 | }
111 |
112 | return reinterpret_cast(typePtr);
113 | }
114 |
115 | CustomJSONData::CustomBeatmapData* CustomJSONData::CustomBeatmapData::BaseCopy() {
116 | auto* copy = CustomJSONData::CustomBeatmapData::New_ctor(numberOfLines);
117 |
118 | // copy the rest
119 | copy->doc = this->doc;
120 | if (this->customData) {
121 | copy->customData = this->customData->GetCopy();
122 | }
123 | if (copy->beatmapCustomData) {
124 | copy->beatmapCustomData = this->beatmapCustomData->GetCopy();
125 | }
126 | if (copy->levelCustomData) {
127 | copy->levelCustomData = this->levelCustomData->GetCopy();
128 | }
129 |
130 | copy->v2orEarlier = v2orEarlier;
131 |
132 | auto enumerator = this->_specialBasicBeatmapEventKeywords->GetEnumerator();
133 | while (enumerator.MoveNext()) {
134 | copy->AddSpecialBasicBeatmapEventKeyword(enumerator.Current);
135 | }
136 |
137 | return copy;
138 | }
139 |
140 | void CustomJSONData::CustomBeatmapEventData::ctor(float time,
141 | ::GlobalNamespace::BasicBeatmapEventType basicBeatmapEventType,
142 | int value, float floatValue) {
143 | static auto const* BeatmapEventData_Ctor =
144 | CRASH_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(BasicBeatmapEventData*), ".ctor", 4));
145 | il2cpp_utils::RunMethodRethrow(this, BeatmapEventData_Ctor, time,
146 | basicBeatmapEventType, value, floatValue);
147 | INVOKE_CTOR();
148 | this->_time_k__BackingField = time;
149 | this->basicBeatmapEventType = basicBeatmapEventType;
150 | this->value = value;
151 | this->floatValue = floatValue;
152 | }
153 |
154 | CustomJSONData::CustomBeatmapEventData* CustomJSONData::CustomBeatmapEventData::GetCopy() {
155 | auto* copy =
156 | CustomJSONData::CustomBeatmapEventData::New_ctor(this->time, type.value__, this->value, this->floatValue);
157 | copy->set_previousSameTypeEventData(previousSameTypeEventData);
158 | copy->set_nextSameTypeEventData(nextSameTypeEventData);
159 | copy->type = type;
160 | copy->____executionOrder_k__BackingField = this->____executionOrder_k__BackingField;
161 | copy->____subtypeIdentifier_k__BackingField = this->____subtypeIdentifier_k__BackingField;
162 | copy->basicBeatmapEventType = basicBeatmapEventType;
163 | copy->subtypeIdentifier = subtypeIdentifier;
164 | copy->sameTypeIndex = sameTypeIndex;
165 | // For some reason this is needed
166 | if (this->customData) {
167 | copy->customData = this->customData->GetCopy();
168 | }
169 | return copy;
170 | }
171 | void CustomJSONData::CustomObstacleData::ctor(float time, float beat, float endBeat, int rotation, int lineIndex,
172 | ::GlobalNamespace::NoteLineLayer lineLayer, float duration, int width,
173 | int height) {
174 | static auto const* ObstacleData_Ctor = il2cpp_utils::FindMethodUnsafe(classof(ObstacleData*), ".ctor", 9);
175 | il2cpp_utils::RunMethodRethrow(this, ObstacleData_Ctor, time, beat, endBeat, rotation, lineIndex,
176 | lineLayer, duration, width, height);
177 | INVOKE_CTOR();
178 | this->____executionOrder_k__BackingField = beat;
179 | this->____subtypeIdentifier_k__BackingField = rotation;
180 | this->aheadTimeNoodle = 0;
181 | }
182 |
183 | CustomJSONData::CustomObstacleData* CustomJSONData::CustomObstacleData::GetCopy() {
184 | auto* copy = CustomJSONData::CustomObstacleData::New_ctor(this->time, this->executionOrder, this->endBeat,
185 | this->subtypeIdentifier, this->lineIndex, this->lineLayer,
186 | this->duration, this->width, height);
187 | if (this->customData) {
188 | copy->customData = this->customData->GetCopy();
189 | }
190 | copy->bpm = this->bpm;
191 | copy->____executionOrder_k__BackingField = this->____executionOrder_k__BackingField;
192 | copy->____subtypeIdentifier_k__BackingField = this->____subtypeIdentifier_k__BackingField;
193 | copy->aheadTimeNoodle = this->aheadTimeNoodle;
194 | return copy;
195 | }
196 |
197 | void CustomJSONData::CustomSliderData::ctor(
198 | GlobalNamespace::SliderData::Type sliderType, ::GlobalNamespace::ColorType colorType, bool hasHeadNote,
199 | float headTime, float headBeat, int headRotation, int headLineIndex, ::GlobalNamespace::NoteLineLayer headLineLayer,
200 | ::GlobalNamespace::NoteLineLayer headBeforeJumpLineLayer, float headControlPointLengthMultiplier,
201 | ::GlobalNamespace::NoteCutDirection headCutDirection, float headCutDirectionAngleOffset, bool hasTailNote,
202 | float tailTime, int tailRotation, int tailLineIndex, ::GlobalNamespace::NoteLineLayer tailLineLayer,
203 | ::GlobalNamespace::NoteLineLayer tailBeforeJumpLineLayer, float tailControlPointLengthMultiplier,
204 | ::GlobalNamespace::NoteCutDirection tailCutDirection, float tailCutDirectionAngleOffset,
205 | ::GlobalNamespace::SliderMidAnchorMode midAnchorMode, int sliceCount, float squishAmount) {
206 | static auto const* SliderData_Ctor = il2cpp_utils::FindMethodUnsafe(classof(SliderData*), ".ctor", 24);
207 | il2cpp_utils::RunMethodRethrow(
208 | this, SliderData_Ctor, time, beat, rotation, sliderType, colorType, hasHeadNote, headTime, headLineIndex,
209 | headLineLayer, headBeforeJumpLineLayer, headControlPointLengthMultiplier, headCutDirection,
210 | headCutDirectionAngleOffset, hasTailNote, tailTime, tailLineIndex, tailLineLayer, tailBeforeJumpLineLayer,
211 | tailControlPointLengthMultiplier, tailCutDirection, tailCutDirectionAngleOffset, midAnchorMode, sliceCount,
212 | squishAmount);
213 | INVOKE_CTOR();
214 | }
215 |
216 | CustomJSONData::CustomSliderData* CustomJSONData::CustomSliderData::GetCopy() {
217 | auto* copy = CustomJSONData::CustomSliderData::New_ctor(
218 | sliderType, colorType, hasHeadNote, time, executionOrder, rotation, headLineIndex, headLineLayer,
219 | headBeforeJumpLineLayer, headControlPointLengthMultiplier, headCutDirection, headCutDirectionAngleOffset,
220 | hasTailNote, tailTime, tailRotation, tailLineIndex, tailLineLayer, tailBeforeJumpLineLayer,
221 | tailControlPointLengthMultiplier, tailCutDirection, tailCutDirectionAngleOffset, midAnchorMode, sliceCount,
222 | squishAmount);
223 | if (this->customData) {
224 | copy->customData = this->customData->GetCopy();
225 | }
226 | copy->bpm = this->bpm;
227 | return copy;
228 | }
229 |
230 | void CustomJSONData::CustomNoteData::ctor(
231 | float time, float beat, int rotation, int lineIndex, ::GlobalNamespace::NoteLineLayer noteLineLayer,
232 | ::GlobalNamespace::NoteLineLayer beforeJumpNoteLineLayer, ::GlobalNamespace::NoteData::GameplayType gameplayType,
233 | ::GlobalNamespace::NoteData::ScoringType scoringType, ::GlobalNamespace::ColorType colorType,
234 | ::GlobalNamespace::NoteCutDirection cutDirection, float timeToNextColorNote, float timeToPrevColorNote,
235 | int flipLineIndex, float flipYSide, float cutDirectionAngleOffset, float cutSfxVolumeMultiplier) {
236 | static auto const* NoteData_Ctor = il2cpp_utils::FindMethodUnsafe(classof(NoteData*), ".ctor", 16);
237 | il2cpp_utils::RunMethodRethrow(this, NoteData_Ctor, time, beat, rotation, lineIndex, noteLineLayer,
238 | beforeJumpNoteLineLayer, gameplayType, scoringType, colorType,
239 | cutDirection, timeToNextColorNote, timeToPrevColorNote, flipLineIndex,
240 | flipYSide, cutDirectionAngleOffset, cutSfxVolumeMultiplier);
241 | INVOKE_CTOR();
242 | this->____executionOrder_k__BackingField = beat;
243 | this->____subtypeIdentifier_k__BackingField = rotation;
244 | this->aheadTimeNoodle = 0;
245 | }
246 |
247 | CustomJSONData::CustomNoteData* CustomJSONData::CustomNoteData::GetCopy() {
248 | auto* copy = CustomJSONData::CustomNoteData::New_ctor(
249 | time, executionOrder, subtypeIdentifier, lineIndex, noteLineLayer, beforeJumpNoteLineLayer, gameplayType,
250 | scoringType, colorType, cutDirection, timeToNextColorNote, timeToPrevColorNote, flipLineIndex, flipYSide,
251 | cutDirectionAngleOffset, cutSfxVolumeMultiplier);
252 | if (this->customData) {
253 | copy->customData = this->customData->GetCopy();
254 | }
255 | copy->bpm = this->bpm;
256 | copy->____executionOrder_k__BackingField = this->____executionOrder_k__BackingField;
257 | copy->____subtypeIdentifier_k__BackingField = this->____subtypeIdentifier_k__BackingField;
258 | copy->aheadTimeNoodle = this->aheadTimeNoodle;
259 | return copy;
260 | }
261 |
262 | void CustomJSONData::CustomWaypointData::ctor(float time, float beat, int rotation, int lineIndex,
263 | GlobalNamespace::NoteLineLayer noteLineLayer,
264 | GlobalNamespace::OffsetDirection offsetDirection) {
265 | static auto const* WaypointData_Ctor = il2cpp_utils::FindMethodUnsafe(classof(WaypointData*), ".ctor", 6);
266 | il2cpp_utils::RunMethodRethrow(this, WaypointData_Ctor, time, beat, rotation, lineIndex, noteLineLayer,
267 | offsetDirection);
268 | INVOKE_CTOR();
269 | this->____executionOrder_k__BackingField = beat;
270 | this->____subtypeIdentifier_k__BackingField = rotation;
271 | }
272 |
273 | CustomJSONData::CustomWaypointData* CustomJSONData::CustomWaypointData::GetCopy() {
274 | auto* copy = CustomJSONData::CustomWaypointData::New_ctor(time, executionOrder, subtypeIdentifier, lineIndex,
275 | lineLayer, offsetDirection);
276 | copy->bpm = this->bpm;
277 | copy->____executionOrder_k__BackingField = this->____executionOrder_k__BackingField;
278 | copy->____subtypeIdentifier_k__BackingField = this->____subtypeIdentifier_k__BackingField;
279 | return copy;
280 | }
--------------------------------------------------------------------------------
/src/CustomBeatmapSaveDatav2.cpp:
--------------------------------------------------------------------------------
1 | #include "CustomBeatmapSaveDatav2.h"
2 | #include "misc/BeatmapFieldUtils.hpp"
3 |
4 | #include "JSONWrapper.h"
5 | #include "VList.h"
6 | #include "JsonUtils.h"
7 |
8 | #include "cpp-semver/shared/cpp-semver.hpp"
9 | #include "paper2_scotland2/shared/Profiler.hpp"
10 |
11 | using namespace GlobalNamespace;
12 | using namespace BeatmapSaveDataVersion2_6_0AndEarlier;
13 |
14 | DEFINE_TYPE(CustomJSONData::v2, CustomBeatmapSaveData);
15 | DEFINE_TYPE(CustomJSONData::v2, CustomBeatmapSaveData_NoteData);
16 | DEFINE_TYPE(CustomJSONData::v2, CustomBeatmapSaveData_SliderData);
17 | DEFINE_TYPE(CustomJSONData::v2, CustomBeatmapSaveData_ObstacleData);
18 | DEFINE_TYPE(CustomJSONData::v2, CustomBeatmapSaveData_EventData);
19 |
20 | void CustomJSONData::v2::CustomBeatmapSaveData::ctor(System::Collections::Generic::List_1* events,
21 | System::Collections::Generic::List_1* notes,
22 | System::Collections::Generic::List_1* sliders,
23 | System::Collections::Generic::List_1* waypoints,
24 | System::Collections::Generic::List_1* obstacles,
25 | SpecialEventKeywordFiltersData* specialEventsKeywordFilters) {
26 | INVOKE_CTOR();
27 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(BeatmapSaveData*), ".ctor", 6);
28 |
29 | il2cpp_utils::RunMethodRethrow(this, ctor, events, notes, sliders, waypoints, obstacles, specialEventsKeywordFilters);
30 | // this->events = events;
31 | // this->notes = notes;
32 | // this->waypoints = waypoints;
33 | // this->obstacles = obstacles;
34 | // this->specialEventsKeywordFilters = specialEventsKeywordFilters;
35 | }
36 |
37 | void CustomJSONData::v2::CustomBeatmapSaveData_NoteData::ctor(float time, int lineIndex,
38 | BeatmapSaveDataCommon::NoteLineLayer lineLayer,
39 | NoteType type,
40 | BeatmapSaveDataCommon::NoteCutDirection cutDirection) {
41 | INVOKE_CTOR();
42 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(NoteData*), ".ctor", 5);
43 | il2cpp_utils::RunMethodRethrow(this, ctor, time, lineIndex, lineLayer, type, cutDirection);
44 | // this->time = time;
45 | // this->lineIndex = lineIndex;
46 | // this->lineLayer = lineLayer;
47 | // this->type = type;
48 | // this->cutDirection = cutDirection;
49 | }
50 |
51 | void CustomJSONData::v2::CustomBeatmapSaveData_ObstacleData::ctor(float time, int lineIndex, ObstacleType type,
52 | float duration, int width) {
53 | INVOKE_CTOR();
54 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(ObstacleData*), ".ctor", 5);
55 | il2cpp_utils::RunMethodRethrow(this, ctor, time, lineIndex, type, duration, width);
56 | this->_time = time;
57 | this->_lineIndex = lineIndex;
58 | this->_type = type;
59 | this->_duration = duration;
60 | this->_width = width;
61 | }
62 |
63 | void CustomJSONData::v2::CustomBeatmapSaveData_EventData::ctor(float time, BeatmapSaveDataCommon::BeatmapEventType type,
64 | int value, float floatValue) {
65 | INVOKE_CTOR();
66 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(EventData*), ".ctor", 4);
67 | il2cpp_utils::RunMethodRethrow(this, ctor, time, type, value, floatValue);
68 |
69 | this->_time = time;
70 | this->_type = type;
71 | this->_value = value;
72 | this->_floatValue = floatValue;
73 | }
74 |
75 | void CustomJSONData::v2::CustomBeatmapSaveData_SliderData::ctor(
76 | ColorType colorType, float headTime, int headLineIndex, BeatmapSaveDataCommon::NoteLineLayer headLineLayer,
77 | float headControlPointLengthMultiplier, BeatmapSaveDataCommon::NoteCutDirection headCutDirection, float tailTime,
78 | int tailLineIndex, BeatmapSaveDataCommon::NoteLineLayer tailLineLayer, float tailControlPointLengthMultiplier,
79 | BeatmapSaveDataCommon::NoteCutDirection tailCutDirection,
80 | BeatmapSaveDataCommon::SliderMidAnchorMode sliderMidAnchorMode) {
81 | INVOKE_CTOR();
82 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(SliderData*), ".ctor", 12);
83 | il2cpp_utils::RunMethodRethrow(
84 | this, ctor, colorType, headTime, headLineIndex, headLineLayer, headControlPointLengthMultiplier, headCutDirection,
85 | tailTime, tailLineIndex, tailLineLayer, tailControlPointLengthMultiplier, tailCutDirection, sliderMidAnchorMode);
86 | }
87 |
88 | static void ConvertBeatmapSaveDataPreV2_5_0(CustomJSONData::v2::CustomBeatmapSaveData* beatmapSaveData) {
89 | CJDLogger::Logger.fmtLog("Converting pre v2.5.0");
90 |
91 | using namespace CustomJSONData::v2;
92 |
93 | auto size = beatmapSaveData->events ? beatmapSaveData->events->_size : 0;
94 | auto list = VList::New();
95 | list->EnsureCapacity(size);
96 |
97 | for (auto* originalEventData : ListW(beatmapSaveData->events)) {
98 | auto* eventData = il2cpp_utils::cast(originalEventData);
99 | CustomBeatmapSaveData_EventData* newData = nullptr;
100 |
101 | // Legacy BPM conversion here is deliberately ommited.
102 | // There are no maps using these events, but plenty missversioned
103 |
104 | if (eventData->type == BeatmapSaveDataCommon::BeatmapEventType::BpmChange) {
105 | if (eventData->value != 0) {
106 | newData = CRASH_UNLESS(CustomBeatmapSaveData_EventData::New_ctor(eventData->time, eventData->type,
107 | eventData->value, (float)eventData->value));
108 | }
109 | } else {
110 | newData = CRASH_UNLESS(
111 | CustomBeatmapSaveData_EventData::New_ctor(eventData->time, eventData->type, eventData->value, 1.0F));
112 | }
113 |
114 | if (newData) {
115 | newData->customData = eventData->customData;
116 | }
117 |
118 | list.push_back(newData ? newData : eventData);
119 | }
120 |
121 | beatmapSaveData->_events = list;
122 | }
123 |
124 | static decltype(CustomJSONData::JSONWrapper::value) GetCustomData(rapidjson::Value const& doc) {
125 | auto customDataIt = doc.FindMember("_customData");
126 | if (customDataIt != doc.MemberEnd() && customDataIt->value.IsObject()) {
127 | return customDataIt->value;
128 | }
129 |
130 | return std::nullopt;
131 | }
132 |
133 | CustomJSONData::v2::CustomBeatmapSaveData*
134 | CustomJSONData::v2::CustomBeatmapSaveData::Deserialize(std::shared_ptr const& sharedDoc) {
135 | auto const& doc = *sharedDoc;
136 |
137 | CJDLogger::Logger.fmtLog("Parse notes");
138 |
139 | Paper::Profiler profile;
140 | profile.startTimer();
141 |
142 | auto notes = VList::New();
143 | auto notesArrIt = doc.FindMember("_notes");
144 |
145 | if (notesArrIt != doc.MemberEnd() && notesArrIt->value.IsArray()) {
146 | auto const& notesArr = notesArrIt->value;
147 | notes->EnsureCapacity(notesArr.Size());
148 |
149 | for (rapidjson::SizeType i = 0; i < notesArr.Size(); i++) {
150 | rapidjson::Value const& note_json = notesArr[i];
151 |
152 | float time = NEJSON::ReadOptionalFloat(note_json, "_time").value_or(0);
153 | int lineIndex = NEJSON::ReadOptionalFloat(note_json, "_lineIndex").value_or(0);
154 | auto lineLayer =
155 | BeatmapSaveDataCommon::NoteLineLayer(NEJSON::ReadOptionalFloat(note_json, "_lineLayer").value_or(0));
156 | auto type =
157 | BeatmapSaveDataVersion2_6_0AndEarlier::NoteType(NEJSON::ReadOptionalFloat(note_json, "_type").value_or(0));
158 | auto cutDirection =
159 | BeatmapSaveDataCommon::NoteCutDirection(NEJSON::ReadOptionalFloat(note_json, "_cutDirection").value_or(0));
160 | auto* note =
161 | CRASH_UNLESS(CustomBeatmapSaveData_NoteData::New_ctor(time, lineIndex, lineLayer, type, cutDirection));
162 |
163 | note->customData = GetCustomData(note_json);
164 |
165 | notes.push_back(note);
166 | }
167 | }
168 |
169 | CJDLogger::Logger.fmtLog("Parsed {} notes", notes.size());
170 |
171 | auto sliders = VList::New();
172 | auto slidersArrIt = doc.FindMember("_sliders");
173 |
174 | if (slidersArrIt != doc.MemberEnd() && slidersArrIt->value.IsArray()) {
175 | auto const& slidersArr = slidersArrIt->value;
176 | sliders->EnsureCapacity(slidersArr.Size());
177 |
178 | for (rapidjson::SizeType i = 0; i < slidersArr.Size(); i++) {
179 | rapidjson::Value const& slider_json = slidersArr[i];
180 |
181 | int lineIndex = slider_json["_lineIndex"].GetInt();
182 | auto lineLayer = BeatmapSaveDataCommon::NoteLineLayer(slider_json["_lineLayer"].GetInt());
183 | auto type = BeatmapSaveDataVersion2_6_0AndEarlier::NoteType(slider_json["_type"].GetInt());
184 | auto cutDirection = BeatmapSaveDataCommon::NoteCutDirection(slider_json["_cutDirection"].GetInt());
185 |
186 | float time = slider_json["_time"].GetFloat();
187 | auto colorType = ColorType(slider_json["_colorType"].GetInt());
188 | int headLineIndex = slider_json["_headLineIndex"].GetInt();
189 | BeatmapSaveDataCommon::NoteLineLayer noteLineLayer = slider_json["_noteLineLayer"].GetInt();
190 | float headControlPointLengthMultiplier = slider_json["_headControlPointLengthMultiplier"].GetFloat();
191 | BeatmapSaveDataCommon::NoteCutDirection noteCutDirection = slider_json["_noteCutDirection"].GetInt();
192 | float tailTime = slider_json["_tailTime"].GetFloat();
193 | int tailLineIndex = slider_json["_tailLineIndex"].GetInt();
194 | BeatmapSaveDataCommon::NoteLineLayer tailLineLayer = slider_json["_tailLineLayer"].GetInt();
195 | float tailControlPointLengthMultiplier = slider_json["_tailControlPointLengthMultiplier"].GetFloat();
196 | BeatmapSaveDataCommon::NoteCutDirection tailCutDirection = slider_json["_tailCutDirection"].GetInt();
197 | BeatmapSaveDataCommon::SliderMidAnchorMode sliderMidAnchorMode = slider_json["_sliderMidAnchorMode"].GetInt();
198 |
199 | auto* slider = CRASH_UNLESS(CustomBeatmapSaveData_SliderData::New_ctor(
200 | colorType, time, headLineIndex, noteLineLayer, headControlPointLengthMultiplier, noteCutDirection, tailTime,
201 | tailLineIndex, tailLineLayer, tailControlPointLengthMultiplier, tailCutDirection, sliderMidAnchorMode));
202 | slider->customData = GetCustomData(slider_json);
203 |
204 | sliders.push_back(slider);
205 | }
206 | }
207 | CJDLogger::Logger.fmtLog("Parsed {} sliders", sliders.size());
208 |
209 | CJDLogger::Logger.fmtLog("Parse obstacles");
210 | auto obstaclesArrIt = doc.FindMember("_obstacles");
211 |
212 | auto obstacles = VList::New();
213 |
214 | if (obstaclesArrIt->value.IsArray()) {
215 | auto const& obstaclesArr = obstaclesArrIt->value;
216 |
217 | obstacles->EnsureCapacity(obstaclesArr.Size());
218 |
219 | for (rapidjson::SizeType i = 0; i < obstaclesArr.Size(); i++) {
220 | rapidjson::Value const& obstacle_json = obstaclesArr[i];
221 |
222 | float time = obstacle_json["_time"].GetFloat();
223 | int lineIndex = obstacle_json["_lineIndex"].GetInt();
224 | auto type = ObstacleType(obstacle_json["_type"].GetInt());
225 | float duration = obstacle_json["_duration"].GetFloat();
226 | int width = obstacle_json["_width"].GetInt();
227 | auto* obstacle =
228 | CRASH_UNLESS(CustomBeatmapSaveData_ObstacleData::New_ctor(time, lineIndex, type, duration, width));
229 |
230 | obstacle->customData = GetCustomData(obstacle_json);
231 |
232 | obstacles.push_back(obstacle);
233 | }
234 | }
235 |
236 | CJDLogger::Logger.fmtLog("Parsed {} obstacles", obstacles.size());
237 |
238 | CJDLogger::Logger.fmtLog("Parse events");
239 |
240 | auto eventsArrIt = doc.FindMember("_events");
241 | auto events = VList::New();
242 |
243 | if (eventsArrIt != doc.MemberEnd() && eventsArrIt->value.IsArray()) {
244 | // Parse events
245 | rapidjson::Value const& eventsArr = eventsArrIt->value;
246 | events->EnsureCapacity(eventsArr.Size());
247 |
248 | for (rapidjson::SizeType i = 0; i < eventsArr.Size(); i++) {
249 | rapidjson::Value const& event_json = eventsArr[i];
250 |
251 | float time = event_json["_time"].GetFloat();
252 | auto type = BeatmapSaveDataCommon::BeatmapEventType(event_json["_type"].GetInt());
253 | int value = event_json["_value"].GetInt();
254 | float floatValue = 0;
255 |
256 | auto floatValueIt = event_json.FindMember("_floatValue");
257 | if (floatValueIt != event_json.MemberEnd()) {
258 | floatValue = floatValueIt->value.GetFloat();
259 | }
260 |
261 | auto* event = CRASH_UNLESS(CustomBeatmapSaveData_EventData::New_ctor(time, type, value, floatValue));
262 | event->customData = GetCustomData(event_json);
263 |
264 | events.push_back(event);
265 | }
266 | }
267 | CJDLogger::Logger.fmtLog("Parsed {} events", events.size());
268 |
269 | CJDLogger::Logger.fmtLog("Parse waypoints");
270 | auto waypoints_arrIt = doc.FindMember("_waypoints");
271 |
272 | VList waypoints = VList::New();
273 |
274 | if (waypoints_arrIt != doc.MemberEnd() && waypoints_arrIt->value.IsArray()) {
275 | rapidjson::Value const& waypoints_arr = doc["_waypoints"];
276 |
277 | waypoints->EnsureCapacity(waypoints_arr.Size());
278 |
279 | for (rapidjson::SizeType i = 0; i < waypoints_arr.Size(); i++) {
280 | rapidjson::Value const& waypoint_json = waypoints_arr[i];
281 |
282 | float time = waypoint_json["_time"].GetFloat();
283 | int lineIndex = waypoint_json["_lineIndex"].GetInt();
284 | auto lineLayer = BeatmapSaveDataCommon::NoteLineLayer(waypoint_json["_lineLayer"].GetInt());
285 | auto offsetDirection = BeatmapSaveDataCommon::OffsetDirection(waypoint_json["_offsetDirection"].GetInt());
286 | auto* waypoint = CustomJSONData::NewFast(time, lineIndex, lineLayer, offsetDirection);
287 | waypoints.push_back(waypoint);
288 | }
289 | }
290 | CJDLogger::Logger.fmtLog("Parsed {} waypoints", waypoints.size());
291 |
292 | CJDLogger::Logger.fmtLog("Parse specialEventsKeywordFilters");
293 | auto specialEventsKeywordFiltersJsonObjIt = doc.FindMember("_specialEventsKeywordFilters");
294 | VList specialEventsKeywordFiltersList = VList::New();
295 |
296 | if (specialEventsKeywordFiltersJsonObjIt != doc.MemberEnd()) {
297 | rapidjson::Value const& specialEventsKeywordFiltersJsonObj = specialEventsKeywordFiltersJsonObjIt->value;
298 |
299 | auto _keywords = specialEventsKeywordFiltersJsonObj.FindMember("_keywords");
300 |
301 | if (_keywords != specialEventsKeywordFiltersJsonObj.MemberEnd()) {
302 | specialEventsKeywordFiltersList->EnsureCapacity(_keywords->value.Size());
303 |
304 | for (auto const& keyword_json : _keywords->value.GetArray()) {
305 | std::string keyword = keyword_json["_keyword"].GetString();
306 | Il2CppString* keyword_il2cpp = il2cpp_utils::newcsstr(keyword);
307 |
308 | auto specialEventsArray = keyword_json["_specialEvents"].GetArray();
309 | auto specialEvents = VList::New(specialEventsArray.Size());
310 |
311 | for (auto const& specialEvent : specialEventsArray) {
312 | // safety, why not?
313 | if (!specialEvent.IsNumber()) {
314 | continue;
315 | }
316 |
317 | specialEvents.push_back(specialEvent.GetInt());
318 | }
319 |
320 | specialEventsKeywordFiltersList.push_back(
321 | CustomJSONData::NewFast(keyword_il2cpp, specialEvents.getPtr()));
322 | }
323 | }
324 | }
325 | auto* specialEventsKeywordFilters =
326 | CustomJSONData::NewFast(specialEventsKeywordFiltersList.getPtr());
327 |
328 | CJDLogger::Logger.fmtLog("Parse root");
329 | auto* saveData = CRASH_UNLESS(
330 | CustomBeatmapSaveData::New_ctor(events, notes, sliders, waypoints, obstacles, specialEventsKeywordFilters));
331 |
332 | saveData->doc = sharedDoc;
333 |
334 | saveData->customEventsData = std::make_shared>();
335 | auto customDataIt = doc.FindMember("_customData");
336 | if (customDataIt->value.IsObject()) {
337 | rapidjson::Value const& customData = customDataIt->value;
338 | saveData->customData = customData;
339 |
340 | auto customEventsIt = customData.FindMember("_customEvents");
341 | if (customEventsIt != customData.MemberEnd() && customEventsIt->value.IsArray()) {
342 | CJDLogger::Logger.fmtLog("Parse custom events");
343 |
344 | rapidjson::Value const& customEventsArr = customEventsIt->value;
345 | for (rapidjson::SizeType i = 0; i < customEventsArr.Size(); i++) {
346 | rapidjson::Value const& eventValue = customEventsArr[i];
347 |
348 | // Any consequences? Nah never
349 | auto typeIt = eventValue.FindMember("_type");
350 | if (typeIt == eventValue.MemberEnd() || typeIt->value.IsNull()) {
351 | continue;
352 | }
353 |
354 | float time = 0;
355 | // Dammit Reaxt
356 | auto timeIt = eventValue.FindMember("_time");
357 | if (timeIt != eventValue.MemberEnd()) {
358 | rapidjson::Value const& timeValue = timeIt->value;
359 | if (timeValue.GetType() == rapidjson::Type::kStringType) {
360 | // Reaxt why
361 | time = std::stof(timeValue.GetString());
362 | } else {
363 | time = timeValue.GetFloat();
364 | }
365 | }
366 |
367 | std::string_view type = typeIt->value.GetString();
368 |
369 | rapidjson::Value const* data = &eventValue["_data"];
370 | saveData->customEventsData->emplace_back(type, time, data);
371 | }
372 |
373 | CJDLogger::Logger.fmtLog("Parsed {} custom events", saveData->customEventsData->size());
374 | }
375 | }
376 |
377 | auto versionIt = doc.FindMember("_version");
378 | if (versionIt != doc.MemberEnd()) {
379 | saveData->_version = versionIt->value.GetString();
380 | } else {
381 | saveData->_version = nullptr;
382 | }
383 |
384 | profile.endTimer();
385 |
386 | CJDLogger::Logger.fmtLog(
387 | "v2 json parse took {}ms",
388 | static_cast(std::chrono::duration_cast(profile.elapsedTime()).count()));
389 | CJDLogger::Logger.fmtLog("v2 Version {}", static_cast(saveData->version ?: "null"));
390 |
391 | return saveData;
392 | }
393 |
--------------------------------------------------------------------------------
/src/CustomEventData.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "CustomEventData.h"
4 |
5 | #include "CJDLogger.h"
6 | #include "GlobalNamespace/BasicBeatmapEventData.hpp"
7 |
8 | #include "CustomJSONDataHooks.h"
9 |
10 | #include "beatsaber-hook/shared/utils/il2cpp-type-check.hpp"
11 |
12 | #include "GlobalNamespace/CallbacksInTime.hpp"
13 |
14 | #include "System/Collections/Generic/Dictionary_2.hpp"
15 |
16 | using namespace CustomJSONData;
17 | using namespace GlobalNamespace;
18 |
19 | DEFINE_TYPE(CustomJSONData, CustomEventData)
20 | DEFINE_TYPE(CustomJSONData, CustomBeatmapDataCallbackWrapper)
21 |
22 | CJD_MOD_EXPORT std::vector CustomEventCallbacks::customEventCallbacks;
23 | CJD_MOD_EXPORT SafePtr>
24 | CustomEventCallbacks::firstNode;
25 |
26 | void CustomEventData::ctor(float time) {
27 | INVOKE_CTOR();
28 | static auto const* ctor = il2cpp_utils::FindMethodUnsafe(classof(BeatmapDataItem*), ".ctor", 4);
29 | il2cpp_utils::RunMethodRethrow(this, ctor, time, 0, 0, BeatmapDataItemType(2));
30 | BeatmapDataItem::_time_k__BackingField = time;
31 | BeatmapDataItem::type = 2;
32 | }
33 | CustomEventData* CustomEventData::New(float time, std::string_view type, size_t typeHash, rapidjson::Value const* data) {
34 | auto event = CustomEventData::New_ctor(time);
35 | CRASH_UNLESS(data);
36 |
37 | event->typeHash = typeHash;
38 | event->type = type;
39 | event->data = data;
40 |
41 | return event;
42 | }
43 |
44 | CustomEventData* CustomEventData::GetCopy() {
45 | auto* copy = CustomJSONData::CustomEventData::New(this->time, this->type, typeHash, this->data);
46 | return copy;
47 | }
48 |
49 | void CustomBeatmapDataCallbackWrapper::ctor() {
50 | INVOKE_CTOR();
51 | reinterpret_cast(this)->_ctor(0.0F, csTypeOf(CustomEventData*),
52 | ArrayW((il2cpp_array_size_t)0));
53 |
54 | redirectEvent = nullptr;
55 | }
56 |
57 | void CustomBeatmapDataCallbackWrapper::CallCallback(BeatmapDataItem* item) {
58 | static auto CustomEventDataKlass = classof(CustomEventData*);
59 | // CRASH_UNLESS(item->klass == CustomEventDataKlass);
60 |
61 |
62 |
63 | PAPER_IL2CPP_CATCH_HANDLER(
64 | //
65 | if (redirectEvent) {
66 | redirectEvent(controller, item);
67 | return;
68 | }
69 | //
70 | )
71 |
72 | // should never get to this point
73 | if (item->klass != CustomEventDataKlass) {
74 | CJDLogger::Logger.debug("Invokiong item {} time {}", il2cpp_utils::ClassStandardName(item->klass), item->time);
75 | return;
76 | }
77 |
78 | PAPER_IL2CPP_CATCH_HANDLER(
79 | //
80 | auto castedItem = il2cpp_utils::cast(item);
81 | for (auto const& customEvents : CustomEventCallbacks::customEventCallbacks) {
82 | try {
83 | customEvents.callback(controller, castedItem);
84 | } catch (std::exception const& e) {
85 | CJDLogger::Logger.fmtLog("Caught exception in callback {}", fmt::ptr(customEvents.callback));
86 | throw e;
87 | } catch (...) {
88 | CJDLogger::Logger.fmtLog("Caught exception in callback {}", fmt::ptr(customEvents.callback));
89 | throw;
90 | }
91 | })
92 | }
93 |
94 | void CJD_MOD_EXPORT CustomEventCallbacks::AddCustomEventCallback(
95 | void (*callback)(GlobalNamespace::BeatmapCallbacksController*, CustomJSONData::CustomEventData*)) {
96 | customEventCallbacks.emplace_back(callback);
97 | }
98 |
99 | void CJD_MOD_EXPORT
100 | CustomEventCallbacks::RegisterCallbacks(GlobalNamespace::BeatmapCallbacksController* callbackController) {
101 | CJDLogger::Logger.fmtLog("REGISTER CUSTOM CALLBACK!");
102 | auto* wrapper = CustomBeatmapDataCallbackWrapper::New_ctor();
103 | wrapper->controller = callbackController;
104 |
105 | // register it
106 | // AddBeatmapCallback
107 | // do this to avoid using delegates
108 | auto callbacksInTime = callbackController->_callbacksInTimes->get_Item(0);
109 | callbacksInTime->AddCallback(wrapper);
110 | CJDLogger::Logger.fmtLog("REGISTERED CUSTOM CALLBACK!");
111 | }
112 |
--------------------------------------------------------------------------------
/src/JSONWrapper.cpp:
--------------------------------------------------------------------------------
1 | // This is my most favorite file in this project
2 |
3 | #include "JSONWrapper.h"
4 |
5 | using namespace CustomJSONData;
6 |
7 | DEFINE_TYPE(CustomJSONData, JSONWrapper);
8 | DEFINE_TYPE(CustomJSONData, JSONWrapperUTF16);
9 | DEFINE_TYPE(CustomJSONData, DocumentWrapper);
10 |
11 | void JSONWrapper::ctor() {
12 | INVOKE_CTOR();
13 | }
14 |
15 | JSONWrapper* JSONWrapper::GetCopy() {
16 | auto* copy = JSONWrapper::New_ctor();
17 |
18 | copy->value = value;
19 | copy->associatedData = associatedData;
20 |
21 | return copy;
22 | }
23 |
24 | void JSONWrapperUTF16::ctor() {
25 | INVOKE_CTOR();
26 | }
27 |
28 | JSONWrapperUTF16* JSONWrapperUTF16::GetCopy() {
29 | auto* copy = JSONWrapperUTF16::New_ctor();
30 |
31 | copy->value = value;
32 | copy->associatedData = associatedData;
33 |
34 | return copy;
35 | }
36 |
--------------------------------------------------------------------------------
/src/hooks/BeatmapHooks.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "CustomJSONDataHooks.h"
3 |
4 | #include "HookUtils.hpp"
5 | #include "CustomBeatmapData.h"
6 |
7 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/zzzz__EventData_def.hpp"
8 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/BeatmapSaveDataItem.hpp"
9 | #include "BeatmapDataLoaderVersion2_6_0AndEarlier/BeatmapDataLoader.hpp"
10 |
11 | #include "BeatmapDataLoaderVersion3/BeatmapDataLoader.hpp"
12 | #include "BeatmapSaveDataVersion3/BeatmapSaveDataItem.hpp"
13 |
14 | #include "BeatmapDataLoaderVersion4/BeatmapDataLoader.hpp"
15 | #include "BeatmapSaveDataVersion4/LightshowSaveData.hpp"
16 |
17 | #include "GlobalNamespace/BpmTimeProcessor.hpp"
18 | #include "GlobalNamespace/EnvironmentKeywords.hpp"
19 | #include "GlobalNamespace/IEnvironmentInfo.hpp"
20 | #include "GlobalNamespace/IEnvironmentLightGroups.hpp"
21 | #include "GlobalNamespace/EnvironmentLightGroups.hpp"
22 | #include "GlobalNamespace/DefaultEnvironmentEvents.hpp"
23 | #include "GlobalNamespace/BeatmapObjectData.hpp"
24 | #include "GlobalNamespace/NoteData.hpp"
25 | #include "GlobalNamespace/BeatmapDataSortedListForTypeAndIds_1.hpp"
26 | #include "GlobalNamespace/BasicBeatmapEventDataProcessor.hpp"
27 | #include "GlobalNamespace/BeatmapDataStrobeFilterTransform.hpp"
28 | #include "GlobalNamespace/LightColorBeatmapEventData.hpp"
29 | #include "GlobalNamespace/EnvironmentIntensityReductionOptions.hpp"
30 | #include "GlobalNamespace/CallbacksInTime.hpp"
31 | #include "GlobalNamespace/IReadonlyBeatmapData.hpp"
32 | #include "GlobalNamespace/BeatmapEventDataLightsExtensions.hpp"
33 | #include "GlobalNamespace/BurstSliderSpawner.hpp"
34 | #include "GlobalNamespace/VariableMovementDataProvider.hpp"
35 | #include "GlobalNamespace/NoteSpawnData.hpp"
36 | #include "GlobalNamespace/NoteCutDirectionExtensions.hpp"
37 | #include "GlobalNamespace/BeatmapObjectSpawnMovementData.hpp"
38 |
39 | #include "System/Action.hpp"
40 |
41 | #include "UnityEngine/JsonUtility.hpp"
42 |
43 | #include "System/Reflection/MemberInfo.hpp"
44 | #include "System/Collections/Generic/InsertionBehavior.hpp"
45 |
46 | #include "beatsaber-hook/shared/utils/typedefs-list.hpp"
47 | #include "beatsaber-hook/shared/utils/hooking.hpp"
48 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
49 | // for rapidjson error parsing
50 | #include "beatsaber-hook/shared/rapidjson/include/rapidjson/error/en.h"
51 |
52 | #include "cpp-semver/shared/cpp-semver.hpp"
53 |
54 | #include "paper2_scotland2/shared/Profiler.hpp"
55 |
56 | #include "sombrero/shared/linq_functional.hpp"
57 | #include "sombrero/shared/Vector2Utils.hpp"
58 | #include "sombrero/shared/Vector3Utils.hpp"
59 |
60 | #include "custom-types/shared/register.hpp"
61 |
62 | #include "songcore/shared/CustomJSONData.hpp"
63 |
64 | #include "JSONWrapper.h"
65 | #include "CustomBeatmapSaveDatav2.h"
66 | #include "CustomBeatmapSaveDatav3.h"
67 | #include "CustomBeatmapData.h"
68 | #include "misc/BeatmapFieldUtils.hpp"
69 | #include "misc/BeatmapDataLoaderUtils.hpp"
70 | #include "CustomJSONDataHooks.h"
71 | #include "CJDLogger.h"
72 | #include "VList.h"
73 |
74 | #include
75 | #include
76 | #include
77 | #include
78 |
79 | using namespace System;
80 | using namespace System::Collections::Generic;
81 | using namespace GlobalNamespace;
82 | using namespace CustomJSONData;
83 | using namespace BeatmapSaveDataVersion3;
84 | using namespace BeatmapSaveDataVersion4;
85 |
86 | MAKE_HOOK_FIND_INSTANCE(CustomBeatmapDataSortedListForTypes_InsertItem,
87 | classof(BeatmapDataSortedListForTypeAndIds_1*), "InsertItem",
88 | System::Collections::Generic::LinkedListNode_1*,
89 | BeatmapDataSortedListForTypeAndIds_1* self, BeatmapDataItem* item) {
90 | auto* list = self->GetList(CustomBeatmapData::GetCustomType(item), item->get_subtypeGroupIdentifier());
91 |
92 | auto* node = list->Insert(item);
93 | // Remove to avoid exception
94 | self->_itemToNodeMap->TryInsert(item, node, InsertionBehavior::OverwriteExisting);
95 |
96 | return node;
97 | }
98 |
99 | MAKE_HOOK_FIND_INSTANCE(CustomBeatmapDataSortedListForTypes_RemoveItem,
100 | classof(BeatmapDataSortedListForTypeAndIds_1*), "RemoveItem", void,
101 | BeatmapDataSortedListForTypeAndIds_1* self, BeatmapDataItem* item) {
102 | auto* list = self->GetList(CustomBeatmapData::GetCustomType(item), item->get_subtypeGroupIdentifier());
103 | System::Collections::Generic::LinkedListNode_1* node = nullptr;
104 | if (self->_itemToNodeMap->TryGetValue(item, byref(node))) {
105 | list->Remove(node);
106 | }
107 | }
108 |
109 | MAKE_PAPER_HOOK_MATCH(BeatmapData_GetCopy, &CustomBeatmapData::GetCopy, BeatmapData*, BeatmapData* self) {
110 | static auto* CustomBeatmapDataKlass = classof(CustomBeatmapData*);
111 |
112 | if (self->klass == CustomBeatmapDataKlass) {
113 | return reinterpret_cast(self)->GetCopyOverride();
114 | }
115 |
116 | return BeatmapData_GetCopy(self);
117 | }
118 |
119 | MAKE_PAPER_HOOK_MATCH(
120 | BeatmapData_GetFilteredCopy, &CustomBeatmapData::GetFilteredCopy, BeatmapData*, BeatmapData* self,
121 | System::Func_2<::GlobalNamespace::BeatmapDataItem*, ::GlobalNamespace::BeatmapDataItem*>* processDataItem) {
122 | static auto* CustomBeatmapDataKlass = classof(CustomBeatmapData*);
123 |
124 | if (self->klass == CustomBeatmapDataKlass) {
125 | return reinterpret_cast(self)->GetFilteredCopyOverride(
126 | [&](auto i) constexpr { return processDataItem->Invoke(i); });
127 | }
128 |
129 | return BeatmapData_GetFilteredCopy(self, processDataItem);
130 | }
131 |
132 | MAKE_PAPER_HOOK_MATCH(CustomAddBeatmapObjectData, &BeatmapData::AddBeatmapObjectData, void, BeatmapData* self,
133 | BeatmapObjectData* item) {
134 | if (auto customSelf = il2cpp_utils::try_cast(self).value_or(nullptr)) {
135 | customSelf->beatmapObjectDatas.emplace_back(item);
136 | }
137 | CustomAddBeatmapObjectData(self, item);
138 | }
139 |
140 | MAKE_PAPER_HOOK_MATCH(CustomInsertBeatmapEventData, &BeatmapData::InsertBeatmapEventData, void, BeatmapData* self,
141 | BeatmapEventData* item) {
142 | if (auto customSelf = il2cpp_utils::try_cast(self).value_or(nullptr)) {
143 | customSelf->beatmapEventDatas.emplace_back(item);
144 | }
145 | CustomInsertBeatmapEventData(self, item);
146 | }
147 | MAKE_PAPER_HOOK_MATCH(BurstSliderSpawner_ProcessSliderData, &BurstSliderSpawner::ProcessSliderData, void,
148 | ::GlobalNamespace::SliderData* sliderData,
149 | ::ByRef<::GlobalNamespace::SliderSpawnData> sliderSpawnData, bool forceIsFirstNote,
150 | ::GlobalNamespace::VariableMovementDataProvider* variableMovementDataProvider,
151 | ::GlobalNamespace::BurstSliderSpawner_ProcessNoteDataDelegate* processNoteData) {
152 |
153 | static auto CustomKlass = classof(CustomJSONData::CustomSliderData*);
154 |
155 | auto* customSliderData = il2cpp_utils::try_cast(sliderData).value_or(nullptr);
156 | if (customSliderData == nullptr) {
157 | return BurstSliderSpawner_ProcessSliderData(sliderData, sliderSpawnData, forceIsFirstNote,
158 | variableMovementDataProvider, processNoteData);
159 | }
160 |
161 | float halfJumpDuration = variableMovementDataProvider->halfJumpDuration;
162 | float headGravity = variableMovementDataProvider->CalculateCurrentNoteJumpGravity(sliderSpawnData->headGravityBase);
163 | float tailGravity = variableMovementDataProvider->CalculateCurrentNoteJumpGravity(sliderSpawnData->tailGravityBase);
164 |
165 | auto vector = Sombrero::FastVector2(sliderSpawnData->headNoteOffset.x,
166 | sliderSpawnData->headNoteOffset.y + headGravity * halfJumpDuration * halfJumpDuration * 0.5f);
167 |
168 | auto vector2 = Sombrero::FastVector2(sliderSpawnData->tailNoteOffset.x,
169 | sliderSpawnData->tailNoteOffset.y + tailGravity * halfJumpDuration * halfJumpDuration * 0.5f);
170 |
171 | auto vector3 = vector2 - vector;
172 | float magnitude = vector3.Magnitude();
173 |
174 | float angle = (NoteCutDirectionExtensions::RotationAngle(sliderData->headCutDirection) - 90.0f +
175 | sliderData->headCutDirectionAngleOffset) * 0.017453292f;
176 | auto vector4 = Sombrero::FastVector2(std::cos(angle), std::sin(angle)) * 0.5f * magnitude;
177 |
178 | int sliceCount = sliderData->sliceCount;
179 | float squishAmount = sliderData->squishAmount;
180 | float time = sliderData->time;
181 | float tailTime = sliderData->tailTime;
182 | float num3 = (tailTime - time) * 0.5f;
183 |
184 | auto bezierCurve = [](Sombrero::FastVector2 p0, Sombrero::FastVector2 p1, Sombrero::FastVector2 p2, float t,
185 | Sombrero::FastVector2& pos, Sombrero::FastVector2& tangent) constexpr {
186 | float num = 1.0f - t;
187 | pos = p0 * num * num + p1 * 2.0f * num * t + p2 * t * t;
188 | tangent = (p1 - p0) * 2.0f * (1.0f - t) + (p2 - p1) * 2.0f * t;
189 | };
190 |
191 | for (int i = 1; i < sliceCount; i++) {
192 | float sliceT = (float)i / (float)(sliceCount - 1);
193 | int index = ((i < sliceCount - 1) ? sliderData->headLineIndex : sliderData->tailLineIndex);
194 | auto noteLineLayer = ((i < sliceCount - 1) ? sliderData->headLineLayer : sliderData->tailLineLayer);
195 |
196 | // TRANSPILE HERE
197 | auto noteData =
198 | CreateCustomBurstNoteData(std::lerp(time, tailTime, sliceT), sliderData->beat, sliderData->rotation, index,
199 | noteLineLayer, sliderData->headBeforeJumpLineLayer, sliderData->colorType,
200 | NoteCutDirection::Any, 1.0f, customSliderData->customData->value);
201 | // copy the AD from the original note
202 | noteData->customData->associatedData = customSliderData->customData->associatedData;
203 | // TRANSPILE END
204 |
205 | noteData->timeToPrevColorNote = sliceT * num3;
206 | Sombrero::FastVector2 position;
207 | Sombrero::FastVector2 tangent;
208 | bezierCurve(Sombrero::FastVector2::zero(), vector4, vector3, sliceT * squishAmount, position, tangent);
209 | noteData->SetCutDirectionAngleOffset(Sombrero::FastVector2::SignedAngle({ 0.0f, -1.0f }, tangent));
210 | noteData->timeToNextColorNote = ((i == sliceCount - 1) ? 1.0f : 0.4f);
211 |
212 | auto headNoteOffset = Sombrero::FastVector3(sliderSpawnData->headNoteOffset);
213 | auto noteSpawnData = NoteSpawnData(headNoteOffset + Sombrero::FastVector3(vector.x, 0.0f, 0.0f),
214 | headNoteOffset + Sombrero::FastVector3(vector.x, 0.0f, 0.0f),
215 | headNoteOffset + Sombrero::FastVector3(vector.x, 0.0f, 0.0f),
216 | headGravity * halfJumpDuration * halfJumpDuration * 0.5f + vector.y);
217 |
218 | processNoteData->Invoke(noteData, noteSpawnData, forceIsFirstNote);
219 | }
220 | }
221 |
222 | void CustomJSONData::InstallBeatmapHooks() {
223 | INSTALL_HOOK_ORIG(CJDLogger::Logger, CustomAddBeatmapObjectData);
224 | INSTALL_HOOK_ORIG(CJDLogger::Logger, CustomInsertBeatmapEventData);
225 |
226 | INSTALL_HOOK_ORIG(CJDLogger::Logger, CustomBeatmapDataSortedListForTypes_InsertItem);
227 | INSTALL_HOOK_ORIG(CJDLogger::Logger, CustomBeatmapDataSortedListForTypes_RemoveItem);
228 | INSTALL_HOOK_ORIG(CJDLogger::Logger, BeatmapData_GetFilteredCopy);
229 | INSTALL_HOOK_ORIG(CJDLogger::Logger, BeatmapData_GetCopy);
230 |
231 | INSTALL_HOOK_ORIG(CJDLogger::Logger, BurstSliderSpawner_ProcessSliderData);
232 | }
--------------------------------------------------------------------------------
/src/hooks/CustomJSONDataHooks.cpp:
--------------------------------------------------------------------------------
1 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/zzzz__EventData_def.hpp"
2 | #include "BeatmapSaveDataVersion2_6_0AndEarlier/BeatmapSaveDataItem.hpp"
3 | #include "BeatmapDataLoaderVersion2_6_0AndEarlier/BeatmapDataLoader.hpp"
4 |
5 | #include "BeatmapDataLoaderVersion3/BeatmapDataLoader.hpp"
6 | #include "BeatmapSaveDataVersion3/BeatmapSaveDataItem.hpp"
7 |
8 | #include "BeatmapDataLoaderVersion4/BeatmapDataLoader.hpp"
9 | #include "BeatmapSaveDataVersion4/LightshowSaveData.hpp"
10 |
11 | #include "GlobalNamespace/BpmTimeProcessor.hpp"
12 | #include "GlobalNamespace/EnvironmentKeywords.hpp"
13 | #include "GlobalNamespace/IEnvironmentInfo.hpp"
14 | #include "GlobalNamespace/IEnvironmentLightGroups.hpp"
15 | #include "GlobalNamespace/EnvironmentLightGroups.hpp"
16 | #include "GlobalNamespace/DefaultEnvironmentEvents.hpp"
17 | #include "GlobalNamespace/BeatmapObjectData.hpp"
18 | #include "GlobalNamespace/NoteData.hpp"
19 | #include "GlobalNamespace/BeatmapDataSortedListForTypeAndIds_1.hpp"
20 | #include "GlobalNamespace/BasicBeatmapEventDataProcessor.hpp"
21 | #include "GlobalNamespace/BeatmapDataStrobeFilterTransform.hpp"
22 | #include "GlobalNamespace/LightColorBeatmapEventData.hpp"
23 | #include "GlobalNamespace/EnvironmentIntensityReductionOptions.hpp"
24 | #include "GlobalNamespace/CallbacksInTime.hpp"
25 | #include "GlobalNamespace/IReadonlyBeatmapData.hpp"
26 | #include "GlobalNamespace/BeatmapEventDataLightsExtensions.hpp"
27 |
28 | #include "System/Action.hpp"
29 |
30 | #include "UnityEngine/JsonUtility.hpp"
31 |
32 | #include "System/Reflection/MemberInfo.hpp"
33 | #include "System/Collections/Generic/InsertionBehavior.hpp"
34 |
35 | #include "beatsaber-hook/shared/utils/typedefs-list.hpp"
36 | #include "beatsaber-hook/shared/utils/hooking.hpp"
37 | #include "beatsaber-hook/shared/config/rapidjson-utils.hpp"
38 | // for rapidjson error parsing
39 | #include "beatsaber-hook/shared/rapidjson/include/rapidjson/error/en.h"
40 |
41 | #include "cpp-semver/shared/cpp-semver.hpp"
42 |
43 | #include "paper2_scotland2/shared/Profiler.hpp"
44 |
45 | #include "sombrero/shared/linq_functional.hpp"
46 | #include "sombrero/shared/Vector2Utils.hpp"
47 | #include "sombrero/shared/Vector3Utils.hpp"
48 |
49 | #include "custom-types/shared/register.hpp"
50 |
51 | #include "songcore/shared/CustomJSONData.hpp"
52 |
53 | #include "JSONWrapper.h"
54 | #include "CustomBeatmapSaveDatav2.h"
55 | #include "CustomBeatmapSaveDatav3.h"
56 | #include "CustomBeatmapData.h"
57 | #include "misc/BeatmapFieldUtils.hpp"
58 | #include "misc/BeatmapDataLoaderUtils.hpp"
59 | #include "CustomJSONDataHooks.h"
60 | #include "CJDLogger.h"
61 | #include "VList.h"
62 |
63 | #include
64 | #include
65 | #include
66 | #include
67 |
68 | using namespace System;
69 | using namespace System::Collections::Generic;
70 | using namespace GlobalNamespace;
71 | using namespace CustomJSONData;
72 | using namespace BeatmapSaveDataVersion3;
73 | using namespace BeatmapSaveDataVersion4;
74 |
75 |
76 |
77 | constexpr bool Approximately(float a, float b) {
78 | return std::abs(b - a) < std::max(1E-06F * std::max(std::abs(a), std::abs(b)), 1E-45F * 8.0F);
79 | }
80 |
81 | // clang-format off
82 | /**
83 | BeatmapData beatmapData2 = new BeatmapData(beatmapData.numberOfLines);
84 | bool flag = environmentIntensityReductionOptions.compressExpand == EnvironmentIntensityReductionOptions.CompressExpandReductionType.RemoveWithStrobeFilter;
85 | bool flag2 = environmentIntensityReductionOptions.rotateRings == EnvironmentIntensityReductionOptions.RotateRingsReductionType.RemoveWithStrobeFilter;
86 | Dictionary dictionary = new Dictionary
87 | {
88 | {
89 | BasicBeatmapEventType.Event0,
90 | new BeatmapDataStrobeFilterTransform.StrobeStreakData()
91 | },
92 | {
93 | BasicBeatmapEventType.Event1,
94 | new BeatmapDataStrobeFilterTransform.StrobeStreakData()
95 | },
96 | {
97 | BasicBeatmapEventType.Event2,
98 | new BeatmapDataStrobeFilterTransform.StrobeStreakData()
99 | },
100 | {
101 | BasicBeatmapEventType.Event3,
102 | new BeatmapDataStrobeFilterTransform.StrobeStreakData()
103 | },
104 | {
105 | BasicBeatmapEventType.Event4,
106 | new BeatmapDataStrobeFilterTransform.StrobeStreakData()
107 | }
108 | };
109 | foreach (BeatmapDataItem beatmapDataItem in beatmapData.allBeatmapDataItems)
110 | {
111 | LightColorBeatmapEventData lightColorBeatmapEventData = beatmapDataItem as LightColorBeatmapEventData;
112 | if (lightColorBeatmapEventData != null)
113 | {
114 | lightColorBeatmapEventData.DisableStrobe();
115 | beatmapData2.InsertBeatmapEventDataInOrder(lightColorBeatmapEventData);
116 | }
117 | else
118 | {
119 | BasicBeatmapEventData basicBeatmapEventData = beatmapDataItem as BasicBeatmapEventData;
120 | if (basicBeatmapEventData == null)
121 | {
122 | BeatmapEventData beatmapEventData = beatmapDataItem as BeatmapEventData;
123 | if (beatmapEventData != null)
124 | {
125 | beatmapData2.InsertBeatmapEventDataInOrder(beatmapEventData);
126 | }
127 | else
128 | {
129 | BeatmapObjectData beatmapObjectData = beatmapDataItem as BeatmapObjectData;
130 | if (beatmapObjectData != null)
131 | {
132 | beatmapData2.AddBeatmapObjectDataInOrder(beatmapObjectData);
133 | }
134 | }
135 | }
136 | else if ((!flag || basicBeatmapEventData.basicBeatmapEventType != BasicBeatmapEventType.Event9) && (!flag2 || basicBeatmapEventData.basicBeatmapEventType != BasicBeatmapEventType.Event8))
137 | {
138 | if (!basicBeatmapEventData.basicBeatmapEventType.IsCoreLightIntensityChangeEvent())
139 | {
140 | beatmapData2.InsertBeatmapEventDataInOrder(basicBeatmapEventData);
141 | }
142 | else if (basicBeatmapEventData.basicBeatmapEventType.IsCoreLightIntensityChangeEvent() && basicBeatmapEventData.HasLightFadeEventDataValue())
143 | {
144 | beatmapData2.InsertBeatmapEventDataInOrder(basicBeatmapEventData);
145 | }
146 | else
147 | {
148 | BeatmapDataStrobeFilterTransform.StrobeStreakData strobeStreakData = dictionary[basicBeatmapEventData.basicBeatmapEventType];
149 | if (strobeStreakData.isActive)
150 | {
151 | if (basicBeatmapEventData.time - strobeStreakData.lastSwitchTime < 0.1f)
152 | {
153 | strobeStreakData.AddStrobeData(basicBeatmapEventData);
154 | }
155 | else
156 | {
157 | if (!Mathf.Approximately(strobeStreakData.strobeStartTime, strobeStreakData.lastSwitchTime))
158 | {
159 | int onEventDataValue = BeatmapDataStrobeFilterTransform.GetOnEventDataValue(strobeStreakData.startColorType);
160 | BasicBeatmapEventData beatmapEventData2 = new BasicBeatmapEventData(strobeStreakData.strobeStartTime, basicBeatmapEventData.basicBeatmapEventType, onEventDataValue, basicBeatmapEventData.floatValue);
161 | beatmapData2.InsertBeatmapEventDataInOrder(beatmapEventData2);
162 | int value;
163 | if (strobeStreakData.lastIsOn)
164 | {
165 | value = BeatmapDataStrobeFilterTransform.GetOnEventDataValue(strobeStreakData.lastColorType);
166 | }
167 | else
168 | {
169 | value = BeatmapDataStrobeFilterTransform.GetFlashAndFadeToBlackEventDataValue(strobeStreakData.lastColorType);
170 | }
171 | BasicBeatmapEventData beatmapEventData3 = new BasicBeatmapEventData(strobeStreakData.lastSwitchTime, basicBeatmapEventData.basicBeatmapEventType, value, basicBeatmapEventData.floatValue);
172 | beatmapData2.InsertBeatmapEventDataInOrder(beatmapEventData3);
173 | }
174 | else
175 | {
176 | beatmapData2.InsertBeatmapEventDataInOrder(strobeStreakData.originalBasicBeatmapEventData);
177 | }
178 | strobeStreakData.StartPotentialStrobe(basicBeatmapEventData);
179 | }
180 | }
181 | else
182 | {
183 | strobeStreakData.StartPotentialStrobe(basicBeatmapEventData);
184 | }
185 | }
186 | }
187 | }
188 | }
189 | foreach (KeyValuePair keyValuePair in dictionary)
190 | {
191 | if (keyValuePair.Value.isActive)
192 | {
193 | beatmapData2.InsertBeatmapEventDataInOrder(keyValuePair.Value.originalBasicBeatmapEventData);
194 | }
195 | }
196 | foreach (string specialBasicBeatmapEventKeyword in beatmapData.specialBasicBeatmapEventKeywords)
197 | {
198 | beatmapData2.AddSpecialBasicBeatmapEventKeyword(specialBasicBeatmapEventKeyword);
199 | }
200 | return beatmapData2;
201 | */
202 | // clang-format on
203 | MAKE_PAPER_HOOK_MATCH(BeatmapDataStrobeFilterTransform_CreateTransformedData,
204 | &BeatmapDataStrobeFilterTransform::CreateTransformedData, IReadonlyBeatmapData*,
205 | IReadonlyBeatmapData* beatmapData,
206 | EnvironmentIntensityReductionOptions* environmentIntensityReductionOptions) {
207 | if (!beatmapData || reinterpret_cast(beatmapData)->klass != classof(CustomBeatmapData*)) {
208 | return BeatmapDataStrobeFilterTransform_CreateTransformedData(beatmapData, environmentIntensityReductionOptions);
209 | }
210 | // Won't work since the constructors are base game
211 | //
212 | // auto fixedBeatmap = BeatmapDataStrobeFilterTransform_CreateTransformedData(beatmapData,
213 | // environmentIntensityReductionOptions);
214 | //
215 | // auto customBeatmapData = il2cpp_utils::cast(beatmapData);
216 | // CustomBeatmapData *newBeatmap;
217 | //
218 | //// if (auto customBeatmapData = il2cpp_utils::try_cast(beatmapData)) {
219 | //// for (auto const &c: customBeatmapData.value()->customEventDatas) {
220 | //// beatmapData2->InsertCustomEventDataInOrder(c);
221 | //// }
222 | // newBeatmap = customBeatmapData->GetFilteredCopyOverride(
223 | // [&](BeatmapDataItem *const &i) -> BeatmapDataItem * {
224 | //
225 | // if (i->type == BeatmapDataItem::BeatmapDataItemType::BeatmapEvent) {
226 | // return nullptr;
227 | // }
228 | //
229 | // return i;
230 | // });
231 | //// } else {
232 | //// newBeatmap = CustomBeatmapData::New_ctor(beatmapData->i_IBeatmapDataBasicInfo()->get_numberOfLines());
233 | ////
234 | //// }
235 | //
236 | //
237 | // auto const &linkedItems = fixedBeatmap->get_allBeatmapDataItems();
238 | // for (auto node = linkedItems->get_First();
239 | // node != nullptr; node = CustomBeatmapData::LinkedListNode_1_get_Next(node)) {
240 | // if (!node->item || node->item->type != BeatmapDataItem::BeatmapDataItemType::BeatmapEvent) {
241 | // continue;
242 | // }
243 | //
244 | // newBeatmap->InsertBeatmapEventDataInOrder(il2cpp_utils::cast(node->item));
245 | // }
246 |
247 | auto* customBeatmapData = il2cpp_utils::cast(beatmapData);
248 | bool flag = environmentIntensityReductionOptions->compressExpand ==
249 | EnvironmentIntensityReductionOptions::CompressExpandReductionType::RemoveWithStrobeFilter;
250 | bool flag2 = environmentIntensityReductionOptions->rotateRings ==
251 | EnvironmentIntensityReductionOptions::RotateRingsReductionType::RemoveWithStrobeFilter;
252 | std::unordered_map dictionary(
253 | { { BasicBeatmapEventType::Event0.value__, BeatmapDataStrobeFilterTransform::StrobeStreakData::New_ctor() },
254 | { BasicBeatmapEventType::Event1.value__, BeatmapDataStrobeFilterTransform::StrobeStreakData::New_ctor() },
255 | { BasicBeatmapEventType::Event2.value__, BeatmapDataStrobeFilterTransform::StrobeStreakData::New_ctor() },
256 | { BasicBeatmapEventType::Event3.value__, BeatmapDataStrobeFilterTransform::StrobeStreakData::New_ctor() },
257 | { BasicBeatmapEventType::Event4.value__, BeatmapDataStrobeFilterTransform::StrobeStreakData::New_ctor() } });
258 |
259 | auto* newBeatmap = customBeatmapData->BaseCopy();
260 |
261 | for (auto const& o : customBeatmapData->beatmapObjectDatas) {
262 | if (!o) {
263 | continue;
264 | }
265 | newBeatmap->AddBeatmapObjectDataInOrder(o);
266 | }
267 |
268 | for (auto const& beatmapDataItem : customBeatmapData->beatmapEventDatas) {
269 | if (!beatmapDataItem) {
270 | continue;
271 | }
272 |
273 | BasicBeatmapEventData* basicBeatmapEventData =
274 | il2cpp_utils::try_cast(beatmapDataItem).value_or(nullptr);
275 | LightColorBeatmapEventData* lightColorBeatmapEventData =
276 | il2cpp_utils::try_cast(beatmapDataItem).value_or(nullptr);
277 |
278 | if (lightColorBeatmapEventData) {
279 | lightColorBeatmapEventData->DisableStrobe();
280 | newBeatmap->InsertBeatmapEventDataInOrder(lightColorBeatmapEventData);
281 | continue;
282 | }
283 | if (!basicBeatmapEventData) {
284 | newBeatmap->InsertBeatmapEventDataInOrder(beatmapDataItem);
285 | continue;
286 | }
287 | if ((!flag || basicBeatmapEventData->basicBeatmapEventType != BasicBeatmapEventType::Event9) &&
288 | (!flag2 || basicBeatmapEventData->basicBeatmapEventType != BasicBeatmapEventType::Event8)) {
289 | if (!BeatmapEventTypeExtensions::IsCoreLightIntensityChangeEvent(basicBeatmapEventData->basicBeatmapEventType)) {
290 | newBeatmap->InsertBeatmapEventDataInOrder(basicBeatmapEventData);
291 | continue;
292 | }
293 |
294 | if (BeatmapEventDataLightsExtensions::HasLightFadeEventDataValue(basicBeatmapEventData)) {
295 | newBeatmap->InsertBeatmapEventDataInOrder(basicBeatmapEventData);
296 | continue;
297 | }
298 |
299 | BeatmapDataStrobeFilterTransform::StrobeStreakData* strobeStreakData =
300 | dictionary[basicBeatmapEventData->basicBeatmapEventType.value__];
301 | CRASH_UNLESS(strobeStreakData);
302 | if (strobeStreakData->isActive) {
303 | if (basicBeatmapEventData->time - strobeStreakData->lastSwitchTime < 0.1F) {
304 | strobeStreakData->AddStrobeData(basicBeatmapEventData);
305 | } else {
306 | if (!Approximately(strobeStreakData->strobeStartTime, strobeStreakData->lastSwitchTime)) {
307 | int onEventDataValue =
308 | BeatmapDataStrobeFilterTransform::GetOnEventDataValue(strobeStreakData->startColorType);
309 | auto* beatmapEventData2 = static_cast(basicBeatmapEventData->GetCopy());
310 | beatmapEventData2->_time_k__BackingField = strobeStreakData->strobeStartTime;
311 | beatmapEventData2->value = onEventDataValue;
312 | newBeatmap->InsertBeatmapEventDataInOrder(beatmapEventData2);
313 | int value = 0;
314 | if (strobeStreakData->lastIsOn) {
315 | value = BeatmapDataStrobeFilterTransform::GetOnEventDataValue(strobeStreakData->lastColorType);
316 | } else {
317 | value = BeatmapDataStrobeFilterTransform::GetFlashAndFadeToBlackEventDataValue(
318 | strobeStreakData->lastColorType);
319 | }
320 | auto* beatmapEventData3 = static_cast(basicBeatmapEventData->GetCopy());
321 | beatmapEventData3->_time_k__BackingField = strobeStreakData->lastSwitchTime;
322 | beatmapEventData3->value = value;
323 | newBeatmap->InsertBeatmapEventDataInOrder(beatmapEventData3);
324 | } else {
325 | newBeatmap->InsertBeatmapEventDataInOrder(strobeStreakData->originalBasicBeatmapEventData);
326 | }
327 | strobeStreakData->StartPotentialStrobe(basicBeatmapEventData);
328 | }
329 | } else {
330 | strobeStreakData->StartPotentialStrobe(basicBeatmapEventData);
331 | }
332 | }
333 | }
334 |
335 | for (auto const& keyValuePair : dictionary) {
336 | if (!keyValuePair.second) {
337 | continue;
338 | }
339 | if (keyValuePair.second->isActive) {
340 | newBeatmap->InsertBeatmapEventDataInOrder(keyValuePair.second->originalBasicBeatmapEventData);
341 | }
342 | }
343 | return newBeatmap->i___GlobalNamespace__IReadonlyBeatmapData();
344 | }
345 |
346 |
347 |
348 | BeatmapCallbacksController* beatmapCallbacksController;
349 |
350 | MAKE_PAPER_HOOK_MATCH(BeatmapCallbacksController_ManualUpdate, &BeatmapCallbacksController::ManualUpdate, void,
351 | BeatmapCallbacksController* self, float songTime) {
352 | if (songTime == self->_prevSongTime) {
353 | return BeatmapCallbacksController_ManualUpdate(self, songTime);
354 | }
355 |
356 | // TRANSPILE HERE
357 | if (self != beatmapCallbacksController) {
358 | CustomEventCallbacks::RegisterCallbacks(self);
359 | beatmapCallbacksController = self;
360 | }
361 | //
362 |
363 | return BeatmapCallbacksController_ManualUpdate(self, songTime);
364 | }
365 |
366 | MAKE_PAPER_HOOK_MATCH(BeatmapCallbacksController_Dispose, &BeatmapCallbacksController::Dispose, void,
367 | BeatmapCallbacksController* self) {
368 | CustomEventCallbacks::firstNode.emplace(nullptr);
369 | return BeatmapCallbacksController_Dispose(self);
370 | }
371 |
372 | static float GetAheadTime(Il2CppObject const* obj) {
373 |
374 | static auto const* CustomNoteKlass = classof(CustomJSONData::CustomNoteData*);
375 | static auto const* CustomObstacleKlass = classof(CustomJSONData::CustomObstacleData*);
376 |
377 | if (obj->klass == CustomNoteKlass) {
378 | return static_cast