#pragma once #include #include #include #include #include #include #include #include #include "termcolor.hpp" // We have to detect and special-case AppleClang at the moment since its C++20 support is finicky and doesn't quite support std::bit_cast #if defined(__clang__) && defined(__apple_build_version__) #define HELPERS_APPLE_CLANG #else #include #endif using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; using usize = std::size_t; using uint = unsigned int; using s8 = std::int8_t; using s16 = std::int16_t; using s32 = std::int32_t; using s64 = std::int64_t; namespace Helpers { template std::string format(const std::string& fmt, Args&&... args) { const int size = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1; if (size <= 0) { return {}; } const auto buf = std::make_unique(size); std::snprintf(buf.get(), size, fmt.c_str(), args ...); return std::string(buf.get(), buf.get() + size - 1); } // Unconditional panic, unlike panicDev which does not panic on user builds template [[noreturn]] static void panic(const char* fmt, Args&&... args) { std::cout << termcolor::on_red << "[FATAL] "; std::printf(fmt, args...); std::cout << termcolor::reset << "\n"; exit(1); } #ifdef PANDA3DS_LIMITED_PANICS template static void panicDev(const char* fmt, Args&&... args) {} #else template [[noreturn]] static void panicDev(const char* fmt, Args&&... args) { panic(fmt, args...); } #endif template static void warn(const char* fmt, Args&&... args) { std::cout << termcolor::on_red << "[Warning] "; std::printf(fmt, args...); std::cout << termcolor::reset << "\n"; } static constexpr bool buildingInDebugMode() { #ifdef NDEBUG return false; #endif return true; } static constexpr bool isUserBuild() { #ifdef PANDA3DS_USER_BUILD return true; #endif return false; } static constexpr bool isHydraCore() { #ifdef PANDA3DS_HYDRA_CORE return true; #endif return false; } static constexpr bool isAndroid() { #ifdef __ANDROID__ return true; #endif return false; } static void debug_printf(const char* fmt, ...) { if constexpr (buildingInDebugMode()) { std::va_list args; va_start(args, fmt); std::vprintf(fmt, args); va_end(args); } } /// Sign extend an arbitrary-size value to 32 bits static constexpr u32 inline signExtend32(u32 value, u32 startingSize) { auto temp = (s32)value; auto bitsToShift = 32 - startingSize; return (u32)(temp << bitsToShift >> bitsToShift); } /// Sign extend an arbitrary-size value to 16 bits static constexpr u16 signExtend16(u16 value, u32 startingSize) { auto temp = (s16)value; auto bitsToShift = 16 - startingSize; return (u16)(temp << bitsToShift >> bitsToShift); } /// Create a mask with `count` number of one bits. template static constexpr T ones() { constexpr usize bitsize = CHAR_BIT * sizeof(T); static_assert(count <= bitsize, "count larger than bitsize of T"); if (count == T(0)) { return T(0); } return static_cast(~static_cast(0)) >> (bitsize - count); } /// Extract bits from an integer-type template static constexpr T getBit(T value) { return (value >> offset) & T(1); } /// Extract bits from an integer-type template static constexpr ReturnT getBits(ValueT value) { static_assert((offset + bits) <= (CHAR_BIT * sizeof(ValueT)), "Invalid bit range"); static_assert(bits > 0, "Invalid bit size"); return ReturnT(ValueT(value >> offset) & ones()); } template static constexpr ValueT getBits(ValueT value) { return getBits(value); } #if defined(HELPERS_APPLE_CLANG) || defined(__ANDROID__) || !defined(__cpp_lib_bit_cast) template constexpr To bit_cast(const From& from) noexcept { return *reinterpret_cast(&from); } #else template constexpr To bit_cast(const From& from) noexcept { return std::bit_cast(from); } #endif }; // namespace Helpers // UDLs for memory size values constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; } constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; } constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; }