diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5dedb8b..7537e41 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,10 +33,10 @@ jobs: arch: x64 machine: ubuntu-latest - # - name: macOS arm64 - # platform: darwin - # arch: arm64 - # machine: macos-15 + - name: macOS arm64 + platform: darwin + arch: arm64 + machine: macos-15 runs-on: ${{ matrix.machine }} diff --git a/extensions/cpp/explorer/src/extension.cpp b/extensions/cpp/explorer/src/extension.cpp index ab38b8b..7a7210f 100644 --- a/extensions/cpp/explorer/src/extension.cpp +++ b/extensions/cpp/explorer/src/extension.cpp @@ -1,5 +1,5 @@ #include "./sfo.hpp" -#include +#include "rpcsx/ui/log.hpp" #include #include @@ -91,7 +91,7 @@ tryFetchGame(const std::filesystem::directory_entry &entry) { auto data = sfo::load(paramSfoPath.string()); if (data.errc != sfo::error::ok) { - std::println(stderr, "{}: error {}", entry.path().string(), data.errc); + elog("%s: error %d", entry.path().c_str(), data.errc); return {}; } diff --git a/extensions/cpp/explorer/src/sfo.cpp b/extensions/cpp/explorer/src/sfo.cpp index 8879cf0..cd99084 100644 --- a/extensions/cpp/explorer/src/sfo.cpp +++ b/extensions/cpp/explorer/src/sfo.cpp @@ -1,6 +1,5 @@ #include "sfo.hpp" #include "rpcsx/ui/file.hpp" -#include "rpcsx/ui/format.hpp" #include "rpcsx/ui/log.hpp" #include #include @@ -81,7 +80,7 @@ std::uint32_t sfo::entry::size() const { return sizeof(std::uint32_t); } - fatal("sfo: invalid format ({})", m_format); + fatal("sfo: invalid format (%d)", static_cast(m_format)); } bool sfo::entry::is_valid() const { @@ -104,8 +103,11 @@ sfo::load_result_t sfo::load(ReadableByteStream stream, #define PSF_CHECK(cond, err) \ if (!static_cast(cond)) { \ if (true || err != error::stream) \ - elog("sfo: Error loading '{}': {}. {}", filename, err, \ - std::source_location::current()); \ + elog("sfo: Error loading '%s': %d. %s:%u:%u", \ + std::string(filename).c_str(), static_cast(err), \ + std::source_location::current().file_name(), \ + std::source_location::current().line(), \ + std::source_location::current().column()); \ result.sfo.clear(); \ result.errc = err; \ return result; \ @@ -192,10 +194,10 @@ sfo::load_result_t sfo::load(ReadableByteStream stream, std::move(value))); } else { // Possibly unsupported format, entry ignored - elog("sfo: Unknown entry format (key='{}', fmt={}, len=0x{:x}, " - "max=0x{:x})", - key, indices[i].param_fmt, indices[i].param_len, - indices[i].param_max); + elog("sfo: Unknown entry format (key='%s', fmt=%d, len=0x%x, " + "max=0x%x)", + std::string(key).c_str(), static_cast(indices[i].param_fmt), + indices[i].param_len, indices[i].param_max); } } @@ -262,11 +264,13 @@ bool sfo::check_registry( if (!entry_ok) { if (value.type() == format::string) { - elog("sfo: {}: Entry '{}' is invalid: string='{}'", src_loc, key, - value.as_string()); + elog("sfo: %s:%u:%u: Entry '%s' is invalid: string='%s'", + src_loc.file_name(), src_loc.line(), src_loc.column(), key.c_str(), + value.as_string().c_str()); } else { // TODO: Better logging of other types - elog("sfo: {}: Entry {} is invalid", src_loc, key, value.as_string()); + elog("sfo: %s:%u:%u: Entry %s is invalid", src_loc.file_name(), + src_loc.line(), src_loc.column(), key.c_str()); } } diff --git a/extensions/cpp/explorer/src/sfo.hpp b/extensions/cpp/explorer/src/sfo.hpp index c3608c0..cc76285 100644 --- a/extensions/cpp/explorer/src/sfo.hpp +++ b/extensions/cpp/explorer/src/sfo.hpp @@ -1,10 +1,8 @@ #pragma once #include "rpcsx/ui/file.hpp" -#include "rpcsx/ui/refl.hpp" #include #include -#include #include #include #include @@ -155,102 +153,3 @@ constexpr bool is_cat_hdd(std::string_view cat) { return cat.size() == 2u && cat[1] != 'D' && cat != "DG" && cat != "MS"; } } // namespace sfo - -template <> struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(sfo::format format, - std::format_context &ctx) const { - std::string_view name; - switch (format) { - case sfo::format::array: - name = rpcsx::ui::getNameOf(); - break; - case sfo::format::string: - name = rpcsx::ui::getNameOf(); - break; - case sfo::format::integer: - name = rpcsx::ui::getNameOf(); - break; - } - - if (name.empty()) { - std::format_to(ctx.out(), "({}){:#x}", - rpcsx::ui::getNameOf(), - std::to_underlying(format)); - return ctx.out(); - } - - std::format_to(ctx.out(), "{}", name); - return ctx.out(); - } -}; - -template <> struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(const std::source_location &location, - std::format_context &ctx) const { - std::format_to(ctx.out(), "{}:{}", location.file_name(), location.line()); - return ctx.out(); - } -}; - -template <> struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(const sfo::registry ®istry, - std::format_context &ctx) const { - for (const auto &entry : registry) { - if (entry.second.type() == sfo::format::array) { - // Format them last - continue; - } - - std::format_to(ctx.out(), "{}: ", entry.first); - - const sfo::entry &data = entry.second; - - if (data.type() == sfo::format::integer) { - std::format_to(ctx.out(), "0x{:x}\n", data.as_integer()); - } else { - std::format_to(ctx.out(), "\"{}\"\n", data.as_string()); - } - } - - for (const auto &entry : registry) { - if (entry.second.type() != sfo::format::array) { - // Formatted before - continue; - } - - std::format_to(ctx.out(), "{}: [", entry.first); - - for (bool first = true; auto byte : std::span( - reinterpret_cast( - entry.second.as_string().data()), - entry.second.size())) { - if (first) { - first = false; - } else { - std::format_to(ctx.out(), ", "); - } - - std::format_to(ctx.out(), "{:x}", byte); - } - - std::format_to(ctx.out(), "]\n"); - } - - return ctx.out(); - } -}; diff --git a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/extension.hpp b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/extension.hpp index 3420f4b..8f9c936 100644 --- a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/extension.hpp +++ b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/extension.hpp @@ -1,8 +1,6 @@ #pragma once #include "Protocol.hpp" // IWYU pragma: export -#include "format.hpp" // IWYU pragma: export -#include "refl.hpp" // IWYU pragma: export #include #include diff --git a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/format.hpp b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/format.hpp deleted file mode 100644 index 3c851ac..0000000 --- a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/format.hpp +++ /dev/null @@ -1,319 +0,0 @@ -#pragma once -#include "refl.hpp" -#include -#include -#include -#include -#include -#include - -namespace rpcsx::ui { -namespace detail { -struct StructFieldInfo { - std::size_t size = 0; - std::size_t align = 0; - std::size_t offset = 0; - std::format_context::iterator (*format)(void *, - std::format_context &ctx) = nullptr; - std::string_view name; -}; - -struct StructFieldQuery { - StructFieldInfo info; - - template constexpr operator T() { - info.size = sizeof(T); - info.align = alignof(T); - if constexpr (std::is_default_constructible_v>) { - info.format = - [](void *object, - std::format_context &ctx) -> std::format_context::iterator { - std::formatter formatter; - return formatter.format(*static_cast(object), ctx); - }; - } - - return {}; - } -}; - -template -std::size_t getFieldOffset(T StructT::*ptr) { - StructT queryStruct; - return std::bit_cast(&(queryStruct.*ptr)) - - std::bit_cast(&queryStruct); -} - -template struct StructRuntimeInfo { - std::unordered_map fieldInfo; - - template void registerField() { - fieldInfo[getFieldOffset(Field)] = getNameOf(); - } - - std::string_view getFieldName(std::size_t offset) { - if (auto it = fieldInfo.find(offset); it != fieldInfo.end()) { - return it->second; - } - - return {}; - } -}; - -struct VariableRuntimeInfo { - std::unordered_map infos; - - std::string_view getVariableName(void *pointer) { - if (auto it = infos.find(pointer); it != infos.end()) { - return it->second; - } - - return {}; - } -}; - -template struct UnwrapFieldInfo; - -template struct UnwrapFieldInfo { - using struct_type = StructT; - using field_type = T; -}; - -template auto &getStructStorage() { - static StructRuntimeInfo structStorage; - return structStorage; -} - -inline auto &getVariableStorage() { - static VariableRuntimeInfo storage; - return storage; -} - -template > -constexpr auto getConstStructInfo() { - auto genInfo = - []( - std::index_sequence) -> std::array { - std::array queries; - static_cast(StructT{queries[I]...}); - - auto result = std::array{queries[I].info...}; - - std::size_t nextOffset = 0; - for (auto &elem : result) { - elem.offset = (nextOffset + (elem.align - 1)) & ~(elem.align - 1); - nextOffset = elem.offset + elem.size; - } - - return result; - }; - - return genInfo(std::make_index_sequence()); -} - -template constexpr auto getStructInfo() { - auto structInfo = getConstStructInfo(); - auto &runtimeInfo = getStructStorage(); - for (auto &field : structInfo) { - field.name = runtimeInfo.getFieldName(field.offset); - } - return structInfo; -} -} // namespace detail - -template void registerField() { - using Info = detail::UnwrapFieldInfo; - auto &storage = detail::getStructStorage(); - storage.template registerField(); -} - -template void registerVariable() { - auto &storage = detail::getVariableStorage(); - storage.infos[&Variable] = getNameOf(); -} -} // namespace rpcsx::ui - -template - requires(std::is_standard_layout_v && std::is_class_v && - rpcsx::ui::fieldCount > 0) && - (!requires(T value) { std::begin(value) != std::end(value); }) -struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(T &s, std::format_context &ctx) const { - std::format_to(ctx.out(), "{}", rpcsx::ui::getNameOf()); - std::format_to(ctx.out(), "{{"); - - auto structInfo = rpcsx::ui::detail::getStructInfo(); - auto bytes = reinterpret_cast(&s); - for (std::size_t i = 0; i < rpcsx::ui::fieldCount; ++i) { - if (i != 0) { - std::format_to(ctx.out(), ", "); - } - - if (!structInfo[i].name.empty()) { - std::format_to(ctx.out(), ".{} = ", structInfo[i].name); - } - - structInfo[i].format(bytes + structInfo[i].offset, ctx); - } - - std::format_to(ctx.out(), "}}"); - return ctx.out(); - } -}; - -template - requires(std::is_enum_v && rpcsx::ui::fieldCount > 0) -struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(T value, - std::format_context &ctx) const { - auto getFieldName = - [](std::underlying_type_t value, - std::index_sequence) -> std::string { - std::string_view result; - ((value == I ? ((result = rpcsx::ui::getNameOf(I)>()), 0) - : 0), - ...); - - if (!result.empty()) { - return std::string(result); - } - - return std::format("{}", value); - }; - - auto queryUnknownField = - []( - std::underlying_type_t value, - std::integral_constant, - std::integer_sequence) -> std::string { - std::string_view result; - if (value < 0) { - ((-value == I + Offset - ? ((result = - rpcsx::ui::getNameOf(-(I + Offset))>()), - 0) - : 0), - ...); - } else { - ((value == I + Offset - ? ((result = rpcsx::ui::getNameOf(I + Offset)>()), - 0) - : 0), - ...); - } - - if (!result.empty()) { - return std::string(result); - } - - return std::format("{}", value); - }; - - std::string fieldName; - - auto underlying = std::to_underlying(value); - - if (underlying < 0) { - fieldName = queryUnknownField( - underlying, std::integral_constant{}, - std::make_integer_sequence{}); - } else if (underlying >= rpcsx::ui::fieldCount) { - fieldName = queryUnknownField( - underlying, - std::integral_constant>{}, - std::make_integer_sequence{}); - } else { - fieldName = getFieldName( - underlying, std::make_index_sequence>()); - } - - if (fieldName[0] >= '0' && fieldName[0] <= '9') { - std::format_to(ctx.out(), "({}){}", rpcsx::ui::getNameOf(), fieldName); - } else { - std::format_to(ctx.out(), "{}::{}", rpcsx::ui::getNameOf(), fieldName); - } - - return ctx.out(); - } -}; - -template - requires requires(T value) { std::begin(value) != std::end(value); } -struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(T &s, std::format_context &ctx) const { - std::format_to(ctx.out(), "["); - - for (bool first = true; auto &elem : s) { - if (first) { - first = false; - } else { - std::format_to(ctx.out(), ", "); - } - - std::format_to(ctx.out(), "{}", elem); - } - - std::format_to(ctx.out(), "]"); - return ctx.out(); - } -}; - -template - requires(!std::is_same_v, char> && - !std::is_same_v, wchar_t> && - !std::is_same_v, char8_t> && - !std::is_same_v, char16_t> && - !std::is_same_v, char32_t> && - std::is_default_constructible_v>) -struct std::formatter { - constexpr std::format_parse_context::iterator - parse(std::format_parse_context &ctx) { - return ctx.begin(); - } - - std::format_context::iterator format(T *ptr, std::format_context &ctx) const { - auto name = rpcsx::ui::detail::getVariableStorage().getVariableName(ptr); - if (!name.empty()) { - std::format_to(ctx.out(), "*{} = ", name); - } else { - std::format_to(ctx.out(), "*"); - } - - if (ptr == nullptr) { - std::format_to(ctx.out(), "nullptr"); - } else { - std::format_to(ctx.out(), "{}:{}", static_cast(ptr), *ptr); - } - return ctx.out(); - } -}; - -#define RX_CONCAT(a, b) RX_CONCAT_IMPL(a, b) -#define RX_CONCAT_IMPL(a, b) a##b - -#define RX_REGISTER_VARIABLE(x) \ - static auto RX_CONCAT(_RX_REGISTER_VARIABLE_, __LINE__) = ([] { \ - ::rpcsx::ui::registerVariable(); \ - return true; \ - }()) - -#define RX_REGISTER_FIELD(x) \ - static auto RX_CONCAT(_RX_REGISTER_FIELD_, __LINE__) = ([] { \ - ::rpcsx::ui::registerField<&x>(); \ - return true; \ - }()) diff --git a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/log.hpp b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/log.hpp index e14963d..50e7647 100644 --- a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/log.hpp +++ b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/log.hpp @@ -1,37 +1,75 @@ #pragma once #include "Protocol.hpp" -#include +#include namespace rpcsx::ui { -template -void log(LogLevel level, std::format_string fmt, Args &&...args) { - Protocol::getDefault()->sendLogMessage( - level, std::vformat(fmt.get(), std::make_format_args(args...))); +[[gnu::format(__printf__, 2, 3)]] +inline void log(LogLevel level, const char *fmt, ...) { + char buffer[256]; + + va_list args; + va_start(args, fmt); + int written = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + buffer[255] = 0; + + Protocol::getDefault()->sendLogMessage(level, buffer); } -template -void ilog(std::format_string fmt, Args &&...args) { - Protocol::getDefault()->sendLogMessage( - LogLevel::Info, std::vformat(fmt.get(), std::make_format_args(args...))); +[[gnu::format(__printf__, 1, 2)]] +inline void ilog(const char *fmt, ...) { + char buffer[256]; + + va_list args; + va_start(args, fmt); + int written = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + buffer[255] = 0; + + Protocol::getDefault()->sendLogMessage(LogLevel::Info, buffer); } -template -void elog(std::format_string fmt, Args &&...args) { - Protocol::getDefault()->sendLogMessage( - LogLevel::Error, std::vformat(fmt.get(), std::make_format_args(args...))); +[[gnu::format(__printf__, 1, 2)]] +inline void elog(const char *fmt, ...) { + char buffer[256]; + + va_list args; + va_start(args, fmt); + int written = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + buffer[255] = 0; + + Protocol::getDefault()->sendLogMessage(LogLevel::Error, buffer); } -template -void wlog(std::format_string fmt, Args &&...args) { - Protocol::getDefault()->sendLogMessage( - LogLevel::Warning, - std::vformat(fmt.get(), std::make_format_args(args...))); +[[gnu::format(__printf__, 1, 2)]] inline void wlog(const char *fmt, ...) { + char buffer[256]; + + va_list args; + va_start(args, fmt); + int written = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + buffer[255] = 0; + + Protocol::getDefault()->sendLogMessage(LogLevel::Warning, buffer); } -template -[[noreturn]] void fatal(std::format_string fmt, Args &&...args) { - Protocol::getDefault()->sendLogMessage( - LogLevel::Fatal, std::vformat(fmt.get(), std::make_format_args(args...))); +[[gnu::format(__printf__, 1, 2), noreturn]] inline void fatal(const char *fmt, + ...) { + char buffer[256]; + + va_list args; + va_start(args, fmt); + int written = std::vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + buffer[255] = 0; + + Protocol::getDefault()->sendLogMessage(LogLevel::Fatal, buffer); std::exit(1); } diff --git a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/refl.hpp b/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/refl.hpp deleted file mode 100644 index 1f34136..0000000 --- a/extensions/cpp/rpcsx-ui-cpp/include/rpcsx/ui/refl.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once - -#include -#include - -#ifdef _MSC_VER -#define RX_PRETTY_FUNCTION __FUNCSIG__ -#elif defined(__GNUC__) -#define RX_PRETTY_FUNCTION __PRETTY_FUNCTION__ -#else -#define RX_PRETTY_FUNCTION "" -#endif - -namespace rpcsx::ui { -namespace detail { -struct AnyStructFieldQuery { - template constexpr operator T &&(); -}; - -template - requires std::is_class_v -constexpr auto calcFieldCount() { - - auto isValidFieldCount = [](std::index_sequence) { - return requires { StructT(((I, AnyStructFieldQuery{}))...); }; - }; - - if constexpr (isValidFieldCount(std::make_index_sequence())) { - return calcFieldCount(); - } else if constexpr (sizeof(StructT) <= N || LastValidCount > 0) { - return LastValidCount; - } else { - return calcFieldCount(); - } -} - -consteval std::string_view unwrapName(std::string_view prefix, - std::string_view pretty, - bool dropNamespace) { -#ifdef _MSC_VER - if (auto pos = pretty.find(prefix); pos != std::string_view::npos) { - pretty.remove_prefix(pos + prefix.size()); - } else { - pretty = {}; - } - - if (auto pos = pretty.rfind('>'); pos != std::string_view::npos) { - pretty.remove_suffix(pretty.size() - pos); - } else { - pretty = {}; - } - - if (auto pos = pretty.rfind(')'); pos != std::string_view::npos) { - pretty.remove_prefix(pos + 1); - } - - if (auto pos = pretty.find(' '); pos != std::string_view::npos) { - pretty.remove_prefix(pos + 1); - } -#else - if (auto pos = pretty.rfind('['); pos != std::string_view::npos) { - pretty.remove_prefix(pos + 1); - } else { - pretty = {}; - } - - if (auto pos = pretty.rfind(prefix); pos != std::string_view::npos) { - pretty.remove_prefix(pos + prefix.size()); - } else { - pretty = {}; - } - if (auto pos = pretty.rfind(')'); pos != std::string_view::npos) { - pretty.remove_prefix(pos + 1); - } - if (pretty.ends_with(']')) { - pretty.remove_suffix(1); - } else { - pretty = {}; - } -#endif - - if (dropNamespace) { - if (auto pos = pretty.rfind(':'); pos != std::string_view::npos) { - pretty.remove_prefix(pos + 1); - } - } - - return pretty; -} - -template constexpr bool isField = false; - -template -constexpr bool isField = true; - -} // namespace detail - -template consteval auto getNameOf() { - std::string_view prefix; -#ifdef _MSC_VER - prefix = "getNameOf<"; -#else - prefix = "V = "; -#endif - return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true); -} - -template - requires(detail::isField || - std::is_enum_v> || - std::is_pointer_v>) -consteval auto getNameOf() { - std::string_view prefix; -#ifdef _MSC_VER - prefix = "getNameOf<"; -#else - prefix = "V = "; -#endif - return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true); -} - -template consteval auto getNameOf() { - std::string_view prefix; -#ifdef _MSC_VER - prefix = "getNameOf<"; -#else - prefix = "T = "; -#endif - return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, false); -} - -namespace detail { -template - requires std::is_enum_v -constexpr auto calcFieldCount() { - if constexpr (requires { EnumT::Count; }) { - return static_cast(EnumT::Count); - } else if constexpr (requires { EnumT::_count; }) { - return static_cast(EnumT::_count); - } else if constexpr (requires { EnumT::count; }) { - return static_cast(EnumT::count); - } else if constexpr (!requires { getNameOf()[0]; }) { - return N; - } else { - constexpr auto c = getNameOf()[0]; - if constexpr (!requires { getNameOf()[0]; }) { - return N; - } else if constexpr (c >= '0' && c <= '9') { - return N; - } else { - return calcFieldCount(); - } - } -} -} // namespace detail - -template -inline constexpr std::size_t fieldCount = detail::calcFieldCount(); -} // namespace rpcsx::ui