From 745faf5d13a01b5615efbe0ce942cd8caf3bb3d5 Mon Sep 17 00:00:00 2001 From: Metalit <48568911+Metalit@users.noreply.github.com> Date: Wed, 6 May 2026 18:27:02 -0600 Subject: [PATCH 1/8] Update to new bshook --- .clang-format | 69 +- .clangd | 7 + .gitignore | 67 +- CMakeLists.txt | 117 ++-- CMakeSettings.json | 16 - build.ps1 | 40 -- copy.ps1 | 26 - createqmod.ps1 | 109 --- include/capstone-helpers.hpp | 51 +- include/extra-typedefs.hpp | 37 + log.ps1 | 6 - qpm.json | 44 +- qpm.shared.json | 79 ++- shared/coroutine.hpp | 275 +++----- shared/delegate.hpp | 1256 ++++++++++++++++------------------ shared/logging.hpp | 28 +- shared/macro-types.hpp | 19 +- shared/macros.hpp | 227 ++---- shared/register.hpp | 267 ++++---- shared/types.hpp | 222 +++--- shared/util.hpp | 100 +-- src/NoteData.hpp | 49 -- src/coroutine.cpp | 305 +++++---- src/delegate.cpp | 76 +- src/logging.cpp | 501 +++++++------- src/register.cpp | 561 +++++++-------- src/test-coroutine.cpp | 33 +- src/test.cpp | 168 ++--- src/test_small.cpp | 4 +- src/type-registration.cpp | 1240 +++++++++++++++++---------------- startAndLog.ps1 | 4 - 31 files changed, 2773 insertions(+), 3230 deletions(-) create mode 100644 .clangd delete mode 100644 CMakeSettings.json delete mode 100644 build.ps1 delete mode 100644 copy.ps1 delete mode 100644 createqmod.ps1 create mode 100644 include/extra-typedefs.hpp delete mode 100644 log.ps1 delete mode 100644 src/NoteData.hpp delete mode 100644 startAndLog.ps1 diff --git a/.clang-format b/.clang-format index 9a5630f..2bf504d 100644 --- a/.clang-format +++ b/.clang-format @@ -1,13 +1,62 @@ +Language: Cpp BasedOnStyle: Google -AllowShortBlocksOnASingleLine: false -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: true -ColumnLimit: 200 -CommentPragmas: NOLINT:.* +AccessModifierOffset: -1 +AlignAfterOpenBracket: BlockIndent +AlignOperands: AlignAfterOperator +AlignTrailingComments: + Kind: Never +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakAfterAttributes: Never +BreakBeforeConceptDeclarations: Always +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false +ColumnLimit: 150 DerivePointerAlignment: false -IncludeBlocks: Preserve +FixNamespaceComments: false +IncludeBlocks: Regroup +IncludeCategories: + - Regex: "^<[^.>]+>$" + Priority: 2 + SortPriority: 3 + CaseSensitive: false + - Regex: '^<.+\.(h|hpp)>$' + Priority: 2 + SortPriority: 4 + CaseSensitive: false + - Regex: '^"[A-Z].*\/.+\.hpp"$' + Priority: 5 + CaseSensitive: true + - Regex: "^.+/shared/.+" + Priority: 1 + SortPriority: 2 + CaseSensitive: true + - Regex: ".*" + Priority: 1 + SortPriority: 1 + CaseSensitive: false +IndentExternBlock: Indent +IndentRequiresClause: false IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 -UseTab: Never -Cpp11BracedListStyle: false +InsertNewlineAtEOF: true +KeepEmptyLinesAtTheStartOfBlocks: true +LineEnding: LF +NamespaceIndentation: All +PackConstructorInitializers: CurrentLine +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 50 +QualifierAlignment: Right +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SkipMacroDefinitionBody: true +SpaceAfterCStyleCast: true +SpacesBeforeTrailingComments: 2 diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..284e9cf --- /dev/null +++ b/.clangd @@ -0,0 +1,7 @@ +Diagnostics: + UnusedIncludes: None +--- +If: + PathMatch: ./extern/.* +Index: + Background: Skip diff --git a/.gitignore b/.gitignore index 7f1c6c2..e5f22a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,57 +1,26 @@ -# Prerequisites -*.d +# VSCode +.cache/ +.vscode/ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# VSCode config stuff -!.vscode/c_cpp_properties.json -!.vscode/tasks.json -.cache - -# NDK stuff -out/ -[Ll]ib/ -[Ll]ibs/ -[Oo]bj/ -[Oo]bjs/ -ndkpath.txt -*.zip +# Jetbrains IDEs +.idea/ +# Files generated by QPM extern/ -extern.cmake +ndkpath.txt qpm_defines.cmake +extern.cmake +mod.json + +# Build outputs and artifacts build/ +*.zip *.qmod -*.backup +# Script outputs and artifacts +*.txt *.log -mod.json -.vs/ +*.html + +# Exceptions +!CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 0663cca..ba2f9dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,89 @@ -# include some defines automatically made by qpm +cmake_minimum_required(VERSION 3.22) + +# Include definitions generated by QPM on restore include(qpm_defines.cmake) -cmake_minimum_required(VERSION 3.22) project(${COMPILE_ID}) -# c++ standard -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED 20) -set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) +# C++ standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED 23) -# define that stores the actual source directory -set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) -set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +# Provide compile commands so they can be used for intellisense +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) -# compile options used -add_link_options(-Wl,--exclude-libs,ALL) +# Set compiler options +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) add_compile_options(-frtti -fPIE -fPIC -fexceptions -fvisibility=hidden) add_compile_options(-Wall -Werror -Wextra -Wno-invalid-offsetof -O3 -Wno-vla-cxx-extension) + # Extra -Wno's that were not there originally to make it work add_compile_options(-Wno-unused-variable -Wno-unused-function) -# compile definitions used -add_compile_definitions(VERSION=\"${MOD_VERSION}\") -add_compile_definitions(MOD_ID=\"${MOD_ID}\") + +# Expose definitions from qpm_defines.cmake +add_compile_definitions(VERSION="${MOD_VERSION}") +add_compile_definitions(MOD_ID="${MOD_ID}") + # TODO: Implement more robustly add_compile_definitions(VERSION_LONG=1) add_compile_definitions(NEED_UNSAFE_CSHARP) add_compile_definitions(UNITY_6) add_compile_definitions(NO_VERBOSE_LOGS) -# For performing debug logging of very hard to diagnose issues -if (NOT DEFINED LOCAL_TEST) - add_compile_definitions(CT_USE_GCDESCRIPTOR_DEBUG) -endif() -string(LENGTH "${CMAKE_CURRENT_LIST_DIR}/" FOLDER_LENGTH) -add_compile_definitions("PAPER_ROOT_FOLDER_LENGTH=${FOLDER_LENGTH}") +# Add any extra definitions from the build script +get_cmake_property(variable_names VARIABLES) + +foreach(variable_name ${variable_names}) + if(variable_name MATCHES "^_bs_build_def_.*$") + string(SUBSTRING ${variable_name} 14 -1 definition_name) + add_compile_definitions(${definition_name}=${${variable_name}}) + message("Added definition: " ${definition_name} "=" ${${variable_name}}) + endif() +endforeach() -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Trim source folder in logcat messages +string(LENGTH "${CMAKE_CURRENT_SOURCE_DIR}/" FOLDER_LENGTH) +add_compile_definitions("PAPER_ROOT_FOLDER_LENGTH=${FOLDER_LENGTH}") -if (DEFINED LOCAL_TEST) +if(DEFINED LOCAL_TEST) MESSAGE(STATUS "Building lib with Test defines") add_compile_definitions(LOCAL_TEST) add_compile_definitions(TEST_DELEGATE) elseif(DEFINED LOCAL_TEST_COROUTINE) MESSAGE(STATUS "Building lib with Coroutine test defines") add_compile_definitions(LOCAL_TEST_COROUTINE) +else() + # For performing debug logging of very hard to diagnose issues + # add_compile_definitions(CT_USE_GCDESCRIPTOR_DEBUG) endif() -# recursively get all src files -RECURSE_FILES(cpp_file_list ${SOURCE_DIR}/*.cpp) -RECURSE_FILES(c_file_list ${SOURCE_DIR}/*.c) -RECURSE_FILES(inline_hook_c ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.c) -RECURSE_FILES(inline_hook_cpp ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.cpp) - -# add all src files to compile -add_library( - ${COMPILE_ID} - SHARED - ${cpp_file_list} - ${c_file_list} - ${inline_hook_c} - ${inline_hook_cpp} -) +# Define the code directories +set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Recursively get all source files +recurse_files(cpp_file_list ${SOURCE_DIR}/*.cpp) +recurse_files(c_file_list ${SOURCE_DIR}/*.c) + +# Add all source files to compilation +add_library(${COMPILE_ID} SHARED ${cpp_file_list} ${c_file_list}) -# add src dir as include dir -target_include_directories(${COMPILE_ID} PRIVATE ${SOURCE_DIR}) -# add include dir as include dir +# Add include directories to compilation target_include_directories(${COMPILE_ID} PRIVATE ${INCLUDE_DIR}) -# add shared dir as include dir target_include_directories(${COMPILE_ID} PUBLIC ${SHARED_DIR}) +# Link necessary Android libraries target_link_libraries(${COMPILE_ID} PRIVATE -llog) -# add extern stuff like libs and other includes + +# Add autogenerated definitions and link libraries for dependencies +add_link_options(-Wl, --exclude-libs, ALL) include(extern.cmake) -add_custom_command(TARGET ${COMPILE_ID} POST_BUILD - COMMAND ${CMAKE_STRIP} -g -S -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" - ) - -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" - ) +add_custom_command( + TARGET ${COMPILE_ID} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "debug" + COMMAND ${CMAKE_COMMAND} -E rename "lib${COMPILE_ID}.so" "debug/lib${COMPILE_ID}.so" + COMMAND ${CMAKE_STRIP} -d --strip-all "debug/lib${COMPILE_ID}.so" -o "lib${COMPILE_ID}.so" + COMMENT "Create stripped and debug libraries" +) diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index f24aa6e..0000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "x64-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "linux_arm64" ], - "buildRoot": "${projectDir}\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeExecutable": "C:\\Program Files\\CMake\\bin\\cmake.exe", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "" - } - ] -} \ No newline at end of file diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index e2a436a..0000000 --- a/build.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [Switch]$clean, - [Parameter(Mandatory=$false)] - [Switch]$local_test, - [Parameter(Mandatory=$false)] - [Switch]$local_test_coroutine -) - -if ($clean.IsPresent) { - if (Test-Path -Path "build") { - Remove-Item build -Recurse - } -} - -$NDKPath = Get-Content $PSScriptRoot/ndkpath.txt - -# Clean-Build-Folder -# build tests - -if ($local_test.IsPresent) { - & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DLOCAL_TEST=a -B build -} elseif ($local_test_coroutine.IsPresent) { - & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DLOCAL_TEST_COROUTINE=a -B build -} else { - & cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build -} -& cmake --build ./build - -$ExitCode = $LastExitCode - -# Post build, we actually want to transform the compile_commands.json file such that it has no \\ characters and ONLY has / characters -# (Get-Content -Path build/compile_commands.json) | -# ForEach-Object {$_ -Replace '\\\\', '/'} | Set-Content -Path build/compile_commands.json - -if (-not ($ExitCode -eq 0)) { - $msg = "ExitCode: " + $ExitCode - Write-Output $msg - exit $ExitCode -} diff --git a/copy.ps1 b/copy.ps1 deleted file mode 100644 index 8b32ea8..0000000 --- a/copy.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -param ( - [Parameter(Mandatory=$false)] - [Switch]$debug_so, - [Parameter(Mandatory=$false)] - [Switch]$log -) - -& ./build.ps1 -if (-not ($LastExitCode -eq 0)) { - echo "build failed, not copying" - exit -} - -if ($debug.IsPresent) { - & adb push build/debug/libcustom-types.so /sdcard/ModData/com.beatgames.beatsaber/Modloader/libs/libcustom-types.so -} else { - & adb push build/libcustom-types.so /sdcard/ModData/com.beatgames.beatsaber/Modloader/libs/libcustom-types.so -} - -& adb shell am force-stop com.beatgames.beatsaber -& adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity -Start-Sleep -Seconds 1.0 -& adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity -if ($log.IsPresent) { - & ./log.ps1 -} diff --git a/createqmod.ps1 b/createqmod.ps1 deleted file mode 100644 index 4b779bd..0000000 --- a/createqmod.ps1 +++ /dev/null @@ -1,109 +0,0 @@ -Param( - [String]$qmodname="", - [Parameter(Mandatory=$false)] - [Switch]$clean -) - -if ($qmodName -eq "") -{ - echo "Give a proper qmod name and try again" - exit -} -$mod = "./mod.json" -$modJson = Get-Content $mod -Raw | ConvertFrom-Json - -$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 - } - $filelist += $path -} - -foreach ($lib in $modJson.libraryFiles) -{ - $path = "./extern/libs/" + $lib - if (-not (Test-Path $path)) - { - $path = "./build/" + $lib - } - $filelist += $path -} - -foreach ($lib in $modJson.lateModFiles) -{ - $path = "./extern/libs/" + $lib - if (-not (Test-Path $path)) - { - $path = "./build/" + $lib - } - $filelist += $path -} - -if (Test-Path "./ExtraFiles") -{ - $extraFiles = @() - $extraEntries = Get-ChildItem ./ExtraFiles/* -Recurse - - foreach ($entry in $extraEntries) - { - $mode = $entry | Select -Expand Mode - if ($mode.Contains("d")) - { - continue - } - - # if not a dir - if (-not $entry.Directory.Name.Contains("ExtraFiles")) - { - $dir = $entry.Directory - $folderPath = $dir.Name + "/" + $entry.Name - while (($dir.Directory) -and (-not $dir.Directory.Name.Contains("ExtraFiles"))) - { - $folderPath = $dir.Directory.Name + "/" + $folderPath - } - - if ($folderPath.Contains("Icons")) - { - continue; - } - $extraFiles += ,$folderPath - } - else - { - $extraFiles += ,$entry.Name - } - } - - foreach ($file in $extraFiles) - { - $path = "./ExtraFiles/" + $file - $filelist += ,$path - } -} -else -{ - echo "No ExtraFiles Directory Found" -} - -$zip = $qmodName + ".zip" -$qmod = $qmodName + ".qmod" - -if (($clean.IsPresent) -and (Test-Path $qmod)) -{ - echo "Making Clean Qmod" - Move-Item $qmod $zip -Force -} - -Compress-Archive -Path $filelist -DestinationPath $zip -Update -Move-Item $zip $qmod -Force diff --git a/include/capstone-helpers.hpp b/include/capstone-helpers.hpp index 2e96a5d..7b25c78 100644 --- a/include/capstone-helpers.hpp +++ b/include/capstone-helpers.hpp @@ -1,21 +1,21 @@ #pragma once -#include "beatsaber-hook/shared/utils/capstone-utils.hpp" #include "logging.hpp" +#include "beatsaber-hook/shared/capstone.hpp" namespace cs { - template - requires ((nToRetOn >= 1 && (szBytes % 4) == 0)) - T findNthSafe(const uint32_t* addr) { - cs_insn* insn = cs_malloc(getHandle()); + template + requires((nToRetOn >= 1 && (szBytes % 4) == 0)) + T findNthSafe(uint32_t const* addr) { + cs_insn* insn = cs_malloc(get_handle()); auto ptr = reinterpret_cast(addr); - auto instructions = reinterpret_cast(addr); + auto instructions = reinterpret_cast(addr); int rCount = retCount; uint32_t nCalls = nToRetOn; size_t sz = szBytes; while (sz > 0) { - bool res = cs_disasm_iter(getHandle(), &instructions, &sz, &ptr, insn); + bool res = cs_disasm_iter(get_handle(), &instructions, &sz, &ptr, insn); custom_types::logger.debug("0x{:08x} diassemb: {} (rCount: {}, nCalls: {}, sz: {})", ptr, insn->mnemonic, rCount, nCalls, sz); if (res) { // Valid decode, so lets check to see if it is a match or we need to break. @@ -23,7 +23,9 @@ namespace cs { if (rCount == 0) { // Early termination! cs_free(insn, 1); - custom_types::logger.debug("Could not find: {} call at: {} within: {} rets! Found all of the rets first!", nToRetOn, fmt::ptr(addr), retCount); + custom_types::logger.debug( + "Could not find: {} call at: {} within: {} rets! Found all of the rets first!", nToRetOn, fmt::ptr(addr), retCount + ); return {}; } rCount--; @@ -40,7 +42,13 @@ namespace cs { if (nCalls == 1) { std::string name(insn->mnemonic); cs_free(insn, 1); - custom_types::logger.debug("Found: {} match, at: {} within: {} rets, but the result was a {}! Cannot compute destination address!", nToRetOn, fmt::ptr(addr), retCount, name.c_str()); + custom_types::logger.debug( + "Found: {} match, at: {} within: {} rets, but the result was a {}! Cannot compute destination address!", + nToRetOn, + fmt::ptr(addr), + retCount, + name.c_str() + ); return {}; } else { nCalls--; @@ -48,11 +56,10 @@ namespace cs { } } // Other instructions are ignored silently - } - else { + } else { // Invalid instructions are ignored silently. // In order to skip these properly, we must increment our instructions, ptr, and size accordingly. - custom_types::logger.debug("FAILED PARSE: 0x{:08x} diassemb: 0x{:08x}", ptr, *(uint32_t*)ptr); + custom_types::logger.debug("FAILED PARSE: 0x{:08x} diassemb: 0x{:08x}", ptr, *(uint32_t*) ptr); sz -= 4; ptr += 4; instructions += 4; @@ -64,23 +71,23 @@ namespace cs { return {}; } - template - requires ((nToRetOn >= 1 && (szBytes % 4) == 0)) - auto findNthBlSafe(const uint32_t* addr) { + template + requires((nToRetOn >= 1 && (szBytes % 4) == 0)) + auto findNthBlSafe(uint32_t const* addr) { if constexpr (includeR) { - return findNthSafe, nToRetOn, &blConv, &insnMatch, retCount, szBytes>(addr); + return findNthSafe, nToRetOn, &bl_conv, &insn_match, retCount, szBytes>(addr); } else { - return findNthSafe, nToRetOn, &blConv, &insnMatch<>, retCount, szBytes>(addr); + return findNthSafe, nToRetOn, &bl_conv, &insn_match<>, retCount, szBytes>(addr); } } - template - requires ((nToRetOn >= 1 && (szBytes % 4) == 0)) - auto findNthBSafe(const uint32_t* addr) { + template + requires((nToRetOn >= 1 && (szBytes % 4) == 0)) + auto findNthBSafe(uint32_t const* addr) { if constexpr (includeR) { - return findNthSafe, nToRetOn, &bConv, &insnMatch, retCount, szBytes>(addr); + return findNthSafe, nToRetOn, &b_conv, &insn_match, retCount, szBytes>(addr); } else { - return findNthSafe, nToRetOn, &bConv, &insnMatch<>, retCount, szBytes>(addr); + return findNthSafe, nToRetOn, &b_conv, &insn_match<>, retCount, szBytes>(addr); } } } diff --git a/include/extra-typedefs.hpp b/include/extra-typedefs.hpp new file mode 100644 index 0000000..79205d6 --- /dev/null +++ b/include/extra-typedefs.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "beatsaber-hook/shared/api.hpp" + +#include +#include + +// After above +#include "utils/HashUtils.h" +#include "utils/Il2CppHashMap.h" +#include "utils/StringUtils.h" + +struct Il2CppImageGlobalMetadata { + TypeDefinitionIndex typeStart; + TypeDefinitionIndex exportedTypeStart; + CustomAttributeIndex customAttributeStart; + MethodIndex entryPointIndex; + Il2CppImage const* image; +}; + +struct NamespaceAndNamePairHash { + size_t operator()(std::pair const& pair) const { + return il2cpp::utils::HashUtils::Combine(il2cpp::utils::StringUtils::Hash(pair.first), il2cpp::utils::StringUtils::Hash(pair.second)); + } +}; + +struct NamespaceAndNamePairEquals { + bool operator()(std::pair const& p1, std::pair const& p2) const { + return !strcmp(p1.first, p2.first) && !strcmp(p1.second, p2.second); + } +}; + +struct Il2CppNameToTypeHandleHashTable : + public Il2CppHashMap, Il2CppMetadataTypeHandle, NamespaceAndNamePairHash, NamespaceAndNamePairEquals> { + Il2CppNameToTypeHandleHashTable() : + Il2CppHashMap, Il2CppMetadataTypeHandle, NamespaceAndNamePairHash, NamespaceAndNamePairEquals>() {} +}; diff --git a/log.ps1 b/log.ps1 deleted file mode 100644 index 79a8241..0000000 --- a/log.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -if (Test-Path ./log.log) -{ - & del log.log -} -& adb logcat -c -& adb logcat > log.log \ No newline at end of file diff --git a/qpm.json b/qpm.json index b6ebb45..dc06e32 100644 --- a/qpm.json +++ b/qpm.json @@ -6,7 +6,7 @@ "info": { "name": "custom-types", "id": "custom-types", - "version": "0.19.0", + "version": "0.20.0", "url": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching", "additionalData": { "overrideSoName": "libcustom-types.so", @@ -21,18 +21,31 @@ "workspace": { "scripts": { "build": [ - "pwsh ./build.ps1" + "python ./extern/includes/mod-scripts/shared/build.py $0:?" ], - "qmod": [ - "pwsh ./build.ps1 -clean", - "qpm qmod manifest", - "pwsh ./createqmod.ps1 CustomTypes -clean" + "build-test": [ + "python ./extern/includes/mod-scripts/shared/build.py -C=-DLOCAL_TEST=\"\" $0:?" ], - "test": [ - "pwsh ./build.ps1 -local_test" + "build-coro": [ + "python ./extern/includes/mod-scripts/shared/build.py -C=-DLOCAL_TEST_COROUTINE=\"\" $0:?" + ], + "copy": [ + "python ./extern/includes/mod-scripts/shared/cpy.py $0:?" + ], + "dbg": [ + "python ./extern/includes/mod-scripts/shared/dbg.py $0:?" + ], + "log": [ + "python ./extern/includes/mod-scripts/shared/log.py -t CustomTypes $0:?" + ], + "profile": [ + "python ./extern/includes/mod-scripts/shared/profile.py $0:?" + ], + "restart": [ + "python ./extern/includes/mod-scripts/shared/restart.py $0:?" ] }, - "ndk": "^27.3.13750724", + "ndk": "^29.0.14206865", "qmodIncludeDirs": [ "./extern/libs", "./build" @@ -42,8 +55,8 @@ }, "dependencies": [ { - "id": "beatsaber-hook", - "versionRange": "^7.4.2", + "id": "mod-scripts", + "versionRange": "^1.0.0", "additionalData": {} }, { @@ -55,13 +68,18 @@ }, { "id": "paper2_scotland2", - "versionRange": "^4.2.1", + "versionRange": "^4.7.0", "additionalData": {} }, { "id": "flamingo", "versionRange": "^1.1.2", "additionalData": {} + }, + { + "id": "beatsaber-hook", + "versionRange": "^8.0.0", + "additionalData": {} } ] -} \ No newline at end of file +} diff --git a/qpm.shared.json b/qpm.shared.json index ce0e736..750b4b0 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -7,7 +7,7 @@ "info": { "name": "custom-types", "id": "custom-types", - "version": "0.19.0", + "version": "0.20.0", "url": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching", "additionalData": { "overrideSoName": "libcustom-types.so", @@ -22,18 +22,31 @@ "workspace": { "scripts": { "build": [ - "pwsh ./build.ps1" + "python ./extern/includes/mod-scripts/shared/build.py $0:?" ], - "qmod": [ - "pwsh ./build.ps1 -clean", - "qpm qmod manifest", - "pwsh ./createqmod.ps1 CustomTypes -clean" + "build-coro": [ + "python ./extern/includes/mod-scripts/shared/build.py -C=-DLOCAL_TEST_COROUTINE=\"\" $0:?" ], - "test": [ - "pwsh ./build.ps1 -local_test" + "build-test": [ + "python ./extern/includes/mod-scripts/shared/build.py -C=-DLOCAL_TEST=\"\" $0:?" + ], + "copy": [ + "python ./extern/includes/mod-scripts/shared/cpy.py $0:?" + ], + "dbg": [ + "python ./extern/includes/mod-scripts/shared/dbg.py $0:?" + ], + "log": [ + "python ./extern/includes/mod-scripts/shared/log.py $0:?" + ], + "profile": [ + "python ./extern/includes/mod-scripts/shared/profile.py $0:?" + ], + "restart": [ + "python ./extern/includes/mod-scripts/shared/restart.py $0:?" ] }, - "ndk": "^27.3.13750724", + "ndk": "^29.0.14206865", "qmodIncludeDirs": [ "./extern/libs", "./build" @@ -43,8 +56,8 @@ }, "dependencies": [ { - "id": "beatsaber-hook", - "versionRange": "^7.4.2", + "id": "mod-scripts", + "versionRange": "^1.0.0", "additionalData": {} }, { @@ -56,17 +69,35 @@ }, { "id": "paper2_scotland2", - "versionRange": "^4.2.1", + "versionRange": "^4.7.0", "additionalData": {} }, { "id": "flamingo", "versionRange": "^1.1.2", "additionalData": {} + }, + { + "id": "beatsaber-hook", + "versionRange": "^8.0.0", + "additionalData": {} } ] }, "restoredDependencies": [ + { + "dependency": { + "id": "scotland2", + "versionRange": "=0.1.7", + "additionalData": { + "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/libsl2.so", + "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/debug_libsl2.so", + "overrideSoName": "libsl2.so", + "branchName": "version/v0_1_7" + } + }, + "version": "0.1.7" + }, { "dependency": { "id": "paper2_scotland2", @@ -120,13 +151,12 @@ { "dependency": { "id": "beatsaber-hook", - "versionRange": "=7.4.2", + "versionRange": "=8.0.0", "additionalData": { - "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v7.4.2/libbeatsaber-hook.so", - "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v7.4.2/debug_libbeatsaber-hook.so", + "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.0.0/libbeatsaber-hook.so", + "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.0.0/debug_libbeatsaber-hook.so", "overrideSoName": "libbeatsaber-hook.so", - "modLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v7.4.2/beatsaber-hook.qmod", - "branchName": "version/v7_4_2", + "branchName": "master", "compileOptions": { "cppFlags": [ "-Wno-extra-qualification" @@ -135,7 +165,7 @@ "cmake": true } }, - "version": "7.4.2" + "version": "8.0.0" }, { "dependency": { @@ -169,16 +199,15 @@ }, { "dependency": { - "id": "scotland2", - "versionRange": "=0.1.7", + "id": "mod-scripts", + "versionRange": "=1.0.0", "additionalData": { - "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/libsl2.so", - "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/debug_libsl2.so", - "overrideSoName": "libsl2.so", - "branchName": "version/v0_1_7" + "headersOnly": true, + "branchName": "version/v1_0_0", + "cmake": false } }, - "version": "0.1.7" + "version": "1.0.0" } ] } \ No newline at end of file diff --git a/shared/coroutine.hpp b/shared/coroutine.hpp index 858fd3b..9786f24 100644 --- a/shared/coroutine.hpp +++ b/shared/coroutine.hpp @@ -1,34 +1,11 @@ #pragma once -#include "coroutine.hpp" -#if __has_include() -#include -namespace custom_types::Helpers { - template - using coro_handle = std::coroutine_handle; - using suspend_always = std::suspend_always; - using suspend_never = std::suspend_never; -} - -#elif __has_include() -#include -namespace custom_types::Helpers { - template - using coro_handle = std::experimental::coroutine_handle; - using suspend_always = std::experimental::suspend_always; - using suspend_never = std::experimental::suspend_never; -} - -#else -#error "You must have a definition for C++ 20 Coroutines in order to include coroutine.hpp! Is your NDK version >= 22? Are your compile flags set?" -#endif - -#include -#include -// TODO: Conditionalize exception include -#include -#include #include "macros.hpp" +#include "beatsaber-hook/shared/find.hpp" +#include "beatsaber-hook/shared/members.hpp" +#include "beatsaber-hook/shared/types.hpp" + +#include #if __has_include("System/Collections/IEnumerator.hpp") #include "System/Collections/IEnumerator.hpp" @@ -41,10 +18,12 @@ namespace custom_types::Helpers { } #endif -#include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" -#include "beatsaber-hook/shared/utils/utils.h" - namespace custom_types::Helpers { + template + using coro_handle = std::coroutine_handle; + using suspend_always = std::suspend_always; + using suspend_never = std::suspend_never; + struct Wrapper { enumeratorT instance; constexpr Wrapper() : instance(nullptr) {} @@ -54,44 +33,27 @@ namespace custom_types::Helpers { /// @brief Generator type used as a coroutine /// @tparam T The type of the value wrapped in the generator. - template + template class generator; using Coroutine = generator; - using CoroFuncType = std::function; + using CoroFuncType = std::function; } -MARK_GEN_REF_PTR_T(custom_types::Helpers::generator); -MARK_REF_PTR_T(custom_types::Helpers::Coroutine); + +MARK_GEN_REF_T_PTR(custom_types::Helpers::generator); +MARK_REF_T(custom_types::Helpers::Coroutine*); +MARK_REF_T(custom_types::Helpers::CoroFuncType*); + // Coroutine* mapped to void* -template<> -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class { - static inline Il2CppClass* get() { - il2cpp_functions::Init(); - return classof(void*); - } -}; template <> -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_arg_class { - static inline Il2CppClass* get(custom_types::Helpers::Coroutine*) { - il2cpp_functions::Init(); - return classof(void*); - } +struct ::i2c::type_check::no_arg_class { + static inline Il2CppClass* get() { return class_of(); } }; + // CoroFuncType* mapped to void* -template<> -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class { - static inline Il2CppClass* get() { - il2cpp_functions::Init(); - return classof(void*); - } -}; template <> -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_arg_class { - static inline Il2CppClass* get(custom_types::Helpers::CoroFuncType*) { - il2cpp_functions::Init(); - return classof(void*); - } +struct ::i2c::type_check::no_arg_class { + static inline Il2CppClass* get() { return class_of(); } }; -MARK_REF_PTR_T(custom_types::Helpers::CoroFuncType); namespace custom_types::Helpers { /// @brief A helper type for creating custom coroutines C# from C++. @@ -107,9 +69,9 @@ namespace custom_types::Helpers { // Generator and promise types inspired greatly by https://github.com/lewissbaker/cppcoro/blob/master/include/cppcoro/generator.hpp /// @brief A generic generator_promise used by a generator /// @tparam T The value type/yields of the generator - template + template class generator_promise { - public: + public: using value_type = std::remove_reference_t; using reference_type = std::conditional_t, T, T&>; using pointer_type = value_type*; @@ -127,22 +89,15 @@ namespace custom_types::Helpers { return {}; } - void unhandled_exception() noexcept { - m_exception = std::current_exception(); - } + void unhandled_exception() noexcept { m_exception = std::current_exception(); } - void return_void() { - } + void return_void() {} - reference_type const value() const noexcept { - return static_cast(m_value); - } - reference_type value() noexcept { - return static_cast(m_value); - } + reference_type const value() const noexcept { return static_cast(m_value); } + reference_type value() noexcept { return static_cast(m_value); } // Don't allow any use of 'co_await' inside the generator coroutine. - template + template Helpers::suspend_never await_transform(U&& value) = delete; void rethrow_if_exception() { @@ -151,28 +106,25 @@ namespace custom_types::Helpers { } } - [[nodiscard]] bool failed() const { - return m_exception != nullptr; - } - private: + [[nodiscard]] bool failed() const { return m_exception != nullptr; } + + private: T m_value; std::exception_ptr m_exception; }; } /// @brief Generator type used as a coroutine /// @tparam T The type of the value wrapped in the generator. - template + template class [[nodiscard]] generator { - public: + public: using promise_type = internal::generator_promise; generator() noexcept : m_coroutine(nullptr) {} - generator(generator&& other) noexcept : m_coroutine(other.m_coroutine) { - other.m_coroutine = nullptr; - } + generator(generator&& other) noexcept : m_coroutine(other.m_coroutine) { other.m_coroutine = nullptr; } - generator(const generator& other) = delete; + generator(generator const& other) = delete; ~generator() { if (m_coroutine) { @@ -185,11 +137,9 @@ namespace custom_types::Helpers { return *this; } - void swap(generator& other) noexcept { - std::swap(m_coroutine, other.m_coroutine); - } + void swap(generator& other) noexcept { std::swap(m_coroutine, other.m_coroutine); } - private: + private: friend struct InternalHelper; friend class internal::generator_promise; explicit generator(coro_handle coroutine) noexcept : m_coroutine(coroutine) {} @@ -198,30 +148,34 @@ namespace custom_types::Helpers { }; namespace internal { - template + template Helpers::generator generator_promise::get_return_object() noexcept { using coroutine_handle = coro_handle>; - return Helpers::generator{ coroutine_handle::from_promise(*this) }; + return Helpers::generator{coroutine_handle::from_promise(*this)}; } } } -DECLARE_CLASS_INTERFACES(custom_types::Helpers, ResetableCoroutine, "System", "Object", sizeof(Il2CppObject), INTERFACE_NAME("System.Collections", "IEnumerator")) { - private: +DECLARE_CLASS_INTERFACES( + custom_types::Helpers, ResetableCoroutine, "System", "Object", sizeof(Il2CppObject), INTERFACE_NAME("System.Collections", "IEnumerator") +) { + private: custom_types::Helpers::CoroFuncType coroCreator; custom_types::Helpers::Wrapper current; // These fields exist as C# fields for semantic purposes only DECLARE_INSTANCE_FIELD(custom_types::Helpers::Coroutine*, currentCoro); DECLARE_INSTANCE_FIELD(bool, valid); - DECLARE_CTOR(ctor, custom_types::Helpers::CoroFuncType* creator); - DECLARE_OVERRIDE_METHOD(bool, MoveNext, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "MoveNext")); - DECLARE_OVERRIDE_METHOD(Il2CppObject*, get_Current, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "get_Current")); - DECLARE_OVERRIDE_METHOD(void, Reset, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "Reset")); + DECLARE_CTOR(ctor, custom_types::Helpers::CoroFuncType * creator); + DECLARE_OVERRIDE_METHOD(bool, MoveNext, i2c::find_method({"System.Collections", "IEnumerator"}, "MoveNext")); + DECLARE_OVERRIDE_METHOD(Il2CppObject*, get_Current, i2c::find_method({"System.Collections", "IEnumerator"}, "get_Current")); + DECLARE_OVERRIDE_METHOD(void, Reset, i2c::find_method({"System.Collections", "IEnumerator"}, "Reset")); DECLARE_DTOR(Finalize); }; -DECLARE_CLASS_INTERFACES(custom_types::Helpers, StandardCoroutine, "System", "Object", sizeof(Il2CppObject), INTERFACE_NAME("System.Collections", "IEnumerator")) { +DECLARE_CLASS_INTERFACES( + custom_types::Helpers, StandardCoroutine, "System", "Object", sizeof(Il2CppObject), INTERFACE_NAME("System.Collections", "IEnumerator") +) { struct CoroutineNotResettable : std::runtime_error { CoroutineNotResettable() : std::runtime_error("StandardCoroutine is not resettable!") {} }; @@ -231,10 +185,10 @@ DECLARE_CLASS_INTERFACES(custom_types::Helpers, StandardCoroutine, "System", "Ob DECLARE_INSTANCE_FIELD(bool, valid); // Construct a StandardCoroutine around a provided Coroutine instance. - DECLARE_CTOR(ctor, custom_types::Helpers::Coroutine* coro); - DECLARE_OVERRIDE_METHOD(bool, MoveNext, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "MoveNext")); - DECLARE_OVERRIDE_METHOD(Il2CppObject*, get_Current, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "get_Current")); - DECLARE_OVERRIDE_METHOD(void, Reset, il2cpp_utils::FindMethod("System.Collections", "IEnumerator", "Reset")); + DECLARE_CTOR(ctor, custom_types::Helpers::Coroutine * coro); + DECLARE_OVERRIDE_METHOD(bool, MoveNext, i2c::find_method({"System.Collections", "IEnumerator"}, "MoveNext")); + DECLARE_OVERRIDE_METHOD(Il2CppObject*, get_Current, i2c::find_method({"System.Collections", "IEnumerator"}, "get_Current")); + DECLARE_OVERRIDE_METHOD(void, Reset, i2c::find_method({"System.Collections", "IEnumerator"}, "Reset")); DECLARE_DTOR(Finalize); }; @@ -250,49 +204,39 @@ namespace custom_types::Helpers { /// @brief A wrapper pointer for coroutines, with conversion operators. /// @tparam The type of the coroutine to wrap in this instance. - template + template struct CoroP { constexpr CoroP(T* p) noexcept : ptr(p) {} constexpr CoroP(void* p) noexcept : ptr(reinterpret_cast(p)) {} CoroP(std::nullptr_t) = delete; - constexpr void* convert() const noexcept { - return (void*)ptr; - } - constexpr operator enumeratorT() const noexcept { - return (enumeratorT)ptr; - } - constexpr operator Wrapper() const noexcept { - return Wrapper((enumeratorT)ptr); - } - constexpr T* operator*() noexcept { - return ptr; - } - constexpr T* operator->() noexcept { - return ptr; - } + constexpr void* convert() const noexcept { return (void*) ptr; } + constexpr operator enumeratorT() const noexcept { return (enumeratorT) ptr; } + constexpr operator Wrapper() const noexcept { return Wrapper((enumeratorT) ptr); } + constexpr T* operator*() noexcept { return ptr; } + constexpr T* operator->() noexcept { return ptr; } - private: + private: T* ptr; }; - static_assert(il2cpp_utils::has_il2cpp_conversion>); - static_assert(il2cpp_utils::has_il2cpp_conversion>); + static_assert(i2c::type_check::wrapper_type>); + static_assert(i2c::type_check::wrapper_type>); /// @brief A helper type for creating custom coroutines C# from C++. /// See ResetableCoroutine and StandardCoroutine for more info. - struct __attribute__((visibility("default"))) CoroutineHelper { - private: + struct CUSTOM_TYPES_EXPORT CoroutineHelper { + private: static void EnsureCoroutines(); - public: + public: /// @brief Creates a new StandardCoroutine from the provided Coroutine instance, which is immediately rendered invalid. /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. + /// @tparam Manual If the coroutine should be manually created instead of with the GC. /// @param c The Coroutine instance to construct the instance with. /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(TArgs&&... c) { + template + static CoroP New(auto&&... c) { EnsureCoroutines(); - auto res = il2cpp_utils::New(new Coroutine(c...)); + auto res = i2c::new_ctor, Manual>(new Coroutine(c...)); if (!res) { throw CoroutineAllocationFailed(); } @@ -301,13 +245,13 @@ namespace custom_types::Helpers { /// @brief Creates a new StandardCoroutine from the provided Coroutine instance, which is immediately rendered invalid. /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. + /// @tparam Manual If the coroutine should be manually created instead of with the GC. /// @param c The Coroutine instance to construct the instance with. /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(Coroutine&& c) { + template + static CoroP New(Coroutine&& c) { EnsureCoroutines(); - auto res = il2cpp_utils::New(new Coroutine(std::move(c))); + auto res = i2c::new_ctor, Manual>(new Coroutine(std::move(c))); if (!res) { throw CoroutineAllocationFailed(); } @@ -316,13 +260,13 @@ namespace custom_types::Helpers { /// @brief Creates a new ResetableCoroutine from the provided Coroutine creator function, which is copied. /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. + /// @tparam Manual If the coroutine should be manually created instead of with the GC. /// @param func The CoroFuncType to construct the instance with. /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(CoroFuncType func) { + template + static CoroP New(CoroFuncType func) { EnsureCoroutines(); - auto res = il2cpp_utils::New(&func); + auto res = i2c::new_ctor, Manual>(&func); if (!res) { throw CoroutineAllocationFailed(); } @@ -332,77 +276,56 @@ namespace custom_types::Helpers { /// @brief Creates a new ResetableCoroutine from the provided Coroutine creator function and arguments used to invoke it. /// The arguments used to invoke this function are copied by value (be careful of references!) /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. - /// @tparam TArgs The arguments to provide to the creator function. + /// @tparam Manual If the coroutine should be manually created instead of with the GC. /// @param func The creator function to wrap. /// @param args The arguments to provide to the function. /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(std::function func, TArgs... args) { - return New(std::function([func, args...] { - return func(args...); - })); - } - - /// @brief Creates a new ResetableCoroutine from the provided Coroutine creator function and arguments used to invoke it. - /// The arguments used to invoke this function are copied by value (be careful of references!) - /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. - /// @tparam TArgs The arguments to provide to the creator function. - /// @param func The creator function to wrap. - /// @param args The arguments to provide to the function. - /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(function_ptr_t func, TArgs... args) { - return New(std::function([func, args...] { - return func(args...); - })); + template + static CoroP New(auto func, auto... args) { + return New(std::function([func, args...] { return func(args...); })); } /// @brief Creates a new ResetableCoroutine from the provided Coroutine creator instance function and arguments used to invoke it. /// The arguments used to invoke this function are copied by value (be careful of references!) /// This function will throw a ::custom_types::Helpers::CoroutineAllocationFailed exception on failure. - /// @tparam cType The creation type of the created coroutine. + /// @tparam Manual If the coroutine should be manually created instead of with the GC. /// @tparam I The instance type of the instance function /// @tparam TArgs The arguments to provide to the creator function. /// @param func The creator function to wrap. - /// @param instance The instance pointer to provide to the coroutine constructor. This instance must always exist when the returned ResetableCoroutine's Reset method is called. + /// @param instance The instance pointer to provide to the coroutine constructor. This instance must always exist when the returned + /// ResetableCoroutine's Reset method is called. /// @param args The arguments to provide to the function. /// @return The created coroutine instance, wrapped in a CoroP. - template - static inline CoroP New(Coroutine (I::*func)(TArgs...), I* instance, TArgs... args) { - return New(std::function([func, instance, args...] { - return std::function(func)(instance, args...); - })); + template + static CoroP New(auto func, auto* instance, auto... args) { + return New(std::function([func, instance, args...] { return std::invoke_r(func, instance, args...); })); } }; /// @brief Shorthand for creating a new coroutine auto new_coro(auto&&... args) { - return ::custom_types::Helpers::CoroutineHelper::New(std::forward(args)...); + return custom_types::Helpers::CoroutineHelper::New(std::forward(args)...); } } namespace std { - template + template void swap(custom_types::Helpers::generator& a, custom_types::Helpers::generator& b) { a.swap(b); } } +MARK_GEN_REF_T(custom_types::Helpers::CoroP); +MARK_REF_T(custom_types::Helpers::CoroP); +MARK_REF_T(custom_types::Helpers::CoroP); + template -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class> { +struct ::i2c::type_check::no_arg_class> { static inline Il2CppClass* get() { - il2cpp_functions::Init(); - static auto klass = il2cpp_utils::GetClassFromName("System.Collections", "IEnumerator"); - + static auto klass = get_class_from_name("System.Collections", "IEnumerator"); return klass; } }; -MARK_GEN_REF_T(custom_types::Helpers::CoroP); -MARK_REF_T(custom_types::Helpers::CoroP); -MARK_REF_T(custom_types::Helpers::CoroP); - -static_assert(il2cpp_utils::il2cpp_reference_type>); -static_assert(il2cpp_utils::il2cpp_reference_type>); +static_assert(i2c::type_check::ref_type>); +static_assert(i2c::type_check::ref_type>); diff --git a/shared/delegate.hpp b/shared/delegate.hpp index 19255e2..6029bc7 100644 --- a/shared/delegate.hpp +++ b/shared/delegate.hpp @@ -1,720 +1,646 @@ #pragma once -#include -#include "./_config.h" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" +#include "_config.h" #include "macros.hpp" +#include "beatsaber-hook/shared/arrayw.hpp" +#include "beatsaber-hook/shared/find.hpp" +#include "beatsaber-hook/shared/members.hpp" +#include "beatsaber-hook/shared/types.hpp" +struct MulticastDelegate : ::Il2CppDelegate { + ::ArrayW delegates; +}; namespace custom_types { + CUSTOM_TYPES_EXPORT int get_delegate_count(); -CUSTOM_TYPES_EXPORT int get_delegate_count(); - -template -using make_boxed_t = std::conditional_t::value, T*, T>; + template + using make_boxed_t = std::conditional_t<::i2c::type_check::value_type, T*, T>; -template -auto unbox_arg(T arg) { - if constexpr (std::is_pointer_v && il2cpp_utils::il2cpp_type_check::need_box>::value) { - return *arg; - } else { - return arg; - } -} - -/// @brief The wrapper for an invokable delegate without an existing context. -/// DO NOT ATTEMPT TO CREATE THIS YOURSELF! -/// @tparam R The return type of the function being called. -/// @tparam TArgs The argument types of the function being called. -template -struct DelegateWrapperStatic : Il2CppObject { - // CT Boilerplate - using ___TargetType = DelegateWrapperStatic; - constexpr static auto ___Base__Size = sizeof(Il2CppObject); - friend ::custom_types::Register; - - public: - struct ___TypeRegistration : ::custom_types::TypeRegistration { - ___TypeRegistration() { - ::custom_types::Register::AddType(this); - ::custom_types::logger.debug("Adding delegate type: {}", name()); - instance = this; - } - std::vector<::custom_types::FieldRegistrator*> const getFields() const override { - return {}; - } - std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { - return {}; - } - std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { - return { &___Invoke_MethodRegistrator, &___dtor_MethodRegistrator, &___InvokeUnboxed_MethodRegistrator }; - } - static inline char* st_fields; - char*& static_fields() override { - return st_fields; - } - size_t static_fields_size() const override { - return 0; + template + auto unbox_arg(T arg) { + if constexpr (std::is_pointer_v && ::i2c::type_check::value_type>) { + return *arg; + } else { + return arg; } - const char* name() const override { - if constexpr (!std::is_same_v) { - static std::string nm{ fmt::format("DelegateWrapper({} ret, {} args, {} sz) ({})", sizeof(RI), sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count()) }; - return nm.c_str(); - } else { - static std::string nm{ fmt::format("DelegateWrapper(void ret, {} args, {} sz) ({})", sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count()) }; - return nm.c_str(); + } + + /// @brief The wrapper for an invokable delegate without an existing context. + /// DO NOT ATTEMPT TO CREATE THIS YOURSELF! + /// @tparam R The return type of the function being called. + /// @tparam TArgs The argument types of the function being called. + template + struct DelegateWrapperStatic : Il2CppObject { + // CT Boilerplate + using ___TargetType = DelegateWrapperStatic; + constexpr static auto ___Base__Size = sizeof(Il2CppObject); + friend ::custom_types::Register; + + public: + struct ___TypeRegistration : ::custom_types::TypeRegistration { + ___TypeRegistration() { + ::custom_types::Register::AddType(this); + ::custom_types::logger.debug("Adding delegate type: {}", name()); + instance = this; } - } - constexpr const char* namespaze() const override { - return "CustomTypes"; - } - constexpr const char* dllName() const override { - return "CustomTypes.dll"; - } - Il2CppClass* baseType() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_functions::defaults->object_class; - } - std::vector const interfaces() const override { - return {}; - } - constexpr Il2CppTypeEnum typeEnum() const override { - return IL2CPP_TYPE_CLASS; - } - constexpr uint32_t typeFlags() const override { - return 0; - } - static inline Il2CppClass* klass_ptr; - Il2CppClass*& klass() const override { - return klass_ptr; - } - constexpr size_t size() const override { - return sizeof(___TargetType); - } - TypeRegistration* customBase() const override { - return nullptr; - } - bool initialized() const override { - return init; - } - void setInitialized() const override { - init = true; - } - static inline bool init = false; - static inline TypeRegistration* instance; - static TypeRegistration* get() { - return instance; - } - }; + std::vector<::custom_types::FieldRegistrator*> const getFields() const override { return {}; } + std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { return {}; } + std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { + return {&___Invoke_MethodRegistrator, &___dtor_MethodRegistrator, &___InvokeUnboxed_MethodRegistrator}; + } + static inline char* st_fields; + char*& static_fields() override { return st_fields; } + size_t static_fields_size() const override { return 0; } + char const* name() const override { + if constexpr (!std::is_same_v) { + static std::string nm{fmt::format( + "DelegateWrapper({} ret, {} args, {} sz) ({})", + sizeof(RI), + sizeof...(TArgsI), + sizeof(___TargetType), + custom_types::get_delegate_count() + )}; + return nm.c_str(); + } else { + static std::string nm{fmt::format( + "DelegateWrapper(void ret, {} args, {} sz) ({})", sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count() + )}; + return nm.c_str(); + } + } + constexpr char const* namespaze() const override { return "CustomTypes"; } + constexpr char const* dllName() const override { return "CustomTypes.dll"; } + Il2CppClass* baseType() const override { + ::i2c::functions::initialize(); + return ::i2c::functions::defaults->object_class; + } + std::vector const interfaces() const override { return {}; } + constexpr Il2CppTypeEnum typeEnum() const override { return IL2CPP_TYPE_CLASS; } + constexpr uint32_t typeFlags() const override { return 0; } + static inline Il2CppClass* klass_ptr; + Il2CppClass*& klass() const override { return klass_ptr; } + constexpr size_t size() const override { return sizeof(___TargetType); } + TypeRegistration* customBase() const override { return nullptr; } + bool initialized() const override { return init; } + void setInitialized() const override { init = true; } + static inline bool init = false; + static inline TypeRegistration* instance; + static TypeRegistration* get() { return instance; } + }; + + public: + static inline ___TypeRegistration __registration_instance_DelegateWrapperStatic; + // Disallow creation + DelegateWrapperStatic() = delete; + // The (potentially capturing) function to invoke + std::function wrappedFunc; + + // The invoke method that wraps the delegate call + static RI Invoke(DelegateWrapperStatic* instance, make_boxed_t... args); + static RI InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args); + + private: + struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "Invoke"; } + constexpr char const* csharpName() const override { return "Invoke"; } + int flags() const override { return METHOD_ATTRIBUTE_STATIC; } + MethodInfo const* virtualMethod() const override { return nullptr; } + Il2CppType const* returnType() const override { return ::i2c::type_of(); } + std::vector params() const override { + ::i2c::functions::initialize(); + return {::i2c::functions::class_get_type(___TypeRegistration::klass_ptr), ::i2c::type_of()...}; + } + uint8_t params_size() const override { return sizeof...(TArgsI) + 1; } + Il2CppMethodPointer methodPointer() const override { return reinterpret_cast(&___TargetType::Invoke); } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; + + struct ___MethodRegistrator_InvokeUnboxed : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "InvokeUnboxed"; } + constexpr char const* csharpName() const override { return "InvokeUnboxed"; } + int flags() const override { return METHOD_ATTRIBUTE_STATIC; } + MethodInfo const* virtualMethod() const override { return nullptr; } + Il2CppType const* returnType() const override { + ::i2c::functions::initialize(); + return ::i2c::type_of(); + } + std::vector params() const override { + ::i2c::functions::initialize(); + return {::i2c::functions::class_get_type(___TypeRegistration::klass_ptr), (::i2c::type_of())...}; + } + uint8_t params_size() const override { return sizeof...(TArgsI) + 1; } + Il2CppMethodPointer methodPointer() const override { return reinterpret_cast(&___TargetType::InvokeUnboxed); } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; + + public: + static inline ___MethodRegistrator_Invoke ___Invoke_MethodRegistrator; + static inline ___MethodRegistrator_InvokeUnboxed ___InvokeUnboxed_MethodRegistrator; + + public: + void dtor(); + + private: + template + struct ___MethodRegistrator_dtor; + template + struct ___MethodRegistrator_dtor : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "dtor"; } + constexpr char const* csharpName() const override { return "dtor"; } + int flags() const override { return METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG; } + MethodInfo const* virtualMethod() const override { return ::i2c::find_method({"System", "Object"}, {"Finalize", 0}); } + Il2CppType const* returnType() const override { + ::i2c::functions::initialize(); + return &::i2c::functions::defaults->void_class->byval_arg; + } + std::vector params() const override { return {}; } + uint8_t params_size() const override { return 0; } + Il2CppMethodPointer methodPointer() const override { + static auto mptr = &DelegateWrapperStatic::dtor; + return reinterpret_cast(+[](DelegateWrapperStatic* inst) { (inst->*mptr)(); }); + } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; - public: - static inline ___TypeRegistration __registration_instance_DelegateWrapperStatic; - // Disallow creation - DelegateWrapperStatic() = delete; - // The (potentially capturing) function to invoke - std::function wrappedFunc; - - // The invoke method that wraps the delegate call - static RI Invoke(DelegateWrapperStatic* instance, make_boxed_t... args); - static RI InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args); - - private: - struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "Invoke"; - } - constexpr const char* csharpName() const override { - return "Invoke"; - } - int flags() const override { - return METHOD_ATTRIBUTE_STATIC; - } - const MethodInfo* virtualMethod() const override { - return nullptr; - } - const Il2CppType* returnType() const override { - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); - } - std::vector params() const override { - il2cpp_functions::Init(); - return { ::il2cpp_functions::class_get_type(___TypeRegistration::klass_ptr), (::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())... }; - } - uint8_t params_size() const override { - return sizeof...(TArgsI) + 1; - } - Il2CppMethodPointer methodPointer() const override { - return reinterpret_cast(&___TargetType::Invoke); - } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; - } + public: + static inline ___MethodRegistrator_dtor ___dtor_MethodRegistrator; }; - struct ___MethodRegistrator_InvokeUnboxed : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "InvokeUnboxed"; - } - constexpr const char* csharpName() const override { - return "InvokeUnboxed"; - } - int flags() const override { - return METHOD_ATTRIBUTE_STATIC; - } - const MethodInfo* virtualMethod() const override { - return nullptr; - } - const Il2CppType* returnType() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); - } - std::vector params() const override { - il2cpp_functions::Init(); - return { ::il2cpp_functions::class_get_type(___TypeRegistration::klass_ptr), (::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())... }; - } - uint8_t params_size() const override { - return sizeof...(TArgsI) + 1; - } - Il2CppMethodPointer methodPointer() const override { - return reinterpret_cast(&___TargetType::InvokeUnboxed); +} // namespace custom_types + +template +struct ::i2c::type_check::no_arg_class*> { + static inline Il2CppClass* get() { return custom_types::DelegateWrapperStatic::___TypeRegistration::klass_ptr; } +}; +MARK_GEN_REF_T_PTR(custom_types::DelegateWrapperStatic); + +namespace custom_types { + // Reopen it here + + /// @brief The invoker function for a delegate with a wrapped type. + /// @tparam R The return type of the function. + /// @tparam TArgs The argument types of the function. + /// @param instance The wrapped instance of this context function. + /// @param args The arguments to pass to this function. + /// @return The return from the wrapped function. + template + RI __attribute__((noinline)) + DelegateWrapperStatic::Invoke(DelegateWrapperStatic* instance, make_boxed_t... args) { + if (!instance || !instance->wrappedFunc) { + custom_types::logger.critical( + "Attempting to invoke delegate on a null or destroyed instance: {}, class: {} ({})", + fmt::ptr(instance), + fmt::ptr(___TypeRegistration::klass_ptr), + ___TypeRegistration::get()->name() + ); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(unbox_arg(args)...); + } else { + return instance->wrappedFunc(unbox_arg(args)...); } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; + } + + /// @brief The unboxed invoker function for a delegate with a wrapped type. + /// @tparam R The return type of the function. + /// @tparam TArgs The argument types of the function. + /// @param instance The wrapped instance of this context function. + /// @param args The arguments to pass to this function. + /// @return The return from the wrapped function. + template + RI __attribute__((noinline)) DelegateWrapperStatic::InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args) { + if (!instance || !instance->wrappedFunc) { + custom_types::logger.critical( + "Attempting to invoke delegate on a null or destroyed instance: {}, class: {} ({})", + fmt::ptr(instance), + fmt::ptr(___TypeRegistration::klass_ptr), + ___TypeRegistration::get()->name() + ); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(args...); + } else { + return instance->wrappedFunc(args...); } - }; + } - public: - static inline ___MethodRegistrator_Invoke ___Invoke_MethodRegistrator; - static inline ___MethodRegistrator_InvokeUnboxed ___InvokeUnboxed_MethodRegistrator; + template + void DelegateWrapperStatic::dtor() { + custom_types::logger.debug( + "Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name() + ); + this->~DelegateWrapperStatic(); + } - public: - void dtor(); + /// @brief The wrapper for an invokable delegate without an existing context. + /// DO NOT ATTEMPT TO CREATE THIS YOURSELF! + /// @tparam R The return type of the function being called. + /// @tparam TArgs The argument types of the function being called. + template + struct DelegateWrapperInstance : Il2CppObject { + // CT Boilerplate + using ___TargetType = DelegateWrapperInstance; + constexpr static auto ___Base__Size = sizeof(Il2CppObject); + friend ::custom_types::Register; + + public: + struct ___TypeRegistration : ::custom_types::TypeRegistration { + ___TypeRegistration() { + ::custom_types::Register::AddType(this); + ::custom_types::logger.debug("Adding instance delegate type: {}", name()); + instance = this; + } + std::vector<::custom_types::FieldRegistrator*> const getFields() const override { return {&___obj_FieldRegistrator}; } + std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { return {}; } + std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { + return {&___Invoke_MethodRegistrator, &___dtor_MethodRegistrator, &___InvokeUnboxed_MethodRegistrator}; + } + static inline char* st_fields; + char*& static_fields() override { return st_fields; } + size_t static_fields_size() const override { return 0; } + char const* name() const override { + if constexpr (!std::is_same_v) { + static std::string nm{fmt::format( + "DelegateWrapper({} ret, {} args, {} sz) ({})", + sizeof(RI), + sizeof...(TArgsI), + sizeof(___TargetType), + custom_types::get_delegate_count() + )}; + return nm.c_str(); + } else { + static std::string nm{fmt::format( + "DelegateWrapper(void ret, {} args, {} sz) ({})", sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count() + )}; + return nm.c_str(); + } + } + constexpr char const* namespaze() const override { return "CustomTypes"; } + constexpr char const* dllName() const override { return "CustomTypes.dll"; } + Il2CppClass* baseType() const override { + ::i2c::functions::initialize(); + return ::i2c::functions::defaults->object_class; + } + std::vector const interfaces() const override { return {}; } + constexpr Il2CppTypeEnum typeEnum() const override { return IL2CPP_TYPE_CLASS; } + constexpr uint32_t typeFlags() const override { return 0; } + static inline Il2CppClass* klass_ptr; + Il2CppClass*& klass() const override { return klass_ptr; } + constexpr std::size_t size() const override { return sizeof(___TargetType); } + constexpr TypeRegistration* customBase() const override { return nullptr; } + bool initialized() const override { return init; } + void setInitialized() const override { init = true; } + static inline bool init = false; + static inline TypeRegistration* instance; + static TypeRegistration* get() { return instance; } + }; + + public: + static inline ___TypeRegistration __registration_instance_DelegateWrapperInstance; + // Disallow creation + DelegateWrapperInstance() = delete; + + // The invoke method that wraps the delegate call + static RI Invoke(DelegateWrapperInstance* instance, make_boxed_t... args); + static RI InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args); + + private: + struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "Invoke"; } + constexpr char const* csharpName() const override { return "Invoke"; } + int flags() const override { return METHOD_ATTRIBUTE_STATIC; } + MethodInfo const* virtualMethod() const override { return nullptr; } + Il2CppType const* returnType() const override { + ::i2c::functions::initialize(); + return ::i2c::type_of(); + } + std::vector params() const override { + ::i2c::functions::initialize(); + return {::i2c::functions::class_get_type(___TypeRegistration::klass_ptr), ::i2c::type_of()...}; + } + uint8_t params_size() const override { return sizeof...(TArgsI) + 1; } + Il2CppMethodPointer methodPointer() const override { return reinterpret_cast(&___TargetType::Invoke); } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; + + struct ___MethodRegistrator_InvokeUnboxed : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "InvokeUnboxed"; } + constexpr char const* csharpName() const override { return "InvokeUnboxed"; } + int flags() const override { return METHOD_ATTRIBUTE_STATIC; } + MethodInfo const* virtualMethod() const override { return nullptr; } + Il2CppType const* returnType() const override { + ::i2c::functions::initialize(); + return ::i2c::type_of(); + } + std::vector params() const override { + ::i2c::functions::initialize(); + return {::i2c::functions::class_get_type(___TypeRegistration::klass_ptr), ::i2c::type_of()...}; + } + uint8_t params_size() const override { return sizeof...(TArgsI) + 1; } + Il2CppMethodPointer methodPointer() const override { return reinterpret_cast(&___TargetType::InvokeUnboxed); } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; + + public: + static inline ___MethodRegistrator_Invoke ___Invoke_MethodRegistrator; + static inline ___MethodRegistrator_InvokeUnboxed ___InvokeUnboxed_MethodRegistrator; + + private: + struct ___FieldRegistrator_obj : ::custom_types::FieldRegistrator { + constexpr char const* name() const override { return "obj"; } + Il2CppType const* type() const override { + ::i2c::functions::initialize(); + return ::i2c::type_of(); + } + constexpr uint16_t fieldAttributes() const override { return FIELD_ATTRIBUTE_PUBLIC; } + constexpr size_t size() const override { return sizeof(TI); } + int32_t offset() const override { return offsetof(___TargetType, obj); } + }; + static inline ___FieldRegistrator_obj ___obj_FieldRegistrator; + + public: + // The instance object + TI obj; + // The (potentially capturing) function to invoke + std::function wrappedFunc; + + void dtor(); + + private: + template + struct ___MethodRegistrator_dtor; + template + struct ___MethodRegistrator_dtor : ::custom_types::MethodRegistrator { + constexpr char const* name() const override { return "dtor"; } + constexpr char const* csharpName() const override { return "dtor"; } + int flags() const override { return METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG; } + MethodInfo const* virtualMethod() const override { return ::i2c::find_method({"System", "Object"}, {"Finalize", 0}); } + Il2CppType const* returnType() const override { + ::i2c::functions::initialize(); + return &i2c::functions::defaults->void_class->byval_arg; + } + std::vector params() const override { return {}; } + uint8_t params_size() const override { return 0; } + Il2CppMethodPointer methodPointer() const override { + static auto mptr = &___TargetType::dtor; + return reinterpret_cast(+[](___TargetType* inst) { (inst->*mptr)(); }); + } + InvokerMethod invoker() const override { return &::custom_types::invoker_creator::invoke; } + }; - private: - template - struct ___MethodRegistrator_dtor; - template - struct ___MethodRegistrator_dtor : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "dtor"; - } - constexpr const char* csharpName() const override { - return "dtor"; - } - int flags() const override { - return METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG; - } - const MethodInfo* virtualMethod() const override { - return ::il2cpp_utils::FindMethodUnsafe("System", "Object", "Finalize", 0); - } - const Il2CppType* returnType() const override { - ::il2cpp_functions::Init(); - return &il2cpp_functions::defaults->void_class->byval_arg; - } - std::vector params() const override { - return {}; - } - uint8_t params_size() const override { - return 0; - } - Il2CppMethodPointer methodPointer() const override { - static auto mptr = &DelegateWrapperStatic::dtor; - return reinterpret_cast(+[](DelegateWrapperStatic* inst) { (inst->*mptr)(); }); - } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; - } + public: + static inline ___MethodRegistrator_dtor ___dtor_MethodRegistrator; }; - public: - static inline ___MethodRegistrator_dtor ___dtor_MethodRegistrator; -}; - } // namespace custom_types -template -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class*> { - static inline Il2CppClass* get() { - return custom_types::DelegateWrapperStatic::___TypeRegistration::klass_ptr; - } -}; -template -struct ::il2cpp_utils::il2cpp_type_check::need_box> { - constexpr static bool value = false; +template +struct ::i2c::type_check::no_arg_class*> { + static inline Il2CppClass* get() { return custom_types::DelegateWrapperInstance::___TypeRegistration::klass_ptr; } }; +MARK_GEN_REF_T_PTR(custom_types::DelegateWrapperInstance); namespace custom_types { -// Reopen it here - -/// @brief The invoker function for a delegate with a wrapped type. -/// @tparam R The return type of the function. -/// @tparam TArgs The argument types of the function. -/// @param instance The wrapped instance of this context function. -/// @param args The arguments to pass to this function. -/// @return The return from the wrapped function. -template -RI __attribute__((noinline)) DelegateWrapperStatic::Invoke(DelegateWrapperStatic* instance, make_boxed_t... args) { - IL2CPP_CATCH_HANDLER( - if (!instance || !instance->wrappedFunc) { - custom_types::logger.critical("Attempting to invoke delegate on a null or destroyed instance: {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), - ___TypeRegistration::get()->name()); - } if constexpr (std::is_same_v) { instance->wrappedFunc(unbox_arg(args)...); } else { return instance->wrappedFunc(unbox_arg(args)...); }) -} - -/// @brief The unboxed invoker function for a delegate with a wrapped type. -/// @tparam R The return type of the function. -/// @tparam TArgs The argument types of the function. -/// @param instance The wrapped instance of this context function. -/// @param args The arguments to pass to this function. -/// @return The return from the wrapped function. -template -RI __attribute__((noinline)) DelegateWrapperStatic::InvokeUnboxed(DelegateWrapperStatic* instance, TArgsI... args) { - IL2CPP_CATCH_HANDLER( + // Reopen it here + + /// @brief The invoker function for a delegate with a wrapped type. + /// @tparam R The return type of the function. + /// @tparam TArgs The argument types of the function. + /// @param instance The wrapped instance of this context function. + /// @param args The arguments to pass to this function. + /// @return The return from the wrapped function. + template + RI __attribute__((noinline)) + DelegateWrapperInstance::Invoke(DelegateWrapperInstance* instance, make_boxed_t... args) { if (!instance || !instance->wrappedFunc) { - custom_types::logger.critical("Attempting to invoke delegate on a null or destroyed instance: {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), - ___TypeRegistration::get()->name()); - } if constexpr (std::is_same_v) { instance->wrappedFunc(args...); } else { return instance->wrappedFunc(args...); }) -} - -template -void DelegateWrapperStatic::dtor() { - custom_types::logger.debug("Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); - this->~DelegateWrapperStatic(); -} - -/// @brief The wrapper for an invokable delegate without an existing context. -/// DO NOT ATTEMPT TO CREATE THIS YOURSELF! -/// @tparam R The return type of the function being called. -/// @tparam TArgs The argument types of the function being called. -template -struct DelegateWrapperInstance : Il2CppObject { - // CT Boilerplate - using ___TargetType = DelegateWrapperInstance; - constexpr static auto ___Base__Size = sizeof(Il2CppObject); - friend ::custom_types::Register; - - public: - struct ___TypeRegistration : ::custom_types::TypeRegistration { - ___TypeRegistration() { - ::custom_types::Register::AddType(this); - ::custom_types::logger.debug("Adding instance delegate type: {}", name()); - instance = this; - } - std::vector<::custom_types::FieldRegistrator*> const getFields() const override { - return { &___obj_FieldRegistrator }; - } - std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { - return {}; - } - std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { - return { &___Invoke_MethodRegistrator, &___dtor_MethodRegistrator, &___InvokeUnboxed_MethodRegistrator }; - } - static inline char* st_fields; - char*& static_fields() override { - return st_fields; - } - size_t static_fields_size() const override { - return 0; - } - const char* name() const override { - if constexpr (!std::is_same_v) { - static std::string nm{ fmt::format("DelegateWrapper({} ret, {} args, {} sz) ({})", sizeof(RI), sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count()) }; - return nm.c_str(); - } else { - static std::string nm{ fmt::format("DelegateWrapper(void ret, {} args, {} sz) ({})", sizeof...(TArgsI), sizeof(___TargetType), custom_types::get_delegate_count()) }; - return nm.c_str(); - } - } - constexpr const char* namespaze() const override { - return "CustomTypes"; - } - constexpr const char* dllName() const override { - return "CustomTypes.dll"; - } - Il2CppClass* baseType() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_functions::defaults->object_class; - } - std::vector const interfaces() const override { - return {}; - } - constexpr Il2CppTypeEnum typeEnum() const override { - return IL2CPP_TYPE_CLASS; - } - constexpr uint32_t typeFlags() const override { - return 0; - } - static inline Il2CppClass* klass_ptr; - Il2CppClass*& klass() const override { - return klass_ptr; - } - constexpr std::size_t size() const override { - return sizeof(___TargetType); - } - constexpr TypeRegistration* customBase() const override { - return nullptr; + custom_types::logger.critical( + "Attempting to invoke instance delegate that is null or has been destroyed! {}, class: {} ({})", + fmt::ptr(instance), + fmt::ptr(___TypeRegistration::klass_ptr), + ___TypeRegistration::get()->name() + ); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(instance->obj, unbox_arg(args)...); + } else { + return instance->wrappedFunc(instance->obj, unbox_arg(args)...); } - bool initialized() const override { - return init; - } - void setInitialized() const override { - init = true; - } - static inline bool init = false; - static inline TypeRegistration* instance; - static TypeRegistration* get() { - return instance; - } - }; + } - public: - static inline ___TypeRegistration __registration_instance_DelegateWrapperInstance; - // Disallow creation - DelegateWrapperInstance() = delete; + /// @brief The unboxed invoker function for a delegate with a wrapped type. + /// @tparam R The return type of the function. + /// @tparam TArgs The argument types of the function. + /// @param instance The wrapped instance of this context function. + /// @param args The arguments to pass to this function. + /// @return The return from the wrapped function. + template + RI __attribute__((noinline)) + DelegateWrapperInstance::InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args) { + if (!instance || !instance->wrappedFunc) { + custom_types::logger.critical( + "Attempting to invoke instance delegate that is null or has been destroyed! {}, class: {} ({})", + fmt::ptr(instance), + fmt::ptr(___TypeRegistration::klass_ptr), + ___TypeRegistration::get()->name() + ); + } + if constexpr (std::is_same_v) { + instance->wrappedFunc(instance->obj, args...); + } else { + return instance->wrappedFunc(instance->obj, args...); + } + } - // The invoke method that wraps the delegate call - static RI Invoke(DelegateWrapperInstance* instance, make_boxed_t... args); - static RI InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args); + template + void DelegateWrapperInstance::dtor() { + custom_types::logger.debug( + "Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name() + ); + this->~DelegateWrapperInstance(); + } - private: - struct ___MethodRegistrator_Invoke : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "Invoke"; - } - constexpr const char* csharpName() const override { - return "Invoke"; - } - int flags() const override { - return METHOD_ATTRIBUTE_STATIC; - } - const MethodInfo* virtualMethod() const override { - return nullptr; - } - const Il2CppType* returnType() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); - } - std::vector params() const override { - il2cpp_functions::Init(); - return { ::il2cpp_functions::class_get_type(___TypeRegistration::klass_ptr), (::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())... }; - } - uint8_t params_size() const override { - return sizeof...(TArgsI) + 1; - } - Il2CppMethodPointer methodPointer() const override { - return reinterpret_cast(&___TargetType::Invoke); - } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; + CUSTOM_TYPES_EXPORT void log_delegate(Il2CppDelegate* d); + + template + T MakeDelegate(Il2CppClass const* delegateClass, DelegateWrapperStatic* inst) { + ::i2c::functions::initialize(); + // TODO: We could still make this a native method info, but for now it's much much easier to just wrap it + // This will make delegates a little bit slower than just a pure native call, since it'll have to box the args + // and then unbox them (as necessary). + // But it should also make them much safer, hopefully solving all sorts of problems. + // This gets the first method (which should be our Invoke static method)'s MethodInfo*. + // It should already have been allocated, so get will cache it for us. + // We need to ensure static initialization of both the dtor method registrator + // and the invoke method registrator: + custom_types::logger.debug("Delegate dtor registrator: {}", fmt::ptr(DelegateWrapperStatic::___dtor_MethodRegistrator.get())); + + auto* delegate = reinterpret_cast(::i2c::functions::object_new(delegateClass)); + // find the ctor method that takes object, intptr + MethodInfo const* ctor_minfo = THROW_UNLESS( + ::custom_types::logger, + ::i2c::find_method( + const_cast(delegateClass), + {".ctor", {}, {i2c::type_of(), &::i2c::functions::defaults->int_class->this_arg}} + ) + ); + + // find the invoke method + MethodInfo const* invoke_minfo; + auto methodSpan = std::span(delegateClass->methods, delegateClass->method_count); + for (auto const& method : methodSpan) { + if (std::string_view(method->name) == "Invoke") { + custom_types::logger.debug("Found Invoke method: {}", fmt::ptr(method)); + invoke_minfo = method; + } } - }; - struct ___MethodRegistrator_InvokeUnboxed : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "InvokeUnboxed"; - } - constexpr const char* csharpName() const override { - return "InvokeUnboxed"; - } - int flags() const override { - return METHOD_ATTRIBUTE_STATIC; + MethodInfo* method; + if (invoke_minfo->has_full_generic_sharing_signature) { + // boxed + method = DelegateWrapperStatic::___Invoke_MethodRegistrator.get(); + } else { + // unboxed + method = DelegateWrapperStatic::___InvokeUnboxed_MethodRegistrator.get(); } - const MethodInfo* virtualMethod() const override { - return nullptr; - } - const Il2CppType* returnType() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); - } - std::vector params() const override { - il2cpp_functions::Init(); - return { ::il2cpp_functions::class_get_type(___TypeRegistration::klass_ptr), (::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())... }; - } - uint8_t params_size() const override { - return sizeof...(TArgsI) + 1; - } - Il2CppMethodPointer methodPointer() const override { - return reinterpret_cast(&___TargetType::InvokeUnboxed); - } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; - } - }; - public: - static inline ___MethodRegistrator_Invoke ___Invoke_MethodRegistrator; - static inline ___MethodRegistrator_InvokeUnboxed ___InvokeUnboxed_MethodRegistrator; + CRASH_UNLESS(::i2c::run_method>(delegate, ctor_minfo, inst, (void*) &method)); - private: - struct ___FieldRegistrator_obj : ::custom_types::FieldRegistrator { - constexpr const char* name() const override { - return "obj"; - } - const Il2CppType* type() const override { - ::il2cpp_functions::Init(); - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); - } - constexpr uint16_t fieldAttributes() const override { - return FIELD_ATTRIBUTE_PUBLIC; - } - constexpr size_t size() const override { - return sizeof(TI); - } - int32_t offset() const override { - return offsetof(___TargetType, obj); + if (invoke_minfo->has_full_generic_sharing_signature) { + delegate->method_ptr = (void (*)()) DelegateWrapperStatic::Invoke; + delegate->invoke_impl = (void (*)()) DelegateWrapperStatic::Invoke; + } else { + delegate->method_ptr = (void (*)()) DelegateWrapperStatic::InvokeUnboxed; + delegate->invoke_impl = (void (*)()) DelegateWrapperStatic::InvokeUnboxed; } - }; - static inline ___FieldRegistrator_obj ___obj_FieldRegistrator; - public: - // The instance object - TI obj; - // The (potentially capturing) function to invoke - std::function wrappedFunc; + delegate->invoke_impl_this = (Il2CppObject*) inst; - void dtor(); + custom_types::logger.debug( + "Created delegate: {} ({}), for instance: {} with MethodInfo*: {}", + fmt::ptr(delegate), + fmt::ptr(delegateClass), + fmt::ptr(inst), + fmt::ptr(method) + ); + log_delegate(delegate); + return reinterpret_cast(delegate); + } - private: - template - struct ___MethodRegistrator_dtor; - template - struct ___MethodRegistrator_dtor : ::custom_types::MethodRegistrator { - constexpr const char* name() const override { - return "dtor"; - } - constexpr const char* csharpName() const override { - return "dtor"; - } - int flags() const override { - return METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG; - } - const MethodInfo* virtualMethod() const override { - return ::il2cpp_utils::FindMethodUnsafe("System", "Object", "Finalize", 0); - } - const Il2CppType* returnType() const override { - ::il2cpp_functions::Init(); - return &il2cpp_functions::defaults->void_class->byval_arg; - } - std::vector params() const override { - return {}; - } - uint8_t params_size() const override { - return 0; - } - Il2CppMethodPointer methodPointer() const override { - static auto mptr = &___TargetType::dtor; - return reinterpret_cast(+[](___TargetType* inst) { (inst->*mptr)(); }); - } - InvokerMethod invoker() const override { - return &::custom_types::invoker_creator::invoke; + template + T MakeDelegate(Il2CppClass const* delegateClass, DelegateWrapperInstance* inst) { + ::i2c::functions::initialize(); + custom_types::logger.debug( + "Delegate instance dtor registrator: {}", fmt::ptr(DelegateWrapperInstance::___dtor_MethodRegistrator.get()) + ); + + auto* delegate = reinterpret_cast(::i2c::functions::object_new(delegateClass)); + // find the ctor method that takes object, intptr + MethodInfo const* ctor_minfo = THROW_UNLESS( + ::custom_types::logger, + ::i2c::find_method( + const_cast(delegateClass), + {".ctor", {}, {i2c::type_of(), &::i2c::functions::defaults->int_class->this_arg}} + ) + ); + + // find the invoke method + MethodInfo const* invoke_minfo; + auto methodSpan = std::span(delegateClass->methods, delegateClass->method_count); + for (auto const& method : methodSpan) { + if (std::string_view(method->name) == "Invoke") { + custom_types::logger.debug("Found Invoke method: {}", fmt::ptr(method)); + invoke_minfo = method; + } } - }; - public: - static inline ___MethodRegistrator_dtor ___dtor_MethodRegistrator; -}; + MethodInfo* method; + if (invoke_minfo->has_full_generic_sharing_signature) { + // boxed + method = DelegateWrapperInstance::___Invoke_MethodRegistrator.get(); + } else { + // unboxed + method = DelegateWrapperInstance::___InvokeUnboxed_MethodRegistrator.get(); + } -} // namespace custom_types + CRASH_UNLESS(::i2c::run_method>(delegate, ctor_minfo, inst, (void*) &method)); -template -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class*> { - static inline Il2CppClass* get() { - return custom_types::DelegateWrapperInstance::___TypeRegistration::klass_ptr; - } -}; -template -struct ::il2cpp_utils::il2cpp_type_check::need_box> { - constexpr static bool value = false; -}; - -namespace custom_types { -// Reopen it here - -/// @brief The invoker function for a delegate with a wrapped type. -/// @tparam R The return type of the function. -/// @tparam TArgs The argument types of the function. -/// @param instance The wrapped instance of this context function. -/// @param args The arguments to pass to this function. -/// @return The return from the wrapped function. -template -RI __attribute__((noinline)) DelegateWrapperInstance::Invoke(DelegateWrapperInstance* instance, make_boxed_t... args) { - IL2CPP_CATCH_HANDLER( - if (!instance || !instance->wrappedFunc) { - custom_types::logger.critical("Attempting to invoke instance delegate that is null or has been destroyed! {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), - ___TypeRegistration::get()->name()); - } if constexpr (std::is_same_v) { instance->wrappedFunc(instance->obj, unbox_arg(args)...); } else { return instance->wrappedFunc(instance->obj, unbox_arg(args)...); }) -} - -/// @brief The unboxed invoker function for a delegate with a wrapped type. -/// @tparam R The return type of the function. -/// @tparam TArgs The argument types of the function. -/// @param instance The wrapped instance of this context function. -/// @param args The arguments to pass to this function. -/// @return The return from the wrapped function. -template -RI __attribute__((noinline)) DelegateWrapperInstance::InvokeUnboxed(DelegateWrapperInstance* instance, TArgsI... args) { - IL2CPP_CATCH_HANDLER( - if (!instance || !instance->wrappedFunc) { - custom_types::logger.critical("Attempting to invoke instance delegate that is null or has been destroyed! {}, class: {} ({})", fmt::ptr(instance), fmt::ptr(___TypeRegistration::klass_ptr), - ___TypeRegistration::get()->name()); - } if constexpr (std::is_same_v) { instance->wrappedFunc(instance->obj, args...); } else { return instance->wrappedFunc(instance->obj, args...); }) -} - -template -void DelegateWrapperInstance::dtor() { - custom_types::logger.debug("Destroying delegate: {}, class: {} ({})", fmt::ptr(this), fmt::ptr(___TypeRegistration::klass_ptr), ___TypeRegistration::get()->name()); - this->~DelegateWrapperInstance(); -} - -CUSTOM_TYPES_EXPORT void log_delegate(Il2CppDelegate* d); - -template -T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperStatic* inst) { - // TODO: We could still make this a native method info, but for now it's much much easier to just wrap it - // This will make delegates a little bit slower than just a pure native call, since it'll have to box the args - // and then unbox them (as necessary). - // But it should also make them much safer, hopefully solving all sorts of problems. - // This gets the first method (which should be our Invoke static method)'s MethodInfo*. - // It should already have been allocated, so get will cache it for us. - // We need to ensure static initialization of both the dtor method registrator - // and the invoke method registrator: - custom_types::logger.debug("Delegate dtor registrator: {}", fmt::ptr(DelegateWrapperStatic::___dtor_MethodRegistrator.get())); - - auto* delegate = reinterpret_cast(il2cpp_functions::object_new(delegateClass)); - // find the ctor method that takes object, intptr - const MethodInfo* ctor_minfo = THROW_UNLESS( - il2cpp_utils::FindMethod(const_cast(delegateClass), ".ctor", std::array{}, - std::array{ il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(), &il2cpp_functions::defaults->int_class->this_arg })); - - // find the invoke method - const MethodInfo* invoke_minfo; - auto methodSpan = std::span(delegateClass->methods, delegateClass->method_count); - for (auto const& method : methodSpan) { - // I hate C APIs - if (strcmp(method->name, "Invoke") == 0) { - custom_types::logger.debug("Found Invoke method: {}", fmt::ptr(method)); - invoke_minfo = method; + if (invoke_minfo->has_full_generic_sharing_signature) { + delegate->method_ptr = (void (*)()) DelegateWrapperInstance::Invoke; + delegate->invoke_impl = (void (*)()) DelegateWrapperInstance::Invoke; + } else { + delegate->method_ptr = (void (*)()) DelegateWrapperInstance::InvokeUnboxed; + delegate->invoke_impl = (void (*)()) DelegateWrapperInstance::InvokeUnboxed; } - } - MethodInfo* method; - if (invoke_minfo->has_full_generic_sharing_signature) { - // boxed - method = DelegateWrapperStatic::___Invoke_MethodRegistrator.get(); - } else { - // unboxed - method = DelegateWrapperStatic::___InvokeUnboxed_MethodRegistrator.get(); - } - - CRASH_UNLESS(il2cpp_utils::RunMethodOpt(delegate, ctor_minfo, inst, (void*)&method)); + delegate->invoke_impl_this = (Il2CppObject*) inst; - if (invoke_minfo->has_full_generic_sharing_signature) { - delegate->method_ptr = (void (*)())DelegateWrapperStatic::Invoke; - delegate->invoke_impl = (void (*)())DelegateWrapperStatic::Invoke; - } else { - delegate->method_ptr = (void (*)())DelegateWrapperStatic::InvokeUnboxed; - delegate->invoke_impl = (void (*)())DelegateWrapperStatic::InvokeUnboxed; + custom_types::logger.debug( + "Created instance delegate: {} ({}), for instance: {} with MethodInfo*: {}", + fmt::ptr(delegate), + fmt::ptr(delegateClass), + fmt::ptr(inst), + fmt::ptr(method) + ); + log_delegate(delegate); + return reinterpret_cast(delegate); } - delegate->invoke_impl_this = (Il2CppObject*)inst; - - custom_types::logger.debug("Created delegate: {} ({}), for instance: {} with MethodInfo*: {}", fmt::ptr(delegate), fmt::ptr(delegateClass), fmt::ptr(inst), fmt::ptr(method)); - log_delegate(delegate); - return reinterpret_cast(delegate); -} - -template -T MakeDelegate(const Il2CppClass* delegateClass, DelegateWrapperInstance* inst) { - custom_types::logger.debug("Delegate instance dtor registrator: {}", fmt::ptr(DelegateWrapperInstance::___dtor_MethodRegistrator.get())); - - auto* delegate = reinterpret_cast(il2cpp_functions::object_new(delegateClass)); - // find the ctor method that takes object, intptr - const MethodInfo* ctor_minfo = THROW_UNLESS( - il2cpp_utils::FindMethod(const_cast(delegateClass), ".ctor", std::array{}, - std::array{ il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(), &il2cpp_functions::defaults->int_class->this_arg })); - - // find the invoke method - const MethodInfo* invoke_minfo; - auto methodSpan = std::span(delegateClass->methods, delegateClass->method_count); - for (auto const& method : methodSpan) { - // I hate C APIs - if (strcmp(method->name, "Invoke") == 0) { - custom_types::logger.debug("Found Invoke method: {}", fmt::ptr(method)); - invoke_minfo = method; - } + /// @brief Makes a delegate wrapping a context function (such as a context lambda). + /// @tparam T The type to return. + /// @tparam I The instance object to provide to this delegate. + /// @tparam R The return type of the delegate. + /// @tparam TArgs The arguments of the delegate. + /// @param delegateClass The Il2CppClass* of the delegate to create. + /// @param instance The (move constructible) instance reference to provide to the delegate. This instance is moved and will no longer be valid. + /// @param f The function to invoke with the delegate. + /// @return The created delegate. + template + T MakeDelegate(Il2CppClass const* delegateClass, std::function const& f) { + ::i2c::functions::initialize(); + // NOTE: This static field MUST be used in order for it to be instantiated within the generic, thus, it is important to NOT remove this log + custom_types::logger.debug( + "Type registration for delegate being created (forcibly instantiated): {}", + DelegateWrapperStatic::__registration_instance_DelegateWrapperStatic.name() + ); + auto* wrapperInstance = reinterpret_cast*>( + ::i2c::functions::object_new(DelegateWrapperStatic::___TypeRegistration::klass_ptr) + ); + wrapperInstance->wrappedFunc = f; + return custom_types::MakeDelegate(delegateClass, wrapperInstance); } - MethodInfo* method; - if (invoke_minfo->has_full_generic_sharing_signature) { - // boxed - method = DelegateWrapperInstance::___Invoke_MethodRegistrator.get(); - } else { - // unboxed - method = DelegateWrapperInstance::___InvokeUnboxed_MethodRegistrator.get(); + /// @brief Makes a delegate wrapping a context function (such as a context lambda). + /// @tparam T The type to return. + /// @tparam I The instance object to provide to this delegate. + /// @tparam R The return type of the delegate. + /// @tparam TArgs The arguments of the delegate. + /// @param instance The (move constructible) instance reference to provide to the delegate. This instance is moved and will no longer be valid. + /// @param f The function to invoke with the delegate. + /// @return The created delegate. + template + T MakeDelegate(std::function const& f) { + return MakeDelegate(::i2c::class_of(), f); } - CRASH_UNLESS(il2cpp_utils::RunMethodOpt(delegate, ctor_minfo, inst, (void*)&method)); - - if (invoke_minfo->has_full_generic_sharing_signature) { - delegate->method_ptr = (void (*)())DelegateWrapperInstance::Invoke; - delegate->invoke_impl = (void (*)())DelegateWrapperInstance::Invoke; - } else { - delegate->method_ptr = (void (*)())DelegateWrapperInstance::InvokeUnboxed; - delegate->invoke_impl = (void (*)())DelegateWrapperInstance::InvokeUnboxed; + template + // TODO: Requires that I has a classof + T MakeDelegate(Il2CppClass const* delegateClass, I obj, std::function const& f) { + ::i2c::functions::initialize(); + custom_types::logger.debug( + "Type registration for delegate being created (forcibly instantiated): {}", + DelegateWrapperInstance::__registration_instance_DelegateWrapperInstance.name() + ); + auto* wrapperInstance = reinterpret_cast*>( + ::i2c::functions::object_new(DelegateWrapperInstance::___TypeRegistration::klass_ptr) + ); + wrapperInstance->wrappedFunc = f; + wrapperInstance->obj = obj; + return custom_types::MakeDelegate(delegateClass, wrapperInstance); } - delegate->invoke_impl_this = (Il2CppObject*)inst; - - custom_types::logger.debug("Created instance delegate: {} ({}), for instance: {} with MethodInfo*: {}", fmt::ptr(delegate), fmt::ptr(delegateClass), fmt::ptr(inst), fmt::ptr(method)); - log_delegate(delegate); - return reinterpret_cast(delegate); -} - -/// @brief Makes a delegate wrapping a context function (such as a context lambda). -/// @tparam T The type to return. -/// @tparam I The instance object to provide to this delegate. -/// @tparam R The return type of the delegate. -/// @tparam TArgs The arguments of the delegate. -/// @param delegateClass The Il2CppClass* of the delegate to create. -/// @param instance The (move constructible) instance reference to provide to the delegate. This instance is moved and will no longer be valid. -/// @param f The function to invoke with the delegate. -/// @return The created delegate. -template -T MakeDelegate(const Il2CppClass* delegateClass, std::function const& f) { - il2cpp_functions::Init(); - // NOTE: This static field MUST be used in order for it to be instantiated within the generic, thus, it is important to NOT remove this log - custom_types::logger.debug("Type registration for delegate being created (forcibly instantiated): {}", DelegateWrapperStatic::__registration_instance_DelegateWrapperStatic.name()); - auto* wrapperInstance = reinterpret_cast*>(il2cpp_functions::object_new(DelegateWrapperStatic::___TypeRegistration::klass_ptr)); - wrapperInstance->wrappedFunc = f; - return custom_types::MakeDelegate(delegateClass, wrapperInstance); -} - -/// @brief Makes a delegate wrapping a context function (such as a context lambda). -/// @tparam T The type to return. -/// @tparam I The instance object to provide to this delegate. -/// @tparam R The return type of the delegate. -/// @tparam TArgs The arguments of the delegate. -/// @param instance The (move constructible) instance reference to provide to the delegate. This instance is moved and will no longer be valid. -/// @param f The function to invoke with the delegate. -/// @return The created delegate. -template -T MakeDelegate(std::function const& f) { - return MakeDelegate(classof(T), f); -} - -template -// TODO: Requires that I has a classof -T MakeDelegate(const Il2CppClass* delegateClass, I obj, std::function const& f) { - il2cpp_functions::Init(); - custom_types::logger.debug("Type registration for delegate being created (forcibly instantiated): {}", - DelegateWrapperInstance::__registration_instance_DelegateWrapperInstance.name()); - auto* wrapperInstance = reinterpret_cast*>(il2cpp_functions::object_new(DelegateWrapperInstance::___TypeRegistration::klass_ptr)); - wrapperInstance->wrappedFunc = f; - wrapperInstance->obj = obj; - return custom_types::MakeDelegate(delegateClass, wrapperInstance); -} - -template -T MakeDelegate(I obj, std::function const& f) { - return MakeDelegate(classof(T), obj, f); -} + template + T MakeDelegate(I obj, std::function const& f) { + return MakeDelegate(::i2c::class_of(), obj, f); + } } // namespace custom_types diff --git a/shared/logging.hpp b/shared/logging.hpp index 2d8f9f4..b3dbc95 100644 --- a/shared/logging.hpp +++ b/shared/logging.hpp @@ -1,6 +1,8 @@ #pragma once -#include "beatsaber-hook/shared/utils/logging.hpp" -#include "./_config.h" + +#include "_config.h" +#include "beatsaber-hook/shared/config.hpp" + #include struct Il2CppAssemblyName; @@ -22,44 +24,44 @@ namespace custom_types { /// @brief Logs the provided Il2CppAssemblyName* with a provided label. /// @param name The Il2CppAssemblyName* to log all fields on. /// @param anameLabel The label for the logging. - CUSTOM_TYPES_EXPORT void logAname(const Il2CppAssemblyName* name, std::string_view anameLabel); + CUSTOM_TYPES_EXPORT void logAname(Il2CppAssemblyName const* name, std::string_view anameLabel); /// @brief Logs the provided Il2CppAssembly* /// @param assem The Il2CppAssembly* to log all fields on. - CUSTOM_TYPES_EXPORT void logAsm(const Il2CppAssembly* assem); + CUSTOM_TYPES_EXPORT void logAsm(Il2CppAssembly const* assem); /// @brief Logs the provided Il2CppCodeGenModule* with a provided label. /// @param module The Il2CppCodeGenModule* to log all fields on. /// @param s The label for the logging. - CUSTOM_TYPES_EXPORT void logCodegen(const Il2CppCodeGenModule* m, std::string_view s); + CUSTOM_TYPES_EXPORT void logCodegen(Il2CppCodeGenModule const* m, std::string_view s); /// @brief Logs the provided Il2CppImage* /// @param img The Il2CppImage* to log all fields on. - CUSTOM_TYPES_EXPORT void logImage(const Il2CppImage* img); + CUSTOM_TYPES_EXPORT void logImage(Il2CppImage const* img); /// @brief Logs the provided Il2CppType* with a provided label. /// @param t The Il2CppType* to log all fields on. /// @param s The label for the logging. - CUSTOM_TYPES_EXPORT void logType(const Il2CppType* t, std::string_view s); + CUSTOM_TYPES_EXPORT void logType(Il2CppType const* t, std::string_view s); /// @brief Logs the provided Il2CppClass*'s vtable - CUSTOM_TYPES_EXPORT void logVtable(const VirtualInvokeData* invokeData); + CUSTOM_TYPES_EXPORT void logVtable(VirtualInvokeData const* invokeData); /// @brief Logs various information about the fields of the provided Il2CppClass* - CUSTOM_TYPES_EXPORT void logFields(const Il2CppClass* klass); + CUSTOM_TYPES_EXPORT void logFields(Il2CppClass const* klass); /// @brief Logs the provided Il2CppRuntimeInterfaceOffsetPair - CUSTOM_TYPES_EXPORT void logInterfaceOffset(const Il2CppRuntimeInterfaceOffsetPair* pair); + CUSTOM_TYPES_EXPORT void logInterfaceOffset(Il2CppRuntimeInterfaceOffsetPair const* pair); /// @brief Logs the provided ParameterInfo* /// @param info The ParameterInfo* to log all fields on. - CUSTOM_TYPES_EXPORT void logParam(const Il2CppType* info, int index); + CUSTOM_TYPES_EXPORT void logParam(Il2CppType const* info, int index); /// @brief Logs the provided MethodInfo* /// @param info The MethodInfo* to log all fields on. - CUSTOM_TYPES_EXPORT void logMethod(const MethodInfo* info); + CUSTOM_TYPES_EXPORT void logMethod(MethodInfo const* info); /// @brief Logs the provided Il2CppClass* /// @param klass The Il2CppClass* to log all fields on. - CUSTOM_TYPES_EXPORT void logAll(const Il2CppClass* klass); + CUSTOM_TYPES_EXPORT void logAll(Il2CppClass const* klass); } diff --git a/shared/macro-types.hpp b/shared/macro-types.hpp index 05fdad7..1e52dff 100644 --- a/shared/macro-types.hpp +++ b/shared/macro-types.hpp @@ -1,33 +1,30 @@ #pragma once -#include "register.hpp" -#include "util.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" + +#include "beatsaber-hook/shared/api.hpp" namespace custom_types { - template + template struct TypeWrapperParent { using ___TargetType = T; using ___TypeRegistration = R; constexpr static auto ___Base__Size = baseSize; - constexpr static bool __IL2CPP_IS_VALUE_TYPE = typeEnum_ != Il2CppTypeEnum::IL2CPP_TYPE_CLASS; - static inline constexpr const int __IL2CPP_REFERENCE_TYPE_SIZE = sizeof(T); uint8_t _baseFields[baseSize]; TypeWrapperParent(TypeWrapperParent&&) = delete; TypeWrapperParent(TypeWrapperParent const&) = delete; - protected: + + protected: TypeWrapperParent() {}; }; - template + template struct TypeWrapperInheritanceParent : public baseT { using ___TargetType = T; using ___TypeRegistration = R; constexpr static auto ___Base__Size = sizeof(baseT); - constexpr static bool __IL2CPP_IS_VALUE_TYPE = typeEnum_ != Il2CppTypeEnum::IL2CPP_TYPE_CLASS; - static inline constexpr const int __IL2CPP_REFERENCE_TYPE_SIZE = sizeof(T); TypeWrapperInheritanceParent(TypeWrapperInheritanceParent&&) = delete; TypeWrapperInheritanceParent(TypeWrapperInheritanceParent const&) = delete; - protected: + + protected: TypeWrapperInheritanceParent() {}; }; } diff --git a/shared/macros.hpp b/shared/macros.hpp index 7420e35..4518d0d 100644 --- a/shared/macros.hpp +++ b/shared/macros.hpp @@ -1,14 +1,16 @@ #pragma once -#include "types.hpp" -#include "register.hpp" -#include -#include -#include -#include + +#include "_config.h" #include "macro-types.hpp" +#include "register.hpp" +#include "types.hpp" #include "util.hpp" -#include "beatsaber-hook/shared/utils/utils.h" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" +#include "beatsaber-hook/shared/members.hpp" +#include "beatsaber-hook/shared/types.hpp" +#include "beatsaber-hook/shared/utils.hpp" + +#include +#include // Explicit macros defined here are laid out all here, ctrl-f for a specific name to find its impls. @@ -44,6 +46,10 @@ #error "DECLARE_VALUE_CODEGEN is already defined! Undefine it before including macros.hpp!" #endif +#ifdef ___DECLARE_TYPE_WRAPPER_INTERNALS +#error "___DECLARE_TYPE_WRAPPER_INTERNALS is already defined! Undefine it before including macros.hpp!" +#endif + #ifdef ___DECLARE_TYPE_WRAPPER #error "___DECLARE_TYPE_WRAPPER is already defined! Undefine it before including macros.hpp!" #endif @@ -53,19 +59,14 @@ #endif #ifndef INTERFACE_NAME -#define INTERFACE_NAME(namespaze_, name_) ::custom_types::interface_helper +#define INTERFACE_NAME(namespaze_, name_) ::i2c::const_type #endif -#ifndef CUSTOM_TYPES_EXPORT_VISIBILITY -#define CUSTOM_TYPES_EXPORT_VISIBILITY __attribute__((visibility("default"))) -#endif - -// Helper macro for declaring classes with and without interfaces -#define ___DECLARE_TYPE_WRAPPER(namespaze_, name_, typeEnum_, baseNamespaze, baseName, baseSize, dllName_, flags_, ...) \ +#define ___DECLARE_TYPE_WRAPPER_INTERNALS(namespaze_, name_, typeEnum_, dllName_, flags_, baseKlass, baseCustom, ...) \ namespace namespaze_ { \ class name_; \ namespace __custom_types_internal { \ - struct CUSTOM_TYPES_EXPORT_VISIBILITY ___TypeRegistration_##name_ : ::custom_types::TypeRegistration { \ + struct CUSTOM_TYPES_EXPORT ___TypeRegistration_##name_ : ::custom_types::TypeRegistration { \ ___TypeRegistration_##name_() { \ ::custom_types::Register::AddType(this); \ instance = this; \ @@ -117,7 +118,7 @@ namespace namespaze_ { \ return dllName_; \ } \ Il2CppClass* baseType() const override { \ - return ::il2cpp_utils::GetClassFromName(baseNamespaze, baseName); \ + return baseKlass; \ } \ std::vector const interfaces() const override { \ return ::custom_types::ExtractClasses<__VA_ARGS__>(); \ @@ -134,7 +135,7 @@ namespace namespaze_ { \ } \ size_t size() const override; \ TypeRegistration* customBase() const override { \ - return nullptr; \ + return baseCustom; \ } \ bool initialized() const override { \ return init; \ @@ -151,123 +152,23 @@ namespace namespaze_ { \ } \ } \ template<> \ -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class { \ +struct ::i2c::type_check::no_arg_class { \ static inline Il2CppClass* get() { \ return namespaze_::__custom_types_internal::___TypeRegistration_##name_::klass_ptr; \ } \ }; \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::need_box { \ - constexpr static bool value = false; \ -}; \ -MARK_REF_PTR_T(namespaze_::name_); \ -class CUSTOM_TYPES_EXPORT_VISIBILITY namespaze_::name_ : \ +MARK_REF_T(namespaze_::name_*); + +// Helper macro for declaring classes with and without interfaces +#define ___DECLARE_TYPE_WRAPPER(namespaze_, name_, typeEnum_, baseNamespaze, baseName, baseSize, dllName_, flags_, ...) \ +___DECLARE_TYPE_WRAPPER_INTERNALS(namespaze_, name_, typeEnum_, dllName_, flags_, ::i2c::get_class_from_name(baseNamespaze, baseName), nullptr, __VA_ARGS__) \ +class CUSTOM_TYPES_EXPORT namespaze_::name_ : \ public ::custom_types::TypeWrapperParent // Helper for declaring classes with and without interfaces with explicit inheritance #define ___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze_, name_, typeEnum_, baseT, dllName_, flags_, baseCustom, ...) \ -namespace namespaze_ { \ - class name_; \ - namespace __custom_types_internal { \ - struct CUSTOM_TYPES_EXPORT_VISIBILITY ___TypeRegistration_##name_ : ::custom_types::TypeRegistration { \ - ___TypeRegistration_##name_() { \ - ::custom_types::Register::AddType(this); \ - instance = this; \ - } \ - static inline std::vector<::custom_types::FieldRegistrator*> fields; \ - std::vector<::custom_types::FieldRegistrator*> const getFields() const override { \ - return fields; \ - } \ - static void addField(::custom_types::FieldRegistrator* inst) { \ - fields.push_back(inst); \ - ::custom_types::logger.debug("Adding instance field: {}.{} new size: {}", #name_, inst->name(), fields.size()); \ - } \ - static inline std::vector<::custom_types::StaticFieldRegistrator*> staticFields; \ - std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { \ - return staticFields; \ - } \ - static void addStaticFieldInstance(::custom_types::StaticFieldRegistrator* inst) { \ - staticFields.push_back(inst); \ - ::custom_types::logger.debug("Adding static field: {}.{} new size: {}", #name_, inst->name(), staticFields.size()); \ - } \ - static inline std::vector<::custom_types::MethodRegistrator*> methods; \ - std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { \ - return methods; \ - } \ - static void addMethod(::custom_types::MethodRegistrator* inst) { \ - methods.push_back(inst); \ - ::custom_types::logger.debug("Adding method: {}.{} new size: {}", #name_, inst->name(), methods.size()); \ - } \ - static inline size_t staticFieldOffset; \ - static size_t addStaticField(size_t sz) { \ - auto tmp = staticFieldOffset; \ - staticFieldOffset += sz; \ - return tmp; \ - } \ - static char* st_fields; \ - char*& static_fields() override { \ - return st_fields; \ - } \ - size_t static_fields_size() const override { \ - return staticFieldOffset; \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - constexpr const char* namespaze() const override { \ - return #namespaze_; \ - } \ - constexpr const char* dllName() const override { \ - return dllName_; \ - } \ - Il2CppClass* baseType() const override { \ - auto klass = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); \ - if (!klass->initialized) il2cpp_functions::Class_Init(klass); \ - return klass; \ - } \ - std::vector const interfaces() const override { \ - return ::custom_types::ExtractClasses<__VA_ARGS__>(); \ - } \ - constexpr Il2CppTypeEnum typeEnum() const override { \ - return typeEnum_; \ - } \ - constexpr uint32_t typeFlags() const override { \ - return flags_; \ - } \ - static Il2CppClass* klass_ptr; \ - Il2CppClass*& klass() const override { \ - return klass_ptr; \ - } \ - size_t size() const override; \ - TypeRegistration* customBase() const override { \ - return baseCustom; \ - } \ - bool initialized() const override { \ - return init; \ - } \ - void setInitialized() const override { \ - init = true; \ - } \ - static bool init; \ - static TypeRegistration* instance; \ - static TypeRegistration* get() { \ - return instance; \ - } \ - }; \ - } \ -} \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class { \ - static inline Il2CppClass* get() { \ - return namespaze_::__custom_types_internal::___TypeRegistration_##name_::klass_ptr; \ - } \ -}; \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::need_box { \ - constexpr static bool value = false; \ -}; \ -MARK_REF_PTR_T(namespaze_::name_); \ -class CUSTOM_TYPES_EXPORT_VISIBILITY namespaze_::name_ : \ +___DECLARE_TYPE_WRAPPER_INTERNALS(namespaze_, name_, typeEnum_, dllName_, flags_, ::custom_types::inited_class_of(), baseCustom, __VA_ARGS__) \ +class CUSTOM_TYPES_EXPORT namespaze_::name_ : \ public ::custom_types::TypeWrapperInheritanceParent // Declares a class with the given namespace, name, base namespace, base name, and baseSize. @@ -378,11 +279,11 @@ ___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE #define DEFINE_TYPE(namespaze, name) \ ::custom_types::TypeRegistration* namespaze::name::___TypeRegistration::instance; \ -static namespaze::name::___TypeRegistration __registration_instance_##name; \ +static namespaze::name::___TypeRegistration CONCAT(__registration_instance_##name##_, __LINE__); \ char* namespaze::name::___TypeRegistration::st_fields; \ Il2CppClass* namespaze::name::___TypeRegistration::klass_ptr; \ bool namespaze::name::___TypeRegistration::init = false; \ -size_t namespaze::name::___TypeRegistration::size() const { return sizeof(namespaze::name); }; +size_t namespaze::name::___TypeRegistration::size() const { return sizeof(namespaze::name); } // TODO: Add a way of declaring abstract/interface types. // This requires messing with method slots even more than I do right now. @@ -423,10 +324,8 @@ struct ___FieldRegistrator_##name_ : ::custom_types::FieldRegistrator { \ return #name_; \ } \ const Il2CppType* type() const override { \ - ::il2cpp_functions::Init(); \ - auto klass = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get();\ - if (!klass->initialized) il2cpp_functions::Class_Init(klass);\ - return ::il2cpp_functions::class_get_type(klass); \ + auto klass = ::custom_types::inited_class_of(); \ + return ::i2c::functions::class_get_type(klass); \ } \ constexpr uint16_t fieldAttributes() const override { \ return flags_; \ @@ -453,14 +352,14 @@ static inline ___FieldRegistrator_##name_ ___##name_##_FieldRegistrator #define DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, visibility_) \ protected: \ -static inline custom_types::field_accessor ___##name_##_FieldAccessor; \ +static inline ::custom_types::field_accessor ___##name_##_FieldAccessor; \ visibility_: \ inline type_& __get_##name_() CT_FIELD_GET_EXCEPT { \ - CT_FIELD_ACCESS_CHECK(this); \ + if (!static_cast(this)) throw ::custom_types::NullAccessException(); \ return ___##name_##_FieldAccessor.read(this, ___##name_##_FieldRegistrator.offset()); \ } \ inline type_ const& __get_##name_() const CT_FIELD_GET_EXCEPT { \ - CT_FIELD_ACCESS_CHECK(this); \ + if (!static_cast(this)) throw ::custom_types::NullAccessException(); \ return ___##name_##_FieldAccessor.read(this, ___##name_##_FieldRegistrator.offset()); \ } \ inline void __set_##name_(type_ v) { ___##name_##_FieldAccessor.write(this, ___##name_##_FieldRegistrator.offset(), std::forward(v)); } @@ -543,8 +442,7 @@ struct ___StaticFieldRegistrator_##name_ : ::custom_types::StaticFieldRegistrato return #name_; \ } \ const Il2CppType* type() const override { \ - ::il2cpp_functions::Init(); \ - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); \ + return ::i2c::type_of(); \ } \ constexpr uint16_t fieldAttributes() const override { \ return FIELD_ATTRIBUTE_PUBLIC | FIELD_ATTRIBUTE_STATIC; \ @@ -561,7 +459,7 @@ public: \ static type_& name_() { \ CRASH_UNLESS(___TargetType::___TypeRegistration::st_fields); \ return *reinterpret_cast(&___TargetType::___TypeRegistration::st_fields[___##name_##_StaticFieldRegistrator.offset()]); \ -} \ +} #ifdef ___CREATE_INSTANCE_METHOD #error "___CREATE_INSTANCE_METHOD is already defined! Undefine it before including macros.hpp!" @@ -589,13 +487,12 @@ struct ___MethodRegistrator_##name_ : ::custom_types::Method return virtualData; \ } \ const Il2CppType* returnType() const override { \ - il2cpp_functions::Init(); \ - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); \ + ::i2c::functions::initialize(); \ + return ::i2c::type_of(); \ } \ std::vector params() const override { \ int32_t counter = 0; \ - il2cpp_functions::Init(); \ - return {(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())...}; \ + return {::i2c::type_of()...}; \ } \ uint8_t params_size() const override { \ return sizeof...(TArgs); \ @@ -626,16 +523,17 @@ ___CREATE_INSTANCE_METHOD(name, specialName, flags | METHOD_ATTRIBUTE_PUBLIC | M #define DECLARE_CTOR(name, ...) \ public: \ void name(__VA_ARGS__); \ -template<::il2cpp_utils::CreationType creationType = ::il2cpp_utils::CreationType::Temporary, class... TArgs> \ -static ___TargetType* New_ctor(TArgs&&... args) { \ - static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ +template \ +static ___TargetType* New_ctor(auto&&... args) { \ + static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ ___TargetType* obj; \ - if constexpr (creationType == ::il2cpp_utils::CreationType::Temporary) { \ - obj = reinterpret_cast<___TargetType*>(::il2cpp_functions::object_new(___TypeRegistration::klass_ptr)); \ + if constexpr (Manual) { \ + obj = reinterpret_cast<___TargetType*>(::i2c::create_manual(___TypeRegistration::klass_ptr)); \ } else { \ - obj = reinterpret_cast<___TargetType*>(::il2cpp_utils::createManual(___TypeRegistration::klass_ptr)); \ + ::i2c::functions::initialize(); \ + obj = reinterpret_cast<___TargetType*>(::i2c::functions::object_new(___TypeRegistration::klass_ptr)); \ } \ - obj->name(std::forward(args)...); \ + obj->name(std::forward(args)...); \ return obj; \ } \ ___CREATE_INSTANCE_METHOD(name, ".ctor", METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME, nullptr) @@ -665,7 +563,7 @@ ___CREATE_INSTANCE_METHOD(name, #name, (overridingMethodInfo->flags & ~METHOD_AT // Declare an overriding method with: return type, name, method it is implementing, parameters... // This macro matches the DECLARE_OVERRIDE_METHOD macro except it matches the method you pass in with the il2cpp type check. #define DECLARE_OVERRIDE_METHOD_MATCH(ret, name, overridingMethod, ...) \ -DECLARE_OVERRIDE_METHOD(ret, name, il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo() __VA_OPT__(, __VA_ARGS__)) +DECLARE_OVERRIDE_METHOD(ret, name, ::i2c::metadata_getter::method_info() __VA_OPT__(, __VA_ARGS__)) #ifdef DECLARE_DTOR #error "DECLARE_DTOR is already defined! Undefine it before including macros.hpp!" @@ -675,7 +573,7 @@ DECLARE_OVERRIDE_METHOD(ret, name, il2cpp_utils::il2cpp_type_check::MetadataGett #define DECLARE_DTOR(name) \ public: \ void name(); \ -___CREATE_INSTANCE_METHOD(name, #name, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::il2cpp_utils::FindMethod("System", "Object", "Finalize")) +___CREATE_INSTANCE_METHOD(name, #name, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::i2c::find_method({"System", "Object"}, "Finalize")) #ifdef DECLARE_SIMPLE_DTOR #error "DECLARE_SIMPLE_DTOR is already defined! Undefine it before including macros.hpp!" @@ -687,7 +585,7 @@ ___CREATE_INSTANCE_METHOD(name, #name, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBU void __Finalize() { \ this->~___TargetType(); \ } \ -___CREATE_INSTANCE_METHOD(__Finalize, "__Finalize", (::il2cpp_utils::FindMethod("System", "Object", "Finalize")->flags & ~METHOD_ATTRIBUTE_ABSTRACT) | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::il2cpp_utils::FindMethod("System", "Object", "Finalize")) +___CREATE_INSTANCE_METHOD(__Finalize, "__Finalize", (::i2c::find_method({"System", "Object"}, "Finalize")->flags & ~METHOD_ATTRIBUTE_ABSTRACT) | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::i2c::find_method({"System", "Object"}, "Finalize")) #ifdef INVOKE_CTOR #error "INVOKE_CTOR is already defined! Undefine it before including macros.hpp!" @@ -730,10 +628,10 @@ void __ctor() { \ INVOKE_CTOR(); \ INVOKE_BASE_CTOR(___TargetType::___TypeRegistration::get()->baseType()); \ } \ -template<::il2cpp_utils::CreationType creationType = ::il2cpp_utils::CreationType::Temporary, class... TArgs> \ -static ___TargetType* New_ctor(TArgs&&... args) { \ - static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ - return THROW_UNLESS(il2cpp_utils::New<___TargetType*, creationType>(___TypeRegistration::klass_ptr, std::forward(args)...)); \ +template \ +static ___TargetType* New_ctor(auto&&... args) { \ + static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ + return ::i2c::new_ctor<___TargetType*, Manual>(___TypeRegistration::klass_ptr, std::forward(args)...); \ } \ ___CREATE_INSTANCE_METHOD(__ctor, ".ctor", METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME, nullptr) @@ -763,13 +661,13 @@ struct ___MethodRegistrator_##name_ : ::custom_types::MethodReg return nullptr; \ } \ const Il2CppType* returnType() const override { \ - il2cpp_functions::Init(); \ - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get(); \ + ::i2c::functions::initialize(); \ + return ::i2c::type_of(); \ } \ std::vector params() const override { \ int32_t counter = 0; \ - il2cpp_functions::Init(); \ - return {(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type::get())...}; \ + ::i2c::functions::initialize(); \ + return {::i2c::type_of()...}; \ } \ uint8_t params_size() const override { \ return sizeof...(TArgs); \ @@ -792,12 +690,11 @@ public: \ static ret name(__VA_ARGS__); \ ___CREATE_STATIC_METHOD(name, #name, METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_STATIC) - namespace custom_types { - template + template void InvokeBaseCtor(Il2CppClass* klass, T* self, TArgs&&... args) { - static auto m = ::il2cpp_utils::FindMethod(klass, ".ctor", std::array{::il2cpp_utils::ExtractIndependentType()...}); - ::il2cpp_utils::RunMethodRethrow(self, m, args...); + static auto m = ::i2c::find_method(klass, {".ctor", {}, {::i2c::type_of()...}}); + ::i2c::run_method(self, m, args...); } } diff --git a/shared/register.hpp b/shared/register.hpp index 3ae1938..3e2dd9a 100644 --- a/shared/register.hpp +++ b/shared/register.hpp @@ -1,18 +1,17 @@ #pragma once + +#include "_config.h" +#include "logging.hpp" +#include "types.hpp" +#include "beatsaber-hook/shared/api.hpp" + #include #include #include -#include -#include -#include -#include "logging.hpp" -#include "types.hpp" -#include "./_config.h" -#include "beatsaber-hook/shared/utils/typedefs.h" template <> struct std::hash> { - size_t operator()(const std::pair& item) const noexcept { + size_t operator()(std::pair const& item) const noexcept { std::size_t h1 = std::hash{}(item.first); std::size_t h2 = std::hash{}(item.second); return h1 ^ (h2 << 1); @@ -20,136 +19,164 @@ struct std::hash> { }; namespace custom_types { -/// @class Public API for registering types -class Register { - friend TypeRegistration; + /// @class Public API for registering types + class Register { + friend TypeRegistration; - private: - CUSTOM_TYPES_EXPORT static std::unordered_map assembs; - CUSTOM_TYPES_EXPORT static std::unordered_map images; - CUSTOM_TYPES_EXPORT static std::shared_mutex assemblyMtx; - CUSTOM_TYPES_EXPORT static std::shared_mutex imageMtx; - CUSTOM_TYPES_EXPORT static std::mutex registrationMtx; - CUSTOM_TYPES_EXPORT static std::mutex classMappingMtx; - CUSTOM_TYPES_EXPORT static bool installed; + private: + CUSTOM_TYPES_EXPORT static std::unordered_map assembs; + CUSTOM_TYPES_EXPORT static std::unordered_map images; + CUSTOM_TYPES_EXPORT static std::shared_mutex assemblyMtx; + CUSTOM_TYPES_EXPORT static std::shared_mutex imageMtx; + CUSTOM_TYPES_EXPORT static std::mutex registrationMtx; + CUSTOM_TYPES_EXPORT static std::mutex classMappingMtx; + CUSTOM_TYPES_EXPORT static bool installed; - CUSTOM_TYPES_EXPORT static std::vector toRegister; - CUSTOM_TYPES_EXPORT static std::vector registeredTypes; - CUSTOM_TYPES_EXPORT static TypeDefinitionIndex typeIdx; + CUSTOM_TYPES_EXPORT static std::vector toRegister; + CUSTOM_TYPES_EXPORT static std::vector registeredTypes; + CUSTOM_TYPES_EXPORT static TypeDefinitionIndex typeIdx; - CUSTOM_TYPES_EXPORT static Il2CppAssembly* createAssembly(std::string_view name, Il2CppImage* img); - CUSTOM_TYPES_EXPORT static const Il2CppImage* createImage(std::string_view name); + CUSTOM_TYPES_EXPORT static Il2CppAssembly* createAssembly(std::string_view name, Il2CppImage* img); + CUSTOM_TYPES_EXPORT static const Il2CppImage* createImage(std::string_view name); - CUSTOM_TYPES_EXPORT static void EnsureHooks(); + CUSTOM_TYPES_EXPORT static void EnsureHooks(); - static void addToMapping(const TypeRegistration* itr) { - auto namePair = std::make_pair(std::string(itr->namespaze()), std::string(itr->name())); - if (!classMapping.contains(namePair)) { - custom_types::logger.debug("Adding new type to class mapping: {}", fmt::ptr(itr->klass())); - // Only add to the class mapping if the exact type created does not - // already exist. - classMapping.insert({ namePair, itr->klass() }); + static void addToMapping(TypeRegistration const* itr) { + auto namePair = std::make_pair(std::string(itr->namespaze()), std::string(itr->name())); + if (!classMapping.contains(namePair)) { + custom_types::logger.debug("Adding new type to class mapping: {}", fmt::ptr(itr->klass())); + // Only add to the class mapping if the exact type created does not + // already exist. + classMapping.insert({namePair, itr->klass()}); + } } - } - public: - CUSTOM_TYPES_EXPORT static std::unordered_map, Il2CppClass*> classMapping; - CUSTOM_TYPES_EXPORT static std::vector classes; - CUSTOM_TYPES_EXPORT static std::vector const& getTypes() { - return registeredTypes; - } - /// @brief Automatically registers all pending types. - /// To add a type to be registered, see: AddType - /// To get the list of all registered types, see: getTypes() - CUSTOM_TYPES_EXPORT static void AutoRegister() { - EnsureHooks(); - std::lock_guard lck(registrationMtx); + public: + CUSTOM_TYPES_EXPORT static std::unordered_map, Il2CppClass*> classMapping; + CUSTOM_TYPES_EXPORT static std::vector classes; + CUSTOM_TYPES_EXPORT static std::vector const& getTypes() { return registeredTypes; } + /// @brief Automatically registers all pending types. + /// To add a type to be registered, see: AddType + /// To get the list of all registered types, see: getTypes() + CUSTOM_TYPES_EXPORT static void AutoRegister() { + EnsureHooks(); + std::lock_guard lck(registrationMtx); - { - std::lock_guard lock(classMappingMtx); - for (auto itr : toRegister) { - itr->createClass(); - custom_types::logger.debug( - "Created class! registration: {}, {}::{} klass: {}, " - "{}::{}, image: {}", - fmt::ptr(itr), itr->namespaze(), itr->name(), fmt::ptr(itr->klass()), itr->klass()->namespaze, itr->klass()->name, fmt::ptr(itr->klass()->image)); - addToMapping(itr); + { + std::lock_guard lock(classMappingMtx); + for (auto itr : toRegister) { + itr->createClass(); + custom_types::logger.debug( + "Created class! registration: {}, {}::{} klass: {}, " + "{}::{}, image: {}", + fmt::ptr(itr), + itr->namespaze(), + itr->name(), + fmt::ptr(itr->klass()), + itr->klass()->namespaze, + itr->klass()->name, + fmt::ptr(itr->klass()->image) + ); + addToMapping(itr); + } } - } - for (auto actual : toRegister) { - // Populate fields, methods, etc. here - if (!actual->initialized()) { - actual->populateFields(); - actual->populateMethods(); - actual->setInitialized(); + for (auto actual : toRegister) { + // Populate fields, methods, etc. here + if (!actual->initialized()) { + actual->populateFields(); + actual->populateMethods(); + actual->setInitialized(); + } + custom_types::logger.debug( + "Registered registration: {}, {}::{} klass: {}, {}::{}, image: {}", + fmt::ptr(actual), + actual->namespaze(), + actual->name(), + fmt::ptr(actual->klass()), + actual->klass()->namespaze, + actual->klass()->name, + fmt::ptr(actual->klass()->image) + ); + registeredTypes.push_back(actual); } - custom_types::logger.debug("Registered registration: {}, {}::{} klass: {}, {}::{}, image: {}", fmt::ptr(actual), actual->namespaze(), actual->name(), fmt::ptr(actual->klass()), actual->klass()->namespaze, - actual->klass()->name, fmt::ptr(actual->klass()->image)); - registeredTypes.push_back(actual); + toRegister.clear(); } - toRegister.clear(); - } - CUSTOM_TYPES_EXPORT static void ExplicitRegister(std::unordered_set toAdd) { - EnsureHooks(); - std::lock_guard lck(registrationMtx); + CUSTOM_TYPES_EXPORT static void ExplicitRegister(std::unordered_set toAdd) { + EnsureHooks(); + std::lock_guard lck(registrationMtx); - // Remove everything we are explicitly registering from the collection of - // types that would be auto registered. This allows us to avoid duplicate - // registrations. - auto itr = toRegister.begin(); - while (itr != toRegister.end()) { - if (toAdd.contains(*itr)) { - itr = toRegister.erase(itr); - } else { - ++itr; + // Remove everything we are explicitly registering from the collection of + // types that would be auto registered. This allows us to avoid duplicate + // registrations. + auto itr = toRegister.begin(); + while (itr != toRegister.end()) { + if (toAdd.contains(*itr)) { + itr = toRegister.erase(itr); + } else { + ++itr; + } } - } - { - std::lock_guard lock(classMappingMtx); - for (auto itr : toAdd) { - itr->createClass(); - custom_types::logger.debug( - "Created class! registration: {}, {}::{} klass: {}, " - "{}::{}, image: {}", - fmt::ptr(itr), itr->namespaze(), itr->name(), fmt::ptr(itr->klass()), itr->klass()->namespaze, itr->klass()->name, fmt::ptr(itr->klass()->image)); - addToMapping(itr); + { + std::lock_guard lock(classMappingMtx); + for (auto itr : toAdd) { + itr->createClass(); + custom_types::logger.debug( + "Created class! registration: {}, {}::{} klass: {}, " + "{}::{}, image: {}", + fmt::ptr(itr), + itr->namespaze(), + itr->name(), + fmt::ptr(itr->klass()), + itr->klass()->namespaze, + itr->klass()->name, + fmt::ptr(itr->klass()->image) + ); + addToMapping(itr); + } } - } - for (auto actual : toAdd) { - if (!actual->initialized()) { - actual->populateFields(); - actual->populateMethods(); - actual->setInitialized(); + for (auto actual : toAdd) { + if (!actual->initialized()) { + actual->populateFields(); + actual->populateMethods(); + actual->setInitialized(); + } + custom_types::logger.debug( + "Registered registration: {}, {}::{} klass: {}, {}::{}, image: {}", + fmt::ptr(actual), + actual->namespaze(), + actual->name(), + fmt::ptr(actual->klass()), + actual->klass()->namespaze, + actual->klass()->name, + fmt::ptr(actual->klass()->image) + ); + registeredTypes.push_back(actual); } - custom_types::logger.debug("Registered registration: {}, {}::{} klass: {}, {}::{}, image: {}", fmt::ptr(actual), actual->namespaze(), actual->name(), fmt::ptr(actual->klass()), actual->klass()->namespaze, - actual->klass()->name, fmt::ptr(actual->klass()->image)); - registeredTypes.push_back(actual); } - } - CUSTOM_TYPES_EXPORT static void AddType(TypeRegistration* type) { - custom_types::logger.debug("Added instance to register: {}", fmt::ptr(type)); - toRegister.push_back(type); - } + CUSTOM_TYPES_EXPORT static void AddType(TypeRegistration* type) { + custom_types::logger.debug("Added instance to register: {}", fmt::ptr(type)); + toRegister.push_back(type); + } - /// @brief Unregisters all custom types, deleting all associated data. - /// You should not (realistically) have a need for this unless you find custom - /// types memory overhead to be significant. - CUSTOM_TYPES_EXPORT static void UnregisterAll() { - std::lock_guard lck(registrationMtx); - Register::typeIdx = kTypeDefinitionIndexInvalid; - for (auto itr : registeredTypes) { - itr->clear(); + /// @brief Unregisters all custom types, deleting all associated data. + /// You should not (realistically) have a need for this unless you find custom + /// types memory overhead to be significant. + CUSTOM_TYPES_EXPORT static void UnregisterAll() { + std::lock_guard lck(registrationMtx); + Register::typeIdx = kTypeDefinitionIndexInvalid; + for (auto itr : registeredTypes) { + itr->clear(); + } + registeredTypes.clear(); + // This will obviously cause all sorts of issues if these types are + // currently being used within il2cpp. + classes.clear(); + classMapping.clear(); + // We also need to correctly delete any created images or assemblies. + // These need to deleted in a very delicate way so as not to have any + // dangling references. } - registeredTypes.clear(); - // This will obviously cause all sorts of issues if these types are - // currently being used within il2cpp. - classes.clear(); - classMapping.clear(); - // We also need to correctly delete any created images or assemblies. - // These need to deleted in a very delicate way so as not to have any - // dangling references. - } -}; + }; } // namespace custom_types diff --git a/shared/types.hpp b/shared/types.hpp index 8cb5177..1a81e57 100644 --- a/shared/types.hpp +++ b/shared/types.hpp @@ -1,16 +1,8 @@ #pragma once -#include -#include "beatsaber-hook/shared/utils/utils.h" -#include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" -#include "beatsaber-hook/shared/utils/typedefs.h" -#include "beatsaber-hook/shared/utils/size-concepts.hpp" -#include -#include -#include -#include -#include + +#include "_config.h" #include "logging.hpp" +#include "beatsaber-hook/shared/types.hpp" struct Il2CppType; struct ParameterInfo; @@ -24,8 +16,8 @@ namespace custom_types { /// @brief An abstract type that holds the information required for an instance field. struct FieldRegistrator { - virtual const char* name() const = 0; - virtual const Il2CppType* type() const = 0; + virtual char const* name() const = 0; + virtual Il2CppType const* type() const = 0; virtual size_t size() const = 0; virtual uint16_t fieldAttributes() const = 0; virtual int32_t offset() const = 0; @@ -33,8 +25,8 @@ namespace custom_types { /// @brief An abstract type that holds the information required for a static field. struct StaticFieldRegistrator { - virtual const char* name() const = 0; - virtual const Il2CppType* type() const = 0; + virtual char const* name() const = 0; + virtual Il2CppType const* type() const = 0; virtual size_t size() const = 0; virtual uint16_t fieldAttributes() const = 0; virtual int32_t offset() const = 0; @@ -42,12 +34,12 @@ namespace custom_types { /// @brief An abstract type that holds the information required for a method. struct MethodRegistrator { - virtual const char* name() const = 0; - virtual const char* csharpName() const = 0; + virtual char const* name() const = 0; + virtual char const* csharpName() const = 0; virtual int flags() const = 0; - virtual const MethodInfo* virtualMethod() const = 0; - virtual const Il2CppType* returnType() const = 0; - virtual std::vector params() const = 0; + virtual MethodInfo const* virtualMethod() const = 0; + virtual Il2CppType const* returnType() const = 0; + virtual std::vector params() const = 0; virtual uint8_t params_size() const = 0; virtual Il2CppMethodPointer methodPointer() const = 0; virtual InvokerMethod invoker() const = 0; @@ -68,7 +60,7 @@ namespace custom_types { info->slot = kInvalidIl2CppMethodSlot; auto ps = params(); info->parameters_count = ps.size(); - auto* paramList = reinterpret_cast(calloc(ps.size(), sizeof(Il2CppType*))); + auto* paramList = reinterpret_cast(calloc(ps.size(), sizeof(Il2CppType*))); for (uint8_t pi = 0; pi < info->parameters_count; pi++) { paramList[pi] = ps[pi]; } @@ -80,7 +72,7 @@ namespace custom_types { /// @brief An abstract type that holds all the information required to register a type. /// The general rationale here is that each of these types contains everything necessary for creating a custom type /// The custom type will get generated and then needs to be assigned properly - struct __attribute__((visibility("default"))) TypeRegistration { + struct CUSTOM_TYPES_EXPORT TypeRegistration { friend Register; virtual std::vector const getFields() const = 0; @@ -89,9 +81,9 @@ namespace custom_types { virtual char*& static_fields() = 0; virtual size_t static_fields_size() const = 0; - virtual const char* name() const = 0; - virtual const char* namespaze() const = 0; - virtual const char* dllName() const = 0; + virtual char const* name() const = 0; + virtual char const* namespaze() const = 0; + virtual char const* dllName() const = 0; virtual Il2CppClass* baseType() const = 0; virtual std::vector const interfaces() const = 0; virtual Il2CppTypeEnum typeEnum() const = 0; @@ -107,62 +99,39 @@ namespace custom_types { void createClass(); void populateFields(); void populateMethods(); - bool checkVirtualsForMatch(MethodRegistrator* info, std::string_view namespaze, std::string_view name, std::string_view methodName, int paramCount); + bool + checkVirtualsForMatch(MethodRegistrator* info, std::string_view namespaze, std::string_view name, std::string_view methodName, int paramCount); /// @brief Populates the vtable and offsets vectors with information from the base type's vtable. void getVtable(std::vector& vtable, std::vector& offsets); void clear(); }; - #if __has_include() - #include - #include - template - constexpr bool has_get = requires(const T& t) { - t.get(); - }; - - #ifndef CUSTOM_TYPES_NO_CONCEPTS - #define CUSTOM_TYPES_USE_CONCEPTS - #endif - - #elif __has_include() - #include - template - using get_type = decltype(T::get()); - - template - constexpr bool has_get = std::experimental::is_detected_v; - - #else - #error No libraries for the implementation of "has_" anything available! - #endif - template constexpr std::false_type false_t{}; - /// @struct A helper structure for getting the name of the type. - template + template struct name_registry {}; /// @struct A helper structure for getting the invoker function of a method - template + template struct invoker_creator {}; /// @struct Type mapping struct - template struct type_tag {}; + template + struct type_tag {}; /// @struct A helper structure for converting parameter types to il2cpp types. - template + template struct parameter_converter; // Create a vector of ParameterInfo objects (good ol tail recursion) // 1 or more parameters - template + template struct parameter_converter { - static inline std::vector get() { - std::vector params; + static inline std::vector get() { + std::vector params; auto& info = params.emplace_back(); - il2cpp_functions::Init(); - const Il2CppType* type = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type

::get(); + ::i2c::functions::initialize(); + Il2CppType const* type = ::i2c::type_of

(); // Ignore name, it will be set when we iterate over all of them (param_1, param_2, etc.) // Ignore position, it will also be set on iteration. // TODO: Maybe some day we can actually use the parameters names themselves! @@ -170,7 +139,7 @@ namespace custom_types { custom_types::logger.warn("Failed to get type of parameter!"); } info = type; - for (const auto& q : parameter_converter::get()) { + for (auto const& q : parameter_converter::get()) { params.push_back(q); } return params; @@ -178,55 +147,51 @@ namespace custom_types { }; // 0 parameters - template + template struct parameter_converter { - static inline std::vector get() { - return std::vector(); - } + static inline std::vector get() { return std::vector(); } }; /// @struct Helper structure for unpacking and packing arguments/return types for invoker function creation struct arg_helper { - template + template static inline Q unpack_arg(void* arg, type_tag) { if constexpr (std::is_pointer_v) { return reinterpret_cast(arg); - } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { + } else if constexpr (::i2c::type_check::wrapper_ref_type) { return Q(arg); } else { return *reinterpret_cast(arg); } } - template + template static inline void* pack_result(Q&& thing) { if constexpr (std::is_pointer_v) { return reinterpret_cast(std::forward(thing)); - } - else if constexpr (il2cpp_utils::has_il2cpp_conversion) { + } else if constexpr (::i2c::type_check::wrapper_type) { return thing.convert(); - } - else { + } else { // We SHOULD simply be able to grab the class and box our result // Once boxed, we should just be able to return without any issue // I DO wonder if our invoke functions miss registration with GC... - auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); + auto* klass = ::i2c::class_of(); if (!klass) { custom_types::logger.critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); return nullptr; } - il2cpp_functions::Init(); - return static_cast(il2cpp_functions::value_box(klass, static_cast(&thing))); + ::i2c::functions::initialize(); + return static_cast(::i2c::functions::value_box(klass, static_cast(&thing))); } } - template + template static inline void pack_result_into(Q&& thing, void* retval) { if constexpr (std::is_pointer_v) { *static_cast(retval) = std::forward(thing); - } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { + } else if constexpr (::i2c::type_check::wrapper_ref_type) { *static_cast(retval) = thing.convert(); } else { - auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); + auto* klass = ::i2c::class_of(); if (!klass) { custom_types::logger.critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); return; @@ -238,80 +203,53 @@ namespace custom_types { } }; - template - struct invoker_creator { - template - static void instance_invoke(TRet(*func)(T*, TArgs...), T* self, void** args, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func(self, - arg_helper::unpack_arg(args[Ns], type_tag{})... - ); - } else { - arg_helper::pack_result_into( - func(self, - arg_helper::unpack_arg(args[Ns], type_tag{})... - ), - retval - ); - } - ) + template + struct invoker_creator { + template + static void instance_invoke(function_ptr_t func, T* self, void** args, std::index_sequence, void* retval) { + if constexpr (std::is_same_v) { + func(self, arg_helper::unpack_arg(args[Ns], type_tag{})...); + } else { + arg_helper::pack_result_into(func(self, arg_helper::unpack_arg(args[Ns], type_tag{})...), retval); + } } - [[gnu::noinline]] - static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, void* obj, void** args, void* retval) { + [[gnu::noinline]] static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] MethodInfo const* m, void* obj, void** args, void* retval) { // We also don't need to use anything from m so it is ignored. // Perhaps far in the future we will check attributes on it - auto func = reinterpret_cast(ptr); + auto func = reinterpret_cast(ptr); T* self = static_cast(obj); auto seq = std::make_index_sequence(); instance_invoke(func, self, args, seq, retval); } - template - [[gnu::noinline]] - static inline TRet wrap(T* self, TArgs ...args) { + template + [[gnu::noinline]] static inline TRet wrap(T* self, TArgs... args) { return (self->*member)(args...); } }; - template - struct invoker_creator { - template - static void static_invoke(TRet(*func)(TArgs...), void** args, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func( - arg_helper::unpack_arg(args[Ns], type_tag{})... - ); - } else { - arg_helper::pack_result_into( - func( - arg_helper::unpack_arg(args[Ns], type_tag{})... - ), - retval - ); - } - ) + template + struct invoker_creator { + template + static void static_invoke(function_ptr_t func, void** args, std::index_sequence, void* retval) { + if constexpr (std::is_same_v) { + func(arg_helper::unpack_arg(args[Ns], type_tag{})...); + } else { + arg_helper::pack_result_into(func(arg_helper::unpack_arg(args[Ns], type_tag{})...), retval); + } } - template - static void static_invoke_method(TRet(*func)(TArgs..., const MethodInfo*), void** args, const MethodInfo* m, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func( - arg_helper::unpack_arg(args[Ns], type_tag{})..., m - ); - } else { - arg_helper::pack_result_into( - func( - arg_helper::unpack_arg(args[Ns], type_tag{})..., m - ), - retval - ); - } - ) + template + static void static_invoke_method( + function_ptr_t func, void** args, MethodInfo const* m, std::index_sequence, void* retval + ) { + if constexpr (std::is_same_v) { + func(arg_helper::unpack_arg(args[Ns], type_tag{})..., m); + } else { + arg_helper::pack_result_into(func(arg_helper::unpack_arg(args[Ns], type_tag{})..., m), retval); + } } - [[gnu::noinline]] - static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { + [[gnu::noinline]] static void + invoke(Il2CppMethodPointer ptr, [[maybe_unused]] MethodInfo const* m, [[maybe_unused]] void* obj, void** args, void* retval) { // We also don't need to use anything from m so it is ignored. // Perhaps far in the future we will check attributes on it @@ -319,13 +257,13 @@ namespace custom_types { // post unity update delegates changed which use this invoke method // they get passed a nullptr ptr arg, so if they do we just take the method pointer from the method info instead! - auto func = ptr ? reinterpret_cast(ptr) : reinterpret_cast(m->methodPointer); + auto func = reinterpret_cast>(ptr ? ptr : m->methodPointer); static_invoke(func, args, seq, retval); } - [[gnu::noinline]] - static void* invoke_method(Il2CppMethodPointer ptr, const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { - auto func = reinterpret_cast(ptr); + [[gnu::noinline]] static void* + invoke_method(Il2CppMethodPointer ptr, MethodInfo const* m, [[maybe_unused]] void* obj, void** args, void* retval) { + auto func = reinterpret_cast>(ptr); auto seq = std::make_index_sequence(); static_invoke_method(func, args, m, seq, retval); } diff --git a/shared/util.hpp b/shared/util.hpp index b930a70..fee66cb 100644 --- a/shared/util.hpp +++ b/shared/util.hpp @@ -1,17 +1,17 @@ #pragma once -#include -#include "beatsaber-hook/shared/utils/type-concepts.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils-exceptions.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" + +#include "_config.h" +#include "beatsaber-hook/shared/api.hpp" +#include "beatsaber-hook/shared/exceptions.hpp" +#include "beatsaber-hook/shared/types.hpp" namespace custom_types { - template + template struct Decomposer; - template + template struct Decomposer { - template + template static constexpr bool equal() { if constexpr (sizeof...(TArgs) != sizeof...(TArgs2)) { return false; @@ -19,7 +19,7 @@ namespace custom_types { return (std::is_same_v && ...); } } - template + template static constexpr bool convertible() { if constexpr (sizeof...(TArgs) != sizeof...(TArgs2)) { return false; @@ -30,91 +30,61 @@ namespace custom_types { } }; - template - struct string_literal { - constexpr string_literal(const char (&str)[N]) { - std::copy_n(str, N, value); - } - char value[N]; - }; - - template - struct interface_helper { - static auto constexpr namespaze = namespaze_; - static auto constexpr name = name_; - }; - - struct CUSTOM_TYPES_EXPORT NullAccessException : il2cpp_utils::exceptions::StackTraceException { - NullAccessException() : il2cpp_utils::exceptions::StackTraceException("Null instance access on a custom type field!") {} + struct CUSTOM_TYPES_EXPORT NullAccessException : ::i2c::trace_exception { + NullAccessException() : ::i2c::trace_exception("Null instance access on a custom type field!") {} }; - template - Il2CppClass* ExtractClass() { - return ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); - } - template - requires requires () { ::il2cpp_utils::GetClassFromName(T::namespaze.value, T::name.value); } - Il2CppClass* ExtractClass() { - return ::il2cpp_utils::GetClassFromName(T::namespaze.value, T::name.value); + template + Il2CppClass* inited_class_of() { + ::i2c::functions::initialize(); + auto klass = ::i2c::class_of(); + if (!klass->initialized) + ::i2c::functions::Class_Init(klass); + return klass; } - template + template std::vector ExtractClasses() { - return {ExtractClass()...}; + return {::i2c::class_of()...}; } -#if __has_feature(cxx_exceptions) - #define CT_FIELD_ACCESS_CHECK(inst) if (!static_cast(inst)) throw ::custom_types::NullAccessException() -#else - #define CT_FIELD_ACCESS_CHECK(inst) if (!static_cast(inst)) SAFE_ABORT_MSG("Null instance access on a custom type field!") -#endif - - template + template struct field_accessor { - inline constexpr T const* field_addr(const void* instance, std::size_t offset) const noexcept { + constexpr T const* field_addr(void const* instance, std::size_t offset) const noexcept { return static_cast(static_cast(static_cast(instance) + offset)); } - inline constexpr T* field_addr(void* instance, std::size_t offset) const noexcept { + constexpr T* field_addr(void* instance, std::size_t offset) const noexcept { return static_cast(static_cast(static_cast(instance) + offset)); } - inline constexpr T& read(void* instance, std::size_t offset) const noexcept { - return *field_addr(instance, offset); - } + constexpr T& read(void* instance, std::size_t offset) const noexcept { return *field_addr(instance, offset); } - inline constexpr T const& read(void const* instance, std::size_t offset) const noexcept { - return *field_addr(instance, offset); - } + constexpr T const& read(void const* instance, std::size_t offset) const noexcept { return *field_addr(instance, offset); } - inline constexpr void write(void* instance, std::size_t offset, T&& v) const noexcept { - *field_addr(instance, offset) = v; - } + constexpr void write(void* instance, std::size_t offset, T&& v) const noexcept { *field_addr(instance, offset) = v; } }; - template + template <::i2c::type_check::ref_type T> struct field_accessor { - inline constexpr void* field_addr(void* instance, std::size_t offset) const noexcept { + constexpr void* field_addr(void* instance, std::size_t offset) const noexcept { return static_cast(static_cast(instance) + offset); } - inline constexpr void const* field_addr(void const* instance, std::size_t offset) const noexcept { + constexpr void const* field_addr(void const* instance, std::size_t offset) const noexcept { return static_cast(static_cast(instance) + offset); } - inline constexpr T& read(void* instance, std::size_t offset) const noexcept { - return *static_cast(field_addr(instance, offset)); - } + constexpr T& read(void* instance, std::size_t offset) const noexcept { return *static_cast(field_addr(instance, offset)); } - inline constexpr T const& read(void const* instance, std::size_t offset) const noexcept { + constexpr T const& read(void const* instance, std::size_t offset) const noexcept { return *static_cast(field_addr(instance, offset)); } - inline void write(void* instance, std::size_t offset, T&& v) const noexcept { - il2cpp_functions::gc_wbarrier_set_field( - static_cast(instance), - static_cast(field_addr(instance, offset)), - il2cpp_utils::il2cpp_reference_type_value(std::forward(v)) + void write(void* instance, std::size_t offset, T&& v) const noexcept { + ::i2c::functions::initialize(); + ::i2c::functions::gc_wbarrier_set_field( + static_cast(instance), static_cast(field_addr(instance, offset)), ::i2c::to_object(v) ); } }; diff --git a/src/NoteData.hpp b/src/NoteData.hpp deleted file mode 100644 index 74c9de2..0000000 --- a/src/NoteData.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// NoteData.hpp -#include "beatsaber-hook/shared/utils/typedefs.h" - -class BeatmapObjectData : public Il2CppObject { - public: - // private BeatmapObjectType k__BackingField - // Offset: 0x10 - int beatmapObjectType; - // private System.Single