From 233b38ad83678118b278183cd4296130e690eb93 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 19 May 2023 14:53:00 -0400 Subject: [PATCH 1/7] Paper switch and format --- .gitignore | 1 - CMakeLists.txt | 2 +- qpm.json | 13 +- qpm.shared.json | 107 ++++++ shared/utils/capstone-utils.hpp | 30 +- shared/utils/hooking.hpp | 8 +- shared/utils/il2cpp-functions.hpp | 23 +- shared/utils/il2cpp-type-check.hpp | 16 +- shared/utils/il2cpp-utils-classes.hpp | 14 +- shared/utils/il2cpp-utils-exceptions.hpp | 24 +- shared/utils/il2cpp-utils-fields.hpp | 6 +- shared/utils/il2cpp-utils-methods.hpp | 24 +- shared/utils/il2cpp-utils-properties.hpp | 4 +- shared/utils/il2cpp-utils.hpp | 6 +- shared/utils/logging.hpp | 438 +++-------------------- shared/utils/logging_wrappers.h | 42 +++ shared/utils/utils-functions.h | 7 +- shared/utils/utils.h | 21 +- src/config/config-utils.cpp | 2 +- src/utils/capstone-utils.cpp | 8 +- src/utils/gc-alloc.cpp | 3 +- src/utils/hook-tracker.cpp | 14 +- src/utils/il2cpp-functions.cpp | 54 ++- src/utils/il2cpp-type-check.cpp | 23 +- src/utils/il2cpp-utils-classes.cpp | 105 +++--- src/utils/il2cpp-utils-exceptions.cpp | 13 +- src/utils/il2cpp-utils-fields.cpp | 17 +- src/utils/il2cpp-utils-methods.cpp | 53 +-- src/utils/il2cpp-utils-properties.cpp | 15 +- src/utils/il2cpp-utils.cpp | 36 +- src/utils/logging.cpp | 404 +-------------------- src/utils/utils.cpp | 52 ++- 32 files changed, 514 insertions(+), 1071 deletions(-) create mode 100644 qpm.shared.json create mode 100644 shared/utils/logging_wrappers.h diff --git a/.gitignore b/.gitignore index 99f4af82..5718c62a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,6 @@ extern/ qpm.shared.json *.backup extern.cmake -qpm_defines.cmake build/ *.log diff --git a/CMakeLists.txt b/CMakeLists.txt index 48bddf54..e2f8b60f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ add_compile_options(-frtti -fPIE -fPIC -fexceptions -flto) add_compile_options(-Wall -Wextra -Werror -Wno-unused-function) # compile definitions used add_compile_definitions(VERSION=\"${MOD_VERSION}\") -add_compile_definitions(ID=\"${MOD_ID}\") +add_compile_definitions(MOD_ID=\"${MOD_ID}\") add_compile_definitions(UNITY_2019) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/qpm.json b/qpm.json index 7e1488de..b02470f3 100644 --- a/qpm.json +++ b/qpm.json @@ -7,9 +7,9 @@ "version": "1.2.3", "url": "https://github.com/sc2ad/beatsaber-hook", "additionalData": { - "branchName": "master", "soLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/libbeatsaber-hook.so", - "debugSoLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/debug_libbeatsaber-hook.so" + "debugSoLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/debug_libbeatsaber-hook.so", + "branchName": "master" } }, "dependencies": [ @@ -20,7 +20,7 @@ }, { "id": "libil2cpp", - "versionRange": ">=0.1.2<0.3.0", + "versionRange": ">=0.1.2, <0.3.0", "additionalData": {} }, { @@ -29,7 +29,12 @@ "additionalData": { "private": true } + }, + { + "id": "paper", + "versionRange": "^1.2.14", + "additionalData": {} } ], - "additionalData": {} + "workspace": null } \ No newline at end of file diff --git a/qpm.shared.json b/qpm.shared.json new file mode 100644 index 00000000..8445d1f8 --- /dev/null +++ b/qpm.shared.json @@ -0,0 +1,107 @@ +{ + "config": { + "sharedDir": "shared", + "dependenciesDir": "extern", + "info": { + "name": "beatsaber-hook", + "id": "beatsaber-hook", + "version": "1.2.3", + "url": "https://github.com/sc2ad/beatsaber-hook", + "additionalData": { + "soLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/libbeatsaber-hook.so", + "debugSoLink": "https://github.com/sc2ad/beatsaber-hook/releases/download/v0.5.8/debug_libbeatsaber-hook.so", + "branchName": "master" + } + }, + "dependencies": [ + { + "id": "modloader", + "versionRange": "^1.2.0", + "additionalData": {} + }, + { + "id": "libil2cpp", + "versionRange": ">=0.1.2, <0.3.0", + "additionalData": {} + }, + { + "id": "capstone", + "versionRange": "^0.1.0", + "additionalData": { + "private": true + } + }, + { + "id": "paper", + "versionRange": "^1.2.14", + "additionalData": {} + } + ], + "workspace": null + }, + "restoredDependencies": [ + { + "dependency": { + "id": "paper", + "versionRange": "=1.2.14", + "additionalData": { + "overrideSoName": "libpaperlog.so" + } + }, + "version": "1.2.14" + }, + { + "dependency": { + "id": "libil2cpp", + "versionRange": "=0.2.3", + "additionalData": { + "headersOnly": true + } + }, + "version": "0.2.3" + }, + { + "dependency": { + "id": "modloader", + "versionRange": "=1.2.3", + "additionalData": { + "soLink": "https://github.com/sc2ad/QuestLoader/releases/download/v1.2.3/libmodloader64.so", + "overrideSoName": "libmodloader.so", + "branchName": "version-v1.2.3" + } + }, + "version": "1.2.3" + }, + { + "dependency": { + "id": "fmt", + "versionRange": "=10.0.0", + "additionalData": { + "headersOnly": true, + "branchName": "version/v10_0_0", + "compileOptions": { + "systemIncludes": [ + "fmt/include/" + ], + "cppFlags": [ + "-DFMT_HEADER_ONLY" + ] + } + } + }, + "version": "10.0.0" + }, + { + "dependency": { + "id": "capstone", + "versionRange": "=0.1.0", + "additionalData": { + "staticLinking": true, + "soLink": "https://github.com/sc2ad/capstone-qpm/releases/download/v0.1.0/libcapstone.a", + "overrideSoName": "libcapstone.a" + } + }, + "version": "0.1.0" + } + ] +} \ No newline at end of file diff --git a/shared/utils/capstone-utils.hpp b/shared/utils/capstone-utils.hpp index 304c26de..d469fc9a 100644 --- a/shared/utils/capstone-utils.hpp +++ b/shared/utils/capstone-utils.hpp @@ -27,15 +27,15 @@ struct AddrSearchPair { auto find_through_hooks(void const* hook, uint32_t initialSearchSize, auto&& func) { // First, check to see if we are hooked. - Logger::get().debug("Finding through potential hook: %p and size: %u", hook, initialSearchSize); + il2cpp_utils::getLogger().fmtLog("Finding through potential hook: {} and size: {}", fmt::ptr(hook), initialSearchSize); auto hooks = HookTracker::GetHooks(hook); if (!hooks.empty()) { uint32_t const* addr = hooks.front().original_data.data(); uint32_t size = hooks.front().original_data.size() * sizeof(uint32_t); - Logger::get().debug("Hook found (%s)! Original data: %p with size: %u", hooks.front().name.c_str(), addr, size); + il2cpp_utils::getLogger().fmtLog("Hook found ({})! Original data: {} with size: {}", hooks.front().name.c_str(), fmt::ptr(addr), size); return func(cs::AddrSearchPair(addr, size), cs::AddrSearchPair(reinterpret_cast(hook), initialSearchSize)); } - Logger::get().debug("No hook found! Searching: %p, %u", hook, initialSearchSize); + il2cpp_utils::getLogger().fmtLog("No hook found! Searching: {}, {}", fmt::ptr(hook), initialSearchSize); return func(cs::AddrSearchPair(reinterpret_cast(hook), initialSearchSize)); } @@ -46,14 +46,14 @@ decltype(auto) findNth(std::array& addrs, uint32_t nToRetOn, while (addrs[searchIdx].remSearchSize > 0) { auto ptr = reinterpret_cast(addrs[searchIdx].addr); bool res = cs_disasm_iter(getHandle(), reinterpret_cast(&addrs[searchIdx].addr), &addrs[searchIdx].remSearchSize, &ptr, insn); - Logger::get().debug("%p diassemb: %s (rCount: %i, nToRetOn: %u, sz: %zu)", (void*)ptr, insn->mnemonic, retCount, nToRetOn, addrs[searchIdx].remSearchSize); + il2cpp_utils::getLogger().fmtLog("{} diassemb: {} (rCount: {}, nToRetOn: {}, sz: {})", fmt::ptr((void*)ptr), insn->mnemonic, retCount, nToRetOn, addrs[searchIdx].remSearchSize); if (res) { // Valid decode, so lets check to see if it is a match or we need to break. if (insn->id == ARM64_INS_RET) { if (retCount == 0) { // Early termination! cs_free(insn, 1); - Logger::get().warning("Could not find: %u call at: %p within: %i rets! Found all of the rets first!", nToRetOn, addrs[searchIdx].addr, retCount); + il2cpp_utils::getLogger().fmtLog("Could not find: {} call at: {} within: {} rets! Found all of the rets first!", nToRetOn, fmt::ptr(addrs[searchIdx].addr), retCount); return (decltype(match(insn)))std::nullopt; } retCount--; @@ -70,7 +70,7 @@ decltype(auto) findNth(std::array& addrs, uint32_t nToRetOn, if (nToRetOn == 1) { std::string name(insn->mnemonic); cs_free(insn, 1); - Logger::get().warning("Found: %u match, at: %p within: %i rets, but the result was a %s! Cannot compute destination address!", nToRetOn, addrs[searchIdx].addr, retCount, name.c_str()); + il2cpp_utils::getLogger().fmtLog("Found: {} match, at: {} within: {} rets, but the result was a {}! Cannot compute destination address!", nToRetOn, fmt::ptr(addrs[searchIdx].addr), retCount, name.c_str()); return (decltype(match(insn)))std::nullopt; } else { nToRetOn--; @@ -87,7 +87,7 @@ decltype(auto) findNth(std::array& addrs, uint32_t nToRetOn, } } // We didn't find it. Let's instead look at the next address/size pair for a match. - Logger::get().debug("Could not find: %u call at: %p within: %i rets at idx: %zu!", nToRetOn, addrs[searchIdx].addr, retCount, searchIdx); + il2cpp_utils::getLogger().fmtLog("Could not find: {} call at: {} within: {} rets at idx: {}!", nToRetOn, fmt::ptr(addrs[searchIdx].addr), retCount, searchIdx); } // If we run out of bytes to parse, we fail cs_free(insn, 1); @@ -106,14 +106,14 @@ auto findNth(const uint32_t* addr, F1&& match, F2&& skip) { size_t sz = szBytes; while (sz > 0) { bool res = cs_disasm_iter(getHandle(), &instructions, &sz, &ptr, insn); - Logger::get().debug("%p diassemb: %s (rCount: %i, nCalls: %u, sz: %zu)", (void*)ptr, insn->mnemonic, rCount, nCalls, sz); + il2cpp_utils::getLogger().fmtLog("{} diassemb: {} (rCount: {}, nCalls: {}, sz: {})", fmt::ptr((void*)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. if (insn->id == ARM64_INS_RET) { if (rCount == 0) { // Early termination! cs_free(insn, 1); - Logger::get().warning("Could not find: %u call at: %p within: %i rets! Found all of the rets first!", nToRetOn, (void*)ptr, retCount); + il2cpp_utils::getLogger().fmtLog("Could not find: {} call at: {} within: {} rets! Found all of the rets first!", nToRetOn, fmt::ptr((void*)ptr), retCount); return (decltype(match(insn)))std::nullopt; } rCount--; @@ -130,7 +130,7 @@ auto findNth(const uint32_t* addr, F1&& match, F2&& skip) { if (nCalls == 1) { std::string name(insn->mnemonic); cs_free(insn, 1); - Logger::get().warning("Found: %u match, at: %p within: %i rets, but the result was a %s! Cannot compute destination address!", nToRetOn, (void*)ptr, retCount, name.c_str()); + il2cpp_utils::getLogger().fmtLog("Found: {} match, at: {} within: {} rets, but the result was a {}! Cannot compute destination address!", nToRetOn, fmt::ptr((void*)ptr), retCount, name.c_str()); return (decltype(match(insn)))std::nullopt; } else { nCalls--; @@ -149,7 +149,7 @@ auto findNth(const uint32_t* addr, F1&& match, F2&& skip) { } // If we run out of bytes to parse, we fail cs_free(insn, 1); - Logger::get().warning("Could not find: %u call at: %p within: %i rets, within size: %zu!", nToRetOn, addr, retCount, szBytes); + il2cpp_utils::getLogger().fmtLog("Could not find: {} call at: {} within: {} rets, within size: {}!", nToRetOn, fmt::ptr(addr), retCount, szBytes); return (decltype(match(insn)))std::nullopt; } @@ -165,14 +165,14 @@ auto findNth(const uint32_t* addr) { size_t sz = szBytes; while (sz > 0) { bool res = cs_disasm_iter(getHandle(), &instructions, &sz, &ptr, insn); - Logger::get().debug("%p diassemb: %s (rCount: %i, nCalls: %u, sz: %zu)", (void*)ptr, insn->mnemonic, rCount, nCalls, sz); + il2cpp_utils::getLogger().fmtLog("{} diassemb: {} (rCount: {}, nCalls: {}, sz: {})", fmt::ptr((void*)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. if (insn->id == ARM64_INS_RET) { if (rCount == 0) { // Early termination! cs_free(insn, 1); - Logger::get().warning("Could not find: %u call at: %p within: %i rets! Found all of the rets first!", nToRetOn, (void*)ptr, retCount); + il2cpp_utils::getLogger().fmtLog("Could not find: {} call at: {} within: {} rets! Found all of the rets first!", nToRetOn, fmt::ptr((void*)ptr), retCount); return (decltype(match(insn)))std::nullopt; } rCount--; @@ -189,7 +189,7 @@ auto findNth(const uint32_t* addr) { if (nCalls == 1) { std::string name(insn->mnemonic); cs_free(insn, 1); - Logger::get().warning("Found: %u match, at: %p within: %i rets, but the result was a %s! Cannot compute destination address!", nToRetOn, (void*)ptr, retCount, name.c_str()); + il2cpp_utils::getLogger().fmtLog("Found: {} match, at: {} within: {} rets, but the result was a {}! Cannot compute destination address!", nToRetOn, fmt::ptr((void*)ptr), retCount, name.c_str()); return (decltype(match(insn)))std::nullopt; } else { nCalls--; @@ -201,7 +201,7 @@ auto findNth(const uint32_t* addr) { else { // Invalid instructions are ignored silently. // In order to skip these properly, we must increment our instructions, ptr, and size accordingly. - Logger::get().warning("FAILED PARSE: %p diassemb: 0x%x", (void*)ptr, *(uint32_t*)ptr); + il2cpp_utils::getLogger().fmtLog("FAILED PARSE: {} diassemb: 0x{:x}", fmt::ptr((void*)ptr), *(uint32_t*)ptr); sz -= 4; ptr += 4; instructions += 4; diff --git a/shared/utils/hooking.hpp b/shared/utils/hooking.hpp index 01c4c9d4..4b82c006 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -600,7 +600,7 @@ retval Hook_##name_::hook_##name_(__VA_ARGS__) template inline void __InstallHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS - logger.info("Installing hook: %s to offset: %p", T::name(), addr); + logger.fmtLog("Installing hook: {} to offset: {}", T::name(), fmt::ptr(addr)); #endif #ifdef __aarch64__ if constexpr (track) { @@ -631,7 +631,7 @@ void InstallHook(L& logger) { auto info = T::getInfo(); if (!info) { #ifndef SUPPRESS_MACRO_LOGS - logger.critical("Attempting to install hook: %s, but method could not be found!", T::name()); + logger.fmtLog("Attempting to install hook: {}, but method could not be found!", T::name()); #endif SAFE_ABORT(); } @@ -645,7 +645,7 @@ void InstallOrigHook(L& logger) { auto info = T::getInfo(); if (!info) { #ifndef SUPPRESS_MACRO_LOGS - logger.critical("Attempting to install hook: %s, but method could not be found!", T::name()); + logger.fmtLog("Attempting to install hook: {}, but method could not be found!", T::name()); #endif SAFE_ABORT(); } @@ -666,7 +666,7 @@ void InstallHookDirect(L& logger, void* dst) { // Install T into the specified address. Null checks dst. if (!dst) { #ifndef SUPPRESS_MACRO_LOGS - logger.critical("Attempting to install direct hook: %s, but was installing to an invalid destination!", T::name()); + logger.fmtLog("Attempting to install direct hook: {}, but was installing to an invalid destination!", T::name()); #endif SAFE_ABORT(); } diff --git a/shared/utils/il2cpp-functions.hpp b/shared/utils/il2cpp-functions.hpp index fe8c0c80..c7ac575e 100644 --- a/shared/utils/il2cpp-functions.hpp +++ b/shared/utils/il2cpp-functions.hpp @@ -1,8 +1,6 @@ -#ifndef IL2CPP_FUNCTIONS_H -#define IL2CPP_FUNCTIONS_H +#pragma once #pragma pack(push) - #include #include #include @@ -118,6 +116,7 @@ static rt name(TArgs&&... args) { \ return il2cpp_##name(args...); \ } \ } + // A class which contains all available il2cpp functions // Created by zoller27osu class il2cpp_functions { @@ -434,16 +433,16 @@ class il2cpp_functions { // must be done on-demand because the pointers aren't necessarily correct at the time of il2cpp_functions::Init static void CheckS_GlobalMetadata() { if (!s_GlobalMetadataHeader) { - static auto& logger = getFuncLogger(); + static auto& logger = ::il2cpp_utils::getLogger(); s_GlobalMetadata = *CRASH_UNLESS(il2cpp_functions::s_GlobalMetadataPtr); s_GlobalMetadataHeader = *CRASH_UNLESS(il2cpp_functions::s_GlobalMetadataHeaderPtr); s_Il2CppMetadataRegistration = *CRASH_UNLESS(il2cpp_functions::s_Il2CppMetadataRegistrationPtr); - logger.debug("sanity: %X (should be 0xFAB11BAF)", s_GlobalMetadataHeader->sanity); - logger.debug("version: %i", s_GlobalMetadataHeader->version); + logger.fmtLog("sanity: {:X} (should be 0xFAB11BAF)", s_GlobalMetadataHeader->sanity); + logger.fmtLog("version: {}", s_GlobalMetadataHeader->version); CRASH_UNLESS((uint32_t)s_GlobalMetadataHeader->sanity == 0xFAB11BAF); - logger.debug("typeDefinitionsOffset: %i", s_GlobalMetadataHeader->typeDefinitionsOffset); - logger.debug("exportedTypeDefinitionsOffset: %i", s_GlobalMetadataHeader->exportedTypeDefinitionsOffset); - logger.debug("nestedTypesOffset: %i", s_GlobalMetadataHeader->nestedTypesOffset); + logger.fmtLog("typeDefinitionsOffset: {}", s_GlobalMetadataHeader->typeDefinitionsOffset); + logger.fmtLog("exportedTypeDefinitionsOffset: {}", s_GlobalMetadataHeader->exportedTypeDefinitionsOffset); + logger.fmtLog("nestedTypesOffset: {}", s_GlobalMetadataHeader->nestedTypesOffset); // TODO: use il2cpp_functions::defaults to define the il2cpp_defaults variable mentioned in il2cpp-class-internals.h } } @@ -461,12 +460,8 @@ class il2cpp_functions { static bool initialized; // Initializes all of the IL2CPP functions via dlopen and dlsym for use. static void Init(); - - static LoggerContextObject& getFuncLogger(); }; #undef API_FUNC -#pragma pack(pop) - -#endif /* IL2CPP_FUNCTIONS_H */ \ No newline at end of file +#pragma pack(pop) \ No newline at end of file diff --git a/shared/utils/il2cpp-type-check.hpp b/shared/utils/il2cpp-type-check.hpp index 60b26d6c..613db253 100644 --- a/shared/utils/il2cpp-type-check.hpp +++ b/shared/utils/il2cpp-type-check.hpp @@ -1,6 +1,8 @@ +#pragma once + #include "il2cpp-functions.hpp" -#ifndef IL2CPP_TYPE_CHECK_H -#define IL2CPP_TYPE_CHECK_H + +#include "logging.hpp" #pragma pack(push) @@ -42,8 +44,8 @@ constexpr bool has_get = std::experimental::is_detected_v; #endif namespace il2cpp_utils { - // Returns the il2cpp_utils logger context singleton. - LoggerContextObject& getLogger(); + + // Returns the first matching class from the given namespace and typeName by searching through all assemblies that are loaded. Il2CppClass* GetClassFromName(std::string_view name_space, std::string_view type_name); @@ -100,7 +102,7 @@ namespace il2cpp_utils { #endif static inline Il2CppClass* get() { il2cpp_functions::Init(); - static auto& logger = getLogger(); + static auto& logger = il2cpp_utils::getLogger(); auto* klass = RET_0_UNLESS(logger, il2cpp_no_arg_class::get()); RET_0_UNLESS(logger, il2cpp_functions::class_is_valuetype(klass)); return il2cpp_functions::Class_GetPtrClass(klass); @@ -566,6 +568,4 @@ namespace il2cpp_utils { } } -#pragma pack(pop) - -#endif /* #ifndef IL2CPP_TYPE_CHECK_H */ +#pragma pack(pop) \ No newline at end of file diff --git a/shared/utils/il2cpp-utils-classes.hpp b/shared/utils/il2cpp-utils-classes.hpp index c3cd3e30..37ff8ee2 100644 --- a/shared/utils/il2cpp-utils-classes.hpp +++ b/shared/utils/il2cpp-utils-classes.hpp @@ -19,7 +19,7 @@ namespace il2cpp_utils { void* val = obj; // nullptr (which runtime_invoke returns for "void" return type!) is different from nullopt (a runtime_invoke error!) if (obj && il2cpp_functions::class_is_valuetype(il2cpp_functions::object_get_class(obj))) { - static auto& logger = getLogger(); + static auto& logger = il2cpp_utils::getLogger(); // So, because il2cpp finds it necessary to box returned value types (and also not deallocate them), we need to free them ourselves. // What we need to do is first extract the value, which we can do by casting and dereferencing // Then we need to PROPERLY free the allocating object at obj @@ -63,11 +63,11 @@ namespace il2cpp_utils { std::string GenericClassStandardName(Il2CppGenericClass* genClass); // Some parts provided by zoller27osu // Logs information about the given Il2CppClass* as log(DEBUG) - void LogClass(LoggerContextObject& logger, Il2CppClass* klass, bool logParents = false) noexcept; + void LogClass(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents = false) noexcept; // Logs all classes (from every namespace) that start with the given prefix // WARNING: THIS FUNCTION IS VERY SLOW. ONLY USE THIS FUNCTION ONCE AND WITH A FAIRLY SPECIFIC PREFIX! - void LogClasses(LoggerContextObject& logger, ::std::string_view classPrefix, bool logParents = false) noexcept; + void LogClasses(Paper::LoggerContext const& logger, ::std::string_view classPrefix, bool logParents = false) noexcept; // Gets the System.Type Il2CppObject* (actually an Il2CppReflectionType*) for an Il2CppClass* Il2CppReflectionType* GetSystemType(const Il2CppClass* klass); @@ -93,7 +93,7 @@ namespace il2cpp_utils { static auto& logger = getLogger(); Il2CppClass* klass = arg_class::get(arg); if (!klass) { - logger.error("Failed to determine class! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); + logger.fmtLog("Failed to determine class! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); } return klass; } @@ -101,7 +101,7 @@ namespace il2cpp_utils { template Il2CppClass* NoArgClass() { // TODO: change ifndef HAS_CODEGEN to 'if compile warnings are not errors'? - static auto& logger = getLogger(); + static auto& logger = il2cpp_utils::getLogger(); #ifndef HAS_CODEGEN using arg_class = il2cpp_type_check::il2cpp_no_arg_class; if constexpr (!has_get) { @@ -122,10 +122,10 @@ namespace il2cpp_utils { template const Il2CppType* ExtractType(T&& arg) { - static auto& logger = getLogger(); + static auto& logger = il2cpp_utils::getLogger(); const Il2CppType* typ = il2cpp_type_check::il2cpp_arg_type::get(arg); if (!typ) - logger.error("ExtractType: failed to determine type! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); + logger.fmtLog("ExtractType: failed to determine type! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); return typ; } diff --git a/shared/utils/il2cpp-utils-exceptions.hpp b/shared/utils/il2cpp-utils-exceptions.hpp index 45bef8d0..ce54fc3d 100644 --- a/shared/utils/il2cpp-utils-exceptions.hpp +++ b/shared/utils/il2cpp-utils-exceptions.hpp @@ -113,29 +113,29 @@ namespace il2cpp_utils { #define IL2CPP_CATCH_HANDLER(...) try { \ __VA_ARGS__ \ } catch (::il2cpp_utils::RunMethodException const& exc) { \ - ::Logger::get().error("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught RunMethodException! what(): %s", exc.what()); \ + ::il2cpp_utils::getLogger().fmtLog("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught RunMethodException! what(): {}", exc.what()); \ exc.log_backtrace(); \ - ::Logger::get().error("Catch handler backtrace..."); \ - ::Logger::get().Backtrace(100); \ + ::il2cpp_utils::getLogger().fmtLog("Catch handler backtrace..."); \ + ::il2cpp_utils::getLogger().Backtrace(100); \ if (exc.ex) { \ exc.rethrow(); \ } \ SAFE_ABORT(); \ } catch (::il2cpp_utils::exceptions::StackTraceException const& exc) { \ - ::Logger::get().error("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught StackTraceException! what(): %s", exc.what()); \ + ::il2cpp_utils::getLogger().fmtLog("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught StackTraceException! what(): {}", exc.what()); \ exc.log_backtrace(); \ - ::Logger::get().error("Catch handler backtrace..."); \ - ::Logger::get().Backtrace(100); \ + ::il2cpp_utils::getLogger().fmtLog("Catch handler backtrace..."); \ + ::il2cpp_utils::getLogger().Backtrace(100); \ SAFE_ABORT(); \ } catch (::std::exception const& exc) { \ - ::Logger::get().error("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught C++ exception! type name: %s, what(): %s", typeid(exc).name(), exc.what()); \ - ::Logger::get().error("Catch handler backtrace..."); \ - ::Logger::get().Backtrace(100); \ + ::il2cpp_utils::getLogger().fmtLog("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught C++ exception! type name: {}, what(): {}", typeid(exc).name(), exc.what()); \ + ::il2cpp_utils::getLogger().fmtLog("Catch handler backtrace..."); \ + ::il2cpp_utils::getLogger().Backtrace(100); \ ::il2cpp_utils::raise(exc); \ } catch (...) { \ - ::Logger::get().error("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught, unknown C++ exception (not std::exception) with no known what() method!"); \ - ::Logger::get().error("Catch handler backtrace..."); \ - ::Logger::get().Backtrace(100); \ + ::il2cpp_utils::getLogger().fmtLog("Caught in mod ID: " _CATCH_HANDLER_ID ": Uncaught, unknown C++ exception (not std::exception) with no known what() method!"); \ + ::il2cpp_utils::getLogger().fmtLog("Catch handler backtrace..."); \ + ::il2cpp_utils::getLogger().Backtrace(100); \ SAFE_ABORT(); \ } diff --git a/shared/utils/il2cpp-utils-fields.hpp b/shared/utils/il2cpp-utils-fields.hpp index a90998a8..c99b3aa6 100644 --- a/shared/utils/il2cpp-utils-fields.hpp +++ b/shared/utils/il2cpp-utils-fields.hpp @@ -18,11 +18,11 @@ namespace il2cpp_utils { // Created by zoller27osu // Logs information about the given FieldInfo* as log(DEBUG) - void LogField(LoggerContextObject& logger, FieldInfo* field); + void LogField(Paper::LoggerContext const& logger, FieldInfo* field); // Created by zoller27osu // Calls LogField on all fields in the given class - void LogFields(LoggerContextObject& logger, Il2CppClass* klass, bool logParents = false); + void LogFields(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents = false); Il2CppClass* GetFieldClass(FieldInfo* field); // Returns the FieldInfo for the field of the given class with the given name @@ -60,7 +60,7 @@ namespace il2cpp_utils { // Check that the TOut requested by the user matches the field. auto* outType = ExtractIndependentType(); if (outType && !IsConvertibleFrom(outType, field->type, false)) { - Logger::get().warning("User requested TOut %s does not match the field's type, %s!", + logger.fmtLog("User requested TOut {} does not match the field's type, {}!", TypeGetSimpleName(outType), TypeGetSimpleName(field->type)); } diff --git a/shared/utils/il2cpp-utils-methods.hpp b/shared/utils/il2cpp-utils-methods.hpp index 6207ce2f..931fefda 100644 --- a/shared/utils/il2cpp-utils-methods.hpp +++ b/shared/utils/il2cpp-utils-methods.hpp @@ -273,7 +273,7 @@ namespace il2cpp_utils { template bool ParameterMatch(const MethodInfo* method, std::array const& genTypes, std::array const& argTypes) { - static auto logger = getLogger().WithContext("ParameterMatch"); + static auto logger = getLogger(); il2cpp_functions::Init(); if (method->parameters_count != argSz) { return false; @@ -289,10 +289,10 @@ namespace il2cpp_utils { if (paramType->type == IL2CPP_TYPE_MVAR) { auto genIdx = paramType->data.genericParameterIndex - method->genericContainer->genericParameterStart; if (genIdx < 0) { - logger.warning("Extracted invalid genIdx %i from parameter %i", genIdx, i); + logger.fmtLog("Extracted invalid genIdx {} from parameter {}", genIdx, i); } else if (genIdx >= genCount) { - logger.warning("ParameterMatch was not supplied enough genTypes to determine type of parameter %i " - "(had %i, needed %i)!", i, genCount, genIdx); + logger.fmtLog("ParameterMatch was not supplied enough genTypes to determine type of parameter {} " + "(had {}, needed {})!", i, genCount, genIdx); } else { auto* klass = genTypes.at(genIdx); paramType = (paramType->byref) ? &klass->this_arg : &klass->byval_arg; @@ -342,7 +342,7 @@ namespace il2cpp_utils { auto* outType = ExtractIndependentType(); if (outType) { if (!IsConvertibleFrom(outType, method->return_type, false)) { - logger.warning("User requested TOut %s does not match the method's return object of type %s!", + logger.fmtLog("User requested TOut {} does not match the method's return object of type {}!", TypeGetSimpleName(outType), TypeGetSimpleName(method->return_type)); throw RunMethodException("Return type of method is not convertible!", method); } @@ -436,7 +436,7 @@ namespace il2cpp_utils { } } } catch (Il2CppExceptionWrapper& wrapper) { - logger.error("%s: Failed with exception: %s", il2cpp_functions::method_get_name(method), + logger.fmtLog("{}: Failed with exception: {}", il2cpp_functions::method_get_name(method), il2cpp_utils::ExceptionToString(wrapper.ex).c_str()); throw RunMethodException(wrapper.ex, method); } @@ -480,9 +480,9 @@ namespace il2cpp_utils { auto* outType = ExtractIndependentType(); if (outType) { if (!IsConvertibleFrom(outType, method->return_type, false)) { - logger.warning("User requested TOut %s does not match the method's return object of type %s!", + logger.fmtLog("User requested TOut {} does not match the method's return object of type {}}!", TypeGetSimpleName(outType), TypeGetSimpleName(method->return_type)); - throw RunMethodException(string_format("Return type of method is not convertible to: %s!", TypeGetSimpleName(outType)), method); + throw RunMethodException(string_format("Return type of method is not convertible to: {}!", TypeGetSimpleName(outType)), method); } } } @@ -533,11 +533,11 @@ namespace il2cpp_utils { // Function made by zoller27osu, modified by Sc2ad // Logs information about the given MethodInfo* as log(DEBUG) - void LogMethod(LoggerContextObject& logger, const MethodInfo* method); + void LogMethod(Paper::LoggerContext const& logger, const MethodInfo* method); // Created by zoller27osu // Calls LogMethod on all methods in the given class - void LogMethods(LoggerContextObject& logger, Il2CppClass* klass, bool logParents = false); + void LogMethods(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents = false); template // Runs a MethodInfo with the specified parameters and instance, with return type TOut. @@ -566,7 +566,7 @@ namespace il2cpp_utils { if (outType) { auto* retType = ExtractType(ret); if (!IsConvertibleFrom(outType, retType, false)) { - logger.warning("User requested TOut %s does not match the method's return object of type %s!", + logger.fmtLog("User requested TOut {} does not match the method's return object of type {}!", TypeGetSimpleName(outType), TypeGetSimpleName(retType)); } } @@ -574,7 +574,7 @@ namespace il2cpp_utils { } if (exp) { - logger.error("%s: Failed with exception: %s", il2cpp_functions::method_get_name(method), + logger.fmtLog("{}: Failed with exception: {}", il2cpp_functions::method_get_name(method), il2cpp_utils::ExceptionToString(exp).c_str()); return ::std::nullopt; } diff --git a/shared/utils/il2cpp-utils-properties.hpp b/shared/utils/il2cpp-utils-properties.hpp index dfdb15f2..a06da90a 100644 --- a/shared/utils/il2cpp-utils-properties.hpp +++ b/shared/utils/il2cpp-utils-properties.hpp @@ -10,11 +10,11 @@ namespace il2cpp_utils { // Created by zoller27osu // Logs information about the given PropertyInfo* as log(DEBUG) - void LogProperty(LoggerContextObject& logger, const PropertyInfo* field); + void LogProperty(Paper::LoggerContext const& logger, const PropertyInfo* field); // Created by zoller27osu // Calls LogProperty on all properties in the given class - void LogProperties(LoggerContextObject& logger, Il2CppClass* klass, bool logParents = false); + void LogProperties(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents = false); // Returns the PropertyInfo for the property of the given class with the given name // Created by zoller27osu diff --git a/shared/utils/il2cpp-utils.hpp b/shared/utils/il2cpp-utils.hpp index 1ad01fa2..87064ff8 100644 --- a/shared/utils/il2cpp-utils.hpp +++ b/shared/utils/il2cpp-utils.hpp @@ -207,7 +207,7 @@ namespace il2cpp_utils { auto* delegate = RET_DEFAULT_UNLESS(logger, il2cpp_utils::NewUnsafe(delegateClass, obj, &method)); auto* asDelegate = reinterpret_cast(delegate); if ((void*)asDelegate->method_ptr != (void*)callback) { - logger.error("Created Delegate's method_ptr (%p) is incorrect (should be %p)!", (void*)asDelegate->method_ptr, callback); + logger.fmtLog("Created Delegate's method_ptr ({}) is incorrect (should be {})!", fmt::ptr((void*)asDelegate->method_ptr), fmt::ptr(callback)); return nullptr; } return delegate; @@ -580,7 +580,7 @@ namespace il2cpp_utils { /// @return True if the MethodInfo* is a valid match, false otherwise. static bool valid(const MethodInfo* info) noexcept { if (!info) { - getLogger().warning("Null MethodInfo* provided to: MethodTypeCheck::valid!"); + il2cpp_utils::getLogger().fmtLog("Null MethodInfo* provided to: MethodTypeCheck::valid!"); return false; } if ((info->flags & METHOD_ATTRIBUTE_STATIC) == 0) { @@ -632,7 +632,7 @@ namespace il2cpp_utils { /// @return True if the MethodInfo* is a valid match, false otherwise. static bool valid(const MethodInfo* info) noexcept { if (!info) { - getLogger().warning("Null MethodInfo* provided to: MethodTypeCheck::valid!"); + il2cpp_utils::getLogger().fmtLog("Null MethodInfo* provided to: MethodTypeCheck::valid!"); return false; } if ((info->flags & METHOD_ATTRIBUTE_STATIC) != 0) { diff --git a/shared/utils/logging.hpp b/shared/utils/logging.hpp index 462ae474..d4f68615 100644 --- a/shared/utils/logging.hpp +++ b/shared/utils/logging.hpp @@ -1,402 +1,80 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include // Included to support cmath's definition of log #include -#include -#include -#include -#include -#include "modloader/shared/modloader.hpp" -#include "utils-functions.h" -#include -#include -#include +#include +#include -namespace Logging { - enum Level { - CRITICAL = ANDROID_LOG_FATAL, - ERROR = ANDROID_LOG_ERROR, - WARNING = ANDROID_LOG_WARN, - INFO = ANDROID_LOG_INFO, - DEBUG = ANDROID_LOG_DEBUG - }; -} +#include "paper/shared/logger.hpp" +#include "paper/shared/modinfo_fmt.hpp" + +#include "utils-functions.h" -#ifdef log -#undef log -#endif +#include "il2cpp-api-types.h" +#include "il2cpp-class-internals.h" -#ifndef LOG_PATH -#define LOG_PATH "/sdcard/Android/data/%s/files/logs/" -#endif +namespace il2cpp_utils { + Paper::LoggerContext const& getLogger(); + std::string ClassStandardName(const Il2CppClass* klass, bool generics); + const char* TypeGetSimpleName(const Il2CppType* type); +} -class Logger; +template<> +struct fmt::formatter : formatter { -/// @class Logger Buffer -/// @brief A buffer for logger data. Used for logging to file in a buffered fashion. -/// Each LoggerBuffer exists to wrap around a single logger instance. -/// Every time log is called on the instance, this buffer is updated (assuming options.toFile is true for the instance) -/// Each buffer contains multiple messages that need to be written out, stored as an std::list -class LoggerBuffer { - friend Logger; - public: - std::list messages; - std::mutex messageLock; - const ModInfo modInfo; - bool closed = false; - static std::string get_logDir(); - std::string get_path() { - std::string cpy = modInfo.version; - std::replace(cpy.begin(), cpy.end(), '.', '_'); - auto val = get_logDir() + modInfo.id + "_" + cpy + ".log"; - return val; + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(Il2CppClass const* p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return formatter::format(::il2cpp_utils::ClassStandardName(p, true), ctx); } - std::size_t length(); - void addMessage(std::string_view msg); - void flush(); - private: - std::string path; - public: - LoggerBuffer(const ModInfo info) : modInfo(info), path(get_path()) {} -}; - -/// @struct Logger Options -/// @brief Provides various options for loggers, including silencing them and logging to file. -struct LoggerOptions { - bool silent = false; - bool toFile = false; - std::string contextSeparator = "::"; - LoggerOptions(bool silent_ = false, bool toFile_ = false) : silent(silent_), toFile(toFile_) {} - LoggerOptions(std::string_view contextSeparator_, bool silent_ = false, bool toFile_ = false) : - silent(silent_), toFile(toFile_), contextSeparator(contextSeparator_) {} }; +template<> +struct fmt::formatter : formatter { -class LoggerContextObject; -class Consumer; - -class Logger { - friend Consumer; - friend LoggerBuffer; - friend LoggerContextObject; - public: - Logger(const ModInfo info, LoggerOptions options_) : options(options_), tag("QuestHook[" + info.id + "|v" + info.version + "]"), modInfo(info), buffer(modInfo) { - if (!init()) { - buffer.closed = true; - } - emplace_safe(buffer); - } - Logger(const ModInfo info) : options(LoggerOptions(false, false)), tag("QuestHook[" + info.id + "|v" + info.version + "]"), modInfo(info), buffer(modInfo) { - if (!init()) { - buffer.closed = true; - } - emplace_safe(buffer); - } - ~Logger() = delete; - void log(Logging::Level lvl, std::string str); - __attribute__((format(printf, 3, 4))) void log(Logging::Level lvl, const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(lvl, string_vformat(fmt, lst)); - va_end(lst); - } - __attribute__((format(printf, 2, 3))) void critical(const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(Logging::CRITICAL, string_vformat(fmt, lst)); - va_end(lst); - } - __attribute__((format(printf, 2, 3))) void error(const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(Logging::ERROR, string_vformat(fmt, lst)); - va_end(lst); - } - __attribute__((format(printf, 2, 3))) void warning(const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(Logging::WARNING, string_vformat(fmt, lst)); - va_end(lst); - } - __attribute__((format(printf, 2, 3))) void info(const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(Logging::INFO, string_vformat(fmt, lst)); - va_end(lst); - } - __attribute__((format(printf, 2, 3))) void debug(const char* fmt, ...) { - if (options.silent) { - return; - } - va_list lst; - va_start(lst, fmt); - log(Logging::DEBUG, string_vformat(fmt, lst)); - va_end(lst); - } - /// @brief Flushes the buffer for this logger instance. - void flush(); - /// @brief Closes the buffer for this logger instance, flushing as necessary. - /// After this call, this logger will no longer log to a buffer, nor will it log to a file. - void close(); - /// @brief Returns the logger that is used within the utils library. This function should not be used outside of the main library - static Logger& get(); - /// @brief Close all open LoggerBuffer objects. Should only be called on a crash or exit of the game. - static void closeAll(); - /// @brief Flush all open LoggerBuffer objects. - static void flushAll(); - /// @brief Initialize this logger. Deletes existing file logs. - /// This happens on default when this instance is constructed. - /// This should also be called anytime the options field is modified. - /// @returns True if the initialization was successful, false otherwise. If false is returned, you should set buffer.closed to true. - bool init(); - /// @brief Call this to silence logs from this logger. Should improve performance slightly. - /// Note that this call causes ALL calls to this particular logger to be silent, including from other mods. - /// Should only be used in particular cases. - constexpr void disable() { - options.silent = true; - } - /// @brief Call this to re-enable logs for this logger. Decreases performance slightly, but provides debug information. - /// Note that this call causes ALL calls to this particular logger to be enabled again, including from other mods that want silence. - /// Should only be used in particular cases. - constexpr void enable() { - options.silent = false; - } - /// @brief Returns the current options for this logger - const LoggerOptions getOptions() const { - return options; - } - /// @brief Writes a backtrace for the provided number of frames. - /// @param frameCount The number of frames to backtrace - void Backtrace(uint16_t frameCount); - /// @brief Enters a logging context. Should be used for more specific logging information. - /// Avoid entering contexts with names that contain % characters. - /// @param context The context name to enter - /// @returns LoggerContextObject that is used within the context. - LoggerContextObject WithContext(std::string_view context); - /// @brief Disable logging for any contexts that start with the provided string. - /// This is thread independent, and will silence across all threads - void DisableContext(std::string_view context); - /// @brief Enables logging for the context. - /// This function does nothing if DisableContext was not called with an exactly matching string before this call. - void EnableContext(std::string_view context); - /// @brief Gets all disabled contexts on this instance. - /// @returns The set of disabled context strings. - const std::unordered_set GetDisabledContexts(); - private: - /// @brief The options associated with this logger - LoggerOptions options; - - std::unordered_set disabledContexts; - /// @brief All created contexts for this instance - std::vector contexts; - /// @brief The mutex for the contexts maps/sets - std::mutex contextMutex; - - std::string tag; - const ModInfo modInfo; - // TODO: Each Logger instance is responsible for their own buffer. - // This means that if a logger instance is disposed (for whatever reason) it needs to clear its buffer pointer from the buffers list. - // This is done in the destructor, but for all intents and purposes, it doesn't need to happen at all. - LoggerBuffer buffer; - static bool consumerStarted; - static std::list buffers; - static std::mutex bufferMutex; - - /// @brief Constructs a context with a parent. This is called by LoggerContextObject.WithContext. - LoggerContextObject WithContext(LoggerContextObject* parent, std::string_view context); - /// @brief Recurses over all children contexts and disables/enables if they start with context. - void RecurseChangeContext(LoggerContextObject* ctx, std::string_view context, bool enable); - - static void emplace_safe(LoggerBuffer& buffer) { - // Obtain lock - std::scoped_lock lock(bufferMutex); - // Emplace, lock is released - Logger::buffers.push_back(&buffer); - } - static void startConsumer(); + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(Il2CppType const* p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return format_to(ctx.out(), "{}|{}", p->attrs, ::il2cpp_utils::TypeGetSimpleName(p)); + } }; -class LoggerContextObject { - friend Logger; - // The actual message to indicate the context. - std::string tag; - bool enabled = true; +template<> +struct fmt::formatter { - /// @brief Iterates over all children and either enables or disables them. - void changeChildren(bool enable) { - for (auto* item : childrenContexts) { - item->enabled = enable; - } + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(MethodInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return format_to(ctx.out(), "{} {}.{}", p->return_type, p->klass, p->name, + fmt::join(p->parameters, p->parameters + p->parameters_count, ", ")); } +}; - LoggerContextObject* parentContext = nullptr; - std::list childrenContexts; +template<> +struct fmt::formatter { - public: - /// @brief Constructs a LoggerContextObject. Should only be called from Logger.WithContext or LoggerContextObject.WithContext - /// @param l Logger instance to use - /// @param context_ The context for this object - /// @param enabled_ If it is enabled or not - LoggerContextObject(Logger& l, std::string_view context_, bool enabled_) : enabled(enabled_), logger(l), context(context_.data()) { - tag.append("(").append(context_.data()).append(") "); - std::scoped_lock lock(logger.contextMutex); - logger.contexts.push_back(this); + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(ParameterInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return format_to(ctx.out(), "{} {} {}", p->token, p->parameter_type, p->name); } +}; - /// @brief Constructs a nested LoggerContextObject. Should only be called from Logger.WithContext or LoggerContextObject.WithContext - /// @param parent LoggerContextObject parent - /// @param context_ The context for this object - /// @param enabled_ If it is enabled or not - LoggerContextObject(LoggerContextObject* const parent, std::string_view context_, bool enabled_) - : enabled(enabled_ && parent->enabled), parentContext(parent), logger(parent->logger), context(context_) - { - tag.append("(").append(context.data()).append(") "); - parentContext->childrenContexts.push_back(this); - std::scoped_lock lock(logger.contextMutex); - logger.contexts.push_back(this); - } - /// @brief Equality operator. - bool operator==(const LoggerContextObject& other) const { - return tag == other.tag && enabled == other.enabled && parentContext == other.parentContext; - } - /// @brief The Logger reference. - Logger& logger; - /// @brief The context of this LoggerContextObject - const std::string context; - /// @brief The parent context (or nullptr if there is no parent) - constexpr const LoggerContextObject* getParent() { - return parentContext; - } - /// @brief The children contexts (empty if there are no children) - const std::list getChildren() { - return childrenContexts; - } - // Cannot copy a context object, see the move constructor instead - LoggerContextObject(const LoggerContextObject&) = delete; - /// @brief Move constructor. Modifies the Logger's contexts collection. - LoggerContextObject(LoggerContextObject&& other) - : tag(std::move(other.tag)), enabled(std::move(other.enabled)), parentContext(other.parentContext), childrenContexts(std::move(other.childrenContexts)), logger(other.logger), context(std::move(other.context)) - { - // Potential race condition if move is called, interrupted, and DisableContext is called (should not result in much issue, though) - logger.contextMutex.try_lock(); - // We we move the context, we need to update the pointer in the contexts collection - for (auto itr = logger.contexts.begin(); itr != logger.contexts.end(); ++itr) { - if (*itr == &other) { - logger.contexts.erase(itr); - break; - } - } - logger.contexts.push_back(this); - logger.contextMutex.unlock(); - } - /// @brief Destructor. Modified the Logger's contexts collection. - ~LoggerContextObject() { - // We delete all of our children - childrenContexts.clear(); - // Then we remove ourselves from our parent - if (parentContext) { - parentContext->childrenContexts.remove(this); - } - // Remove ourselves from logger.contexts - logger.contextMutex.lock(); - for (auto itr = logger.contexts.begin(); itr != logger.contexts.end(); ++itr) { - if (*itr == this) { - logger.contexts.erase(itr); - break; - } - } - logger.contextMutex.unlock(); - } +template<> +struct fmt::formatter { - void log(Logging::Level lvl, std::string str) const { - if (enabled) { - logger.log(lvl, tag + str); - } + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(FieldInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return format_to(ctx.out(), "{}|{:x} {} {}", p->offset, p->token, p->type, p->name); } +}; - void log_v(Logging::Level lvl, std::string_view fmt, va_list lst) const { - logger.log(lvl, tag + string_vformat(fmt, lst)); - } - - __attribute__((format(printf, 3, 4))) void log(Logging::Level lvl, const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(lvl, fmt, lst); - va_end(lst); - } - } - __attribute__((format(printf, 2, 3))) void critical(const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(Logging::CRITICAL, fmt, lst); - va_end(lst); - } - } - __attribute__((format(printf, 2, 3))) void error(const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(Logging::ERROR, fmt, lst); - va_end(lst); - } - } - __attribute__((format(printf, 2, 3))) void warning(const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(Logging::WARNING, fmt, lst); - va_end(lst); - } - } - __attribute__((format(printf, 2, 3))) void info(const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(Logging::INFO, fmt, lst); - va_end(lst); - } - } - __attribute__((format(printf, 2, 3))) void debug(const char* fmt, ...) const { - if (enabled) { - va_list lst; - va_start(lst, fmt); - log_v(Logging::DEBUG, fmt, lst); - va_end(lst); - } - } - /// @brief Writes a backtrace for the provided number of frames. - /// @param frameCount The number of frames to backtrace - void Backtrace(uint16_t frameCount); - /// @brief Enter a new context. This call forwards to logger.WithContext(this, ctx). - /// Avoid entering contexts with names that contain % characters. - /// @param ctx The context name to enter - /// @returns The LoggerContextObject in the context - LoggerContextObject WithContext(std::string_view ctx) { - auto tmp = logger.WithContext(this, ctx); - // Copy over enabled - tmp.enabled = enabled; - return tmp; - } -}; \ No newline at end of file diff --git a/shared/utils/logging_wrappers.h b/shared/utils/logging_wrappers.h new file mode 100644 index 00000000..2ad879b6 --- /dev/null +++ b/shared/utils/logging_wrappers.h @@ -0,0 +1,42 @@ +#pragma once + +#include "logging.hpp" + +#include "typedefs-wrappers.hpp" +#include "typedefs-string.hpp" + +template +struct fmt::formatter> { + +// Formats the point p using the parsed format specification (presentation) +// stored in this formatter. +template +auto format(SafePtr const& p, FormatContext &ctx) -> decltype(ctx.out()) { +// ctx.out() is an output iterator to write to. +return format_to(ctx.out(), "{}|{}", p.operator bool(), fmt::ptr(p)); +} +}; + +template +struct fmt::formatter> { + +// Formats the point p using the parsed format specification (presentation) +// stored in this formatter. +template +auto format(SafePtr const& p, FormatContext &ctx) -> decltype(ctx.out()) { +// ctx.out() is an output iterator to write to. +return format_to(ctx.out(), "{}|{}", p.operator bool(), fmt::ptr(p)); +} +}; + +template<> +struct fmt::formatter : formatter { + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(StringW const &p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return formatter::format(p.operator std::string(), ctx); + } +}; \ No newline at end of file diff --git a/shared/utils/utils-functions.h b/shared/utils/utils-functions.h index 800224e3..b95164a9 100644 --- a/shared/utils/utils-functions.h +++ b/shared/utils/utils-functions.h @@ -1,5 +1,4 @@ -#ifndef UTILS_FUNCTIONS_H -#define UTILS_FUNCTIONS_H +#pragma once #include #include @@ -70,6 +69,4 @@ namespace backtrace_helpers { }; _Unwind_Reason_Code unwindCallback(struct _Unwind_Context *context, void *arg); size_t captureBacktrace(void **buffer, uint16_t max, uint16_t skip = 0); -} - -#endif /* UTILS_FUNCTIONS_H */ +} \ No newline at end of file diff --git a/shared/utils/utils.h b/shared/utils/utils.h index 3cb289c7..32a16550 100644 --- a/shared/utils/utils.h +++ b/shared/utils/utils.h @@ -1,5 +1,4 @@ -#ifndef UTILS_H_INCLUDED -#define UTILS_H_INCLUDED +#pragma once #pragma pack(push) @@ -28,6 +27,8 @@ namespace std { // #warning please alert sc2ad/beatsaber-hook that "std::experimental::source_location is live" (sharing your Android NDK version) then comment this out! #endif +#include + // For use in ClassOrInstance concept #if __has_include() #include @@ -100,7 +101,7 @@ auto&& unwrap_optionals(T&& arg) { #define RET_UNLESS(retval, loggerContext, ...) ({ \ auto&& __temp__ = (__VA_ARGS__); \ if (!__temp__) { \ - loggerContext.error("%s (in %s at %s:%i) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + loggerContext.fmtLog("{} (in {} at {}:{}) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ return retval; \ } \ unwrap_optionals(__temp__); }) @@ -118,8 +119,8 @@ auto&& unwrap_optionals(T&& arg) { #define THROW_OR_RET_NULL(contextLogger, ...) ({ \ auto&& __temp__ = (__VA_ARGS__); \ if (!__temp__) { \ - contextLogger.error("%s (in %s at %s:%i) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - throw ::il2cpp_utils::Il2CppUtilsException(contextLogger.context, #__VA_ARGS__ " is false!", __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + contextLogger.fmtLog("{} (in {} at {}:{}) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + throw ::il2cpp_utils::Il2CppUtilsException(contextLogger.tag, #__VA_ARGS__ " is false!", __PRETTY_FUNCTION__, __FILE__, __LINE__); \ } \ unwrap_optionals(__temp__); }) #else @@ -135,7 +136,7 @@ auto&& unwrap_optionals(T&& arg) { #define THROW_OR_RET_NULL(contextLogger, ...) ({ \ auto&& __temp__ = (__VA_ARGS__); \ if (!__temp__) { \ - contextLogger.error("%s (in %s at %s:%i) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + contextLogger.error("{} (in {} at {}:{}) returned false!", #__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ return nullptr; \ } \ unwrap_optionals(__temp__); }) @@ -229,7 +230,7 @@ template uintptr_t getBase(T pc) { static_assert(sizeof(T) >= sizeof(void*)); Dl_info info; - static auto& logger = Logger::get(); + static auto& logger = ::il2cpp_utils::getLogger(); RET_0_UNLESS(logger, dladdr((void*)pc, &info)); return (uintptr_t)info.dli_fbase; } @@ -267,7 +268,7 @@ void resetSS(std::stringstream& ss); // Prints the given number of "tabs" as spaces to the given output stream. void tabs(std::ostream& os, int tabs, int spacesPerTab = 2); // Logs the given stringstream and clears it. -void print(std::stringstream& ss, Logging::Level lvl = Logging::INFO); +void print(std::stringstream& ss, Paper::LogLevel lvl = Paper::LogLevel::INF); extern "C" { #endif /* __cplusplus */ @@ -297,6 +298,4 @@ uintptr_t findUniquePatternInLibil2cpp(bool& multiple, const char* pattern, cons } #endif /* __cplusplus */ -#pragma pack(pop) - -#endif /* UTILS_H_INCLUDED */ +#pragma pack(pop) \ No newline at end of file diff --git a/src/config/config-utils.cpp b/src/config/config-utils.cpp index fac2d0af..4e65230b 100644 --- a/src/config/config-utils.cpp +++ b/src/config/config-utils.cpp @@ -22,7 +22,7 @@ using namespace rapidjson; bool Configuration::ensureObject() { if (!config.IsObject()) { - Logger::get().warning("Config data for mod was invalid! Clearing."); + il2cpp_utils::getLogger().fmtLog("Config data for mod was invalid! Clearing."); config.SetObject(); return false; } diff --git a/src/utils/capstone-utils.cpp b/src/utils/capstone-utils.cpp index 968c9e1b..1dc9ff0f 100644 --- a/src/utils/capstone-utils.cpp +++ b/src/utils/capstone-utils.cpp @@ -8,8 +8,8 @@ bool valid = false; #define VERSION "0.0.0" #endif -#ifndef ID -#define ID "beatsaber-hook" +#ifndef MOD_ID +#define MOD_ID "beatsaber-hook" #endif namespace cs { @@ -17,10 +17,10 @@ void __attribute__((constructor)) init_capstone() { cs_err e1 = cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &handle); cs_option(handle, CS_OPT_DETAIL, 1); if (e1) { - __android_log_print(Logging::CRITICAL, "QuestHook[" ID "|" VERSION "] capstone", "Capstone initialization failed! %u", e1); + __android_log_print(ANDROID_LOG_FATAL, "QuestHook[" MOD_ID "|" VERSION "] capstone", "Capstone initialization failed! %u", e1); SAFE_ABORT(); } - __android_log_print(Logging::INFO, "QuestHook[" ID "|" VERSION "] capstone", "Capstone initialized!"); + __android_log_print(ANDROID_LOG_INFO, "QuestHook[" MOD_ID "|" VERSION "] capstone", "Capstone initialized!"); valid = true; } diff --git a/src/utils/gc-alloc.cpp b/src/utils/gc-alloc.cpp index e0459eaf..41e903af 100644 --- a/src/utils/gc-alloc.cpp +++ b/src/utils/gc-alloc.cpp @@ -1,3 +1,4 @@ +#include #include "shared/utils/gc-alloc.hpp" #include "shared/utils/il2cpp-functions.hpp" #include "shared/utils/logging.hpp" @@ -12,7 +13,7 @@ } else { auto* ptr = calloc(1, sz); // We cannot use our logger because we allocate it using this function. - __android_log_print(Logging::WARNING, "QuestHook[GC_Alloc]", "Allocation at: %p for size: %lu fallback to calloc!", ptr, sz); + __android_log_print(ANDROID_LOG_ERROR, "QuestHook[GC_Alloc]", "Allocation at: %p for size: %lu fallback to calloc!", ptr, sz); return ptr; } } diff --git a/src/utils/hook-tracker.cpp b/src/utils/hook-tracker.cpp index 91014764..13ec0e93 100644 --- a/src/utils/hook-tracker.cpp +++ b/src/utils/hook-tracker.cpp @@ -74,12 +74,12 @@ const void* HookTracker::GetOrigInternal(const void* const location) noexcept { #include void HookTracker::CombineHooks() noexcept { - static auto logger = Logger::get().WithContext("HookTracker"); + static auto logger = il2cpp_utils::getLogger(); auto libsFolder = string_format(LIBS_FILE_PATH, Modloader::getApplicationId().c_str()); auto tmpPath = Modloader::getDestinationPath(); DIR* dir = opendir(libsFolder.c_str()); if (dir == nullptr) { - logger.warning("Failed to open libs folder! At path: %s", libsFolder.c_str()); + logger.fmtLog("Failed to open libs folder! At path: {}", libsFolder.c_str()); return; } struct dirent* dp; @@ -90,26 +90,26 @@ void HookTracker::CombineHooks() noexcept { auto* image = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); auto* err = dlerror(); if (image == nullptr || err != nullptr) { - logger.warning("Failed to dlopen: %s! %s", path.c_str(), err); + logger.fmtLog("Failed to dlopen: {}! {}", path.c_str(), err); continue; } // Open the library, look for a function called: __HOOKTRACKER_GET_HOOKS auto* getter = dlsym(image, "__HOOKTRACKER_GET_HOOKS"); if (getter == nullptr) { - logger.warning("Failed to find symbol: %s", "__HOOKTRACKER_GET_HOOKS"); + logger.fmtLog("Failed to find symbol: {}", "__HOOKTRACKER_GET_HOOKS"); continue; } // Of course, if the function returns something that is of a different HookInfo type, for example, this may cause all sorts of pain. auto otherHooks = *reinterpret_cast>*(*)()>(getter)(); - Logger::get().debug("Found other hooks: %zu for module: %s", otherHooks.size(), path.c_str()); - for (auto itr : otherHooks) { + logger.fmtLog("Found other hooks: {} for module: {}", otherHooks.size(), path.c_str()); + for (const auto& itr : otherHooks) { // For each void*, find our match auto match = hooks.find(itr.first); if (match == hooks.end()) { hooks.insert({itr.first, itr.second}); } else { // Add only unique items - for (auto item : itr.second) { + for (const auto& item : itr.second) { bool unique = true; for (auto& existing : match->second) { if (existing == item) { diff --git a/src/utils/il2cpp-functions.cpp b/src/utils/il2cpp-functions.cpp index 9c6f2319..92d112f0 100644 --- a/src/utils/il2cpp-functions.cpp +++ b/src/utils/il2cpp-functions.cpp @@ -395,7 +395,7 @@ bool il2cpp_functions::find_GC_free(const uint32_t* Runtime_Shutdown) { il2cpp_functions::il2cpp_GC_free = reinterpret_cast(sigMatch); return true; } - static auto logger = il2cpp_functions::getFuncLogger().WithContext("find_GC_free"); + static auto logger = il2cpp_utils::getLogger(); auto blrStart = cs::find_through_hooks(Runtime_Shutdown, 4096, [](auto... pairs) { std::array tracked{pairs...}; @@ -417,7 +417,7 @@ bool il2cpp_functions::find_GC_SetWriteBarrier(const uint32_t* set_wbarrier_fiel if (!set_wbarrier_field) { return false; } - static auto logger = il2cpp_functions::getFuncLogger().WithContext("find_GC_SetWriteBarrier"); + static auto logger = il2cpp_utils::getLogger(); auto tmp = cs::findNthB<1>(set_wbarrier_field); if (!tmp) return false; il2cpp_GarbageCollector_SetWriteBarrier = reinterpret_cast(*tmp); @@ -432,7 +432,7 @@ void* __wrapper_gc_malloc_uncollectable(size_t sz, [[maybe_unused]] void* desc) } bool il2cpp_functions::trace_GC_AllocFixed(const uint32_t* DomainGetCurrent) { - static auto logger = il2cpp_functions::getFuncLogger().WithContext("trace_GC_AllocFixed"); + static auto logger = il2cpp_utils::getLogger(); // Domain::GetCurrent has a single bl to GarbageCollector::AllocateFixed // MetadataCache::InitializeGCSafe is 3rd bl after first b.ne, which is the 6th b(.lt, .ne), t(bz, nz), c(bz, nz) auto tmp = cs::findNthBl<1>(DomainGetCurrent); @@ -462,28 +462,24 @@ static std::optional loadFind(cs_insn* insn) { return (insn->id == ARM64_INS_LDR || insn->id == ARM64_INS_LDP) ? std::optional(reinterpret_cast(insn->address)) : std::nullopt; } -LoggerContextObject& il2cpp_functions::getFuncLogger() { - static auto logger = Logger::get().WithContext("il2cpp_functions"); - return logger; -} - #define API_SYM(name) \ *(void**)(&il2cpp_##name) = dlsym(imagehandle, "il2cpp_" #name); \ -logger.debug("Loaded: " #name ", error: %s", dlerror()) +il2cpp_utils::getLogger().fmtLog("Loaded: " #name ", error: {}", dlerror()) // Autogenerated // Initializes all of the IL2CPP functions via dlopen and dlsym for use. void il2cpp_functions::Init() { if (initialized) { return; } - static auto logger = getFuncLogger().WithContext("Init"); - logger.info("il2cpp_functions: Init: Initializing all IL2CPP Functions..."); + Paper::Logger::RegisterFileContextId(il2cpp_utils::getLogger().tag); + auto const& logger = il2cpp_utils::getLogger(); + logger.fmtLog("il2cpp_functions: Init: Initializing all IL2CPP Functions..."); dlerror(); // clears existing errors auto path = Modloader::getLibIl2CppPath(); void *imagehandle = dlopen(path.c_str(), RTLD_GLOBAL | RTLD_LAZY); if (!imagehandle) { - logger.error("Failed to dlopen %s: %s!", path.c_str(), dlerror()); + logger.fmtLog("Failed to dlopen {}: {}!", path.c_str(), dlerror()); return; } #ifdef UNITY_2019 @@ -751,21 +747,21 @@ void il2cpp_functions::Init() { // MANUALLY DEFINED CONST DEFINITIONS *(void**)(&il2cpp_class_get_type_const) = dlsym(imagehandle, "il2cpp_class_get_type"); - logger.info("Loaded: il2cpp_class_get_type CONST VERSION!"); + il2cpp_utils::getLogger().fmtLog("Loaded: il2cpp_class_get_type CONST VERSION!"); *(void**)(&il2cpp_class_get_name_const) = dlsym(imagehandle, "il2cpp_class_get_name"); - logger.info("Loaded: il2cpp_class_get_name CONST VERSION!"); + il2cpp_utils::getLogger().fmtLog("Loaded: il2cpp_class_get_name CONST VERSION!"); // XREF TRACES // TODO: Consider making all of these optional and having only those that are truly used crash on fail // Alternatively, have none of them crash on fail, but on usage { auto Array_NewSpecific_addr = cs::readb(reinterpret_cast(il2cpp_array_new_specific)); - logger.debug("Array::NewSpecific offset: %lX", reinterpret_cast(Array_NewSpecific_addr) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("Array::NewSpecific offset: {:X}", reinterpret_cast(Array_NewSpecific_addr) - getRealOffset(0)); auto match = cs::findNthBl<1>(Array_NewSpecific_addr); if (!match) SAFE_ABORT_MSG("Failed to find Class::Init!"); il2cpp_Class_Init = reinterpret_cast(*match); // Class::Init. 0x846A68 in 1.5, 0x9EC0A4 in 1.7.0, 0xA6D1B8 in 1.8.0b1 - logger.debug("Class::Init found? offset: %lX", reinterpret_cast(il2cpp_Class_Init) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("Class::Init found? offset: {:X}", reinterpret_cast(il2cpp_Class_Init) - getRealOffset(0)); } { @@ -775,7 +771,7 @@ void il2cpp_functions::Init() { if (!typeinfo) SAFE_ABORT_MSG("Failed to find MetadataCache::GetTypeInfoFromTypeIndex!"); il2cpp_MetadataCache_GetTypeInfoFromTypeIndex = reinterpret_cast(*typeinfo); // MetadataCache::GetTypeInfoFromTypeIndex. offset 0x84F764 in 1.5, 0x9F5250 in 1.7.0, 0xA7A79C in 1.8.0b1 - logger.debug("MetadataCache::GetTypeInfoFromTypeIndex found? offset: %lX", reinterpret_cast(il2cpp_MetadataCache_GetTypeInfoFromTypeIndex) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("MetadataCache::GetTypeInfoFromTypeIndex found? offset: {:X}", reinterpret_cast(il2cpp_MetadataCache_GetTypeInfoFromTypeIndex) - getRealOffset(0)); } { @@ -785,7 +781,7 @@ void il2cpp_functions::Init() { if (!result) SAFE_ABORT_MSG("Failed to find MetadataCache::GetTypeInfoFromDefinitionIndex!"); il2cpp_MetadataCache_GetTypeInfoFromTypeDefinitionIndex = reinterpret_cast(*result); // MetadataCache::GetTypeInfoFromTypeDefinitionIndex. offset 0x84FBA4 in 1.5, 0x9F5690 in 1.7.0, 0xA75958 in 1.8.0b1 - logger.debug("MetadataCache::GetTypeInfoFromTypeDefinitionIndex found? offset: %p, %lX", il2cpp_MetadataCache_GetTypeInfoFromTypeDefinitionIndex, reinterpret_cast(il2cpp_MetadataCache_GetTypeInfoFromTypeDefinitionIndex) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("MetadataCache::GetTypeInfoFromTypeDefinitionIndex found? offset: {}, {:X}", fmt::ptr(il2cpp_MetadataCache_GetTypeInfoFromTypeDefinitionIndex), reinterpret_cast(il2cpp_MetadataCache_GetTypeInfoFromTypeDefinitionIndex) - getRealOffset(0)); } { @@ -793,7 +789,7 @@ void il2cpp_functions::Init() { if (!type_getName) SAFE_ABORT_MSG("Failed to find Type::GetName!"); il2cpp__Type_GetName_ = reinterpret_cast(*type_getName); // Type::GetName. offset 0x8735DC in 1.5, 0xA1A458 in 1.7.0, 0xA7B634 in 1.8.0b1 - logger.debug("Type::GetName found? offset: %lX", reinterpret_cast(il2cpp__Type_GetName_) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("Type::GetName found? offset: {:X}", reinterpret_cast(il2cpp__Type_GetName_) - getRealOffset(0)); } { @@ -810,7 +806,7 @@ void il2cpp_functions::Init() { auto result = cs::findNthB<1>(*caseStart); if (!result) SAFE_ABORT_MSG("Failed to find GenericClass::GetClass!"); il2cpp_GenericClass_GetClass = reinterpret_cast(*result); - logger.debug("GenericClass::GetClass found? offset: %lX", ((uintptr_t)il2cpp_GenericClass_GetClass) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("GenericClass::GetClass found? offset: {:X}", ((uintptr_t)il2cpp_GenericClass_GetClass) - getRealOffset(0)); } { @@ -820,7 +816,7 @@ void il2cpp_functions::Init() { auto result = cs::findNthB<1>(*ptrCase); if (!result) SAFE_ABORT_MSG("Failed to find Class::GetPtrClass!"); il2cpp_Class_GetPtrClass = reinterpret_cast(*result); - logger.debug("Class::GetPtrClass(Il2CppClass*) found? offset: %lX", ((uintptr_t)il2cpp_Class_GetPtrClass) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("Class::GetPtrClass(Il2CppClass*) found? offset: {:X}", ((uintptr_t)il2cpp_Class_GetPtrClass) - getRealOffset(0)); } { @@ -828,7 +824,7 @@ void il2cpp_functions::Init() { auto result = cs::findNthBl<1>(reinterpret_cast(il2cpp_domain_get_assemblies)); if (!result) SAFE_ABORT_MSG("Failed to find Assembly::GetAllAssemblies!"); il2cpp_Assembly_GetAllAssemblies = reinterpret_cast(*result); - logger.debug("Assembly::GetAllAssemblies found? offset: %lX", ((uintptr_t)il2cpp_Assembly_GetAllAssemblies) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("Assembly::GetAllAssemblies found? offset: {:X}", ((uintptr_t)il2cpp_Assembly_GetAllAssemblies) - getRealOffset(0)); } { @@ -837,17 +833,17 @@ void il2cpp_functions::Init() { auto Runtime_Shutdown = cs::findNthB<1>(reinterpret_cast(il2cpp_shutdown)); if (!Runtime_Shutdown) SAFE_ABORT_MSG("Failed to find Runtime::Shutdown!"); if (find_GC_free(*Runtime_Shutdown)) { - logger.debug("gc::GarbageCollector::FreeFixed found? offset: %lX", ((uintptr_t)il2cpp_GC_free) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("gc::GarbageCollector::FreeFixed found? offset: {:X}", ((uintptr_t)il2cpp_GC_free) - getRealOffset(0)); } // GarbageCollector::SetWriteBarrier(void*) if (find_GC_SetWriteBarrier(reinterpret_cast(il2cpp_gc_wbarrier_set_field))) { - logger.debug("GarbageCollector::SetWriteBarrier found? offset: %lX", ((uintptr_t)il2cpp_GarbageCollector_SetWriteBarrier) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("GarbageCollector::SetWriteBarrier found? offset: {:X}", ((uintptr_t)il2cpp_GarbageCollector_SetWriteBarrier) - getRealOffset(0)); } // GarbageCollector::AllocateFixed(size_t, void*) auto result = cs::findNthB<1>(reinterpret_cast(il2cpp_domain_get)); if (!result) SAFE_ABORT_MSG("Failed to find Domain::Get!"); if (find_GC_AllocFixed(*result)) { - logger.debug("GarbageCollector::AllocateFixed found? offset: %lX", ((uintptr_t)il2cpp_GarbageCollector_AllocateFixed) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("GarbageCollector::AllocateFixed found? offset: {:X}", ((uintptr_t)il2cpp_GarbageCollector_AllocateFixed) - getRealOffset(0)); } } hasGCFuncs = il2cpp_GarbageCollector_AllocateFixed != nullptr && il2cpp_GC_free != nullptr; @@ -863,7 +859,7 @@ void il2cpp_functions::Init() { auto defaults_addr = cs::getpcaddr<1, 1>(*ldr); if (!defaults_addr) SAFE_ABORT_MSG("Failed to find pcaddr around 6th load in Runtime::InitUtf16!"); defaults = reinterpret_cast(std::get<2>(*defaults_addr)); - logger.debug("il2cpp_defaults found: %p (offset: %lX)", defaults, ((uintptr_t)defaults) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("il2cpp_defaults found: {} (offset: {:X})", fmt::ptr(defaults), ((uintptr_t)defaults) - getRealOffset(0)); // FIELDS // Extract locations of s_GlobalMetadataHeader, s_Il2CppMetadataRegistration, & s_GlobalMetadata @@ -882,8 +878,8 @@ void il2cpp_functions::Init() { if (!tmp) SAFE_ABORT_MSG("Failed to find 5th pcaddr for s_GlobalMetadataPtr!"); s_GlobalMetadataPtr = reinterpret_cast( std::get<2>(*tmp)); - logger.debug("%p %p %p metadata pointers", s_GlobalMetadataHeaderPtr, s_Il2CppMetadataRegistrationPtr, s_GlobalMetadataPtr); - logger.debug("All global constants found!"); + il2cpp_utils::getLogger().fmtLog("{} {} {} metadata pointers", fmt::ptr(s_GlobalMetadataHeaderPtr), fmt::ptr(s_Il2CppMetadataRegistrationPtr), fmt::ptr(s_GlobalMetadataPtr)); + il2cpp_utils::getLogger().fmtLog("All global constants found!"); } // WeakPtr stuff somewhere @@ -900,5 +896,5 @@ void il2cpp_functions::Init() { dlclose(imagehandle); initialized = true; - logger.info("il2cpp_functions: Init: Successfully loaded all il2cpp functions!"); + il2cpp_utils::getLogger().fmtLog("il2cpp_functions: Init: Successfully loaded all il2cpp functions!"); } diff --git a/src/utils/il2cpp-type-check.cpp b/src/utils/il2cpp-type-check.cpp index 4a8058c9..b311545b 100644 --- a/src/utils/il2cpp-type-check.cpp +++ b/src/utils/il2cpp-type-check.cpp @@ -4,18 +4,13 @@ #include namespace il2cpp_utils { - LoggerContextObject& getLogger() { - static auto logger = Logger::get().WithContext("il2cpp_utils"); - return logger; - } - // It doesn't matter what types these are, they just need to be used correctly within the methods static std::unordered_map, Il2CppClass*, hash_pair> namesToClassesCache; static std::mutex nameHashLock; Il2CppClass* GetClassFromName(std::string_view name_space, std::string_view type_name) { il2cpp_functions::Init(); - static auto logger = getLogger().WithContext("GetClassFromName"); + static auto logger = getLogger(); // TODO: avoid creating std::string at any point except new pair insertion via P0919 // Check cache @@ -35,7 +30,7 @@ namespace il2cpp_utils { auto assemb = allAssemb[i]; auto img = il2cpp_functions::assembly_get_image(assemb); if (!img) { - logger.error("Assembly with name: %s has a null image!", assemb->aname.name); + il2cpp_utils::getLogger().fmtLog("Assembly with name: {} has a null image!", assemb->aname.name); continue; } auto klass = il2cpp_functions::class_from_name(img, name_space.data(), type_name.data()); @@ -46,14 +41,14 @@ namespace il2cpp_utils { return klass; } } - logger.error("Could not find class with namepace: %s and name: %s", + il2cpp_utils::getLogger().fmtLog("Could not find class with namepace: {} and name: {}", name_space.data(), type_name.data()); return nullptr; } Il2CppClass* MakeGeneric(const Il2CppClass* klass, std::vector args) { il2cpp_functions::Init(); - static auto logger = getLogger().WithContext("MakeGeneric"); + static auto logger = getLogger(); static auto typ = RET_0_UNLESS(logger, il2cpp_functions::defaults->systemtype_class); auto klassType = RET_0_UNLESS(logger, GetSystemType(klass)); @@ -61,7 +56,7 @@ namespace il2cpp_utils { // Call Type.MakeGenericType on it auto arr = il2cpp_functions::array_new_specific(typ, args.size()); if (!arr) { - logger.error("Failed to make new array with length: %zu", args.size()); + il2cpp_utils::getLogger().fmtLog("Failed to make new array with length: {}", args.size()); return nullptr; } @@ -69,7 +64,7 @@ namespace il2cpp_utils { for (auto arg : args) { auto* o = GetSystemType(arg); if (!o) { - logger.error("Failed to get type for %s", il2cpp_functions::class_get_name_const(arg)); + il2cpp_utils::getLogger().fmtLog("Failed to get type for {}", il2cpp_functions::class_get_name_const(arg)); return nullptr; } il2cpp_array_set(arr, void*, i, reinterpret_cast(o)); @@ -83,7 +78,7 @@ namespace il2cpp_utils { Il2CppClass* MakeGeneric(const Il2CppClass* klass, const Il2CppType** types, uint32_t numTypes) { il2cpp_functions::Init(); - static auto logger = getLogger().WithContext("GetClassFromName"); + static auto logger = getLogger(); static auto typ = RET_0_UNLESS(logger, il2cpp_functions::defaults->systemtype_class); auto klassType = RET_0_UNLESS(logger, GetSystemType(klass)); @@ -91,7 +86,7 @@ namespace il2cpp_utils { // Call Type.MakeGenericType on it auto arr = il2cpp_functions::array_new_specific(typ, numTypes); if (!arr) { - logger.error("Failed to make new array with length: %u", numTypes); + il2cpp_utils::getLogger().fmtLog("Failed to make new array with length: {}", numTypes); return nullptr; } @@ -99,7 +94,7 @@ namespace il2cpp_utils { const Il2CppType* arg = types[i]; auto* o = GetSystemType(arg); if (!o) { - logger.error("Failed to get system type for %s", il2cpp_functions::type_get_name(arg)); + il2cpp_utils::getLogger().fmtLog("Failed to get system type for {}", il2cpp_functions::type_get_name(arg)); return nullptr; } il2cpp_array_set(arr, void*, i, reinterpret_cast(o)); diff --git a/src/utils/il2cpp-utils-classes.cpp b/src/utils/il2cpp-utils-classes.cpp index a7b0df98..9833e943 100644 --- a/src/utils/il2cpp-utils-classes.cpp +++ b/src/utils/il2cpp-utils-classes.cpp @@ -5,6 +5,7 @@ #include "../../shared/utils/il2cpp-utils-fields.hpp" #include #include +#include #include "../../shared/utils/alphanum.hpp" #include "shared/utils/gc-alloc.hpp" @@ -25,12 +26,12 @@ namespace il2cpp_utils { return "?"; } - void LogClass(LoggerContextObject& logger, Il2CppClass* klass, bool logParents) noexcept { + void LogClass(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) noexcept { il2cpp_functions::Init(); RET_V_UNLESS(logger, klass); if (loggedClasses.count(klass)) { - logger.debug("Already logged %p!", klass); + il2cpp_utils::getLogger().fmtLog("Already logged {}!", fmt::ptr(klass)); return; } loggedClasses.insert(klass); @@ -51,45 +52,45 @@ namespace il2cpp_utils { } } - logger.debug("%i ======================CLASS INFO FOR CLASS: %s======================", indent, ClassStandardName(klass).c_str()); + il2cpp_utils::getLogger().fmtLog("{} ======================CLASS INFO FOR CLASS: {}======================", indent, ClassStandardName(klass)); void* myIter = nullptr; if (!methodInit) { // log results of Class::Init - logger.warning("klass->initialized: %i, init_pending: %i, has_initialization_error: %i, initializationExceptionGCHandle: %Xll", - klass->initialized, klass->init_pending, klass->has_initialization_error, klass->initializationExceptionGCHandle); + il2cpp_utils::getLogger().fmtLog("klass->initialized: {}, init_pending: {}, has_initialization_error: {}, initializationExceptionGCHandle: {:X}", + (bool) klass->initialized, (uint8_t) klass->init_pending, (uint8_t) klass->has_initialization_error, klass->initializationExceptionGCHandle); auto* m1 = il2cpp_functions::class_get_methods(klass, &myIter); // attempt again to initialize the method data if (klass->method_count && !klass->methods) { - logger.error("Class::Init and class_get_methods failed to initialize klass->methods! class_get_methods returned: %p", - m1); + il2cpp_utils::getLogger().fmtLog("Class::Init and class_get_methods failed to initialize klass->methods! class_get_methods returned: {}", + fmt::ptr(m1)); if (m1) LogMethod(logger, m1); } } - logger.debug("Pointer: %p", klass); - logger.debug("Type Token: %i", il2cpp_functions::class_get_type_token(klass)); + il2cpp_utils::getLogger().fmtLog("Pointer: {}", fmt::ptr(klass)); + il2cpp_utils::getLogger().fmtLog("Type Token: {}", il2cpp_functions::class_get_type_token(klass)); auto typeDefIdx = klass->generic_class ? klass->generic_class->typeDefinitionIndex : il2cpp_functions::MetadataCache_GetIndexForTypeDefinition(klass); - logger.debug("TypeDefinitionIndex: %i", typeDefIdx); + il2cpp_utils::getLogger().fmtLog("TypeDefinitionIndex: {}", typeDefIdx); // Repair the typeDefinition value if it was null but we found one if (!klass->typeDefinition && typeDefIdx > 0) klass->typeDefinition = il2cpp_functions::MetadataCache_GetTypeDefinitionFromIndex(typeDefIdx); - logger.debug("Type definition: %p", klass->typeDefinition); + il2cpp_utils::getLogger().fmtLog("Type definition: {}", fmt::ptr(klass->typeDefinition)); - logger.debug("Assembly Name: %s", il2cpp_functions::class_get_assemblyname(klass)); + il2cpp_utils::getLogger().fmtLog("Assembly Name: {}", il2cpp_functions::class_get_assemblyname(klass)); auto* typ = il2cpp_functions::class_get_type(klass); if (typ) { - logger.debug("Type name: %s", il2cpp_functions::type_get_name(typ)); + il2cpp_utils::getLogger().fmtLog("Type name: {}", il2cpp_functions::type_get_name(typ)); if (auto* reflName = il2cpp_functions::Type_GetName(typ, IL2CPP_TYPE_NAME_FORMAT_REFLECTION)) { - logger.debug("Type reflection name: %s", reflName); + il2cpp_utils::getLogger().fmtLog("Type reflection name: {}", reflName); il2cpp_functions::free(reflName); } - logger.debug("Fully qualifed type name: %s", il2cpp_functions::type_get_assembly_qualified_name(typ)); + il2cpp_utils::getLogger().fmtLog("Fully qualifed type name: {}", il2cpp_functions::type_get_assembly_qualified_name(typ)); } - logger.debug("Rank: %i", il2cpp_functions::class_get_rank(klass)); - logger.debug("Flags: 0x%.8X", il2cpp_functions::class_get_flags(klass)); - logger.debug("Event Count: %i", klass->event_count); - logger.debug("Method Count: %i", klass->method_count); - logger.debug("Is Generic: %i", il2cpp_functions::class_is_generic(klass)); - logger.debug("Is Abstract: %i", il2cpp_functions::class_is_abstract(klass)); + il2cpp_utils::getLogger().fmtLog("Rank: {}", il2cpp_functions::class_get_rank(klass)); + il2cpp_utils::getLogger().fmtLog("Flags: 0x{:8X}", il2cpp_functions::class_get_flags(klass)); + il2cpp_utils::getLogger().fmtLog("Event Count: {}", klass->event_count); + il2cpp_utils::getLogger().fmtLog("Method Count: {}", klass->method_count); + il2cpp_utils::getLogger().fmtLog("Is Generic: {}", il2cpp_functions::class_is_generic(klass)); + il2cpp_utils::getLogger().fmtLog("Is Abstract: {}", il2cpp_functions::class_is_abstract(klass)); // Some methods, such as GenericClass::GetClass, may not initialize all fields in Il2CppClass, and thus not meet all implicit contracts defined by the comments in Il2CppClass's struct definition. // But unless we're blind, the only method that sets is_generic on non-methods is MetadataCache::FromTypeDefinition. That method also contains the only assignment of genericContainerIndex. @@ -98,56 +99,56 @@ namespace il2cpp_utils { // 2. Even if is_generic wasn't set, a positive genericContainerIndex was intentionally set that way and is a valid index. if (klass->is_generic || klass->genericContainerIndex > 0) { auto* genContainer = il2cpp_functions::MetadataCache_GetGenericContainerFromIndex(klass->genericContainerIndex); - logger.debug("genContainer: idx %i, ownerIndex: %i, is_method: %i", klass->genericContainerIndex, genContainer->ownerIndex, genContainer->is_method); + il2cpp_utils::getLogger().fmtLog("genContainer: idx {}, ownerIndex: {}, is_method: {}", klass->genericContainerIndex, genContainer->ownerIndex, genContainer->is_method); if (genContainer->ownerIndex != typeDefIdx) { - logger.error("genContainer ownerIndex mismatch!"); + il2cpp_utils::getLogger().fmtLog("genContainer ownerIndex mismatch!"); } for (int i = 0; i < genContainer->type_argc; i++) { auto genParamIdx = genContainer->genericParameterStart + i; auto* genParam = il2cpp_functions::MetadataCache_GetGenericParameterFromIndex(genParamIdx); if (genParam) { - logger.debug("genParam #%i, idx %i: ownerIdx %i, name %s, num %i, flags (see " - "IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_X in il2cpp-tabledefs.h) 0x%.2X", i, genParamIdx, genParam->ownerIndex, + il2cpp_utils::getLogger().fmtLog("genParam #{}, idx {}: ownerIdx {}, name {}, num {}, flags (see " + "IL2CPP_GENERIC_PARAMETER_ATTRIBUTE_X in il2cpp-tabledefs.h) 0x{:2X}", i, genParamIdx, genParam->ownerIndex, il2cpp_functions::MetadataCache_GetStringFromIndex(genParam->nameIndex), genParam->num, genParam->flags); } else { - logger.warning("genParam %i, idx %i: null", i, genParamIdx); + il2cpp_utils::getLogger().fmtLog("genParam {}, idx {}: null", i, genParamIdx); } } } else { - logger.debug("genericContainerIndex: %i", klass->genericContainerIndex); + il2cpp_utils::getLogger().fmtLog("genericContainerIndex: {}", klass->genericContainerIndex); } - logger.debug("%i =========METHODS=========", indent); + il2cpp_utils::getLogger().fmtLog("{} =========METHODS=========", indent); LogMethods(logger, klass); - logger.debug("%i =======END METHODS=======", indent); + il2cpp_utils::getLogger().fmtLog("{} =======END METHODS=======", indent); auto* declaring = il2cpp_functions::class_get_declaring_type(klass); - logger.debug("declaring type: %p (%s)", declaring, declaring ? ClassStandardName(declaring).c_str() : ""); + il2cpp_utils::getLogger().fmtLog("declaring type: {} ({})", fmt::ptr(declaring), declaring ? ClassStandardName(declaring).c_str() : ""); if (declaring && logParents) LogClass(logger, declaring, logParents); auto* element = il2cpp_functions::class_get_element_class(klass); - logger.debug("element class: %p ('%s', self = %p)", element, element ? ClassStandardName(element).c_str() : "", klass); + il2cpp_utils::getLogger().fmtLog("element class: {} ('{}', self = {})", fmt::ptr(element), element ? ClassStandardName(element).c_str() : "", fmt::ptr(klass)); if (element && element != klass && logParents) LogClass(logger, element, logParents); - logger.debug("%i =======PROPERTIES=======", indent); + il2cpp_utils::getLogger().fmtLog("{} =======PROPERTIES=======", indent); LogProperties(logger, klass); - logger.debug("%i =====END PROPERTIES=====", indent); - logger.debug("%i =========FIELDS=========", indent); + il2cpp_utils::getLogger().fmtLog("{} =====END PROPERTIES=====", indent); + il2cpp_utils::getLogger().fmtLog("{} =========FIELDS=========", indent); LogFields(logger, klass); - logger.debug("%i =======END FIELDS=======", indent); + il2cpp_utils::getLogger().fmtLog("{} =======END FIELDS=======", indent); auto* parent = il2cpp_functions::class_get_parent(klass); - logger.debug("parent: %p (%s)", parent, parent ? ClassStandardName(parent).c_str() : ""); + il2cpp_utils::getLogger().fmtLog("parent: {} ({})", fmt::ptr(parent), parent ? ClassStandardName(parent).c_str() : ""); if (parent && logParents) LogClass(logger, parent, logParents); - logger.debug("%i, ==================================================================================", indent); + il2cpp_utils::getLogger().fmtLog("{}, ==================================================================================", indent); indent--; } static std::unordered_map>> classToGenericClassMap; void BuildGenericsMap() { - static auto logger = getLogger().WithContext("BuildGenericsMap"); + static auto logger = getLogger(); il2cpp_functions::Init(); auto* metadataReg = RET_V_UNLESS(logger, il2cpp_functions::s_Il2CppMetadataRegistration); - logger.debug("metadataReg: %p, offset = %lX", metadataReg, ((uintptr_t)metadataReg) - getRealOffset(0)); + il2cpp_utils::getLogger().fmtLog("metadataReg: {}, offset = {:X}", fmt::ptr(metadataReg), ((uintptr_t)metadataReg) - getRealOffset(0)); int uncached_class_count = 0; for (int i=0; i < metadataReg->genericClassesCount; i++) { @@ -163,10 +164,10 @@ namespace il2cpp_utils { classToGenericClassMap[typeDefClass][genClassName.c_str()] = genClass; } - logger.debug("uncached_class_count: %i (%f proportion of total)", uncached_class_count, uncached_class_count * 1.0 / metadataReg->genericClassesCount); + il2cpp_utils::getLogger().fmtLog("uncached_class_count: {} ({} proportion of total)", uncached_class_count, uncached_class_count * 1.0 / metadataReg->genericClassesCount); } - void LogClasses(LoggerContextObject& logger, std::string_view classPrefix, bool logParents) noexcept { + void LogClasses(Paper::LoggerContext const& logger, std::string_view classPrefix, bool logParents) noexcept { il2cpp_functions::Init(); BuildGenericsMap(); @@ -181,18 +182,18 @@ namespace il2cpp_utils { for (size_t i = 0; i < size; ++i) { // Get image for each assembly if (!assembs[i]) { - logger.warning("Assembly %zu was null! Skipping.", i); + il2cpp_utils::getLogger().fmtLog("Assembly {} was null! Skipping.", i); continue; } - logger.debug("Scanning assembly \"%s\"", assembs[i]->aname.name); + il2cpp_utils::getLogger().fmtLog("Scanning assembly \"{}\"", assembs[i]->aname.name); auto* img = il2cpp_functions::assembly_get_image(assembs[i]); if (!img) { - logger.warning("Assembly's image was null! Skipping."); + il2cpp_utils::getLogger().fmtLog("Assembly's image was null! Skipping."); continue; } if (img->nameToClassHashTable == nullptr) { - logger.debug("Assembly's nameToClassHashTable is empty. Populating it instead."); + il2cpp_utils::getLogger().fmtLog("Assembly's nameToClassHashTable is empty. Populating it instead."); img->nameToClassHashTable = new Il2CppNameToTypeDefinitionIndexHashTable(); for (uint32_t index = 0; index < img->typeCount; index++) { @@ -207,30 +208,30 @@ namespace il2cpp_utils { } } - for (auto itr = img->nameToClassHashTable->begin(); itr != img->nameToClassHashTable->end(); ++itr) { + for (auto & itr : *img->nameToClassHashTable) { // ->first is a KeyWrapper(pair(namespaceName, className)) // ->second is TypeDefinitionIndex - if (strncmp(classPrefix.data(), itr->first.key.second, classPrefix.length()) == 0) { + if (strncmp(classPrefix.data(), itr.first.key.second, classPrefix.length()) == 0) { // Starts with! // Convert TypeDefinitionIndex --> class - auto klazz = il2cpp_functions::MetadataCache_GetTypeInfoFromTypeDefinitionIndex(itr->second); + auto klazz = il2cpp_functions::MetadataCache_GetTypeInfoFromTypeDefinitionIndex(itr.second); matches[ClassStandardName(klazz)] = klazz; } } } usleep(1000); // 0.001s - logger.debug("LogClasses:"); + il2cpp_utils::getLogger().fmtLog("LogClasses:"); for ( const auto &pair : matches ) { LogClass(logger, pair.second, logParents); indent = -1; for ( const auto &genPair : classToGenericClassMap[pair.second] ) { - logger.debug("%s", genPair.first.c_str()); + il2cpp_utils::getLogger().fmtLog("{}", genPair.first.c_str()); } usleep(1000); // 0.001s } - logger.debug("LogClasses(\"%s\") is complete.", classPrefix.data()); - logger.debug("maxIndent: %i", maxIndent); + il2cpp_utils::getLogger().fmtLog("LogClasses(\"{}\") is complete.", classPrefix.data()); + il2cpp_utils::getLogger().fmtLog("maxIndent: {}", maxIndent); } void AddTypeToNametoClassHashTable(const Il2CppImage* img, TypeDefinitionIndex index) { diff --git a/src/utils/il2cpp-utils-exceptions.cpp b/src/utils/il2cpp-utils-exceptions.cpp index 5088351d..e4872c59 100644 --- a/src/utils/il2cpp-utils-exceptions.cpp +++ b/src/utils/il2cpp-utils-exceptions.cpp @@ -1,3 +1,4 @@ +#include #include "../../shared/utils/il2cpp-utils-exceptions.hpp" #include "../../shared/utils/il2cpp-functions.hpp" @@ -32,10 +33,10 @@ namespace il2cpp_utils { // It will be our caller's responsibility to determine what to do AFTER the backtrace is logged-- whether it be to terminate or rethrow. // Logs the backtrace with the Logging::ERROR level, using the global logger instance. static void log_backtrace_full(void* const* stacktrace_buffer, uint16_t stacktrace_size) { - static auto logger = Logger::get().WithContext("UNCAUGHT-EXCEPTION"); - logger.error("Logging backtrace for RunMethodException with size: %u...", stacktrace_size); - logger.error("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); - logger.error("pid: %i, tid: %i", getpid(), gettid()); + static auto logger = il2cpp_utils::getLogger(); + logger.fmtLog("Logging backtrace for RunMethodException with size: {}...", stacktrace_size); + logger.fmtLog("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); + logger.fmtLog("pid: {}, tid: {}", getpid(), gettid()); for (uint16_t i = 0; i < stacktrace_size; ++i) { Dl_info info; if (dladdr(stacktrace_buffer[i], &info)) { @@ -47,12 +48,12 @@ namespace il2cpp_utils { if (status) { demangled = info.dli_sname; } - logger.error(" #%02i pc %016lx %s (%s)\n", i, addr, info.dli_fname, demangled); + logger.fmtLog(" #{:02} pc {:X} {} ({})\n", i, addr, info.dli_fname, demangled); if (!status) { free(const_cast(demangled)); } } else { - logger.error(" #%02i pc %016lx %s\n", i, addr, info.dli_fname); + logger.fmtLog(" #{:02} pc {:X} {}\n", i, addr, info.dli_fname); } } } diff --git a/src/utils/il2cpp-utils-fields.cpp b/src/utils/il2cpp-utils-fields.cpp index 4d1b2fe4..0b24d596 100644 --- a/src/utils/il2cpp-utils-fields.cpp +++ b/src/utils/il2cpp-utils-fields.cpp @@ -3,13 +3,14 @@ #include "../../shared/utils/hashing.hpp" #include "../../shared/utils/utils.h" #include +#include namespace il2cpp_utils { static std::unordered_map, FieldInfo*, hash_pair> classesNamesToFieldsCache; static std::mutex nameFieldLock; FieldInfo* FindField(Il2CppClass* klass, std::string_view fieldName) { - static auto logger = getLogger().WithContext("FindField"); + static auto logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -24,7 +25,7 @@ namespace il2cpp_utils { nameFieldLock.unlock(); auto field = il2cpp_functions::class_get_field_from_name(klass, fieldName.data()); if (!field) { - logger.error("could not find field %s in class '%s'!", fieldName.data(), ClassStandardName(klass).c_str()); + logger.fmtLog("could not find field {} in class '{}'!", fieldName.data(), ClassStandardName(klass).c_str()); LogFields(logger, klass); if (klass->parent != klass) field = FindField(klass->parent, fieldName); } @@ -35,12 +36,12 @@ namespace il2cpp_utils { } Il2CppClass* GetFieldClass(FieldInfo* field) { - static auto logger = getLogger().WithContext("GetFieldClass"); + static auto logger = getLogger(); auto type = RET_0_UNLESS(logger, il2cpp_functions::field_get_type(field)); return il2cpp_functions::class_from_il2cpp_type(type); } - void LogField(LoggerContextObject& logger, FieldInfo* field) { + void LogField(Paper::LoggerContext const& logger, FieldInfo* field) { il2cpp_functions::Init(); RET_V_UNLESS(logger, field); @@ -52,19 +53,19 @@ namespace il2cpp_utils { name = name ? name : "__noname__"; auto offset = il2cpp_functions::field_get_offset(field); - logger.debug("%s%s %s; // 0x%lx, flags: 0x%.4X", flagStr, typeStr, name, offset, flags); + il2cpp_utils::getLogger().fmtLog("{}{} {}; // 0x{:x}, flags: 0x{:4X}", flagStr, typeStr, name, offset, flags); } - void LogFields(LoggerContextObject& logger, Il2CppClass* klass, bool logParents) { + void LogFields(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) { il2cpp_functions::Init(); RET_V_UNLESS(logger, klass); void* myIter = nullptr; FieldInfo* field; if (klass->name) il2cpp_functions::Class_Init(klass); - if (logParents) logger.info("class name: %s", ClassStandardName(klass).c_str()); + if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); - logger.debug("field_count: %i", klass->field_count); + il2cpp_utils::getLogger().fmtLog("field_count: {}", klass->field_count); while ((field = il2cpp_functions::class_get_fields(klass, &myIter))) { LogField(logger, field); } diff --git a/src/utils/il2cpp-utils-methods.cpp b/src/utils/il2cpp-utils-methods.cpp index e6ac7340..5f94e84f 100644 --- a/src/utils/il2cpp-utils-methods.cpp +++ b/src/utils/il2cpp-utils-methods.cpp @@ -2,6 +2,7 @@ #include "../../shared/utils/il2cpp-utils-methods.hpp" #include "../../shared/utils/hashing.hpp" #include +#include namespace std { // From https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine @@ -40,7 +41,7 @@ namespace il2cpp_utils { const MethodInfo* MakeGenericMethod(const MethodInfo* info, std::vector types) noexcept #endif { - static auto logger = getLogger().WithContext("MakeGenericMethod"); + static auto logger = getLogger(); il2cpp_functions::Init(); // Ensure it exists and is generic THROW_OR_RET_NULL(logger, info); @@ -49,20 +50,20 @@ namespace il2cpp_utils { // Create the Il2CppReflectionMethod* from the MethodInfo* using the MethodInfo's type auto* infoObj = il2cpp_functions::method_get_object(info, nullptr); if (!infoObj) { - logger.error("Failed to get MonoMethod from MethodInfo: %p", info); + il2cpp_utils::getLogger().fmtLog("Failed to get MonoMethod from MethodInfo: {}", fmt::ptr(info)); THROW_OR_RET_NULL(logger, infoObj); } // Populate generic parameters into array auto* arr = reinterpret_cast*>(il2cpp_functions::array_new(typeClass, types.size())); if (!arr) { - logger.error("Failed to create array of length: %lu", types.size()); + il2cpp_utils::getLogger().fmtLog("Failed to create array of length: {}", types.size()); THROW_OR_RET_NULL(logger, arr); } int i = 0; for (auto* klass : types) { auto* typeObj = GetSystemType(klass); if (!typeObj) { - logger.error("Failed to get type object from class: %s", il2cpp_functions::class_get_name_const(klass)); + il2cpp_utils::getLogger().fmtLog("Failed to get type object from class: {}", il2cpp_functions::class_get_name_const(klass)); THROW_OR_RET_NULL(logger, typeObj); } arr->values[i] = typeObj; @@ -73,13 +74,13 @@ namespace il2cpp_utils { auto res = il2cpp_utils::RunMethod(infoObj, "MakeGenericMethod", arr); const auto* returnedInfoObj = RET_0_UNLESS(logger, res); if (!returnedInfoObj) { - logger.error("Failed to get Il2CppReflectionMethod from MakeGenericMethod!"); + il2cpp_utils::getLogger().fmtLog("Failed to get Il2CppReflectionMethod from MakeGenericMethod!"); THROW_OR_RET_NULL(logger, returnedInfoObj); } // Get MethodInfo* back from generic instantiated method const auto* inflatedInfo = il2cpp_functions::method_get_from_reflection(returnedInfoObj); if (!inflatedInfo) { - logger.error("Got null MethodInfo* from Il2CppReflectionMethod: %p", returnedInfoObj); + il2cpp_utils::getLogger().fmtLog("Got null MethodInfo* from Il2CppReflectionMethod: {}", fmt::ptr(returnedInfoObj)); THROW_OR_RET_NULL(logger, inflatedInfo); } // Return method to be invoked by caller @@ -88,7 +89,7 @@ namespace il2cpp_utils { const MethodInfo* ResolveVtableSlot(Il2CppClass* klass, Il2CppClass* declaringClass, uint16_t slot) noexcept { il2cpp_functions::Init(); - static auto logger = getLogger().WithContext("ResolveVtableSlot"); + static auto logger = getLogger(); if(il2cpp_functions::class_is_interface(declaringClass)) { RET_DEFAULT_UNLESS(logger, slot < declaringClass->vtable_count); for (uint16_t i = 0; i < klass->interface_offsets_count; i++) { @@ -98,7 +99,7 @@ namespace il2cpp_utils { return klass->vtable[offset + slot].method; } } - logger.error("could not find method in slot %i of interface '%s' in class '%s'!", slot, ClassStandardName(declaringClass).c_str(), ClassStandardName(klass).c_str()); + il2cpp_utils::getLogger().fmtLog("could not find method in slot {} of interface '{}' in class '{}'!", slot, ClassStandardName(declaringClass).c_str(), ClassStandardName(klass).c_str()); } else { RET_DEFAULT_UNLESS(logger, slot < klass->vtable_count); @@ -118,7 +119,7 @@ namespace il2cpp_utils { #endif { il2cpp_functions::Init(); - static auto logger = getLogger().WithContext("FindMethodUnsafe"); + static auto logger = getLogger(); RET_DEFAULT_UNLESS(logger, klass); // Check Cache @@ -134,7 +135,7 @@ namespace il2cpp_utils { // Recurses through klass's parents auto methodInfo = il2cpp_functions::class_get_method_from_name(klass, methodName.data(), argsCount); if (!methodInfo) { - logger.error("could not find method %s with %i parameters in class '%s'!", methodName.data(), argsCount, ClassStandardName(klass).c_str()); + il2cpp_utils::getLogger().fmtLog("could not find method {} with {} parameters in class '{}'!", methodName.data(), argsCount, ClassStandardName(klass).c_str()); LogMethods(logger, const_cast(klass), true); RET_DEFAULT_UNLESS(logger, methodInfo); } @@ -159,7 +160,7 @@ namespace il2cpp_utils { const MethodInfo* FindMethodUnsafe(Il2CppObject* instance, std::string_view methodName, int argsCount) noexcept #endif { - static auto logger = getLogger().WithContext("FindMethodUnsafe"); + static auto logger = getLogger(); il2cpp_functions::Init(); auto klass = RET_DEFAULT_UNLESS(logger, il2cpp_functions::object_get_class(instance)); return FindMethodUnsafe(klass, methodName, argsCount); @@ -171,7 +172,7 @@ namespace il2cpp_utils { const MethodInfo* FindMethod(FindMethodInfo& info) noexcept #endif { - static auto logger = getLogger().WithContext("FindMethod"); + static auto logger = getLogger(); il2cpp_functions::Init(); auto* klass = info.klass; RET_DEFAULT_UNLESS(logger, klass); @@ -203,13 +204,13 @@ namespace il2cpp_utils { if (info.returnType == returnClass) { if (perfectMatch) { multiplePerfectMatches = true; - logger.error("Multiple perfect matches???"); + il2cpp_utils::getLogger().fmtLog("Multiple perfect matches???"); } else perfectMatch = current; } if (il2cpp_functions::class_is_assignable_from(info.returnType, returnClass)) { if (returnMatch) { - logger.debug("Multiple return type matches."); + il2cpp_utils::getLogger().fmtLog("Multiple return type matches."); multipleReturnMatches = true; } else returnMatch = current; @@ -243,7 +244,7 @@ namespace il2cpp_utils { ss << TypeGetSimpleName(t); } ss << ") in class '" << ClassStandardName(klass) << "'!"; - logger.error("%s", ss.str().c_str()); + il2cpp_utils::getLogger().fmtLog("{}", ss.str().c_str()); LogMethods(logger, klass); RET_DEFAULT_UNLESS(logger, !methodInfo || multipleBasicMatches); } @@ -253,7 +254,7 @@ namespace il2cpp_utils { return methodInfo; } - void LogMethods(LoggerContextObject& logger, Il2CppClass* klass, bool logParents) { + void LogMethods(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) { RET_V_UNLESS(logger, klass); if (klass->name) { @@ -261,18 +262,18 @@ namespace il2cpp_utils { il2cpp_functions::Class_Init(klass); } if (klass->method_count && !(klass->methods)) { - logger.warning("Class is valid and claims to have methods but ->methods is null! class name: %s", ClassStandardName(klass).c_str()); + il2cpp_utils::getLogger().fmtLog("Class is valid and claims to have methods but ->methods is null! class name: {}", ClassStandardName(klass).c_str()); return; } - if (logParents) logger.info("class name: %s", ClassStandardName(klass).c_str()); + if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); - logger.debug("method_count: %i", klass->method_count); + il2cpp_utils::getLogger().fmtLog("method_count: {}", klass->method_count); for (int i = 0; i < klass->method_count; i++) { if (klass->methods[i]) { - logger.debug("Method %i:", i); + il2cpp_utils::getLogger().fmtLog("Method {}:", i); LogMethod(logger, klass->methods[i]); } else { - logger.warning("Method: %i Does not exist!", i); + il2cpp_utils::getLogger().fmtLog("Method: {} Does not exist!", i); } } usleep(100); // 0.0001s @@ -281,7 +282,7 @@ namespace il2cpp_utils { } } - void LogMethod(LoggerContextObject& logger, const MethodInfo* method) { + void LogMethod(Paper::LoggerContext const& logger, const MethodInfo* method) { il2cpp_functions::Init(); RET_V_UNLESS(logger, method); @@ -310,18 +311,18 @@ namespace il2cpp_utils { const auto& paramStrRef = paramStream.str(); const char* paramStr = paramStrRef.c_str(); // TODO: add after methodName - logger.debug("%s%s %s(%s);", flagStr, retTypeStr, methodName, paramStr); + il2cpp_utils::getLogger().fmtLog("{}{} {}({});", flagStr, retTypeStr, methodName, paramStr); } bool IsConvertibleFrom(const Il2CppType* to, const Il2CppType* from, bool asArgs) { - static auto logger = getLogger().WithContext("IsConvertibleFrom"); + static auto logger = getLogger(); RET_0_UNLESS(logger, to); RET_0_UNLESS(logger, from); if (asArgs) { if (to->byref) { if (!from->byref) { - logger.debug("to (%s, %p) is ref/out while from (%s, %p) is not. Not convertible.", - TypeGetSimpleName(to), to, TypeGetSimpleName(from), from); + il2cpp_utils::getLogger().fmtLog("to ({}, {}) is ref/out while from ({}, {}) is not. Not convertible.", + TypeGetSimpleName(to), fmt::ptr(to), TypeGetSimpleName(from), fmt::ptr(from)); return false; } } diff --git a/src/utils/il2cpp-utils-properties.cpp b/src/utils/il2cpp-utils-properties.cpp index abb6ba46..2092a7f6 100644 --- a/src/utils/il2cpp-utils-properties.cpp +++ b/src/utils/il2cpp-utils-properties.cpp @@ -2,6 +2,7 @@ #include "../../shared/utils/il2cpp-utils-properties.hpp" #include "../../shared/utils/utils.h" #include +#include #include "../../shared/utils/hashing.hpp" namespace il2cpp_utils { @@ -9,7 +10,7 @@ namespace il2cpp_utils { static std::mutex classPropertiesLock; const PropertyInfo* FindProperty(Il2CppClass* klass, std::string_view propName) { - static auto logger = getLogger().WithContext("FindProperty"); + static auto logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -24,7 +25,7 @@ namespace il2cpp_utils { classPropertiesLock.unlock(); auto prop = il2cpp_functions::class_get_property_from_name(klass, propName.data()); if (!prop) { - logger.error("could not find property %s in class '%s'!", propName.data(), ClassStandardName(klass).c_str()); + il2cpp_utils::getLogger().fmtLog("could not find property {} in class '{}'!", propName.data(), ClassStandardName(klass)); LogProperties(logger, klass); if (klass->parent != klass) prop = FindProperty(klass->parent, propName); } @@ -38,7 +39,7 @@ namespace il2cpp_utils { return FindProperty(GetClassFromName(nameSpace, className), propertyName); } - void LogProperty(LoggerContextObject& logger, const PropertyInfo* prop) { + void LogProperty(Paper::LoggerContext const& logger, const PropertyInfo* prop) { il2cpp_functions::Init(); RET_V_UNLESS(logger, prop); @@ -58,19 +59,19 @@ namespace il2cpp_utils { } auto typeStr = type ? TypeGetSimpleName(type) : "?type?"; - logger.debug("%s%s %s { %s; %s; }; // flags: 0x%.4X", flagStr, typeStr, name, getterName, setterName, flags); + il2cpp_utils::getLogger().fmtLog("{}{} {} {{ {}; {}; }}; // flags: 0x{:4X}", flagStr, typeStr, name, getterName, setterName, flags); } - void LogProperties(LoggerContextObject& logger, Il2CppClass* klass, bool logParents) { + void LogProperties(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) { il2cpp_functions::Init(); RET_V_UNLESS(logger, klass); void* myIter = nullptr; const PropertyInfo* prop; if (klass->name) il2cpp_functions::Class_Init(klass); - if (logParents) logger.info("class name: %s", ClassStandardName(klass).c_str()); + if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); - logger.debug("property_count: %i", klass->property_count); + il2cpp_utils::getLogger().fmtLog("property_count: {}", klass->property_count); while ((prop = il2cpp_functions::class_get_properties(klass, &myIter))) { LogProperty(logger, prop); } diff --git a/src/utils/il2cpp-utils.cpp b/src/utils/il2cpp-utils.cpp index 38345203..d1b429e5 100644 --- a/src/utils/il2cpp-utils.cpp +++ b/src/utils/il2cpp-utils.cpp @@ -37,14 +37,14 @@ namespace il2cpp_utils { } bool ParameterMatch(const MethodInfo* method, std::vector genTypes, std::vector argTypes) { - static auto logger = getLogger().WithContext("ParameterMatch"); + static auto logger = getLogger(); il2cpp_functions::Init(); if (method->parameters_count != argTypes.size()) { return false; } auto genCount = (method->is_generic && !method->is_inflated) ? method->genericContainer->type_argc : 0; if ((size_t)genCount != genTypes.size()) { - logger.warning("Potential method match had wrong number of generics %i (expected %lu)", + il2cpp_utils::getLogger().fmtLog("Potential method match had wrong number of generics {} (expected {})", genCount, genTypes.size()); return false; } @@ -54,10 +54,10 @@ namespace il2cpp_utils { if (paramType->type == IL2CPP_TYPE_MVAR) { auto genIdx = paramType->data.genericParameterIndex - method->genericContainer->genericParameterStart; if (genIdx < 0) { - logger.warning("Extracted invalid genIdx %i from parameter %i", genIdx, i); + il2cpp_utils::getLogger().fmtLog("Extracted invalid genIdx {} from parameter {}", genIdx, i); } else if (genIdx >= genCount) { - logger.warning("ParameterMatch was not supplied enough genTypes to determine type of parameter %i " - "(had %i, needed %i)!", i, genCount, genIdx); + il2cpp_utils::getLogger().fmtLog("ParameterMatch was not supplied enough genTypes to determine type of parameter {} " + "(had {}, needed {})!", i, genCount, genIdx); } else { auto* klass = genTypes.at(genIdx); paramType = (paramType->byref) ? &klass->this_arg : &klass->byval_arg; @@ -137,13 +137,13 @@ namespace il2cpp_utils { } Il2CppClass* GetParamClass(const MethodInfo* method, int paramIdx) { - static auto logger = getLogger().WithContext("GetParamClass"); + static auto logger = getLogger(); auto type = RET_0_UNLESS(logger, il2cpp_functions::method_get_param(method, paramIdx)); return il2cpp_functions::class_from_il2cpp_type(type); } Il2CppReflectionType* MakeGenericType(Il2CppReflectionType* gt, Il2CppArray* types) { - static auto logger = getLogger().WithContext("MakeGenericType"); + static auto logger = getLogger(); il2cpp_functions::Init(); auto runtimeType = RET_0_UNLESS(logger, il2cpp_functions::defaults->runtimetype_class); @@ -153,7 +153,7 @@ namespace il2cpp_utils { void* params[] = {reinterpret_cast(gt), reinterpret_cast(types)}; auto genericType = il2cpp_functions::runtime_invoke(makeGenericMethod, nullptr, params, &exp); if (exp) { - logger.error("il2cpp_utils: MakeGenericType: Failed with exception: %s", ExceptionToString(exp).c_str()); + il2cpp_utils::getLogger().fmtLog("il2cpp_utils: MakeGenericType: Failed with exception: {}", ExceptionToString(exp).c_str()); return nullptr; } return reinterpret_cast(genericType); @@ -165,7 +165,7 @@ namespace il2cpp_utils { } Il2CppReflectionType* GetSystemType(const Il2CppClass* klass) { - static auto logger = getLogger().WithContext("GetSystemType"); + static auto logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -178,12 +178,12 @@ namespace il2cpp_utils { } void GenericsToStringHelper(Il2CppGenericClass* genClass, std::ostream& os) { - static auto logger = getLogger().WithContext("GenericsToStringHelper"); + static auto logger = getLogger(); auto genContext = &genClass->context; auto* genInst = genContext->class_inst; if (!genInst) { genInst = genContext->method_inst; - if (genInst) logger.warning("Missing class_inst! Trying method_inst?"); + if (genInst) il2cpp_utils::getLogger().fmtLog("Missing class_inst! Trying method_inst?"); } if (genInst) { os << "<"; @@ -195,7 +195,7 @@ namespace il2cpp_utils { } os << ">"; } else { - logger.warning("context->class_inst missing for genClass!"); + il2cpp_utils::getLogger().fmtLog("context->class_inst missing for genClass!"); } } @@ -234,18 +234,18 @@ namespace il2cpp_utils { } Il2CppObject* createManual(const Il2CppClass* klass) noexcept { - static auto logger = getLogger().WithContext("createManual"); + static auto logger = getLogger(); if (!klass) { - logger.error("Cannot create a manual object on a null class!"); + il2cpp_utils::getLogger().fmtLog("Cannot create a manual object on a null class!"); return nullptr; } if (!klass->initialized) { - logger.error("Cannot create an object that does not have an initialized class: %p", klass); + il2cpp_utils::getLogger().fmtLog("Cannot create an object that does not have an initialized class: {}", fmt::ptr(klass)); return nullptr; } auto* obj = reinterpret_cast(gc_alloc_specific(klass->instance_size)); if (!obj) { - logger.error("Failed to allocate GC specific area for instance size: %u", klass->instance_size); + il2cpp_utils::getLogger().fmtLog("Failed to allocate GC specific area for instance size: {}", klass->instance_size); return nullptr; } obj->klass = const_cast(klass); @@ -303,10 +303,10 @@ namespace il2cpp_utils { } bool AssertMatch(const Il2CppObject* source, Il2CppClass* klass) { - static auto logger = getLogger().WithContext("AssertMatch"); + static auto logger = getLogger(); il2cpp_functions::Init(); if (!Match(source, klass)) { - logger.critical("source with class '%s' does not match class '%s'!", + il2cpp_utils::getLogger().fmtLog("source with class '{}' does not match class '{}'!", ClassStandardName(source->klass).c_str(), ClassStandardName(klass).c_str()); SAFE_ABORT(); } diff --git a/src/utils/logging.cpp b/src/utils/logging.cpp index 944e3b06..41f110d9 100644 --- a/src/utils/logging.cpp +++ b/src/utils/logging.cpp @@ -1,400 +1,8 @@ -// TODO: Move this to another place and make it #define agnostic -// Ideally, we would be able to call a one time setup function, perhaps with a ModInfo parameter -// This would either return or initialize a logger instance for us to use with future calls to "log" +#include "shared/utils/logging.hpp" -#include "../../shared/utils/logging.hpp" -#include "modloader/shared/modloader.hpp" -#include -#include -#include -#include "../../shared/utils/utils-functions.h" -#include "../../shared/utils/utils.h" -#include -#include -#include -#include -#include +static Paper::LoggerContext loggerContext = + Paper::Logger::WithContextRuntime("beatsaber-hook-" VERSION, {}); -#ifndef VERSION -#define VERSION "0.0.0" -#endif - -std::list Logger::buffers; -bool Logger::consumerStarted = false; -std::mutex Logger::bufferMutex; - -const char* get_level(Logging::Level level) { - switch (level) - { - case Logging::Level::CRITICAL: - return "CRITICAL"; - case Logging::Level::ERROR: - return "ERROR"; - case Logging::Level::WARNING: - return "WARNING"; - case Logging::Level::INFO: - return "INFO"; - case Logging::Level::DEBUG: - return "DEBUG"; - default: - return "UNKNOWN"; - } -} - -bool createdGlobal = false; - -LoggerBuffer& get_global() { - static LoggerBuffer g(ModInfo{"GlobalLog", VERSION}); - if (!createdGlobal) { - if (fileexists(g.get_path())) { - deletefile(g.get_path()); - } - __android_log_print(Logging::INFO, "QuestHook[Logging]", "Created get_global() log at path: %s", g.get_path().c_str()); - createdGlobal = true; - } - return g; -} - -// UtilsLogger will (by default) log to file. -Logger& Logger::get() { - static auto utilsLogger = new Logger(ModInfo{"UtilsLogger", VERSION}, LoggerOptions(false, true)); - return *utilsLogger; -} - -void LoggerBuffer::flush() { - if (length() == 0) { - // If we have nothing to write, exit early. - return; - } - // We can open the file without locking path, because it is only created on initialization. - std::ofstream file(path, std::ios::app); - if (!file.is_open()) { - __android_log_print(Logging::CRITICAL, Logger::get().tag.c_str(), "Could not open file: %s when flushing buffer!", path.c_str()); - } - // Then, iterate over all messages and write each of them to the file. - // We already must hold the lock for this call. - // Assuming we always append to the END of the list, we could theoretically get away without locking on this call (except for length 1) - messageLock.lock(); - for (; !messages.empty(); messages.pop_front()) { - file << messages.front().c_str() << '\n'; - } - messageLock.unlock(); - file.close(); -} - -std::size_t LoggerBuffer::length() { - if (closed) { - // Ignore messages to write if we are closed. - return 0; - } - return messages.size(); -} - -std::string LoggerBuffer::get_logDir() { - // Copy it - static std::string d = string_format(LOG_PATH, Modloader::getApplicationId().c_str()); - return d; -} - -void LoggerBuffer::addMessage(std::string_view msg) { - if (closed) { - return; - } - messageLock.lock(); - messages.emplace_back(msg.data()); - messageLock.unlock(); -} - -// Now, we COULD be a lot more reasonable and spawn a thread for each buffer logger -// However, I think having one should be fine. -// Flushing while holding the bufferMutex means that new loggers take awhile to create (everything else must be flushed) -// Flushing itself is pretty quick, new messages aren't allowed to be added while the writing is happening (I'm not sure HOW quick it is) -class Consumer { - public: - void operator()() { - // Goal here is that we want to iterate over all of the buffers - // For each one, we flush our log to the file specified by the path of that buffer. - while (true) { - // Lock our bufferMutex - Logger::bufferMutex.lock(); - for (auto* buffer : Logger::buffers) { - // For each buffer, we want to flush all of the messages. - // However, we want to do so in a fashion that isn't terribly unreasonable. - buffer->flush(); - // Ideally, we thread_yield after each buffer flush (may not need to, though) - // std::this_thread::yield(); - } - // Also do the get_global() buffer - get_global().flush(); - Logger::bufferMutex.unlock(); - // Sleep for a bit without the lock to allow other threads to create loggers and add them - std::this_thread::sleep_for(std::chrono::microseconds(500)); - } - } -}; - -void Logger::flushAll() { - __android_log_write(Logging::CRITICAL, Logger::get().tag.c_str(), "Flushing all buffers!"); - Logger::bufferMutex.lock(); - for (auto* buffer : Logger::buffers) { - buffer->flush(); - } - get_global().flush(); - Logger::bufferMutex.unlock(); - __android_log_write(Logging::CRITICAL, Logger::get().tag.c_str(), "All buffers flushed!"); -} - -void Logger::closeAll() { - __android_log_write(Logging::CRITICAL, Logger::get().tag.c_str(), "Closing all buffers!"); - Logger::bufferMutex.lock(); - for (auto* buffer : Logger::buffers) { - buffer->flush(); - buffer->closed = true; - } - get_global().flush(); - get_global().closed = true; - Logger::bufferMutex.unlock(); - __android_log_write(Logging::CRITICAL, Logger::get().tag.c_str(), "All buffers closed!"); -} - -bool Logger::init() { - // So, we want to take a look at our options. - // If we have fileLog set to true, we want to clear the file pointed to by this log. - // That means that we want to delete the existing file (because storing a bunch is pretty obnoxious) - if (options.toFile) { - if (fileexists(buffer.get_path())) { - deletefile(buffer.get_path()); - } - // Now, create the file and paths as necessary. - if (!direxists(buffer.get_logDir())) { - mkpath(buffer.get_logDir()); - __android_log_print(Logging::INFO, tag.c_str(), "Created logger buffer dir: %s", buffer.get_logDir().c_str()); - } - std::ofstream str(buffer.get_path()); - if (!str.is_open()) { - __android_log_print(Logging::CRITICAL, tag.c_str(), "Could not open logger buffer file: %s!", buffer.get_path().c_str()); - return false; - } else { - str.close(); - } - } - return true; -} - -void Logger::flush() { - // Flush our buffer. - // We do this by locking it and reading all of its messages to completion. - Logger::bufferMutex.lock(); - buffer.flush(); - get_global().flush(); - Logger::bufferMutex.unlock(); -} - -void Logger::close() { - Logger::bufferMutex.lock(); - buffer.flush(); - get_global().flush(); - buffer.closed = true; - Logger::bufferMutex.unlock(); -} - -void Logger::startConsumer() { - if (!Logger::consumerStarted) { - consumerStarted = true; - __android_log_write(Logging::INFO, Logger::get().tag.c_str(), "Started consumer thread!"); - std::thread(Consumer()).detach(); - } -} - -void Logger::Backtrace(uint16_t frameCount) { - if (options.silent) return; - void* buffer[frameCount]; - // Skip the captureBacktrace method AND this method - backtrace_helpers::captureBacktrace(buffer, frameCount, 2); - debug("Printing backtrace with: %u max lines:", frameCount); - log(Logging::DEBUG, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); - debug("pid: %i, tid: %i", getpid(), gettid()); - for (uint16_t i = 0; i < frameCount; ++i) { - Dl_info info; - if (dladdr(buffer[i], &info)) { - // Buffer points to 1 instruction ahead - long addr = reinterpret_cast(buffer[i]) - reinterpret_cast(info.dli_fbase) - 4; - if (info.dli_sname) { - int status; - const char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); - if (status) { - demangled = info.dli_sname; - } - debug(" #%02i pc %016lx %s (%s)", i, addr, info.dli_fname, demangled); - if (!status) { - free(const_cast(demangled)); - } - } else { - debug(" #%02i pc %016lx %s", i, addr, info.dli_fname); - } - } - } -} - -void LoggerContextObject::Backtrace(uint16_t frameCount) { - if (!enabled) return; - void* buffer[frameCount]; - // Skip the captureBacktrace method AND this method - backtrace_helpers::captureBacktrace(buffer, frameCount, 2); - debug("Printing backtrace with: %u max lines:", frameCount); - log(Logging::DEBUG, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); - debug("pid: %i, tid: %i", getpid(), gettid()); - for (uint16_t i = 0; i < frameCount; ++i) { - Dl_info info; - if (dladdr(buffer[i], &info)) { - // Buffer points to 1 instruction ahead - long addr = reinterpret_cast(buffer[i]) - reinterpret_cast(info.dli_fbase) - 4; - if (info.dli_sname) { - int status; - const char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); - if (status) { - demangled = info.dli_sname; - } - debug(" #%02i pc %016lx %s (%s)", i, addr, info.dli_fname, demangled); - if (!status) { - free(const_cast(demangled)); - } - } else { - debug(" #%02i pc %016lx %s", i, addr, info.dli_fname); - } - } - } -} - -LoggerContextObject Logger::WithContext(std::string_view context) { - contextMutex.lock(); - for (auto item : disabledContexts) { - if (context.starts_with(item)) { - // Disable context - contextMutex.unlock(); - return LoggerContextObject(*this, context, false); - } - } - // If I am silent, my context objects are disabled. - contextMutex.unlock(); - return LoggerContextObject(*this, context, !options.silent); -} - -LoggerContextObject Logger::WithContext(LoggerContextObject* parent, std::string_view context) { - contextMutex.lock(); - for (auto item : disabledContexts) { - if (context.starts_with(item)) { - // Disable context - if (parent) { - contextMutex.unlock(); - return LoggerContextObject(parent, parent->context + options.contextSeparator + context.data(), false); - } else { - contextMutex.unlock(); - return LoggerContextObject(*this, context, false); - } - } - } - // If I am silent, my context objects are disabled. - if (parent) { - contextMutex.unlock(); - return LoggerContextObject(parent, parent->context + options.contextSeparator + context.data(), !options.silent); - } else { - contextMutex.unlock(); - return LoggerContextObject(*this, context, !options.silent); - } -} - -void Logger::RecurseChangeContext(LoggerContextObject* ctx, std::string_view context, bool enable) { - if (!ctx) { - // Failsafe - return; - } - if (ctx->context.starts_with(context)) { - ctx->enabled = enable; - ctx->changeChildren(enable); - } else { - // For each child, if we didn't match, check to see if its context starts with the context to disable - for (auto* child : ctx->childrenContexts) { - RecurseChangeContext(child, context, enable); - } - } -} - -void Logger::DisableContext(std::string_view context) { - contextMutex.lock(); - disabledContexts.emplace(context.data()); - // We should also iterate over all disabledContexts and determine if any existing contexts need to be disabled. - // Existing contexts could have parent contexts, though. - for (auto* ctx : contexts) { - // For each context, only check those without parents (top level, recurse down) - if (ctx->parentContext == nullptr) { - RecurseChangeContext(ctx, context, false); - } - } - contextMutex.unlock(); -} - -void Logger::EnableContext(std::string_view context) { - contextMutex.lock(); - auto itr = disabledContexts.find(std::string(context)); - if (itr != disabledContexts.end()) { - disabledContexts.erase(itr); - } - for (auto* ctx : contexts) { - // For each context, only check those without parents (top level, recurse down) - if (ctx->parentContext == nullptr) { - RecurseChangeContext(ctx, context, true); - } - } - contextMutex.unlock(); -} - -const std::unordered_set Logger::GetDisabledContexts() { - return disabledContexts; -} - -#define LOG_MAX_CHARS 1000 -void Logger::log(Logging::Level lvl, std::string str) { - if (options.silent) { - return; - } - // Chunk string for logcat buffer - if (str.length() > LOG_MAX_CHARS) { - std::size_t i = 0; - while (i < str.length()) { - auto sub = str.substr(i, LOG_MAX_CHARS); - auto newline = sub.find('\n'); - if (newline != std::string::npos) { - sub = sub.substr(0, newline); - i += newline + 1; // Skip actual newline character - } else { - i += LOG_MAX_CHARS; - } - __android_log_write(lvl, tag.c_str(), sub.c_str()); - } - } else { - __android_log_write(lvl, tag.c_str(), str.c_str()); - } - if (options.toFile) { - // If we want to log to file, we want to write to a shared buffer. - // This buffer should be consumed by a separate thread (started if we haven't yet started any consumer) - // The overhead of this thread should be pretty minimal, all things considered, even if it handles every Logger instance. - // The first thing we need to do is create our buffer, and add it to our buffers (lock while doing so) - // Then, we need to start our thread if we haven't started it already - // Then, we need to add our data to the buffer (lock while doing so) - // The thread needs to consume from these buffers (locks while doing so) - auto now = std::chrono::system_clock::now(); - auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; - auto in_time = std::chrono::system_clock::to_time_t(now); - std::tm bt = *std::localtime(&in_time); - std::ostringstream oss; - oss << std::put_time(&bt, "%m-%d %H:%M:%S.") << std::setfill('0') << std::setw(3) << ms.count(); - auto msg = oss.str() + " " + get_level(lvl) + " " + tag + ": " + str.c_str(); - // __android_log_print(Logging::DEBUG, tag.c_str(), "Logging message: %s to file!", msg.c_str()); - Logger::bufferMutex.lock(); - buffer.addMessage(msg); - get_global().addMessage(msg); - Logger::bufferMutex.unlock(); - startConsumer(); - } -} +Paper::LoggerContext const &il2cpp_utils::getLogger() { + return loggerContext; +} \ No newline at end of file diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index acef100e..6855cf16 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -38,35 +38,48 @@ namespace backtrace_helpers { } } +void WaitForCompleteFlush() { + using namespace std::chrono_literals; + + std::this_thread::sleep_for(500us); + Paper::Logger::WaitForFlush(); + while (Paper::Internal::logQueue.size_approx() > 0) { + std::this_thread::sleep_for(500us); + } +} + void safeAbort(const char* func, const char* file, int line, uint16_t frameCount) { - static auto logger = Logger::get().WithContext("CRASH_UNLESS"); + static auto logger = il2cpp_utils::getLogger(); // we REALLY want this to appear at least once in the log (for fastest fixing) for (int i = 0; i < 2; i++) { usleep(100000L); // 0.1s // TODO: Make this eventually have a passed in context - logger.critical("Aborting in %s at %s:%i", func, file, line); + logger.fmtLog("Aborting in {} at {}:{}", func, file, line); } logger.Backtrace(frameCount); - Logger::closeAll(); usleep(100000L); // 0.1s + WaitForCompleteFlush(); std::terminate(); // cleans things up and then calls abort } -__attribute__((format(printf, 4, 5))) void safeAbortMsg(const char* func, const char* file, int line, const char* fmt, ...) { - static auto logger = Logger::get().WithContext("CRASH_UNLESS"); +void safeAbortMsg(const char* func, const char* file, int line, const char* fmt, ...) { + static auto logger = il2cpp_utils::getLogger(); // we REALLY want this to appear at least once in the log (for fastest fixing) for (int i = 0; i < 2; i++) { usleep(100000L); // 0.1s // TODO: Make this eventually have a passed in context - logger.critical("Aborting in %s at %s:%i", func, file, line); + logger.fmtLog("Aborting in {} at {}:{}", func, file, line); + va_list lst; va_start(lst, fmt); - logger.log_v(Logging::CRITICAL, fmt, lst); + logger.fmtLog("{}", string_vformat(fmt, lst)); va_end(lst); + + } logger.Backtrace(512); - Logger::closeAll(); usleep(100000L); // 0.1s + WaitForCompleteFlush(); std::terminate(); // cleans things up and then calls abort } @@ -81,11 +94,14 @@ void tabs(std::ostream& os, int tabs, int spacesPerTab) { } } -void print(std::stringstream& ss, Logging::Level lvl) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +void print(std::stringstream& ss, Paper::LogLevel lvl) { ss << std::endl; - Logger::get().log(lvl, "%s", ss.str().c_str()); + il2cpp_utils::getLogger().fmtLog("{}", ss.str().c_str()); resetSS(ss); } +#pragma clang diagnostic pop std::string string_vformat(const std::string_view format, va_list args) { size_t size = vsnprintf(nullptr, 0, format.data(), args) + 1; // Extra space for '\0' @@ -149,13 +165,13 @@ void analyzeBytes(std::stringstream& ss, const void* ptr, int indent) { static uintptr_t soSize = 0; uintptr_t getLibil2cppSize() { - static auto contextLogger = Logger::get().WithContext("getSize"); + static auto contextLogger = il2cpp_utils::getLogger(); if (soSize == 0) { struct stat st; if (!stat(Modloader::getLibIl2CppPath().c_str(), &st)) { soSize = st.st_size; } - contextLogger.debug("libil2cpp.so size: 0x%lx", soSize); + il2cpp_utils::getLogger().fmtLog("libil2cpp.so size: 0x{:x}", soSize); } return soSize; } @@ -297,18 +313,18 @@ uintptr_t findPattern(uintptr_t dwAddress, const char* pattern, uintptr_t dwSear uintptr_t findUniquePattern(bool& multiple, uintptr_t dwAddress, const char* pattern, const char* label, uintptr_t dwSearchRangeLen) { uintptr_t firstMatchAddr = 0, newMatchAddr, start = dwAddress, dwEnd = dwAddress + dwSearchRangeLen; int matches = 0; - Logger::get().debug("Sigscan for pattern: %s", pattern); + il2cpp_utils::getLogger().fmtLog("Sigscan for pattern: {}", pattern); while (start > 0 && start < dwEnd && (newMatchAddr = findPattern(start, pattern, dwEnd - start))) { if (!firstMatchAddr) firstMatchAddr = newMatchAddr; matches++; - if (label) Logger::get().debug("Sigscan found possible \"%s\": offset 0x%lX, pointer 0x%lX", label, newMatchAddr - dwAddress, newMatchAddr); + if (label) il2cpp_utils::getLogger().fmtLog("Sigscan found possible \"{}\": offset 0x{:X}, pointer 0x{:X}", label, newMatchAddr - dwAddress, newMatchAddr); start = newMatchAddr + 1; - Logger::get().debug("start = 0x%lX, end = 0x%lX", start, dwEnd); + il2cpp_utils::getLogger().fmtLog("start = 0x{:X}, end = 0x{:X}", start, dwEnd); usleep(1000); } if (matches > 1) { multiple = true; - Logger::get().warning("Multiple sig scan matches for \"%s\"!", label); + il2cpp_utils::getLogger().fmtLog("Multiple sig scan matches for \"{}\"!", label); } return firstMatchAddr; } @@ -384,11 +400,11 @@ std::u16string_view csstrtostr(Il2CppString* in) // Thanks DaNike! void dump(int before, int after, void* ptr) { - Logger::get().debug("Dumping Immediate Pointer: %p: %08x", ptr, *reinterpret_cast(ptr)); + il2cpp_utils::getLogger().fmtLog("Dumping Immediate Pointer: {}: {:08x}", fmt::ptr(ptr), *reinterpret_cast(ptr)); auto begin = static_cast(ptr) - before; auto end = static_cast(ptr) + after; for (auto cur = begin; cur != end; ++cur) { - Logger::get().debug("%p: %08x", cur, *cur); + il2cpp_utils::getLogger().fmtLog("{}: {:08x}", fmt::ptr(cur), *cur); } } From 529b3865a3d0e6385d05f5337643de9dbe768588 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 19 May 2023 15:37:24 -0400 Subject: [PATCH 2/7] Array formatter --- shared/utils/logging_wrappers.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/utils/logging_wrappers.h b/shared/utils/logging_wrappers.h index 2ad879b6..4498f917 100644 --- a/shared/utils/logging_wrappers.h +++ b/shared/utils/logging_wrappers.h @@ -4,6 +4,7 @@ #include "typedefs-wrappers.hpp" #include "typedefs-string.hpp" +#include "typedefs-array.hpp" template struct fmt::formatter> { @@ -39,4 +40,16 @@ struct fmt::formatter : formatter { // ctx.out() is an output iterator to write to. return formatter::format(p.operator std::string(), ctx); } +}; + +template +struct fmt::formatter const> : formatter const> { + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(ArrayW const &p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return formatter const>::format(p.ref_to(), ctx); + } }; \ No newline at end of file From 55c24851bbc6ce88532689fa10e9f8cae3338145 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 19 May 2023 15:39:45 -0400 Subject: [PATCH 3/7] List formatter --- shared/utils/logging_wrappers.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/utils/logging_wrappers.h b/shared/utils/logging_wrappers.h index 4498f917..fb1ee06d 100644 --- a/shared/utils/logging_wrappers.h +++ b/shared/utils/logging_wrappers.h @@ -5,6 +5,7 @@ #include "typedefs-wrappers.hpp" #include "typedefs-string.hpp" #include "typedefs-array.hpp" +#include "typedefs-list.hpp" template struct fmt::formatter> { @@ -52,4 +53,16 @@ struct fmt::formatter const> : formatter cons // ctx.out() is an output iterator to write to. return formatter const>::format(p.ref_to(), ctx); } +}; + +template +struct fmt::formatter const> : formatter const> { + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(ListWrapper const &p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return formatter const>::format(p.operator const std::span(), ctx); + } }; \ No newline at end of file From afb4b449b020cfd7e1b6d73183ffd1a3007e26c9 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 19 May 2023 16:35:33 -0400 Subject: [PATCH 4/7] bs_hook::Il2CppWrapperType formatter --- shared/utils/logging_wrappers.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/utils/logging_wrappers.h b/shared/utils/logging_wrappers.h index fb1ee06d..869aae26 100644 --- a/shared/utils/logging_wrappers.h +++ b/shared/utils/logging_wrappers.h @@ -2,6 +2,7 @@ #include "logging.hpp" +#include "base-wrapper-type.hpp" #include "typedefs-wrappers.hpp" #include "typedefs-string.hpp" #include "typedefs-array.hpp" @@ -43,6 +44,18 @@ struct fmt::formatter : formatter { } }; +template<> +struct fmt::formatter : formatter { + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + template + auto format(bs_hook::Il2CppWrapperType const &p, FormatContext &ctx) -> decltype(ctx.out()) { + // ctx.out() is an output iterator to write to. + return formatter::format(p.convert(), ctx); + } +}; + template struct fmt::formatter const> : formatter const> { From a36b9c02c633d8b63a30fa51d99ab67a6b2b67f2 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Sat, 20 May 2023 09:54:09 -0400 Subject: [PATCH 5/7] Accept children of bs_hook::Il2CppWrapperType --- shared/utils/{logging_wrappers.h => logging_wrappers.hpp} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename shared/utils/{logging_wrappers.h => logging_wrappers.hpp} (95%) diff --git a/shared/utils/logging_wrappers.h b/shared/utils/logging_wrappers.hpp similarity index 95% rename from shared/utils/logging_wrappers.h rename to shared/utils/logging_wrappers.hpp index 869aae26..bb38bbba 100644 --- a/shared/utils/logging_wrappers.h +++ b/shared/utils/logging_wrappers.hpp @@ -44,8 +44,9 @@ struct fmt::formatter : formatter { } }; -template<> -struct fmt::formatter : formatter { +template +requires(std::is_base_of_v) +struct fmt::formatter : formatter { // Formats the point p using the parsed format specification (presentation) // stored in this formatter. From 08cf4f5b46f8fe3d3804c60a5069eaa69ea1bf66 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:08:18 -0400 Subject: [PATCH 6/7] Reduce verbosity, handle null --- shared/utils/logging.hpp | 42 +++++++++------------- shared/utils/logging_wrappers.hpp | 58 +++++++++++-------------------- 2 files changed, 38 insertions(+), 62 deletions(-) diff --git a/shared/utils/logging.hpp b/shared/utils/logging.hpp index d4f68615..cbbf188e 100644 --- a/shared/utils/logging.hpp +++ b/shared/utils/logging.hpp @@ -25,56 +25,48 @@ struct fmt::formatter : formatter { // stored in this formatter. template auto format(Il2CppClass const* p, FormatContext &ctx) -> decltype(ctx.out()) { + if (!p) return format_to(ctx.out(), "null"); // ctx.out() is an output iterator to write to. return formatter::format(::il2cpp_utils::ClassStandardName(p, true), ctx); } }; template<> struct fmt::formatter : formatter { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. template auto format(Il2CppType const* p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - return format_to(ctx.out(), "{}|{}", p->attrs, ::il2cpp_utils::TypeGetSimpleName(p)); + if (!p) return format_to(ctx.out(), "null"); + + return format_to(ctx.out(), "{:x}|{}", p->attrs, ::il2cpp_utils::TypeGetSimpleName(p)); } }; template<> -struct fmt::formatter { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. +struct fmt::formatter { template - auto format(MethodInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - return format_to(ctx.out(), "{} {}.{}", p->return_type, p->klass, p->name, - fmt::join(p->parameters, p->parameters + p->parameters_count, ", ")); + auto format(ParameterInfo const& p, FormatContext &ctx) -> decltype(ctx.out()) { + return format_to(ctx.out(), "{} {}", p.parameter_type, p.name); } }; -template<> -struct fmt::formatter { - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. +template<> +struct fmt::formatter { template - auto format(ParameterInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - return format_to(ctx.out(), "{} {} {}", p->token, p->parameter_type, p->name); + auto format(MethodInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { + if (!p) return format_to(ctx.out(), "null"); + + return format_to(ctx.out(), "{} {}.{}({})", p->return_type, p->klass, p->name, + fmt::join(p->parameters, p->parameters + p->parameters_count, ", ")); } }; + template<> struct fmt::formatter { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. template auto format(FieldInfo const* p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - return format_to(ctx.out(), "{}|{:x} {} {}", p->offset, p->token, p->type, p->name); + if (!p) return format_to(ctx.out(), "null"); + return format_to(ctx.out(), "{:x}|{:x} {} {}", p->offset, p->token, p->type, p->name); } }; diff --git a/shared/utils/logging_wrappers.hpp b/shared/utils/logging_wrappers.hpp index bb38bbba..19991cb9 100644 --- a/shared/utils/logging_wrappers.hpp +++ b/shared/utils/logging_wrappers.hpp @@ -9,74 +9,58 @@ #include "typedefs-list.hpp" template -struct fmt::formatter> { +struct fmt::formatter const> { + template + auto format(SafePtr const &p, FormatContext &ctx) -> decltype(ctx.out()) { + if (!p.isHandleValid()) return format_to(ctx.out(), "SafePtr(null)"); -// Formats the point p using the parsed format specification (presentation) -// stored in this formatter. -template -auto format(SafePtr const& p, FormatContext &ctx) -> decltype(ctx.out()) { -// ctx.out() is an output iterator to write to. -return format_to(ctx.out(), "{}|{}", p.operator bool(), fmt::ptr(p)); -} + return format_to(ctx.out(), "{}|{}", p.operator bool(), fmt::ptr(p.ptr())); + } }; template -struct fmt::formatter> { +struct fmt::formatter const> { -// Formats the point p using the parsed format specification (presentation) -// stored in this formatter. -template -auto format(SafePtr const& p, FormatContext &ctx) -> decltype(ctx.out()) { -// ctx.out() is an output iterator to write to. -return format_to(ctx.out(), "{}|{}", p.operator bool(), fmt::ptr(p)); -} + template + auto format(SafePtrUnity const &p, FormatContext &ctx) -> decltype(ctx.out()) { + if (!p.isHandleValid()) return format_to(ctx.out(), "SafePtrUnity(null)"); + + return format_to(ctx.out(), "{}{}", p.isHandleValid() ? "" : "destroyed ", fmt::ptr(p.ptr())); + } }; template<> struct fmt::formatter : formatter { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. template auto format(StringW const &p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. + if (!p) return "null"; return formatter::format(p.operator std::string(), ctx); } }; -template -requires(std::is_base_of_v) -struct fmt::formatter : formatter { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. +template requires(std::is_base_of_v) +struct fmt::formatter : formatter { template - auto format(bs_hook::Il2CppWrapperType const &p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - return formatter::format(p.convert(), ctx); + auto format(bs_hook::Il2CppWrapperType const &p, FormatContext &ctx) -> decltype(ctx.out()) { + if (!p) return "null"; + return formatter::format(p.convert(), ctx); } }; template struct fmt::formatter const> : formatter const> { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. template auto format(ArrayW const &p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. + if (!p) return "null"; return formatter const>::format(p.ref_to(), ctx); } }; template struct fmt::formatter const> : formatter const> { - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. template auto format(ListWrapper const &p, FormatContext &ctx) -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. + if (!p) return "null"; return formatter const>::format(p.operator const std::span(), ctx); } }; \ No newline at end of file From 69de0d8240f58e00bb3c236cbbb8def7fe99e38c Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:09:17 -0400 Subject: [PATCH 7/7] Bring back some if guards, remove static loggers and use some logger formatters --- shared/utils/il2cpp-functions.hpp | 10 ++++-- shared/utils/il2cpp-type-check.hpp | 2 +- shared/utils/il2cpp-utils-classes.hpp | 8 ++--- shared/utils/il2cpp-utils-methods.hpp | 2 +- shared/utils/utils-functions.h | 9 +++-- shared/utils/utils.h | 2 +- src/utils/hook-tracker.cpp | 2 +- src/utils/il2cpp-functions.cpp | 3 -- src/utils/il2cpp-type-check.cpp | 6 ++-- src/utils/il2cpp-utils-classes.cpp | 2 +- src/utils/il2cpp-utils-exceptions.cpp | 2 +- src/utils/il2cpp-utils-fields.cpp | 10 +++--- src/utils/il2cpp-utils-methods.cpp | 51 ++++++++++++++------------- src/utils/il2cpp-utils-properties.cpp | 10 +++--- src/utils/il2cpp-utils.cpp | 26 +++++++------- src/utils/logging.cpp | 6 ++-- src/utils/utils.cpp | 4 +-- 17 files changed, 81 insertions(+), 74 deletions(-) diff --git a/shared/utils/il2cpp-functions.hpp b/shared/utils/il2cpp-functions.hpp index c7ac575e..25b10108 100644 --- a/shared/utils/il2cpp-functions.hpp +++ b/shared/utils/il2cpp-functions.hpp @@ -1,4 +1,6 @@ -#pragma once +// ifguard because pragma once doesn't work as intended +#ifndef IL2CPP_FUNCTIONS_H +#define IL2CPP_FUNCTIONS_H #pragma pack(push) #include @@ -433,7 +435,7 @@ class il2cpp_functions { // must be done on-demand because the pointers aren't necessarily correct at the time of il2cpp_functions::Init static void CheckS_GlobalMetadata() { if (!s_GlobalMetadataHeader) { - static auto& logger = ::il2cpp_utils::getLogger(); + auto& logger = ::il2cpp_utils::getLogger(); s_GlobalMetadata = *CRASH_UNLESS(il2cpp_functions::s_GlobalMetadataPtr); s_GlobalMetadataHeader = *CRASH_UNLESS(il2cpp_functions::s_GlobalMetadataHeaderPtr); s_Il2CppMetadataRegistration = *CRASH_UNLESS(il2cpp_functions::s_Il2CppMetadataRegistrationPtr); @@ -464,4 +466,6 @@ class il2cpp_functions { #undef API_FUNC -#pragma pack(pop) \ No newline at end of file +#pragma pack(pop) + +#endif \ No newline at end of file diff --git a/shared/utils/il2cpp-type-check.hpp b/shared/utils/il2cpp-type-check.hpp index 613db253..8301f766 100644 --- a/shared/utils/il2cpp-type-check.hpp +++ b/shared/utils/il2cpp-type-check.hpp @@ -102,7 +102,7 @@ namespace il2cpp_utils { #endif static inline Il2CppClass* get() { il2cpp_functions::Init(); - static auto& logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); auto* klass = RET_0_UNLESS(logger, il2cpp_no_arg_class::get()); RET_0_UNLESS(logger, il2cpp_functions::class_is_valuetype(klass)); return il2cpp_functions::Class_GetPtrClass(klass); diff --git a/shared/utils/il2cpp-utils-classes.hpp b/shared/utils/il2cpp-utils-classes.hpp index 37ff8ee2..3dd7a1a2 100644 --- a/shared/utils/il2cpp-utils-classes.hpp +++ b/shared/utils/il2cpp-utils-classes.hpp @@ -19,7 +19,7 @@ namespace il2cpp_utils { void* val = obj; // nullptr (which runtime_invoke returns for "void" return type!) is different from nullopt (a runtime_invoke error!) if (obj && il2cpp_functions::class_is_valuetype(il2cpp_functions::object_get_class(obj))) { - static auto& logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); // So, because il2cpp finds it necessary to box returned value types (and also not deallocate them), we need to free them ourselves. // What we need to do is first extract the value, which we can do by casting and dereferencing // Then we need to PROPERLY free the allocating object at obj @@ -90,7 +90,7 @@ namespace il2cpp_utils { Il2CppClass* ExtractClass(T&& arg) { using Dt = ::std::decay_t; using arg_class = il2cpp_type_check::il2cpp_arg_class
; - static auto& logger = getLogger(); + auto const& logger = getLogger(); Il2CppClass* klass = arg_class::get(arg); if (!klass) { logger.fmtLog("Failed to determine class! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); @@ -101,7 +101,7 @@ namespace il2cpp_utils { template Il2CppClass* NoArgClass() { // TODO: change ifndef HAS_CODEGEN to 'if compile warnings are not errors'? - static auto& logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); #ifndef HAS_CODEGEN using arg_class = il2cpp_type_check::il2cpp_no_arg_class; if constexpr (!has_get) { @@ -122,7 +122,7 @@ namespace il2cpp_utils { template const Il2CppType* ExtractType(T&& arg) { - static auto& logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); const Il2CppType* typ = il2cpp_type_check::il2cpp_arg_type::get(arg); if (!typ) logger.fmtLog("ExtractType: failed to determine type! Tips: instead of nullptr, pass the Il2CppType* or Il2CppClass* of the argument instead!"); diff --git a/shared/utils/il2cpp-utils-methods.hpp b/shared/utils/il2cpp-utils-methods.hpp index 931fefda..8c1749d2 100644 --- a/shared/utils/il2cpp-utils-methods.hpp +++ b/shared/utils/il2cpp-utils-methods.hpp @@ -273,7 +273,7 @@ namespace il2cpp_utils { template bool ParameterMatch(const MethodInfo* method, std::array const& genTypes, std::array const& argTypes) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); if (method->parameters_count != argSz) { return false; diff --git a/shared/utils/utils-functions.h b/shared/utils/utils-functions.h index b95164a9..10e011aa 100644 --- a/shared/utils/utils-functions.h +++ b/shared/utils/utils-functions.h @@ -1,4 +1,7 @@ -#pragma once +// ifdef guard required + +#ifndef UTILS_FUNCTIONS_H +#define UTILS_FUNCTIONS_H #include #include @@ -69,4 +72,6 @@ namespace backtrace_helpers { }; _Unwind_Reason_Code unwindCallback(struct _Unwind_Context *context, void *arg); size_t captureBacktrace(void **buffer, uint16_t max, uint16_t skip = 0); -} \ No newline at end of file +} + +#endif /* UTILS_FUNCTIONS_H */ \ No newline at end of file diff --git a/shared/utils/utils.h b/shared/utils/utils.h index 32a16550..818bd27f 100644 --- a/shared/utils/utils.h +++ b/shared/utils/utils.h @@ -230,7 +230,7 @@ template uintptr_t getBase(T pc) { static_assert(sizeof(T) >= sizeof(void*)); Dl_info info; - static auto& logger = ::il2cpp_utils::getLogger(); + auto const& logger = ::il2cpp_utils::getLogger(); RET_0_UNLESS(logger, dladdr((void*)pc, &info)); return (uintptr_t)info.dli_fbase; } diff --git a/src/utils/hook-tracker.cpp b/src/utils/hook-tracker.cpp index 13ec0e93..c4006081 100644 --- a/src/utils/hook-tracker.cpp +++ b/src/utils/hook-tracker.cpp @@ -74,7 +74,7 @@ const void* HookTracker::GetOrigInternal(const void* const location) noexcept { #include void HookTracker::CombineHooks() noexcept { - static auto logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); auto libsFolder = string_format(LIBS_FILE_PATH, Modloader::getApplicationId().c_str()); auto tmpPath = Modloader::getDestinationPath(); DIR* dir = opendir(libsFolder.c_str()); diff --git a/src/utils/il2cpp-functions.cpp b/src/utils/il2cpp-functions.cpp index 92d112f0..ff4a9d6b 100644 --- a/src/utils/il2cpp-functions.cpp +++ b/src/utils/il2cpp-functions.cpp @@ -395,7 +395,6 @@ bool il2cpp_functions::find_GC_free(const uint32_t* Runtime_Shutdown) { il2cpp_functions::il2cpp_GC_free = reinterpret_cast(sigMatch); return true; } - static auto logger = il2cpp_utils::getLogger(); auto blrStart = cs::find_through_hooks(Runtime_Shutdown, 4096, [](auto... pairs) { std::array tracked{pairs...}; @@ -417,7 +416,6 @@ bool il2cpp_functions::find_GC_SetWriteBarrier(const uint32_t* set_wbarrier_fiel if (!set_wbarrier_field) { return false; } - static auto logger = il2cpp_utils::getLogger(); auto tmp = cs::findNthB<1>(set_wbarrier_field); if (!tmp) return false; il2cpp_GarbageCollector_SetWriteBarrier = reinterpret_cast(*tmp); @@ -432,7 +430,6 @@ void* __wrapper_gc_malloc_uncollectable(size_t sz, [[maybe_unused]] void* desc) } bool il2cpp_functions::trace_GC_AllocFixed(const uint32_t* DomainGetCurrent) { - static auto logger = il2cpp_utils::getLogger(); // Domain::GetCurrent has a single bl to GarbageCollector::AllocateFixed // MetadataCache::InitializeGCSafe is 3rd bl after first b.ne, which is the 6th b(.lt, .ne), t(bz, nz), c(bz, nz) auto tmp = cs::findNthBl<1>(DomainGetCurrent); diff --git a/src/utils/il2cpp-type-check.cpp b/src/utils/il2cpp-type-check.cpp index b311545b..c35b2273 100644 --- a/src/utils/il2cpp-type-check.cpp +++ b/src/utils/il2cpp-type-check.cpp @@ -10,7 +10,7 @@ namespace il2cpp_utils { Il2CppClass* GetClassFromName(std::string_view name_space, std::string_view type_name) { il2cpp_functions::Init(); - static auto logger = getLogger(); + auto const& logger = getLogger(); // TODO: avoid creating std::string at any point except new pair insertion via P0919 // Check cache @@ -48,7 +48,7 @@ namespace il2cpp_utils { Il2CppClass* MakeGeneric(const Il2CppClass* klass, std::vector args) { il2cpp_functions::Init(); - static auto logger = getLogger(); + auto const& logger = getLogger(); static auto typ = RET_0_UNLESS(logger, il2cpp_functions::defaults->systemtype_class); auto klassType = RET_0_UNLESS(logger, GetSystemType(klass)); @@ -78,7 +78,7 @@ namespace il2cpp_utils { Il2CppClass* MakeGeneric(const Il2CppClass* klass, const Il2CppType** types, uint32_t numTypes) { il2cpp_functions::Init(); - static auto logger = getLogger(); + auto const& logger = getLogger(); static auto typ = RET_0_UNLESS(logger, il2cpp_functions::defaults->systemtype_class); auto klassType = RET_0_UNLESS(logger, GetSystemType(klass)); diff --git a/src/utils/il2cpp-utils-classes.cpp b/src/utils/il2cpp-utils-classes.cpp index 9833e943..b8c860d0 100644 --- a/src/utils/il2cpp-utils-classes.cpp +++ b/src/utils/il2cpp-utils-classes.cpp @@ -145,7 +145,7 @@ namespace il2cpp_utils { static std::unordered_map>> classToGenericClassMap; void BuildGenericsMap() { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); auto* metadataReg = RET_V_UNLESS(logger, il2cpp_functions::s_Il2CppMetadataRegistration); il2cpp_utils::getLogger().fmtLog("metadataReg: {}, offset = {:X}", fmt::ptr(metadataReg), ((uintptr_t)metadataReg) - getRealOffset(0)); diff --git a/src/utils/il2cpp-utils-exceptions.cpp b/src/utils/il2cpp-utils-exceptions.cpp index e4872c59..707102b2 100644 --- a/src/utils/il2cpp-utils-exceptions.cpp +++ b/src/utils/il2cpp-utils-exceptions.cpp @@ -33,7 +33,7 @@ namespace il2cpp_utils { // It will be our caller's responsibility to determine what to do AFTER the backtrace is logged-- whether it be to terminate or rethrow. // Logs the backtrace with the Logging::ERROR level, using the global logger instance. static void log_backtrace_full(void* const* stacktrace_buffer, uint16_t stacktrace_size) { - static auto logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); logger.fmtLog("Logging backtrace for RunMethodException with size: {}...", stacktrace_size); logger.fmtLog("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); logger.fmtLog("pid: {}, tid: {}", getpid(), gettid()); diff --git a/src/utils/il2cpp-utils-fields.cpp b/src/utils/il2cpp-utils-fields.cpp index 0b24d596..ecab1714 100644 --- a/src/utils/il2cpp-utils-fields.cpp +++ b/src/utils/il2cpp-utils-fields.cpp @@ -10,7 +10,7 @@ namespace il2cpp_utils { static std::mutex nameFieldLock; FieldInfo* FindField(Il2CppClass* klass, std::string_view fieldName) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -36,7 +36,7 @@ namespace il2cpp_utils { } Il2CppClass* GetFieldClass(FieldInfo* field) { - static auto logger = getLogger(); + auto const& logger = getLogger(); auto type = RET_0_UNLESS(logger, il2cpp_functions::field_get_type(field)); return il2cpp_functions::class_from_il2cpp_type(type); } @@ -53,7 +53,7 @@ namespace il2cpp_utils { name = name ? name : "__noname__"; auto offset = il2cpp_functions::field_get_offset(field); - il2cpp_utils::getLogger().fmtLog("{}{} {}; // 0x{:x}, flags: 0x{:4X}", flagStr, typeStr, name, offset, flags); + logger.fmtLog("{}{} {}; // 0x{:x}, flags: 0x{:4X}", flagStr, typeStr, name, offset, flags); } void LogFields(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) { @@ -63,9 +63,9 @@ namespace il2cpp_utils { void* myIter = nullptr; FieldInfo* field; if (klass->name) il2cpp_functions::Class_Init(klass); - if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); + if (logParents) logger.fmtLog("class name: {}", ClassStandardName(klass).c_str()); - il2cpp_utils::getLogger().fmtLog("field_count: {}", klass->field_count); + logger.fmtLog("field_count: {}", klass->field_count); while ((field = il2cpp_functions::class_get_fields(klass, &myIter))) { LogField(logger, field); } diff --git a/src/utils/il2cpp-utils-methods.cpp b/src/utils/il2cpp-utils-methods.cpp index 5f94e84f..aed08184 100644 --- a/src/utils/il2cpp-utils-methods.cpp +++ b/src/utils/il2cpp-utils-methods.cpp @@ -3,6 +3,8 @@ #include "../../shared/utils/hashing.hpp" #include #include +#include "shared/utils/logging_wrappers.hpp" +#include "shared/utils/logging.hpp" namespace std { // From https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine @@ -41,7 +43,7 @@ namespace il2cpp_utils { const MethodInfo* MakeGenericMethod(const MethodInfo* info, std::vector types) noexcept #endif { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); // Ensure it exists and is generic THROW_OR_RET_NULL(logger, info); @@ -50,20 +52,20 @@ namespace il2cpp_utils { // Create the Il2CppReflectionMethod* from the MethodInfo* using the MethodInfo's type auto* infoObj = il2cpp_functions::method_get_object(info, nullptr); if (!infoObj) { - il2cpp_utils::getLogger().fmtLog("Failed to get MonoMethod from MethodInfo: {}", fmt::ptr(info)); + logger.fmtLog("Failed to get MonoMethod from MethodInfo: {}", info); THROW_OR_RET_NULL(logger, infoObj); } // Populate generic parameters into array auto* arr = reinterpret_cast*>(il2cpp_functions::array_new(typeClass, types.size())); if (!arr) { - il2cpp_utils::getLogger().fmtLog("Failed to create array of length: {}", types.size()); + logger.fmtLog("Failed to create array of length: {}", types.size()); THROW_OR_RET_NULL(logger, arr); } int i = 0; for (auto* klass : types) { auto* typeObj = GetSystemType(klass); if (!typeObj) { - il2cpp_utils::getLogger().fmtLog("Failed to get type object from class: {}", il2cpp_functions::class_get_name_const(klass)); + logger.fmtLog("Failed to get type object from class: {}", il2cpp_functions::class_get_name_const(klass)); THROW_OR_RET_NULL(logger, typeObj); } arr->values[i] = typeObj; @@ -74,22 +76,22 @@ namespace il2cpp_utils { auto res = il2cpp_utils::RunMethod(infoObj, "MakeGenericMethod", arr); const auto* returnedInfoObj = RET_0_UNLESS(logger, res); if (!returnedInfoObj) { - il2cpp_utils::getLogger().fmtLog("Failed to get Il2CppReflectionMethod from MakeGenericMethod!"); + logger.fmtLog("Failed to get Il2CppReflectionMethod from MakeGenericMethod!"); THROW_OR_RET_NULL(logger, returnedInfoObj); } // Get MethodInfo* back from generic instantiated method const auto* inflatedInfo = il2cpp_functions::method_get_from_reflection(returnedInfoObj); if (!inflatedInfo) { - il2cpp_utils::getLogger().fmtLog("Got null MethodInfo* from Il2CppReflectionMethod: {}", fmt::ptr(returnedInfoObj)); + logger.fmtLog("Got null MethodInfo* from Il2CppReflectionMethod: {}", fmt::ptr(returnedInfoObj)); THROW_OR_RET_NULL(logger, inflatedInfo); } // Return method to be invoked by caller return inflatedInfo; } - const MethodInfo* ResolveVtableSlot(Il2CppClass* klass, Il2CppClass* declaringClass, uint16_t slot) noexcept { + const MethodInfo* ResolveVtableSlot(Il2CppClass const* klass, Il2CppClass const* declaringClass, uint16_t slot) noexcept { il2cpp_functions::Init(); - static auto logger = getLogger(); + auto const& logger = getLogger(); if(il2cpp_functions::class_is_interface(declaringClass)) { RET_DEFAULT_UNLESS(logger, slot < declaringClass->vtable_count); for (uint16_t i = 0; i < klass->interface_offsets_count; i++) { @@ -99,7 +101,7 @@ namespace il2cpp_utils { return klass->vtable[offset + slot].method; } } - il2cpp_utils::getLogger().fmtLog("could not find method in slot {} of interface '{}' in class '{}'!", slot, ClassStandardName(declaringClass).c_str(), ClassStandardName(klass).c_str()); + logger.fmtLog("could not find method in slot {} of interface '{}' in class '{}'!", slot, declaringClass, klass); } else { RET_DEFAULT_UNLESS(logger, slot < klass->vtable_count); @@ -119,7 +121,7 @@ namespace il2cpp_utils { #endif { il2cpp_functions::Init(); - static auto logger = getLogger(); + auto const& logger = getLogger(); RET_DEFAULT_UNLESS(logger, klass); // Check Cache @@ -135,7 +137,7 @@ namespace il2cpp_utils { // Recurses through klass's parents auto methodInfo = il2cpp_functions::class_get_method_from_name(klass, methodName.data(), argsCount); if (!methodInfo) { - il2cpp_utils::getLogger().fmtLog("could not find method {} with {} parameters in class '{}'!", methodName.data(), argsCount, ClassStandardName(klass).c_str()); + logger.fmtLog("could not find method {} with {} parameters in class '{}'!", methodName.data(), argsCount, ClassStandardName(klass)); LogMethods(logger, const_cast(klass), true); RET_DEFAULT_UNLESS(logger, methodInfo); } @@ -160,7 +162,7 @@ namespace il2cpp_utils { const MethodInfo* FindMethodUnsafe(Il2CppObject* instance, std::string_view methodName, int argsCount) noexcept #endif { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); auto klass = RET_DEFAULT_UNLESS(logger, il2cpp_functions::object_get_class(instance)); return FindMethodUnsafe(klass, methodName, argsCount); @@ -172,7 +174,7 @@ namespace il2cpp_utils { const MethodInfo* FindMethod(FindMethodInfo& info) noexcept #endif { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); auto* klass = info.klass; RET_DEFAULT_UNLESS(logger, klass); @@ -204,13 +206,13 @@ namespace il2cpp_utils { if (info.returnType == returnClass) { if (perfectMatch) { multiplePerfectMatches = true; - il2cpp_utils::getLogger().fmtLog("Multiple perfect matches???"); + logger.fmtLog("Multiple perfect matches???"); } else perfectMatch = current; } if (il2cpp_functions::class_is_assignable_from(info.returnType, returnClass)) { if (returnMatch) { - il2cpp_utils::getLogger().fmtLog("Multiple return type matches."); + logger.fmtLog("Multiple return type matches."); multipleReturnMatches = true; } else returnMatch = current; @@ -244,7 +246,7 @@ namespace il2cpp_utils { ss << TypeGetSimpleName(t); } ss << ") in class '" << ClassStandardName(klass) << "'!"; - il2cpp_utils::getLogger().fmtLog("{}", ss.str().c_str()); + logger.fmtLog("{}", ss.str().c_str()); LogMethods(logger, klass); RET_DEFAULT_UNLESS(logger, !methodInfo || multipleBasicMatches); } @@ -262,18 +264,18 @@ namespace il2cpp_utils { il2cpp_functions::Class_Init(klass); } if (klass->method_count && !(klass->methods)) { - il2cpp_utils::getLogger().fmtLog("Class is valid and claims to have methods but ->methods is null! class name: {}", ClassStandardName(klass).c_str()); + logger.fmtLog("Class is valid and claims to have methods but ->methods is null! class name: {}", klass); return; } - if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); + if (logParents) logger.fmtLog("class name: {}", klass); - il2cpp_utils::getLogger().fmtLog("method_count: {}", klass->method_count); + logger.fmtLog("method_count: {}", klass->method_count); for (int i = 0; i < klass->method_count; i++) { if (klass->methods[i]) { - il2cpp_utils::getLogger().fmtLog("Method {}:", i); + logger.fmtLog("Method {}:", i); LogMethod(logger, klass->methods[i]); } else { - il2cpp_utils::getLogger().fmtLog("Method: {} Does not exist!", i); + logger.fmtLog("Method: {} Does not exist!", i); } } usleep(100); // 0.0001s @@ -309,19 +311,18 @@ namespace il2cpp_utils { paramStream << (name ? name : "__noname__"); } const auto& paramStrRef = paramStream.str(); - const char* paramStr = paramStrRef.c_str(); // TODO: add after methodName - il2cpp_utils::getLogger().fmtLog("{}{} {}({});", flagStr, retTypeStr, methodName, paramStr); + logger.fmtLog("{}{} {}({});", flagStr, retTypeStr, methodName, paramStrRef); } bool IsConvertibleFrom(const Il2CppType* to, const Il2CppType* from, bool asArgs) { - static auto logger = getLogger(); + auto const& logger = getLogger(); RET_0_UNLESS(logger, to); RET_0_UNLESS(logger, from); if (asArgs) { if (to->byref) { if (!from->byref) { - il2cpp_utils::getLogger().fmtLog("to ({}, {}) is ref/out while from ({}, {}) is not. Not convertible.", + logger.fmtLog("to ({}, {}) is ref/out while from ({}, {}) is not. Not convertible.", TypeGetSimpleName(to), fmt::ptr(to), TypeGetSimpleName(from), fmt::ptr(from)); return false; } diff --git a/src/utils/il2cpp-utils-properties.cpp b/src/utils/il2cpp-utils-properties.cpp index 2092a7f6..35d116af 100644 --- a/src/utils/il2cpp-utils-properties.cpp +++ b/src/utils/il2cpp-utils-properties.cpp @@ -10,7 +10,7 @@ namespace il2cpp_utils { static std::mutex classPropertiesLock; const PropertyInfo* FindProperty(Il2CppClass* klass, std::string_view propName) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -25,7 +25,7 @@ namespace il2cpp_utils { classPropertiesLock.unlock(); auto prop = il2cpp_functions::class_get_property_from_name(klass, propName.data()); if (!prop) { - il2cpp_utils::getLogger().fmtLog("could not find property {} in class '{}'!", propName.data(), ClassStandardName(klass)); + logger.fmtLog("could not find property {} in class '{}'!", propName, klass); LogProperties(logger, klass); if (klass->parent != klass) prop = FindProperty(klass->parent, propName); } @@ -59,7 +59,7 @@ namespace il2cpp_utils { } auto typeStr = type ? TypeGetSimpleName(type) : "?type?"; - il2cpp_utils::getLogger().fmtLog("{}{} {} {{ {}; {}; }}; // flags: 0x{:4X}", flagStr, typeStr, name, getterName, setterName, flags); + logger.fmtLog("{}{} {} {{ {}; {}; }}; // flags: 0x{:4X}", flagStr, typeStr, name, getterName, setterName, flags); } void LogProperties(Paper::LoggerContext const& logger, Il2CppClass* klass, bool logParents) { @@ -69,9 +69,9 @@ namespace il2cpp_utils { void* myIter = nullptr; const PropertyInfo* prop; if (klass->name) il2cpp_functions::Class_Init(klass); - if (logParents) il2cpp_utils::getLogger().fmtLog("class name: {}", ClassStandardName(klass).c_str()); + if (logParents) logger.fmtLog("class name: {}", klass); - il2cpp_utils::getLogger().fmtLog("property_count: {}", klass->property_count); + logger.fmtLog("property_count: {}", klass); while ((prop = il2cpp_functions::class_get_properties(klass, &myIter))) { LogProperty(logger, prop); } diff --git a/src/utils/il2cpp-utils.cpp b/src/utils/il2cpp-utils.cpp index d1b429e5..1cb1cb08 100644 --- a/src/utils/il2cpp-utils.cpp +++ b/src/utils/il2cpp-utils.cpp @@ -37,14 +37,14 @@ namespace il2cpp_utils { } bool ParameterMatch(const MethodInfo* method, std::vector genTypes, std::vector argTypes) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); if (method->parameters_count != argTypes.size()) { return false; } auto genCount = (method->is_generic && !method->is_inflated) ? method->genericContainer->type_argc : 0; if ((size_t)genCount != genTypes.size()) { - il2cpp_utils::getLogger().fmtLog("Potential method match had wrong number of generics {} (expected {})", + logger.fmtLog("Potential method match had wrong number of generics {} (expected {})", genCount, genTypes.size()); return false; } @@ -54,9 +54,9 @@ namespace il2cpp_utils { if (paramType->type == IL2CPP_TYPE_MVAR) { auto genIdx = paramType->data.genericParameterIndex - method->genericContainer->genericParameterStart; if (genIdx < 0) { - il2cpp_utils::getLogger().fmtLog("Extracted invalid genIdx {} from parameter {}", genIdx, i); + logger.fmtLog("Extracted invalid genIdx {} from parameter {}", genIdx, i); } else if (genIdx >= genCount) { - il2cpp_utils::getLogger().fmtLog("ParameterMatch was not supplied enough genTypes to determine type of parameter {} " + logger.fmtLog("ParameterMatch was not supplied enough genTypes to determine type of parameter {} " "(had {}, needed {})!", i, genCount, genIdx); } else { auto* klass = genTypes.at(genIdx); @@ -137,13 +137,13 @@ namespace il2cpp_utils { } Il2CppClass* GetParamClass(const MethodInfo* method, int paramIdx) { - static auto logger = getLogger(); + auto const& logger = getLogger(); auto type = RET_0_UNLESS(logger, il2cpp_functions::method_get_param(method, paramIdx)); return il2cpp_functions::class_from_il2cpp_type(type); } Il2CppReflectionType* MakeGenericType(Il2CppReflectionType* gt, Il2CppArray* types) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); auto runtimeType = RET_0_UNLESS(logger, il2cpp_functions::defaults->runtimetype_class); @@ -165,7 +165,7 @@ namespace il2cpp_utils { } Il2CppReflectionType* GetSystemType(const Il2CppClass* klass) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); RET_0_UNLESS(logger, klass); @@ -178,12 +178,12 @@ namespace il2cpp_utils { } void GenericsToStringHelper(Il2CppGenericClass* genClass, std::ostream& os) { - static auto logger = getLogger(); + auto const& logger = getLogger(); auto genContext = &genClass->context; auto* genInst = genContext->class_inst; if (!genInst) { genInst = genContext->method_inst; - if (genInst) il2cpp_utils::getLogger().fmtLog("Missing class_inst! Trying method_inst?"); + if (genInst) logger.fmtLog("Missing class_inst! Trying method_inst?"); } if (genInst) { os << "<"; @@ -195,7 +195,7 @@ namespace il2cpp_utils { } os << ">"; } else { - il2cpp_utils::getLogger().fmtLog("context->class_inst missing for genClass!"); + logger.fmtLog("context->class_inst missing for genClass!"); } } @@ -234,7 +234,7 @@ namespace il2cpp_utils { } Il2CppObject* createManual(const Il2CppClass* klass) noexcept { - static auto logger = getLogger(); + auto const& logger = getLogger(); if (!klass) { il2cpp_utils::getLogger().fmtLog("Cannot create a manual object on a null class!"); return nullptr; @@ -303,10 +303,10 @@ namespace il2cpp_utils { } bool AssertMatch(const Il2CppObject* source, Il2CppClass* klass) { - static auto logger = getLogger(); + auto const& logger = getLogger(); il2cpp_functions::Init(); if (!Match(source, klass)) { - il2cpp_utils::getLogger().fmtLog("source with class '{}' does not match class '{}'!", + logger.fmtLog("source with class '{}' does not match class '{}'!", ClassStandardName(source->klass).c_str(), ClassStandardName(klass).c_str()); SAFE_ABORT(); } diff --git a/src/utils/logging.cpp b/src/utils/logging.cpp index 41f110d9..14933eec 100644 --- a/src/utils/logging.cpp +++ b/src/utils/logging.cpp @@ -1,8 +1,8 @@ #include "shared/utils/logging.hpp" -static Paper::LoggerContext loggerContext = - Paper::Logger::WithContextRuntime("beatsaber-hook-" VERSION, {}); - Paper::LoggerContext const &il2cpp_utils::getLogger() { + static Paper::LoggerContext loggerContext = + Paper::Logger::WithContextRuntime("beatsaber-hook-" VERSION, {}); + return loggerContext; } \ No newline at end of file diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 6855cf16..1cd70109 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -49,7 +49,7 @@ void WaitForCompleteFlush() { } void safeAbort(const char* func, const char* file, int line, uint16_t frameCount) { - static auto logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); // we REALLY want this to appear at least once in the log (for fastest fixing) for (int i = 0; i < 2; i++) { usleep(100000L); // 0.1s @@ -63,7 +63,7 @@ void safeAbort(const char* func, const char* file, int line, uint16_t frameCount } void safeAbortMsg(const char* func, const char* file, int line, const char* fmt, ...) { - static auto logger = il2cpp_utils::getLogger(); + auto const& logger = il2cpp_utils::getLogger(); // we REALLY want this to appear at least once in the log (for fastest fixing) for (int i = 0; i < 2; i++) { usleep(100000L); // 0.1s