From bd3de1f79e09954ccf6e5e558263f77303f8b86c Mon Sep 17 00:00:00 2001 From: Daniel <790119+DanTheMan827@users.noreply.github.com> Date: Tue, 9 Jul 2024 22:25:13 -0500 Subject: [PATCH] Update for 1.37 --- .github/actions/canary-ndk/action.yml | 35 +++++ .github/workflows/build-ndk.yml | 120 +++++++++++++++++ .gitignore | 27 +++- .vscode/c_cpp_properties.json | 22 ++-- .vscode/launch.json | 42 ++++++ .vscode/tasks.json | 122 +++++++++++++----- Android.mk | 57 --------- Application.mk | 5 - CMakeLists.txt | 77 +++++++++++ bmbfmod.json | 25 ---- build.ps1 | 8 -- buildBMBF.ps1 | 6 - cmake/git.cmake | 26 ++++ cmake/gtest.cmake | 43 +++++++ cmake/qpm.cmake | 33 +++++ cmake/strip.cmake | 28 ++++ cmake/targets/android-ndk.cmake | 45 +++++++ cmake/targets/quest.cmake | 8 ++ cmake/utils.cmake | 14 ++ cmake/vcpkg.cmake | 12 ++ copy.ps1 | 12 -- default-config.json | 1 - include/_config.hpp | 4 + include/main.hpp | 22 ++-- mod.template.json | 16 +++ ndkpath.txt | 1 - qpm.json | 66 ++++++++-- qpm.shared.json | 177 ++++++++++++++++++++++++++ scripts/build.ps1 | 32 +++++ scripts/copy.ps1 | 81 ++++++++++++ scripts/createqmod.ps1 | 78 ++++++++++++ scripts/ndk-stack.ps1 | 29 +++++ scripts/pull-tombstone.ps1 | 52 ++++++++ scripts/restart-game.ps1 | 2 + scripts/start-logging.ps1 | 78 ++++++++++++ scripts/validate-modjson.ps1 | 50 ++++++++ src/main.cpp | 68 +++++----- startlogging.bat | 2 - 38 files changed, 1309 insertions(+), 217 deletions(-) create mode 100644 .github/actions/canary-ndk/action.yml create mode 100644 .github/workflows/build-ndk.yml create mode 100644 .vscode/launch.json delete mode 100644 Android.mk delete mode 100644 Application.mk create mode 100644 CMakeLists.txt delete mode 100644 bmbfmod.json delete mode 100644 build.ps1 delete mode 100644 buildBMBF.ps1 create mode 100644 cmake/git.cmake create mode 100644 cmake/gtest.cmake create mode 100644 cmake/qpm.cmake create mode 100644 cmake/strip.cmake create mode 100644 cmake/targets/android-ndk.cmake create mode 100644 cmake/targets/quest.cmake create mode 100644 cmake/utils.cmake create mode 100644 cmake/vcpkg.cmake delete mode 100644 copy.ps1 delete mode 100644 default-config.json create mode 100644 include/_config.hpp create mode 100644 mod.template.json delete mode 100644 ndkpath.txt create mode 100644 qpm.shared.json create mode 100644 scripts/build.ps1 create mode 100644 scripts/copy.ps1 create mode 100644 scripts/createqmod.ps1 create mode 100644 scripts/ndk-stack.ps1 create mode 100644 scripts/pull-tombstone.ps1 create mode 100644 scripts/restart-game.ps1 create mode 100644 scripts/start-logging.ps1 create mode 100644 scripts/validate-modjson.ps1 delete mode 100644 startlogging.bat diff --git a/.github/actions/canary-ndk/action.yml b/.github/actions/canary-ndk/action.yml new file mode 100644 index 0000000..81f9b3a --- /dev/null +++ b/.github/actions/canary-ndk/action.yml @@ -0,0 +1,35 @@ +name: "Setup canary ndk" +description: "Sets up canary ndk" +outputs: + ndk-path: + value: ${{ steps.path.outputs.path }} + description: "Output path of the ndk" + cache-hit: + value: ${{ steps.cache.outputs.cache-hit }} + description: "Whether a cache hit occurred for the ndk" +runs: + using: "composite" + steps: + - name: NDK cache + id: cache + uses: actions/cache@v3 + with: + path: ${HOME}/android-ndk-r27-canary/ + key: ${{ runner.os }}-ndk-r27-canary + + - name: Download canary ndk + if: ${{ !steps.cache.outputs.cache-hit }} + env: + CANARY_URL: https://github.com/QuestPackageManager/ndk-canary-archive/releases/download/27.0.1/android-ndk-10883340-linux-x86_64.zip + run: wget ${CANARY_URL} -O ${HOME}/ndk.zip + shell: bash + + - name: Unzip ndk + if: ${{ !steps.cache.outputs.cache-hit }} + run: 7z x "${HOME}/ndk.zip" -o"${HOME}/" + shell: bash + + - name: Set output + id: path + shell: bash + run: echo "path=${HOME}/android-ndk-r27-canary" >> ${GITHUB_OUTPUT} diff --git a/.github/workflows/build-ndk.yml b/.github/workflows/build-ndk.yml new file mode 100644 index 0000000..7aa5b2f --- /dev/null +++ b/.github/workflows/build-ndk.yml @@ -0,0 +1,120 @@ +name: NDK build + +on: + workflow_dispatch: + push: + tags: + - "v*" + branches: + - 'master' + - 'main' + - 'dev/*' + - 'feat/*' + - 'fix/*' + paths-ignore: + - '**.yml' + - '!.github/workflows/build-ndk.yml' + - '**.json' + - '!qpm.json' + - '!qpm.shared.json' + - '!mod.template.json' + - '**.txt' + - '!CMakeLists.txt' + - '**.ps1' + - '!build.ps1' + - '!createqmod.ps1' + - '**.md' + - '.gitignore' + pull_request: + branches: + - 'master' + - 'main' + - 'dev/*' + - 'feat/*' + - 'fix/*' + +env: + module_id: "bts-yeeter" + qmod_name: "BTS Yeeter" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + name: Checkout + with: + submodules: true + lfs: true + + - uses: seanmiddleditch/gha-setup-ninja@v3 + + # Use canary NDK to avoid lesser known compile bugs + - name: Setup canary NDK + id: setup-ndk + uses: ./.github/actions/canary-ndk + + - name: Create ndkpath.txt + run: | + echo ${{ steps.setup-ndk.outputs.ndk-path }} > ${GITHUB_WORKSPACE}/ndkpath.txt + cat ${GITHUB_WORKSPACE}/ndkpath.txt + + # get version from pushed tag + - name: Extract version + if: startsWith(github.ref, 'refs/tags/v') + id: version + run: | + echo "TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_OUTPUT} + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> ${GITHUB_OUTPUT} + + - name: Setup qpm + uses: Fernthedev/qpm-action@main + with: + workflow_token: ${{ secrets.GITHUB_TOKEN }} + restore: true + cache: true + publish: false + + - name: Build & Create Qmod + run: | + cd ${GITHUB_WORKSPACE} + qpm s qmod + + - name: Get Library Name + id: libname + run: | + cd ./build/ + pattern="lib${module_id}*.so" + files=( $pattern ) + echo "NAME=${files[0]}" >> ${GITHUB_OUTPUT} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.libname.outputs.NAME }} + path: ./build/${{ steps.libname.outputs.NAME }} + if-no-files-found: error + + - name: Upload qmod artifact + uses: actions/upload-artifact@v4 + with: + name: ${{env.qmod_name}}.qmod + path: ./${{ env.qmod_name }}.qmod + if-no-files-found: error + + # if we had a tag, we should make a release + - name: Upload release artifacts + if: startsWith(github.ref, 'refs/tags/v') + id: upload_file_release + uses: softprops/action-gh-release@v0.1.15 + with: + tag_name: ${{ github.event.inputs.version }} + draft: true + generate_release_notes: true + files: | + ./build/${{ steps.libname.outputs.NAME }} + ./build/debug_${{ steps.libname.outputs.NAME }} + ./${{ env.qmod_name }}.qmod + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 107e024..6faf441 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.cache/ + # Prerequisites *.d @@ -34,6 +36,10 @@ # VSCode config stuff !.vscode/c_cpp_properties.json !.vscode/tasks.json +!.vscode/settings.json + +# Jetbrains IDEs +.idea/ # NDK stuff out/ @@ -41,7 +47,24 @@ out/ [Ll]ibs/ [Oo]bj/ [Oo]bjs/ +ndkpath.txt *.zip -extern/ +*.txt +*.log Android.mk.backup -qpm.shared.json + +# QPM stuff +[Ee]xtern/ +*.qmod +mod.json +qpm_defines.cmake +![Cc][Mm]ake[Ll]ists.txt + +# CMake stuff +[Bb]uild/ +cmake-build-*/ +extern.cmake + +# QMOD Schema +mod.json.schema +ndkpath.txt diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index cbd6f1b..237657f 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -2,23 +2,27 @@ "configurations": [ { "defines": [ - "VERSION=\"0.2.0\"", - "ID=\"bts-yeeter\"", + "MOD_ID=\"bts-yeeter\"", + "VERSION=\"0.1.0\"", "__GNUC__", "__aarch64__" ], "includePath": [ - "${workspaceFolder}/**", - "${workspaceFolder}/shared", - "${workspaceFolder}/include", - "${workspaceFolder}/extern/**", - "${workspaceFolder}/extern/libil2cpp/il2cpp/libil2cpp", - "G:/Android/ndk/**" + "${workspaceFolder}/extern/includes/libil2cpp/il2cpp/libil2cpp", + "${workspaceFolder}/extern/includes/codegen/include", + "${workspaceFolder}/extern/includes", + "${workspaceFolder}/include", + "${workspaceFolder}/shared", + "${workspaceFolder}", + + "./**", + "${default}" ], "name": "Quest", "cStandard": "c11", "cppStandard": "c++20", - "intelliSenseMode": "clang-x64" + "intelliSenseMode": "clang-x64", + "compileCommands": "${workspaceFolder}/build/compile_commands.json" } ], "version": 4 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1813709 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,42 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Beat Saber", + "type": "fb-lldb", + "request": "launch", + "preLaunchTask": "Powershell Build and Copy", + "android": { + "application": { + "package": "com.beatgames.beatsaber", + "activity": "com.unity3d.player.UnityPlayerActivity" + }, + "lldbConfig": { + "sourceMaps": [], + "librarySearchPaths": [ + "${workspaceFolder}/build/debug/", + "${workspaceFolder}/extern/libs/" + ] + } + } + }, + { + "name": "Attach to running Beat Saber Instance", + "type": "fb-lldb", + "request": "attach", + "android": { + "application": { + "package": "com.beatgames.beatsaber", + "activity": "com.unity3d.player.UnityPlayerActivity" + }, + "lldbConfig": { + "sourceMaps": [], + "librarySearchPaths": [ + "${workspaceFolder}/build/debug/", + "${workspaceFolder}/extern/libs/", + ] + } + } + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d2bde51..6db2c2b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,68 +1,120 @@ { "version": "2.0.0", + "windows": { + "options": { + "shell": { + "executable": "pwsh.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, "tasks": [ { - "label": "NDK Build", - "detail": "Builds the library using ndk-build.cmd", + "label": "Build", + "detail": "Builds the library", "type": "shell", - "command": "ndk-build", - "windows": { - "command": "ndk-build.cmd" + "command": "./scripts/build.ps1", + "group": { + "kind": "build", + "isDefault": true }, - "args": ["NDK_PROJECT_PATH=.", "APP_BUILD_SCRIPT=./Android.mk", "NDK_APPLICATION_MK=./Application.mk"], - "group": "build", "options": { "env": {} - } + }, + "problemMatcher": [] }, { - "label": "Powershell Build", - "detail": "Builds the library using Powershell (recommended)", + "label": "Build (Clean)", + "detail": "Builds the library from a clean state", "type": "shell", - "command": "./build.ps1", - "windows": { - "command": "./build.ps1" - }, - "group": { - "kind": "build", - "isDefault": true - }, + "command": "./scripts/build.ps1 -clean", + "group": "build", "options": { "env": {} - } + }, + "problemMatcher": [] }, { - "label": "Powershell Build and Copy", - "detail": "Builds and copies the library to the Quest using adb and force-quits Beat Saber", + "label": "Copy", + "detail": "Builds and copies the library to the Quest using ADB and force-quits Beat Saber", "type": "shell", - "command": "./copy.ps1", - "windows": { - "command": "./copy.ps1" - }, + "command": "./scripts/copy.ps1", "group": "build", "options": { "env": {} } }, { - "label": "BMBF Build", - "detail": "Builds a .zip to be uploaded into BMBF", + "label": "Start Logging", + "detail": "Begin logging from the Quest to the console", "type": "shell", - "command": "./buildBMBF.ps1", - "windows": { - "command": "./buildBMBF.ps1" - }, + "command": "./scripts/start-logging.ps1", + }, + { + "label": "Start logging to file", + "detail": "Begin logging from the Quest to the console and saving output to a file 'logcat.log'", + "type": "shell", + "command": "./scripts/start-logging.ps1 --file", + }, + { + "label": "Build QMOD", + "detail": "Builds a .qmod to be installed into BMBF or QuestPatcher", + "type": "shell", + "command": "./scripts/build.ps1 && ./scripts/createqmod.ps1", "args": [], "group": "build", "options": { "env": {} - } + }, + "problemMatcher": [] + }, + { + "label": "Restart Beat Saber", + "detail": "Force-quits and restarts Beat Saber on the Quest", + "type": "shell", + "command": "./scripts/restart-game.ps1", + "problemMatcher": [] + }, + { + "label": "Process Stack", + "detail": "Processes a tombstone using the debug .so to find file locations", + "type": "shell", + "command": "./scripts/ndk-stack.ps1", + "problemMatcher": [] }, { - "label": "Start logging", - "detail": "Records a log to log.txt using adb logcat", + "label": "Pull Tombstone", + "detail": "Finds and pulls the most recent tombstone from your quest", "type": "shell", - "command": "./startlogging.bat" + "command": "./scripts/pull-tombstone.ps1", + "problemMatcher": [] } ] } diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 4bfa4fb..0000000 --- a/Android.mk +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) 2009 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -LOCAL_PATH := $(call my-dir) -TARGET_ARCH_ABI := $(APP_ABI) - -rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) - -# Creating prebuilt for dependency: beatsaber-hook - version: 0.8.4 -include $(CLEAR_VARS) -LOCAL_MODULE := -include $(CLEAR_VARS) -# Creating prebuilt for dependency: modloader - version: 1.0.4 -include $(CLEAR_VARS) -LOCAL_MODULE := modloader -LOCAL_EXPORT_C_INCLUDES := extern/modloader -LOCAL_SRC_FILES := extern/libmodloader.so -include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: codegen - version: 0.6.2 -include $(CLEAR_VARS) -LOCAL_MODULE := codegen_0_6_2 -LOCAL_EXPORT_C_INCLUDES := extern/codegen -LOCAL_SRC_FILES := extern/libcodegen_0_6_2.so -include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: beatsaber-hook - version: 1.0.12 -include $(CLEAR_VARS) -LOCAL_MODULE := beatsaber-hook_1_0_12 -LOCAL_EXPORT_C_INCLUDES := extern/beatsaber-hook -LOCAL_SRC_FILES := extern/libbeatsaber-hook_1_0_12.so -LOCAL_CPP_FEATURES += exceptions -include $(PREBUILT_SHARED_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := bts-yeeter -LOCAL_SRC_FILES += $(call rwildcard,src/,*.cpp) -LOCAL_SRC_FILES += $(call rwildcard,extern/beatsaber-hook/src/inline-hook,*.cpp) -LOCAL_SRC_FILES += $(call rwildcard,extern/beatsaber-hook/src/inline-hook,*.c) -LOCAL_SHARED_LIBRARIES += modloader -LOCAL_SHARED_LIBRARIES += beatsaber-hook_1_0_12 -LOCAL_SHARED_LIBRARIES += codegen_0_6_2 -LOCAL_LDLIBS += -llog -LOCAL_CFLAGS += -I'extern/libil2cpp/il2cpp/libil2cpp' -DID='"bts-yeeter"' -DVERSION='"0.2.0"' -I'./shared' -I'./extern' -isystem'extern/codegen/include' -LOCAL_CPPFLAGS += -std=c++2a -LOCAL_C_INCLUDES += ./include ./src -include $(BUILD_SHARED_LIBRARY) diff --git a/Application.mk b/Application.mk deleted file mode 100644 index e7e447f..0000000 --- a/Application.mk +++ /dev/null @@ -1,5 +0,0 @@ -APP_ABI := arm64-v8a -APP_PLATFORM := 25 -APP_PIE:= true -APP_STL := c++_static -APP_CPPFLAGS := -std=gnu++2a diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..495c112 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.22) + +option(QUEST "Build for quest" ON) + +# Globals +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED 20) +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) + +set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + +add_compile_definitions(MOD_ID="${CMAKE_PROJECT_NAME}") +add_compile_definitions(VERSION="${CMAKE_PROJECT_VERSION}") + +add_compile_options(-frtti -fexceptions -fvisibility=hidden -fPIE -fPIC) + +# Include. Include order matters! +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/qpm.cmake) + +if(${CMAKE_BUILD_TYPE} STREQUAL "RELEASE" OR ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo" OR ${CMAKE_BUILD_TYPE} STREQUAL "MinSizeRel") + # Better optimizations + add_compile_options(-O3) + + # LTO + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + add_compile_options(-flto) +endif() + +if(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG" OR ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + add_compile_options(-g) +endif() + +# Targets +if(QUEST) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/targets/quest.cmake) +endif() + +# Post build +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/strip.cmake) + +# stop symbols leaking +# TODO: Fix +# add_link_options(-Wl, --exclude-libs, ALL) +project( bts-yeeter + VERSION ${PACKAGE_VERSION}) + +# Set COMPILE_ID for qpm purposes +set(COMPILE_ID ${CMAKE_PROJECT_NAME}) + +# recursively get all src files +RECURSE_FILES(cpp_file_list ${SOURCE_DIR}/*.cpp) +RECURSE_FILES(c_file_list ${SOURCE_DIR}/*.c) + +add_library( + ${CMAKE_PROJECT_NAME} + SHARED + ${cpp_file_list} + ${c_file_list} + ${inline_hook_c} + ${inline_hook_cpp} +) + +# for inline hook +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -llog) + +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${INCLUDE_DIR}) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${SHARED_DIR}) + +# beatsaber hook inline hook + +RECURSE_FILES(src_inline_hook_beatsaber_hook_local_extra_c ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.c) +RECURSE_FILES(src_inline_hook_beatsaber_hook_local_extra_cpp ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.cpp) + +target_sources(${COMPILE_ID} PRIVATE ${src_inline_hook_beatsaber_hook_local_extra_c}) +target_sources(${COMPILE_ID} PRIVATE ${src_inline_hook_beatsaber_hook_local_extra_cpp}) diff --git a/bmbfmod.json b/bmbfmod.json deleted file mode 100644 index 03e39f3..0000000 --- a/bmbfmod.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id": "bts-yeeter", - "name": "BTS Yeeter", - "version": "0.2.0", - "icon": "cover.png", - "coverImageFilename": "cover.png", - "author": "gabriella ✨#6859", - "description": [ - "Stops the annoying BTS characters from spawning whilst playing their maps" - ], - "category": "Gameplay", - "gameVersion": "1.13.2", - "platform": "Quest", - "components": [ - { - "type": "HookMod", - "installAction": { - "installLibraryFile": "libbts-yeeter.so" - }, - "uninstallAction": { - "removeLibraryFile": "libbts-yeeter.so" - } - } - ] -} diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index b24935d..0000000 --- a/build.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -$NDKPath = Get-Content $PSScriptRoot/ndkpath.txt - -$buildScript = "$NDKPath/build/ndk-build" -if (-not ($PSVersionTable.PSEdition -eq "Core")) { - $buildScript += ".cmd" -} - -& $buildScript NDK_PROJECT_PATH=$PSScriptRoot APP_BUILD_SCRIPT=$PSScriptRoot/Android.mk NDK_APPLICATION_MK=$PSScriptRoot/Application.mk diff --git a/buildBMBF.ps1 b/buildBMBF.ps1 deleted file mode 100644 index 0117617..0000000 --- a/buildBMBF.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -# Builds a .zip file for loading with BMBF -& $PSScriptRoot/build.ps1 - -if ($?) { - Compress-Archive -Path "./libs/arm64-v8a/libbts-yeeter.so", "./bmbfmod.json" -DestinationPath "./bts-yeeter_v0.2.0.zip" -Update -} diff --git a/cmake/git.cmake b/cmake/git.cmake new file mode 100644 index 0000000..b23401f --- /dev/null +++ b/cmake/git.cmake @@ -0,0 +1,26 @@ +include_guard() + +# TODO: Make this a header file instead of global defines +# to avoid recompile when git changes + +# get git info +execute_process(COMMAND git config user.name OUTPUT_VARIABLE GIT_USER) +execute_process(COMMAND git branch --show-current OUTPUT_VARIABLE GIT_BRANCH) +execute_process(COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE GIT_COMMIT) +execute_process(COMMAND git diff-index --quiet HEAD RESULT_VARIABLE GIT_MODIFIED) + +string(STRIP "${GIT_USER}" GIT_USER) +string(STRIP "${GIT_BRANCH}" GIT_BRANCH) +string(STRIP "${GIT_COMMIT}" GIT_COMMIT) +string(STRIP "${GIT_MODIFIED}" GIT_MODIFIED) + +message(STATUS "GIT_USER: ${GIT_USER}") +message(STATUS "GIT_BRANCH: ${GIT_BRANCH}") +message(STATUS "GIT_COMMIT: 0x${GIT_COMMIT}") +message(STATUS "GIT_MODIFIED: ${GIT_MODIFIED}") + +# set git defines +add_compile_definitions(GIT_USER=\"${GIT_USER}\") +add_compile_definitions(GIT_BRANCH=\"${GIT_BRANCH}\") +add_compile_definitions(GIT_COMMIT=0x00${GIT_COMMIT}) +add_compile_definitions(GIT_MODIFIED=${GIT_MODIFIED}) diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake new file mode 100644 index 0000000..098ceb1 --- /dev/null +++ b/cmake/gtest.cmake @@ -0,0 +1,43 @@ +include_guard() + +message("Compiling with GTest") + +# GTest +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() + +# Run at end to link with project +cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _setup_gtest_project()) + +function(_setup_gtest_project) + # recursively get all src files + RECURSE_FILES(cpp_test_file_list ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp) + RECURSE_FILES(c_test_file_list ${CMAKE_CURRENT_SOURCE_DIR}/test/*.c) + + add_executable( + ${PROJECT_NAME}_test + ${cpp_test_file_list} + ${c_test_file_list} + ) + target_link_libraries( + ${PROJECT_NAME}_test + PRIVATE ${PROJECT_NAME} + GTest::gtest_main + ) + + target_include_directories(${PROJECT_NAME}_test PRIVATE ${INCLUDE_DIR}) + target_include_directories(${PROJECT_NAME}_test PRIVATE ${SHARED_DIR}) + target_include_directories(${PROJECT_NAME}_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test) + + include(GoogleTest) + gtest_discover_tests(${PROJECT_NAME}_test) +endfunction(_setup_gtest_project) diff --git a/cmake/qpm.cmake b/cmake/qpm.cmake new file mode 100644 index 0000000..256ee8c --- /dev/null +++ b/cmake/qpm.cmake @@ -0,0 +1,33 @@ +include_guard() + +# Necessary for extern.cmake +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils.cmake) + + +# read in information about the mod from qpm.json +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/qpm.json PACKAGE_JSON) + +string(JSON PACKAGE_INFO GET ${PACKAGE_JSON} info) + +string(JSON PACKAGE_NAME GET ${PACKAGE_INFO} name) +string(JSON PACKAGE_ID GET ${PACKAGE_INFO} id) +string(JSON PACKAGE_VERSION GET ${PACKAGE_INFO} version) + +message(STATUS "PACKAGE NAME: ${PACKAGE_NAME}") +message(STATUS "PACKAGE VERSION: ${PACKAGE_VERSION}") + +string(JSON EXTERN_DIR_NAME GET ${PACKAGE_JSON} dependenciesDir) +string(JSON SHARED_DIR_NAME GET ${PACKAGE_JSON} sharedDir) + +set(EXTERN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${EXTERN_DIR_NAME}) +set(SHARED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${SHARED_DIR_NAME}) + +# TODO: This is empty by the time this is called +set(COMPILE_ID ${CMAKE_PROJECT_NAME}) + +# Setup QPM Extern +# TODO: Setup qpm extern from toolchain +cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _setup_qpm_project()) +function(_setup_qpm_project) + include(${CMAKE_CURRENT_SOURCE_DIR}/extern.cmake) +endfunction(_setup_qpm_project) \ No newline at end of file diff --git a/cmake/strip.cmake b/cmake/strip.cmake new file mode 100644 index 0000000..251feba --- /dev/null +++ b/cmake/strip.cmake @@ -0,0 +1,28 @@ +# Run at end to link with project +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android") + cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _setup_linux_strip_project()) +endif() + +function(_setup_linux_strip_project) + # Strip debug symbols + add_custom_command(TARGET ${COMPILE_ID} POST_BUILD + COMMAND ${CMAKE_STRIP} -d --strip-all + "lib${COMPILE_ID}.so" -o "stripped_lib${COMPILE_ID}.so" + COMMENT "Strip debug symbols done on final binary.") + + add_custom_command(TARGET ${COMPILE_ID} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory debug + COMMENT "Make directory for debug symbols" + ) + add_custom_command(TARGET ${COMPILE_ID} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename lib${COMPILE_ID}.so debug/lib${COMPILE_ID}.so + COMMENT "Rename the lib to debug_ since it has debug symbols" + ) + + # strip debug symbols from the .so and all dependencies + add_custom_command(TARGET ${COMPILE_ID} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename stripped_lib${COMPILE_ID}.so lib${COMPILE_ID}.so + COMMENT "Rename the stripped lib to regular" + ) +endfunction(_setup_linux_strip_project) + diff --git a/cmake/targets/android-ndk.cmake b/cmake/targets/android-ndk.cmake new file mode 100644 index 0000000..57bc00d --- /dev/null +++ b/cmake/targets/android-ndk.cmake @@ -0,0 +1,45 @@ +include_guard() + +if(NOT DEFINED CMAKE_ANDROID_NDK) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ndkpath.txt") + file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/ndkpath.txt" CMAKE_ANDROID_NDK) + else() + if(EXISTS $ENV{ANDROID_NDK_HOME}) + set(CMAKE_ANDROID_NDK $ENV{ANDROID_NDK_HOME}) + elseif(EXISTS $ENV{ANDROID_NDK_LATEST_HOME}) + set(CMAKE_ANDROID_NDK $ENV{ANDROID_NDK_LATEST_HOME}) + endif() + endif() +endif() + +if(NOT DEFINED CMAKE_ANDROID_NDK) + message(Big time error buddy, no NDK) +endif() + +string(REPLACE "\\" "/" CMAKE_ANDROID_NDK ${CMAKE_ANDROID_NDK}) + +message(STATUS "Using NDK ${CMAKE_ANDROID_NDK}") + +# check if contains space +if(CMAKE_ANDROID_NDK MATCHES " ") + message(FATAL_ERROR "CMAKE_ANDROID_NDK contains a space! Please remove it!") +endif() + + +# Quest is armv8-64 +# Uses Android 12-14 now + +set(ANDROID_PLATFORM 24) +set(ANDROID_ABI arm64-v8a) +set(ANDROID_STL c++_static) +set(ANDROID_USE_LEGACY_TOOLCHAIN_FILE OFF) + +#TODO: Fix this warning +if(CMAKE_TOOLCHAIN_FILE MATCHES ".+") + message(WARNING "CMAKE_TOOLCHAIN_FILE already defined, overwriting! ${CMAKE_TOOLCHAIN_FILE}") +endif() + +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake) + +# Set triplet for vcpkg +set(VCPKG_TARGET_TRIPLET arm64-android) \ No newline at end of file diff --git a/cmake/targets/quest.cmake b/cmake/targets/quest.cmake new file mode 100644 index 0000000..4260438 --- /dev/null +++ b/cmake/targets/quest.cmake @@ -0,0 +1,8 @@ +include_guard() + +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/targets/android-ndk.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/qpm.cmake) + +add_compile_definitions(QUEST) +add_compile_definitions(UNITY_2021) +add_compile_definitions(NEED_UNSAFE_CSHARP) \ No newline at end of file diff --git a/cmake/utils.cmake b/cmake/utils.cmake new file mode 100644 index 0000000..417bbe3 --- /dev/null +++ b/cmake/utils.cmake @@ -0,0 +1,14 @@ +include_guard() + +# get files by filter recursively +MACRO(RECURSE_FILES return_list filter) + FILE(GLOB_RECURSE new_list ${filter}) + SET(file_list "") + + FOREACH(file_path ${new_list}) + SET(file_list ${file_list} ${file_path}) + ENDFOREACH() + + LIST(REMOVE_DUPLICATES file_list) + SET(${return_list} ${file_list}) +ENDMACRO() \ No newline at end of file diff --git a/cmake/vcpkg.cmake b/cmake/vcpkg.cmake new file mode 100644 index 0000000..f21b216 --- /dev/null +++ b/cmake/vcpkg.cmake @@ -0,0 +1,12 @@ +include_guard() + +# vcpkg config +message("VCPKG $ENV{VCPKG_ROOT}") + +# chain the toolchain file for vcpkg +if(CMAKE_TOOLCHAIN_FILE NOT MATCHES ".+") + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE ${CMAKE_TOOLCHAIN_FILE}) +endif() + +string(REPLACE "\\" "/" VCPKG_ROOT_WINDOWS_FIX $ENV{VCPKG_ROOT}) +set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT_WINDOWS_FIX}/scripts/buildsystems/vcpkg.cmake) \ No newline at end of file diff --git a/copy.ps1 b/copy.ps1 deleted file mode 100644 index 5c3f432..0000000 --- a/copy.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -& $PSScriptRoot/build.ps1 -if ($?) { - adb push libs/arm64-v8a/libbts-yeeter.so /sdcard/Android/data/com.beatgames.beatsaber/files/mods/libbts-yeeter.so - if ($?) { - adb shell am force-stop com.beatgames.beatsaber - adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity - if ($args[0] -eq "--log") { - $timestamp = Get-Date -Format "MM-dd HH:mm:ss.fff" - adb logcat -T "$timestamp" main-modloader:W QuestHook[bts-yeeter`|v0.2.0]:* AndroidRuntime:E *:S - } - } -} diff --git a/default-config.json b/default-config.json deleted file mode 100644 index 9e26dfe..0000000 --- a/default-config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/include/_config.hpp b/include/_config.hpp new file mode 100644 index 0000000..3722509 --- /dev/null +++ b/include/_config.hpp @@ -0,0 +1,4 @@ +#pragma once + +#define MOD_EXPORT __attribute__((visibility("default"))) +#define MOD_EXTERN_FUNC extern "C" MOD_EXPORT \ No newline at end of file diff --git a/include/main.hpp b/include/main.hpp index 4e95dab..69c4d55 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -1,14 +1,20 @@ #pragma once -// Include the modloader header, which allows us to tell the modloader which mod this is, and the version etc. -#include "modloader/shared/modloader.hpp" +// Include the modloader header, which allows us to tell the modloader which mod +// this is, and the version etc. +#include "scotland2/shared/modloader.h" -// beatsaber-hook is a modding framework that lets us call functions and fetch field values from in the game -// It also allows creating objects, configuration, and importantly, hooking methods to modify their values -#include "beatsaber-hook/shared/utils/logging.hpp" +// beatsaber-hook is a modding framework that lets us call functions and fetch +// field values from in the game It also allows creating objects, configuration, +// and importantly, hooking methods to modify their values #include "beatsaber-hook/shared/config/config-utils.hpp" +#include "beatsaber-hook/shared/utils/hooking.hpp" #include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" +#include "beatsaber-hook/shared/utils/logging.hpp" + + +#include "paper/shared/logger.hpp" + +#include "_config.hpp" -// Define these functions here so that we can easily read configuration and log information from other files -Configuration& getConfig(); -Logger& getLogger(); \ No newline at end of file +constexpr auto PaperLogger = Paper::ConstLoggerContext("bts-yeeter"); diff --git a/mod.template.json b/mod.template.json new file mode 100644 index 0000000..9077d64 --- /dev/null +++ b/mod.template.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json", + "_QPVersion": "0.1.1", + "name": "${mod_name}", + "id": "${mod_id}", + "coverImage": "cover.png", + "author": "gabriella ✨#6859", + "version": "0.3.0", + "packageId": "com.beatgames.beatsaber", + "packageVersion": "1.37.0_9064817954", + "description": "Stops the annoying BTS characters from spawning whilst playing their maps.", + "dependencies": [], + "modFiles": ["${binary}"], + "libraryFiles": [], + "fileCopies": [] +} diff --git a/ndkpath.txt b/ndkpath.txt deleted file mode 100644 index 2895312..0000000 --- a/ndkpath.txt +++ /dev/null @@ -1 +0,0 @@ -G:\Android\ndk diff --git a/qpm.json b/qpm.json index 4c3f8d1..c35706d 100644 --- a/qpm.json +++ b/qpm.json @@ -1,28 +1,68 @@ { "sharedDir": "shared", "dependenciesDir": "extern", + "version": "0.1.0", "info": { "name": "BTS Yeeter", "id": "bts-yeeter", - "version": "0.2.0", + "version": "0.1.0", "url": null, - "additionalData": {} + "additionalData": { + "overrideSoName": "libbts-yeeter.so", + "cmake": true + } + }, + "workspace": { + "scripts": { + "build": [ + "pwsh ./scripts/build.ps1" + ], + "clean": ["pwsh ./scripts/build.ps1 -clean"], + "copy": [ + "pwsh ./scripts/copy.ps1 $0:?" + ], + "logcat": [ + "pwsh ./scripts/start-logging.ps1" + ], + "qmod": [ + "pwsh ./scripts/build.ps1", + "pwsh ./scripts/createqmod.ps1" + ], + "restart": [ + "pwsh ./scripts/restart-game.ps1" + ], + "stack": [ + "pwsh ./scripts/ndk-stack.ps1" + ], + "tomb": [ + "pwsh ./scripts/pull-tombstone.ps1" + ] + }, + "qmodIncludeDirs": ["./build", "./extern/libs"], + "qmodOutput": "./BTS Yeeter.qmod" }, "dependencies": [ { "id": "beatsaber-hook", - "versionRange": "*", - "additionalData": { - "extraFiles": [ - "src/inline-hook" - ] - } + "versionRange": "^5.1.9", + "additionalData": {} }, { - "id": "codegen", - "versionRange": "*", + "id": "paper", + "versionRange": "^3.6.4", "additionalData": {} + }, + { + "id": "bs-cordl", + "versionRange": "3700.*", + "additionalData": {} + }, + { + "id": "scotland2", + "versionRange": "^0.1.4", + "additionalData": { + "includeQmod": false + } } - ], - "additionalData": {} -} \ No newline at end of file + ] +} diff --git a/qpm.shared.json b/qpm.shared.json new file mode 100644 index 0000000..28fc9ba --- /dev/null +++ b/qpm.shared.json @@ -0,0 +1,177 @@ +{ + "config": { + "version": "0.1.0", + "sharedDir": "shared", + "dependenciesDir": "extern", + "info": { + "name": "BTS Yeeter", + "id": "bts-yeeter", + "version": "0.1.0", + "url": null, + "additionalData": { + "overrideSoName": "libbts-yeeter.so", + "cmake": true + } + }, + "workspace": { + "scripts": { + "build": [ + "pwsh ./scripts/build.ps1" + ], + "clean": [ + "pwsh ./scripts/build.ps1 -clean" + ], + "copy": [ + "pwsh ./scripts/copy.ps1 $0:?" + ], + "logcat": [ + "pwsh ./scripts/start-logging.ps1" + ], + "qmod": [ + "pwsh ./scripts/build.ps1", + "pwsh ./scripts/createqmod.ps1" + ], + "restart": [ + "pwsh ./scripts/restart-game.ps1" + ], + "stack": [ + "pwsh ./scripts/ndk-stack.ps1" + ], + "tomb": [ + "pwsh ./scripts/pull-tombstone.ps1" + ] + }, + "qmodIncludeDirs": [ + "./build", + "./extern/libs" + ], + "qmodIncludeFiles": [], + "qmodOutput": "./BTS Yeeter.qmod" + }, + "dependencies": [ + { + "id": "beatsaber-hook", + "versionRange": "^5.1.9", + "additionalData": {} + }, + { + "id": "paper", + "versionRange": "^3.6.4", + "additionalData": {} + }, + { + "id": "bs-cordl", + "versionRange": "3700.*", + "additionalData": {} + }, + { + "id": "scotland2", + "versionRange": "^0.1.4", + "additionalData": { + "includeQmod": false + } + } + ] + }, + "restoredDependencies": [ + { + "dependency": { + "id": "bs-cordl", + "versionRange": "=3700.0.0", + "additionalData": { + "headersOnly": true, + "branchName": "version/v3700_0_0", + "compileOptions": { + "includePaths": [ + "include" + ], + "cppFeatures": [], + "cppFlags": [ + "-DNEED_UNSAFE_CSHARP", + "-fdeclspec", + "-DUNITY_2021", + "-DHAS_CODEGEN" + ] + } + } + }, + "version": "3700.0.0" + }, + { + "dependency": { + "id": "paper", + "versionRange": "=3.6.4", + "additionalData": { + "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/libpaperlog.so", + "debugSoLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/debug_libpaperlog.so", + "overrideSoName": "libpaperlog.so", + "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/paperlog.qmod", + "branchName": "version/v3_6_4", + "compileOptions": { + "systemIncludes": [ + "shared/utfcpp/source" + ] + }, + "cmake": false + } + }, + "version": "3.6.4" + }, + { + "dependency": { + "id": "beatsaber-hook", + "versionRange": "=5.1.9", + "additionalData": { + "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.9/libbeatsaber-hook_5_1_9.so", + "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.9/debug_libbeatsaber-hook_5_1_9.so", + "branchName": "version/v5_1_9", + "cmake": true + } + }, + "version": "5.1.9" + }, + { + "dependency": { + "id": "libil2cpp", + "versionRange": "=0.3.2", + "additionalData": { + "headersOnly": true, + "cmake": false + } + }, + "version": "0.3.2" + }, + { + "dependency": { + "id": "scotland2", + "versionRange": "=0.1.4", + "additionalData": { + "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.4/libsl2.so", + "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.4/debug_libsl2.so", + "overrideSoName": "libsl2.so", + "branchName": "version/v0_1_4" + } + }, + "version": "0.1.4" + }, + { + "dependency": { + "id": "fmt", + "versionRange": "=10.0.0", + "additionalData": { + "headersOnly": true, + "branchName": "version/v10_0_0", + "compileOptions": { + "systemIncludes": [ + "fmt/include/" + ], + "cppFlags": [ + "-DFMT_HEADER_ONLY" + ] + } + } + }, + "version": "10.0.0" + } + ] +} \ No newline at end of file diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..1e44b0c --- /dev/null +++ b/scripts/build.ps1 @@ -0,0 +1,32 @@ +Param( + [Parameter(Mandatory=$false)] + [Switch] $clean, + + [Parameter(Mandatory=$false)] + [Switch] $help +) + +if ($help -eq $true) { + Write-Output "`"Build`" - Copiles your mod into a `".so`" or a `".a`" library" + Write-Output "`n-- Arguments --`n" + + Write-Output "-Clean `t`t Deletes the `"build`" folder, so that the entire library is rebuilt" + + exit +} + +# if user specified clean, remove all build files +if ($clean.IsPresent) { + if (Test-Path -Path "build") { + remove-item build -R -Force + } +} + + +if (($clean.IsPresent) -or (-not (Test-Path -Path "build"))) { + new-item -Path build -ItemType Directory +} + +& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build +& cmake --build ./build +exit $LASTEXITCODE diff --git a/scripts/copy.ps1 b/scripts/copy.ps1 new file mode 100644 index 0000000..50351a7 --- /dev/null +++ b/scripts/copy.ps1 @@ -0,0 +1,81 @@ +Param( + [Parameter(Mandatory=$false)] + [Switch] $clean, + + [Parameter(Mandatory=$false)] + [Switch] $log, + + [Parameter(Mandatory=$false)] + [Switch] $useDebug, + + [Parameter(Mandatory=$false)] + [Switch] $self, + + [Parameter(Mandatory=$false)] + [Switch] $all, + + [Parameter(Mandatory=$false)] + [String] $custom="", + + [Parameter(Mandatory=$false)] + [String] $file="", + + [Parameter(Mandatory=$false)] + [Switch] $help +) + +if ($help -eq $true) { + Write-Output "`"Copy`" - Builds and copies your mod to your quest, and also starts Beat Saber with optional logging" + Write-Output "`n-- Arguments --`n" + + Write-Output "-Clean `t`t Performs a clean build (equvilant to running `"build -clean`")" + Write-Output "-UseDebug `t Copies the debug version of the mod to your quest" + Write-Output "-Log `t`t Logs Beat Saber using the `"Start-Logging`" command" + + Write-Output "`n-- Logging Arguments --`n" + + & $PSScriptRoot/start-logging.ps1 -help -excludeHeader + + exit +} + +& $PSScriptRoot/build.ps1 -clean:$clean + +if ($LASTEXITCODE -ne 0) { + Write-Output "Failed to build, exiting..." + exit $LASTEXITCODE +} + +& $PSScriptRoot/validate-modjson.ps1 +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$modJson = Get-Content "./mod.json" -Raw | ConvertFrom-Json + +$modFiles = $modJson.modFiles + +foreach ($fileName in $modFiles) { + if ($useDebug -eq $true) { + & adb push build/debug/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/early_mods/$fileName + } else { + & adb push build/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/early_mods/$fileName + } +} + +$lateModFiles = $modJson.lateModFiles + +foreach ($fileName in $lateModFiles) { + if ($useDebug -eq $true) { + & adb push build/debug/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName + } else { + & adb push build/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName + } +} + + +& $PSScriptRoot/restart-game.ps1 + +if ($log -eq $true) { + & adb logcat -c + & $PSScriptRoot/start-logging.ps1 -self:$self -all:$all -custom:$custom -file:$file +} \ No newline at end of file diff --git a/scripts/createqmod.ps1 b/scripts/createqmod.ps1 new file mode 100644 index 0000000..1f87623 --- /dev/null +++ b/scripts/createqmod.ps1 @@ -0,0 +1,78 @@ +Param( + [Parameter(Mandatory=$false)] + [String] $qmodName="", + + [Parameter(Mandatory=$false)] + [Switch] $help +) + +if ($help -eq $true) { + Write-Output "`"createqmod`" - Creates a .qmod file with your compiled libraries and mod.json." + Write-Output "`n-- Arguments --`n" + + Write-Output "-QmodName `t The file name of your qmod" + + exit +} + +$mod = "./mod.json" + +& $PSScriptRoot/validate-modjson.ps1 +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$modJson = Get-Content $mod -Raw | ConvertFrom-Json + +if ($qmodName -eq "") { + $qmodName = $modJson.name +} + +$filelist = @($mod) + +$cover = "./" + $modJson.coverImage +if ((-not ($cover -eq "./")) -and (Test-Path $cover)) { + $filelist += ,$cover +} + +foreach ($mod in $modJson.modFiles) { + $path = "./build/" + $mod + if (-not (Test-Path $path)) { + $path = "./extern/libs/" + $mod + } + if (-not (Test-Path $path)) { + Write-Output "Error: could not find dependency: $path" + exit 1 + } + $filelist += $path +} + +foreach ($mod in $modJson.lateModFiles) { + $path = "./build/" + $mod + if (-not (Test-Path $path)) { + $path = "./extern/libs/" + $mod + } + if (-not (Test-Path $path)) { + Write-Output "Error: could not find dependency: $path" + exit 1 + } + $filelist += $path +} + + +foreach ($lib in $modJson.libraryFiles) { + $path = "./build/" + $lib + if (-not (Test-Path $path)) { + $path = "./extern/libs/" + $lib + } + if (-not (Test-Path $path)) { + Write-Output "Error: could not find dependency: $path" + exit 1 + } + $filelist += $path +} + +$zip = $qmodName + ".zip" +$qmod = $qmodName + ".qmod" + +Compress-Archive -Path $filelist -DestinationPath $zip -Update +Move-Item $zip $qmod -Force \ No newline at end of file diff --git a/scripts/ndk-stack.ps1 b/scripts/ndk-stack.ps1 new file mode 100644 index 0000000..1a364a4 --- /dev/null +++ b/scripts/ndk-stack.ps1 @@ -0,0 +1,29 @@ +Param( + [Parameter(Mandatory=$false)] + [String] $logName = "RecentCrash.log", + + [Parameter(Mandatory=$false)] + [Switch] $help +) + +if ($help -eq $true) { + Write-Output "`"NDK-Stack`" - Processes a tombstone using the debug .so to find file locations" + Write-Output "`n-- Arguments --`n" + + Write-Output "LogName `t`t The file name of the tombstone to process" + + exit +} + +if (Test-Path "./ndkpath.txt") { + $NDKPath = Get-Content ./ndkpath.txt +} else { + $NDKPath = $ENV:ANDROID_NDK_HOME +} + +$stackScript = "$NDKPath/ndk-stack" +if (-not ($PSVersionTable.PSEdition -eq "Core")) { + $stackScript += ".cmd" +} + +Get-Content $logName | & $stackScript -sym ./build/debug/ > "$($logName)_processed.log" diff --git a/scripts/pull-tombstone.ps1 b/scripts/pull-tombstone.ps1 new file mode 100644 index 0000000..10fbc24 --- /dev/null +++ b/scripts/pull-tombstone.ps1 @@ -0,0 +1,52 @@ +Param( + [Parameter(Mandatory=$false)] + [String] $fileName = "RecentCrash.log", + + [Parameter(Mandatory=$false)] + [Switch] $analyze, + + [Parameter(Mandatory=$false)] + [Switch] $help +) + +if ($help -eq $true) { + Write-Output "`"Pull-Tombstone`" - Finds and pulls the most recent tombstone from your quest, optionally analyzing it with ndk-stack" + Write-Output "`n-- Arguments --`n" + + Write-Output "-FileName `t The name for the output file, defaulting to RecentCrash.log" + Write-Output "-Analyze `t Runs ndk-stack on the file after pulling" + + exit +} + +$global:currentDate = get-date +$global:recentDate = $Null +$global:recentTombstone = $Null + +for ($i = 0; $i -lt 3; $i++) { + $stats = & adb shell stat /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$i + $date = (Select-String -Input $stats -Pattern "(?<=Modify: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?=.\d{9})").Matches.Value + if([string]::IsNullOrEmpty($date)) { + Write-Output "Failed to pull tombstone, exiting..." + exit 1; + } + $dateObj = [datetime]::ParseExact($date, "yyyy-MM-dd HH:mm:ss", $Null) + $difference = [math]::Round(($currentDate - $dateObj).TotalMinutes) + if ($difference -eq 1) { + Write-Output "Found tombstone_0$i $difference minute ago" + } else { + Write-Output "Found tombstone_0$i $difference minutes ago" + } + if (-not $recentDate -or $recentDate -lt $dateObj) { + $recentDate = $dateObj + $recentTombstone = $i + } +} + +Write-Output "Latest tombstone was tombstone_0$recentTombstone" + +& adb pull /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$recentTombstone $fileName + +if ($analyze) { + & $PSScriptRoot/ndk-stack.ps1 -logName:$fileName +} diff --git a/scripts/restart-game.ps1 b/scripts/restart-game.ps1 new file mode 100644 index 0000000..fd0196a --- /dev/null +++ b/scripts/restart-game.ps1 @@ -0,0 +1,2 @@ +adb shell am force-stop com.beatgames.beatsaber +adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity diff --git a/scripts/start-logging.ps1 b/scripts/start-logging.ps1 new file mode 100644 index 0000000..e60fc2d --- /dev/null +++ b/scripts/start-logging.ps1 @@ -0,0 +1,78 @@ +Param( + [Parameter(Mandatory=$false)] + [Switch] $self, + + [Parameter(Mandatory=$false)] + [Switch] $all, + + [Parameter(Mandatory=$false)] + [String] $custom="", + + [Parameter(Mandatory=$false)] + [String] $file="", + + [Parameter(Mandatory=$false)] + [Switch] $help, + + [Parameter(Mandatory=$false)] + [Switch] $excludeHeader +) + +if ($help -eq $true) { + if ($excludeHeader -eq $false) { + Write-Output "`"Start-Logging`" - Logs Beat Saber using `"adb logcat`"" + Write-Output "`n-- Arguments --`n" + } + + Write-Output "-Self `t`t Only Logs your mod and Crashes" + Write-Output "-All `t`t Logs everything, including logs made by the Quest itself" + Write-Output "-Custom `t Specify a specific logging pattern, e.g `"custom-types|questui`"" + Write-Output "`t`t NOTE: The pattern `"AndroidRuntime|CRASH|scotland2|Unity`" is always appended to a custom pattern" + Write-Output "-File `t`t Saves the output of the log to the file name given" + + exit +} + +$bspid = adb shell pidof com.beatgames.beatsaber +$command = "adb logcat " + +if ($all -eq $false) { + $loops = 0 + while ([string]::IsNullOrEmpty($bspid) -and $loops -lt 3) { + Start-Sleep -Milliseconds 100 + $bspid = adb shell pidof com.beatgames.beatsaber + $loops += 1 + } + + if ([string]::IsNullOrEmpty($bspid)) { + Write-Output "Could not connect to adb, exiting..." + exit 1 + } + + $command += "--pid $bspid" +} + +if ($all -eq $false) { + $pattern = "(" + if ($self -eq $true) { + & $PSScriptRoot/validate-modjson.ps1 + $modID = (Get-Content "./mod.json" -Raw | ConvertFrom-Json).id + $pattern += "$modID|" + } + if (![string]::IsNullOrEmpty($custom)) { + $pattern += "$custom|" + } + if ($pattern -eq "(") { + $pattern = "( INFO| DEBUG| WARN| ERROR| CRITICAL|" + } + $pattern += "AndroidRuntime|CRASH|scotland2|Unity )" + $command += " | Select-String -pattern `"$pattern`"" +} + +if (![string]::IsNullOrEmpty($file)) { + $command += " | Out-File -FilePath $PSScriptRoot\$file" +} + +Write-Output "Logging using Command `"$command`"" +adb logcat -c +Invoke-Expression $command diff --git a/scripts/validate-modjson.ps1 b/scripts/validate-modjson.ps1 new file mode 100644 index 0000000..73a04e8 --- /dev/null +++ b/scripts/validate-modjson.ps1 @@ -0,0 +1,50 @@ +$mod = "./mod.json" +$modTemplate = "./mod.template.json" +$qpmShared = "./qpm.shared.json" + +if (Test-Path -Path $modTemplate) { + $update = -not (Test-Path -Path $mod) + + if (-not $update) { + $update = (Get-Item $modTemplate).LastWriteTime -gt (Get-Item $mod).LastWriteTime + } + + if (-not $update -and (Test-Path -Path $qpmShared)) { + $update = (Get-Item $qpmShared).LastWriteTime -gt (Get-Item $mod).LastWriteTime + } + + if ($update) { + & qpm qmod manifest + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + } +} +elseif (-not (Test-Path -Path $mod)) { + Write-Output "Error: mod.json and mod.template.json were not present" + exit 1 +} + +Write-Output "Creating qmod from mod.json" + +$psVersion = $PSVersionTable.PSVersion.Major +if ($psVersion -ge 6) { + $schemaUrl = "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json" + Invoke-WebRequest $schemaUrl -OutFile ./mod.schema.json + + $schema = "./mod.schema.json" + $modJsonRaw = Get-Content $mod -Raw + $modSchemaRaw = Get-Content $schema -Raw + + Remove-Item $schema + + Write-Output "Validating mod.json..." + if (-not ($modJsonRaw | Test-Json -Schema $modSchemaRaw)) { + Write-Output "Error: mod.json is not valid" + exit 1 + } +} +else { + Write-Output "Could not validate mod.json with schema: powershell version was too low (< 6)" +} +exit diff --git a/src/main.cpp b/src/main.cpp index 9a9cc37..9d8e613 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,47 +1,49 @@ #include "main.hpp" +#include "scotland2/shared/modloader.h" #include "GlobalNamespace/BTSCharacterSpawnController.hpp" #include "GlobalNamespace/BTSCharacterSpawnAnimationController.hpp" -using namespace GlobalNamespace; -static ModInfo modInfo; // Stores the ID and version of our mod, and is sent to the modloader upon startup +using namespace GlobalNamespace; -// Loads the config from disk using our modInfo, then returns it for use -Configuration& getConfig() { - static Configuration config(modInfo); - config.Load(); - return config; +MAKE_HOOK_MATCH(BTSCharacterSpawnAnimationController_PlayAnimation, &BTSCharacterSpawnAnimationController::PlayAnimation, void, BTSCharacterSpawnAnimationController* self) { + BTSCharacterSpawnAnimationController_PlayAnimation(self); + self->StopAnimation(); + PaperLogger.info("BTS character was detected and stopped."); } -// When the characters spawn, just stop their animiation and they go away! -MAKE_HOOK_OFFSETLESS(PlayAni, void, BTSCharacterSpawnAnimationController* self) { - PlayAni(self); - self->StopAnimation(); - getLogger().info("BTS character was detected and stopped."); +MAKE_HOOK_MATCH(BTSCharacterSpawnAnimationController_ResumeAnimation, &BTSCharacterSpawnAnimationController::ResumeAnimation, void, BTSCharacterSpawnAnimationController* self) { + BTSCharacterSpawnAnimationController_ResumeAnimation(self); + self->StopAnimation(); + PaperLogger.info("BTS character was detected and stopped."); } -// Returns a logger, useful for printing debug messages -Logger& getLogger() { - static Logger* logger = new Logger(modInfo); - return *logger; -} +/** + * Stores the ID and version of our mod, and is sent to the modloader upon startup + */ +static modloader::ModInfo modInfo{MOD_ID, VERSION, 0}; + -// Called at the early stages of game loading -extern "C" void setup(ModInfo& info) { - info.id = ID; - info.version = VERSION; - modInfo = info; - - getConfig().Load(); // Load the config file - getLogger().info("Completed setup!"); +/** + * Called at the early stages of game loading + */ +MOD_EXTERN_FUNC void setup(CModInfo *info) noexcept { + *info = modInfo.to_c(); + + // File logging + Paper::Logger::RegisterFileContextId(PaperLogger.tag); + + PaperLogger.info("Completed setup!"); } -// Called later on in the game loading - a good time to install function hooks -extern "C" void load() { - il2cpp_functions::Init(); +/** + * Called later on in the game loading - a good time to install function hooks + */ +MOD_EXTERN_FUNC void late_load() noexcept { + il2cpp_functions::Init(); - getLogger().info("Installing hooks..."); - INSTALL_HOOK_OFFSETLESS(getLogger(), PlayAni, il2cpp_utils::FindMethodUnsafe("", "BTSCharacterSpawnAnimationController", "PlayAnimation", 0)); - INSTALL_HOOK_OFFSETLESS(getLogger(), PlayAni, il2cpp_utils::FindMethodUnsafe("", "BTSCharacterSpawnAnimationController", "ResumeAnimation", 0)); - getLogger().info("Installed all hooks!"); -} \ No newline at end of file + PaperLogger.info("Installing hooks..."); + INSTALL_HOOK(PaperLogger, BTSCharacterSpawnAnimationController_PlayAnimation); + INSTALL_HOOK(PaperLogger, BTSCharacterSpawnAnimationController_ResumeAnimation); + PaperLogger.info("Installed all hooks!"); +} diff --git a/startlogging.bat b/startlogging.bat deleted file mode 100644 index 652e342..0000000 --- a/startlogging.bat +++ /dev/null @@ -1,2 +0,0 @@ -@ECHO OFF -adb logcat > log.txt \ No newline at end of file