diff --git a/src/common/Arch.h b/src/common/Arch.h deleted file mode 100644 index e2ba7da..0000000 --- a/src/common/Arch.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#if defined(__x86_64__) || defined(_M_X64) -#define ARCH_X86_64 1 -#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) -#define ARCH_X86 1 -#elif defined(__aarch64__) || defined(_M_ARM64) -#define ARCH_AARCH64 1 -#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) || \ - defined(__ppc__) || defined(__PPC__) || defined(_ARCH_PPC) -#define ARCH_PPC 1 -#endif diff --git a/src/common/BoundedQueue.h b/src/common/BoundedQueue.h deleted file mode 100644 index 8510b95..0000000 --- a/src/common/BoundedQueue.h +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include "PolyfillThread.h" - -namespace Base -{ - -namespace detail -{ -constexpr size_t DefaultCapacity = 0x1000; -} // namespace detail - -template -class SPSCQueue -{ - static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two."); - - public: - template - bool TryEmplace(Args&&... args) - { - return Emplace(std::forward(args)...); - } - - template - void EmplaceWait(Args&&... args) - { - Emplace(std::forward(args)...); - } - - bool TryPop(T& t) { return Pop(t); } - - bool PopWait(T& t) { return Pop(t); } - - bool PopWait(T& t, std::stop_token stopToken) { return Pop(t, stopToken); } - - T PopWait() - { - T t; - Pop(t); - return t; - } - - T PopWait(std::stop_token stopToken) - { - T t; - Pop(t, stopToken); - return t; - } - - private: - enum class PushMode - { - Try, - Wait, - Count - }; - - enum class PopMode - { - Try, - Wait, - WaitWithStopToken, - Count - }; - - template - bool Emplace(Args&&... args) - { - const size_t write_index = m_write_index.load(std::memory_order::relaxed); - - if constexpr (Mode == PushMode::Try) - { - // Check if we have free slots to write to. - if ((write_index - m_read_index.load(std::memory_order::acquire)) == Capacity) - { - return false; - } - } - else if constexpr (Mode == PushMode::Wait) - { - // Wait until we have free slots to write to. - std::unique_lock lock{producer_cv_mutex}; - producer_cv.wait(lock, [this, write_index] - { return (write_index - m_read_index.load(std::memory_order::acquire)) < Capacity; }); - } - else - { - static_assert(Mode < PushMode::Count, "Invalid PushMode."); - } - - // Determine the position to write to. - const size_t pos = write_index % Capacity; - - // Emplace into the queue. - new (std::addressof(m_data[pos])) T(std::forward(args)...); - - // Increment the write index. - ++m_write_index; - - // Notify the consumer that we have pushed into the queue. - std::scoped_lock lock{consumer_cv_mutex}; - consumer_cv.notify_one(); - - return true; - } - - template - bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) - { - const size_t read_index = m_read_index.load(std::memory_order::relaxed); - - if constexpr (Mode == PopMode::Try) - { - // Check if the queue is empty. - if (read_index == m_write_index.load(std::memory_order::acquire)) - { - return false; - } - } - else if constexpr (Mode == PopMode::Wait) - { - // Wait until the queue is not empty. - std::unique_lock lock{consumer_cv_mutex}; - consumer_cv.wait( - lock, [this, read_index] { return read_index != m_write_index.load(std::memory_order::acquire); }); - } - else if constexpr (Mode == PopMode::WaitWithStopToken) - { - // Wait until the queue is not empty. - std::unique_lock lock{consumer_cv_mutex}; - Base::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] - { return read_index != m_write_index.load(std::memory_order::acquire); }); - if (stop_token.stop_requested()) - { - return false; - } - } - else - { - static_assert(Mode < PopMode::Count, "Invalid PopMode."); - } - - // Determine the position to read from. - const size_t pos = read_index % Capacity; - - // Pop the data off the queue, moving it. - t = std::move(m_data[pos]); - - // Increment the read index. - ++m_read_index; - - // Notify the producer that we have popped off the queue. - std::scoped_lock lock{producer_cv_mutex}; - producer_cv.notify_one(); - - return true; - } - - alignas(128) std::atomic_size_t m_read_index{0}; - alignas(128) std::atomic_size_t m_write_index{0}; - - std::array m_data; - - std::condition_variable_any producer_cv; - std::mutex producer_cv_mutex; - std::condition_variable_any consumer_cv; - std::mutex consumer_cv_mutex; -}; - -template -class MPSCQueue -{ - public: - template - bool TryEmplace(Args&&... args) - { - std::scoped_lock lock{writeMutex}; - return spscQueue.TryEmplace(std::forward(args)...); - } - - template - void EmplaceWait(Args&&... args) - { - std::scoped_lock lock{writeMutex}; - spscQueue.EmplaceWait(std::forward(args)...); - } - - bool TryPop(T& t) { return spscQueue.TryPop(t); } - - bool PopWait(T& t) { return spscQueue.PopWait(t); } - - bool PopWait(T& t, std::stop_token stop_token) { return spscQueue.PopWait(t, stop_token); } - - T PopWait() { return spscQueue.PopWait(); } - - T PopWait(std::stop_token stop_token) { return spscQueue.PopWait(stop_token); } - - private: - SPSCQueue spscQueue; - std::mutex writeMutex; -}; - -template -class MPMCQueue -{ - public: - template - bool TryEmplace(Args&&... args) - { - std::scoped_lock lock{writeMutex}; - return spscQueue.TryEmplace(std::forward(args)...); - } - - template - void EmplaceWait(Args&&... args) - { - std::scoped_lock lock{writeMutex}; - spscQueue.EmplaceWait(std::forward(args)...); - } - - bool TryPop(T& t) - { - std::scoped_lock lock{readMutex}; - return spscQueue.TryPop(t); - } - - bool PopWait(T& t) - { - std::scoped_lock lock{readMutex}; - return spscQueue.PopWait(t); - } - - bool PopWait(T& t, std::stop_token stopToken) - { - std::scoped_lock lock{readMutex}; - return spscQueue.PopWait(t, stopToken); - } - - T PopWait() - { - std::scoped_lock lock{readMutex}; - return spscQueue.PopWait(); - } - - T PopWait(std::stop_token stop_token) - { - std::scoped_lock lock{readMutex}; - return spscQueue.PopWait(stop_token); - } - - private: - SPSCQueue spscQueue; - std::mutex writeMutex; - std::mutex readMutex; -}; - -} // namespace Base diff --git a/src/common/Config.cpp b/src/common/Config.cpp deleted file mode 100644 index b709b5e..0000000 --- a/src/common/Config.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. - -#include "Config.h" - -#include - -namespace Config -{ - -static int widthWindow = 640; - -static int heightWindow = 480; - -static bool logAdvanced = false; - -static std::string typeLog = "async"; - -int windowWidth() -{ - return widthWindow; -} - -int windowHeight() -{ - return heightWindow; -} - -bool isLogAdvanced() -{ - return logAdvanced; -} - -std::string logType() -{ - return typeLog; -} - -void Load(const std::filesystem::path& path) -{ - /* - // If the configuration file does not exist, create it and return - std::error_code error; - if (!std::filesystem::exists(path, error)) - { - Save(path); - return; - } - - toml::value data; - - try - { - data = toml::parse(path); - } - catch (std::exception& ex) - { - fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); - return; - } - if (data.contains("General")) - { - const toml::value& general = data.at("General"); - - widthWindow = toml::find_or(general, "Window Width", 640); - heightWindow = toml::find_or(general, "Window Height", 480); - - logAdvanced = toml::find_or(general, "Advanced Log", false); - typeLog = toml::find_or(general, "Log Type", "async"); - } - */ -} - -void Save(const std::filesystem::path& path) -{ - /* - toml::ordered_value data; - - std::error_code error; - if (std::filesystem::exists(path, error)) - { - try - { - data = toml::parse(path); - } - catch (const std::exception& ex) - { - fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what()); - return; - } - } - else - { - if (error) - { - fmt::print("Filesystem error: {}\n", error.message()); - } - fmt::print("Saving new configuration file {}\n", path.string()); - } - - data["General"]["Window Width"] = widthWindow; - data["General"]["Window Height"] = heightWindow; - data["General"]["Advanced Log"] = logAdvanced; - data["General"]["Log Type"] = typeLog; - - std::ofstream file(path, std::ios::binary); - file << data; - file.close(); - */ -} - -} // namespace Config diff --git a/src/common/Config.h b/src/common/Config.h deleted file mode 100644 index 5f7ed27..0000000 --- a/src/common/Config.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2025 Pound Emulator Project. All rights reserved. - -#pragma once - -#include - -namespace Config -{ - -void Load(const std::filesystem::path& path); -void Save(const std::filesystem::path& path); - -int windowWidth(); - -int windowHeight(); - -bool isLogAdvanced(); - -std::string logType(); - -} // namespace Config diff --git a/src/common/Enum.h b/src/common/Enum.h deleted file mode 100644 index 3d3a719..0000000 --- a/src/common/Enum.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once -#include "Types.h" - -#define DECLARE_ENUM_FLAG_OPERATORS(type) \ - [[nodiscard]] constexpr type operator|(type a, type b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) | static_cast(b)); \ - } \ - [[nodiscard]] constexpr type operator&(type a, type b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) & static_cast(b)); \ - } \ - [[nodiscard]] constexpr type operator^(type a, type b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) ^ static_cast(b)); \ - } \ - [[nodiscard]] constexpr type operator<<(type a, type b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) << static_cast(b)); \ - } \ - [[nodiscard]] constexpr type operator>>(type a, type b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) >> static_cast(b)); \ - } \ - constexpr type& operator|=(type& a, type b) noexcept \ - { \ - a = a | b; \ - return a; \ - } \ - constexpr type& operator&=(type& a, type b) noexcept \ - { \ - a = a & b; \ - return a; \ - } \ - constexpr type& operator^=(type& a, type b) noexcept \ - { \ - a = a ^ b; \ - return a; \ - } \ - constexpr type& operator<<=(type& a, type b) noexcept \ - { \ - a = a << b; \ - return a; \ - } \ - constexpr type& operator>>=(type& a, type b) noexcept \ - { \ - a = a >> b; \ - return a; \ - } \ - [[nodiscard]] constexpr type operator~(type key) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(~static_cast(key)); \ - } \ - [[nodiscard]] constexpr bool True(type key) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(key) != 0; \ - } \ - [[nodiscard]] constexpr bool False(type key) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(key) == 0; \ - } - -namespace Base -{ - -template -class Flags -{ - public: - using IntType = std::underlying_type_t; - - Flags() {} - - Flags(IntType t) : m_bits(t) {} - - template - Flags(T f, Tx... fx) - { - set(f, fx...); - } - - template - void set(Tx... fx) - { - m_bits |= bits(fx...); - } - - void set(Flags flags) { m_bits |= flags.m_bits; } - - template - void clr(Tx... fx) - { - m_bits &= ~bits(fx...); - } - - void clr(Flags flags) { m_bits &= ~flags.m_bits; } - - template - bool any(Tx... fx) const - { - return (m_bits & bits(fx...)) != 0; - } - - template - bool all(Tx... fx) const - { - const IntType mask = bits(fx...); - return (m_bits & mask) == mask; - } - - bool test(T f) const { return any(f); } - - bool isClear() const { return m_bits == 0; } - - void clrAll() { m_bits = 0; } - - u32 raw() const { return m_bits; } - - Flags operator&(const Flags& other) const { return Flags(m_bits & other.m_bits); } - - Flags operator|(const Flags& other) const { return Flags(m_bits | other.m_bits); } - - Flags operator^(const Flags& other) const { return Flags(m_bits ^ other.m_bits); } - - bool operator==(const Flags& other) const { return m_bits == other.m_bits; } - - bool operator!=(const Flags& other) const { return m_bits != other.m_bits; } - - private: - IntType m_bits = 0; - - static IntType bit(T f) { return IntType(1) << static_cast(f); } - - template - static IntType bits(T f, Tx... fx) - { - return bit(f) | bits(fx...); - } - - static IntType bits() { return 0; } -}; - -} // namespace Base diff --git a/src/common/Error.h b/src/common/Error.h deleted file mode 100644 index 77c335d..0000000 --- a/src/common/Error.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include - -namespace Base -{ - -// Like GetLastErrorMsg(), but passing an explicit error code. -// Defined in error.cpp. -[[nodiscard]] std::string NativeErrorToString(const s32 e); - -// Generic function to get last error message. -// Call directly after the command or use the error num. -// This function might change the error code. -// Defined in error.cpp. -[[nodiscard]] std::string GetLastErrorMsg(); - -} // namespace Base diff --git a/src/common/IoFile.cpp b/src/common/IoFile.cpp deleted file mode 100644 index 4fb69c4..0000000 --- a/src/common/IoFile.cpp +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include - -#include "IoFile.h" - -#include "Error.h" -#include "Logging/Log.h" -#include "PathUtil.h" - -#ifdef _WIN32 -#define ftruncate _chsize_s -#define fsync _commit - -#include -#include -#include -#else -#include -#endif - -#ifdef _MSC_VER -#define fileno _fileno -#define fseeko _fseeki64 -#define ftello _ftelli64 -#endif - -namespace fs = std::filesystem; - -namespace Base::FS -{ - -#ifdef _WIN32 - -[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileMode type) -{ - switch (type) - { - case FileMode::BinaryMode: - switch (mode) - { - case FileAccessMode::Read: - return L"rb"; - case FileAccessMode::Write: - return L"wb"; - case FileAccessMode::Append: - return L"ab"; - case FileAccessMode::ReadWrite: - return L"r+b"; - case FileAccessMode::ReadAppend: - return L"a+b"; - } - break; - case FileMode::TextMode: - switch (mode) - { - case FileAccessMode::Read: - return L"r"; - case FileAccessMode::Write: - return L"w"; - case FileAccessMode::Append: - return L"a"; - case FileAccessMode::ReadWrite: - return L"r+"; - case FileAccessMode::ReadAppend: - return L"a+"; - } - break; - } - - return L""; -} - -[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) -{ - switch (flag) - { - case FileShareFlag::ShareNone: - default: - return _SH_DENYRW; - case FileShareFlag::ShareReadOnly: - return _SH_DENYWR; - case FileShareFlag::ShareWriteOnly: - return _SH_DENYRD; - case FileShareFlag::ShareReadWrite: - return _SH_DENYNO; - } -} - -#else - -[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileMode type) -{ - switch (type) - { - case FileMode::BinaryMode: - switch (mode) - { - case FileAccessMode::Read: - return "rb"; - case FileAccessMode::Write: - return "wb"; - case FileAccessMode::Append: - return "ab"; - case FileAccessMode::ReadWrite: - return "r+b"; - case FileAccessMode::ReadAppend: - return "a+b"; - } - break; - case FileMode::TextMode: - switch (mode) - { - case FileAccessMode::Read: - return "r"; - case FileAccessMode::Write: - return "w"; - case FileAccessMode::Append: - return "a"; - case FileAccessMode::ReadWrite: - return "r+"; - case FileAccessMode::ReadAppend: - return "a+"; - } - break; - } - - return ""; -} - -#endif - -[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) -{ - switch (origin) - { - case SeekOrigin::SetOrigin: - default: - return SEEK_SET; - case SeekOrigin::CurrentPosition: - return SEEK_CUR; - case SeekOrigin::End: - return SEEK_END; - } -} - -IOFile::IOFile() = default; - -IOFile::IOFile(const std::string& path, FileAccessMode mode, FileMode type, FileShareFlag flag) -{ - Open(path, mode, type, flag); -} - -IOFile::IOFile(std::string_view path, FileAccessMode mode, FileMode type, FileShareFlag flag) -{ - Open(path, mode, type, flag); -} - -IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileMode type, FileShareFlag flag) -{ - Open(path, mode, type, flag); -} - -IOFile::~IOFile() -{ - Close(); -} - -IOFile::IOFile(IOFile&& other) noexcept -{ - std::swap(filePath, other.filePath); - std::swap(fileAccessMode, other.fileAccessMode); - std::swap(fileType, other.fileType); - std::swap(file, other.file); -} - -IOFile& IOFile::operator=(IOFile&& other) noexcept -{ - std::swap(filePath, other.filePath); - std::swap(fileAccessMode, other.fileAccessMode); - std::swap(fileType, other.fileType); - std::swap(file, other.file); - return *this; -} - -int IOFile::Open(const fs::path& path, FileAccessMode mode, FileMode type, FileShareFlag flag) -{ - Close(); - - filePath = path; - fileAccessMode = mode; - fileType = type; - - errno = 0; - int result = 0; - -#ifdef _WIN32 - if (flag != FileShareFlag::ShareNone) - { - file = ::_wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag)); - result = errno; - } - else - { - result = ::_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); - } -#else - file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); - result = errno; -#endif - - if (!IsOpen()) - { - const auto ec = std::error_code{result, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to open the file at path={}, error_message={}", PathToUTF8String(filePath), - ec.message()); - } - - return result; -} - -void IOFile::Close() -{ - if (!IsOpen()) - { - return; - } - - errno = 0; - - const bool closeResult = ::fclose(file) == 0; - - if (!closeResult) - { - const auto ec = std::error_code{errno, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to close the file at path={}, ec_message={}", PathToUTF8String(filePath), - ec.message()); - } - - file = nullptr; - -#ifdef _WIN64 - if (fileMapping && fileAccessMode == FileAccessMode::ReadWrite) - { - ::CloseHandle(std::bit_cast(fileMapping)); - } -#endif -} - -void IOFile::Unlink() -{ - if (!IsOpen()) - { - return; - } - - // Mark the file for deletion - std::error_code fsError; - fs::remove_all(filePath, fsError); - if (fsError) - { - LOG_ERROR(Base_Filesystem, "Failed to remove the file at '{}'. Reason: {}", PathToUTF8String(filePath), - fsError.message()); - } -} - -uptr IOFile::GetFileMapping() -{ - if (fileMapping) - { - return fileMapping; - } -#ifdef _WIN64 - const s32 fd = fileno(file); - - HANDLE hfile = reinterpret_cast(::_get_osfhandle(fd)); - HANDLE mapping = nullptr; - - /* if (fileAccessMode == FileAccessMode::ReadWrite) { - mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_WRITE, PAGE_READWRITE, SEC_COMMIT, 0, - NULL, NULL, 0); - } else { - mapping = hfile; - }*/ - - mapping = hfile; - - fileMapping = std::bit_cast(mapping); - return fileMapping; -#else - fileMapping = fileno(file); - return fileMapping; -#endif -} - -std::string IOFile::ReadString(size_t length) const -{ - std::vector string_buffer(length); - - const u64 charsRead = ReadSpan(string_buffer); - const auto stringSize = charsRead != length ? charsRead : length; - - return std::string{string_buffer.data(), stringSize}; -} - -bool IOFile::Flush() const -{ - if (!IsOpen()) - { - return false; - } - - errno = 0; - - const bool flushResult = ::fflush(file) == 0; - - if (!flushResult) - { - const std::error_code ec = {errno, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to flush the file at path={}, ec_message={}", PathToUTF8String(filePath), - ec.message()); - } - - return flushResult; -} - -bool IOFile::Commit() const -{ - if (!IsOpen()) - { - return false; - } - - errno = 0; - - bool commitResult = ::fflush(file) == 0 && ::fsync(::fileno(file)) == 0; - - if (!commitResult) - { - const std::error_code ec = {errno, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to commit the file at path={}, ec_message={}", PathToUTF8String(filePath), - ec.message()); - } - - return commitResult; -} - -bool IOFile::SetSize(u64 size) const -{ - if (!IsOpen()) - { - return false; - } - - errno = 0; - - const bool setSizeResult = ::ftruncate(::fileno(file), static_cast(size)) == 0; - - if (!setSizeResult) - { - const std::error_code ec = {errno, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}", - PathToUTF8String(filePath), size, ec.message()); - } - - return setSizeResult; -} - -u64 IOFile::GetSize() const -{ - if (!IsOpen()) - { - return 0; - } - - // Flush any unwritten buffered data into the file prior to retrieving the file size. - std::fflush(file); - - u64 fSize = 0; - // fs::file_size can cause a exception if it is not a valid file - try - { - std::error_code ec{}; - fSize = fs::file_size(filePath, ec); - if (fSize == -1 || !fSize) - { - LOG_ERROR(Base_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", - PathToUTF8String(filePath), ec.message()); - return 0; - } - } - catch (const std::exception& ex) - { - LOG_ERROR(Base_Filesystem, "Exception trying to get file size. Exception: {}", ex.what()); - return 0; - } - - return fSize; -} - -bool IOFile::Seek(s64 offset, SeekOrigin origin) const -{ - if (!IsOpen()) - { - return false; - } - - if (False(fileAccessMode & (FileAccessMode::Write | FileAccessMode::Append))) - { - u64 size = GetSize(); - if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) - { - LOG_ERROR(Base_Filesystem, "Seeking past the end of the file"); - return false; - } - else if (origin == SeekOrigin::SetOrigin && static_cast(offset) > size) - { - LOG_ERROR(Base_Filesystem, "Seeking past the end of the file"); - return false; - } - else if (origin == SeekOrigin::End && offset > 0) - { - LOG_ERROR(Base_Filesystem, "Seeking past the end of the file"); - return false; - } - } - - errno = 0; - - const s32 seekResult = fseeko(file, offset, ToSeekOrigin(origin)) == 0; - - if (!seekResult) - { - const std::error_code ec = {errno, std::generic_category()}; - LOG_ERROR(Base_Filesystem, "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}", - PathToUTF8String(filePath), offset, static_cast(origin), ec.message()); - } - - return seekResult; -} - -s64 IOFile::Tell() const -{ - if (!IsOpen()) - { - return 0; - } - - errno = 0; - - return ftello(file); -} - -u64 GetDirectorySize(const std::filesystem::path& path) -{ - if (!fs::exists(path)) - { - return 0; - } - - u64 total = 0; - for (const auto& entry : fs::recursive_directory_iterator(path)) - { - if (fs::is_regular_file(entry.path())) - { - // fs::file_size can cause a exception if it is not a valid file - try - { - std::error_code ec{}; - u64 fileSize = fs::file_size(entry.path(), ec); - if (fileSize != -1 && fileSize) - { - total += fileSize; - } - else - { - LOG_ERROR(Base_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", - PathToUTF8String(entry.path()), ec.message()); - } - } - catch (const std::exception& ex) - { - LOG_ERROR(Base_Filesystem, "Exception trying to get file size. Exception: {}", ex.what()); - } - } - } - return total; -} - -} // namespace Base::FS diff --git a/src/common/IoFile.h b/src/common/IoFile.h deleted file mode 100644 index 5cca462..0000000 --- a/src/common/IoFile.h +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include - -#include "Enum.h" -#include "PathUtil.h" - -namespace Base::FS -{ - -enum class FileAccessMode -{ - /** - * If the file at path exists, it opens the file for reading. - * If the file at path does not exist, it fails to open the file. - */ - Read = 1 << 0, - /** - * If the file at path exists, the existing contents of the file are erased. - * The empty file is then opened for writing. - * If the file at path does not exist, it creates and opens a new empty file for writing. - */ - Write = 1 << 1, - /** - * If the file at path exists, it opens the file for reading and writing. - * If the file at path does not exist, it fails to open the file. - */ - ReadWrite = Read | Write, - /** - * If the file at path exists, it opens the file for appending. - * If the file at path does not exist, it creates and opens a new empty file for appending. - */ - Append = 1 << 2, - /** - * If the file at path exists, it opens the file for both reading and appending. - * If the file at path does not exist, it creates and opens a new empty file for both - * reading and appending. - */ - ReadAppend = Read | Append -}; -DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode) - -enum class FileMode -{ - BinaryMode, - TextMode -}; - -enum class FileShareFlag -{ - ShareNone, // Provides exclusive access to the file. - ShareReadOnly, // Provides read only shared access to the file. - ShareWriteOnly, // Provides write only shared access to the file. - ShareReadWrite // Provides read and write shared access to the file. -}; - -enum class SeekOrigin : u32 -{ - SetOrigin, // Seeks from the start of the file. - CurrentPosition, // Seeks from the current file pointer position. - End // Seeks from the end of the file. -}; - -class IOFile final -{ - public: - IOFile(); - - explicit IOFile(const std::string& path, FileAccessMode mode, FileMode type = FileMode::BinaryMode, - FileShareFlag flag = FileShareFlag::ShareReadOnly); - - explicit IOFile(std::string_view path, FileAccessMode mode, FileMode type = FileMode::BinaryMode, - FileShareFlag flag = FileShareFlag::ShareReadOnly); - explicit IOFile(const fs::path& path, FileAccessMode mode, FileMode type = FileMode::BinaryMode, - FileShareFlag flag = FileShareFlag::ShareReadOnly); - - ~IOFile(); - - IOFile(const IOFile&) = delete; - IOFile& operator=(const IOFile&) = delete; - - IOFile(IOFile&& other) noexcept; - IOFile& operator=(IOFile&& other) noexcept; - - fs::path GetPath() const { return filePath; } - - FileAccessMode GetAccessMode() const { return fileAccessMode; } - - FileMode GetType() const { return fileType; } - - bool IsOpen() const { return file != nullptr; } - - uptr GetFileMapping(); - - int Open(const fs::path& path, FileAccessMode mode, FileMode type = FileMode::BinaryMode, - FileShareFlag flag = FileShareFlag::ShareReadOnly); - void Close(); - - void Unlink(); - - bool Flush() const; - bool Commit() const; - - bool SetSize(u64 size) const; - u64 GetSize() const; - - bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const; - s64 Tell() const; - - template - size_t Read(T& data) const - { - if constexpr (std::contiguous_iterator) - { - using ContiguousType = typename T::value_type; - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return ReadSpan(data); - } - else - { - return ReadObject(data) ? 1 : 0; - } - } - - template - size_t Write(const T& data) const - { - if constexpr (std::contiguous_iterator) - { - using ContiguousType = typename T::value_type; - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return WriteSpan(data); - } - else - { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return WriteObject(data) ? 1 : 0; - } - } - - template - size_t ReadSpan(std::span data) const - { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - - if (!IsOpen()) - { - return 0; - } - - return ReadRaw(data.data(), data.size()); - } - - template - size_t ReadRaw(void* data, size_t size) const - { - return std::fread(data, sizeof(T), size, file); - } - - template - size_t WriteSpan(std::span data) const - { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - - if (!IsOpen()) - { - return 0; - } - - return std::fwrite(data.data(), sizeof(T), data.size(), file); - } - - template - bool ReadObject(T& object) const - { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); - - if (!IsOpen()) - { - return false; - } - - return std::fread(&object, sizeof(T), 1, file) == 1; - } - - template - size_t WriteRaw(const void* data, size_t size) const - { - return std::fwrite(data, sizeof(T), size, file); - } - - template - bool WriteObject(const T& object) const - { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - static_assert(!std::is_pointer_v, "T must not be a pointer to an object."); - - if (!IsOpen()) - { - return false; - } - - return std::fwrite(&object, sizeof(T), 1, file) == 1; - } - - std::string ReadString(size_t length) const; - - size_t WriteString(std::span string) const { return WriteSpan(string); } - - static size_t WriteBytes(const fs::path path, const auto& data) - { - IOFile out(path, FileAccessMode::Write); - return out.Write(data); - } - - private: - fs::path filePath{}; - FileAccessMode fileAccessMode{}; - FileMode fileType{}; - - std::FILE* file = nullptr; - uptr fileMapping = 0; -}; - -u64 GetDirectorySize(const fs::path& path); - -} // namespace Base::FS diff --git a/src/common/Logging/Backend.cpp b/src/common/Logging/Backend.cpp deleted file mode 100644 index 13aa748..0000000 --- a/src/common/Logging/Backend.cpp +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include -#include -#include - -#include "common/BoundedQueue.h" -#include "common/IoFile.h" -#include "common/PathUtil.h" -#include "common/StringUtil.h" -#include "common/Thread.h" - -#include "Backend.h" -#include "Log.h" -#include "LogEntry.h" -#include "TextFormatter.h" - -namespace Base { -namespace Log { - -using namespace Base::FS; - -// Base backend with shell functions -class BaseBackend { -public: - virtual ~BaseBackend() = default; - virtual void Write(const Entry &entry) = 0; - virtual void Flush() = 0; -}; - - -// Backend that writes to stdout and with color -class ColorConsoleBackend : public BaseBackend { -public: - explicit ColorConsoleBackend() = default; - - ~ColorConsoleBackend() = default; - - void Write(const Entry &entry) override { - if (enabled.load(std::memory_order_relaxed)) { - PrintColoredMessage(entry); - } - } - - void Flush() override { - // stdout shouldn't be buffered - } - - void SetEnabled(bool enabled_) { - enabled = enabled_; - } - -private: - std::atomic enabled = true; -}; - -// Backend that writes to a file passed into the constructor -class FileBackend : public BaseBackend { -public: - explicit FileBackend(const fs::path &filename) - : file(filename, FS::FileAccessMode::Write, FS::FileMode::TextMode) - {} - - ~FileBackend() { - file.Close(); - } - - void Write(const Entry &entry) override { - if (!enabled) { - return; - } - - if (entry.formatted) { - bytesWritten += file.WriteString(FormatLogMessage(entry).append(1, '\n')); - } - else { - bytesWritten += file.WriteString(entry.message); - } - - // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. - constexpr u64 writeLimit = 100_MB; - const bool writeLimitExceeded = bytesWritten > writeLimit; - if (entry.logLevel >= Level::Error || writeLimitExceeded) { - if (writeLimitExceeded) { - // Stop writing after the write limit is exceeded. - // Don't close the file so we can print a stacktrace if necessary - enabled = false; - } - file.Flush(); - } - } - - void Flush() override { - file.Flush(); - } - -private: - Base::FS::IOFile file; - std::atomic enabled = true; - size_t bytesWritten = 0; -}; - -bool currentlyInitialising = true; - -// Static state as a singleton. -class Impl { -public: - static Impl& Instance() { - if (!instance) { - throw std::runtime_error("Using Logging instance before its initialization"); - } - return *instance; - } - - static void Initialize(const std::string_view logFile) { - if (instance) { - LOG_WARNING(Log, "Reinitializing logging backend"); - return; - } - const auto logDir = GetUserPath(PathType::LogDir); - Filter filter; - //filter.ParseFilterString(Config::getLogFilter()); - instance = std::unique_ptr(new Impl(logDir / logFile, filter), Deleter); - currentlyInitialising = false; - } - - static bool IsActive() { - return instance != nullptr; - } - - static void Start() { - instance->StartBackendThread(); - } - - static void Stop() { - instance->StopBackendThread(); - } - - Impl(const Impl&) = delete; - Impl& operator=(const Impl&) = delete; - - Impl(Impl&&) = delete; - Impl& operator=(Impl&&) = delete; - - void SetGlobalFilter(const Filter& f) { - filter = f; - } - - void SetColorConsoleBackendEnabled(bool enabled) { - colorConsoleBackend->SetEnabled(enabled); - } - - void PushEntry(Class logClass, Level logLevel, const char *filename, u32 lineNum, - const char *function, const std::string &message) { - - if (!filter.CheckMessage(logClass, logLevel)) { - return; - } - - using std::chrono::duration_cast; - using std::chrono::microseconds; - using std::chrono::steady_clock; - - const Entry entry = { - .timestamp = duration_cast(steady_clock::now() - timeOrigin), - .logClass = logClass, - .logLevel = logLevel, - .filename = filename, - .lineNum = lineNum, - .function = function, - .message = message, - }; - if (Config::logType() == "async") { - messageQueue.EmplaceWait(entry); - } else { - ForEachBackend([&entry](BaseBackend* backend) { if (backend) { backend->Write(entry); } }); - std::fflush(stdout); - } - } - - void PushEntryNoFmt(Class logClass, Level logLevel, const std::string &message) { - if (!filter.CheckMessage(logClass, logLevel)) { - return; - } - - using std::chrono::duration_cast; - using std::chrono::microseconds; - using std::chrono::steady_clock; - - const Entry entry = { - .timestamp = duration_cast(steady_clock::now() - timeOrigin), - .logClass = logClass, - .logLevel = logLevel, - .message = message, - .formatted = false - }; - if (Config::logType() == "async") { - messageQueue.EmplaceWait(entry); - } else { - ForEachBackend([&entry](BaseBackend* backend) { if (backend) { backend->Write(entry); } }); - std::fflush(stdout); - } - } - -private: - Impl(const fs::path &fileBackendFilename, const Filter &filter) : - filter(filter) { -#ifdef _WIN32 - HANDLE conOut = GetStdHandle(STD_OUTPUT_HANDLE); - // Get current console mode - ul32 mode = 0; - GetConsoleMode(conOut, &mode); - // Set WinAPI to use a more 'modern' approach, by enabling VT - // Allows ASCII escape codes - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - // Write adjusted mode back - SetConsoleMode(conOut, mode); -#endif - colorConsoleBackend = std::make_unique(); - fileBackend = std::make_unique(fileBackendFilename); - } - - ~Impl() { - Stop(); - fileBackend.reset(); - colorConsoleBackend.reset(); - } - - void StartBackendThread() { - backendThread = std::jthread([this](std::stop_token stopToken) { - Base::SetCurrentThreadName("[Xe] Log"); - Entry entry = {}; - const auto writeLogs = [this, &entry]() { - ForEachBackend([&entry](BaseBackend *backend) { backend->Write(entry); }); - }; - while (!stopToken.stop_requested()) { - if (messageQueue.PopWait(entry, stopToken)) - writeLogs(); - } - // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a - // case where a system is repeatedly spamming logs even on close. - s32 maxLogsToWrite = filter.IsDebug() ? std::numeric_limits::max() : 100; - while (maxLogsToWrite-- > 0) { - if (messageQueue.TryPop(entry)) { - writeLogs(); - } else { - break; - } - } - }); - } - - void StopBackendThread() { - backendThread.request_stop(); - if (backendThread.joinable()) { - backendThread.join(); - } - - ForEachBackend([](BaseBackend *backend) { backend->Flush(); }); - } - - void ForEachBackend(std::function lambda) { - lambda(colorConsoleBackend.get()); - lambda(fileBackend.get()); - } - - static void Deleter(Impl* ptr) { - delete ptr; - } - - static inline std::unique_ptr instance{ nullptr, Deleter }; - - Filter filter; - std::unique_ptr colorConsoleBackend = {}; - std::unique_ptr fileBackend = {}; - - MPSCQueue messageQueue = {}; - std::chrono::steady_clock::time_point timeOrigin = std::chrono::steady_clock::now(); - std::jthread backendThread; -}; - -std::vector filepaths{}; - -void DeleteOldLogs(const fs::path& path, u64 num_logs, const u16 logLimit) { - const std::string filename = path.filename().string(); - const std::chrono::time_point Now = std::chrono::system_clock::now(); - const time_t timeNow = std::chrono::system_clock::to_time_t(Now); - const tm* time = std::localtime(&timeNow); - // We want to get rid of anything that isn't that current day's date - const std::string currentDate = fmt::format("{}-{}-{}", time->tm_mon + 1, time->tm_mday, 1900 + time->tm_year); - if (filename.find(currentDate) == std::string::npos) { - fs::remove_all(path); - return; - } - // We want to delete in date of creation, so just add it to a array - if (num_logs >= logLimit) { - filepaths.push_back(path); - } -} - -u64 CreateIntegralTimestamp(const std::string &date) { - const u64 monthPos = date.find('-'); - if (monthPos == std::string::npos) { - return 0; - } - const std::string month = date.substr(0, monthPos); - const u64 dayPos = date.find('-', monthPos); - if (dayPos == std::string::npos) { - return 0; - } - const u64 yearPos = date.find('-', dayPos); - const std::string day = date.substr(monthPos+1); - if (yearPos == std::string::npos) { - return 0; - } - const std::string year = date.substr(yearPos + 1); - const u64 yearInt = std::stoull(year); - const std::string timestamp = fmt::format("{}{}{}", month, day, yearInt - 1900); - return std::stoull(timestamp); -} - -void CleanupOldLogs(const std::string_view &logFileBase, const fs::path &logDir, const u16 logLimit) { - const fs::path LogFile = logFileBase; - // Track how many logs we have - size_t numLogs = 0; - for (auto &entry : fs::directory_iterator(logDir)) { - if (entry.is_regular_file()) { - const fs::path path = entry.path(); - const std::string ext = path.extension().string(); - if (!path.has_extension()) { - // Skip anything that isn't a log file - continue; - } - if (ext != LogFile.extension()) { - // Skip anything that isn't a log file - continue; - } - numLogs++; - DeleteOldLogs(path, numLogs, logLimit); - } else { - // Skip anything that isn't a file - continue; - } - } - if (filepaths.empty()) { - return; - } - u64 numToDelete{ logLimit }; - std::map date_sorted_paths{}; - for (const auto &path : filepaths) { - const std::string stem = path.stem().string(); - u64 basePos = stem.find('_'); - // If we cannot get the base, just delete it - if (basePos == std::string::npos) { - numToDelete--; - fs::remove_all(path); - } else { - const std::string base = stem.substr(0, basePos); - const u64 datePos = base.find('_', basePos+1); - const std::string date = base.substr(datePos+1); - const u64 dateInt = CreateIntegralTimestamp(date); - if (datePos == std::string::npos) { - // If we cannot find the date, just delete it - numToDelete--; - fs::remove_all(path); - } else { - const u64 timePos = base.find('_', datePos+1); - const std::string time = base.substr(timePos+1); - const u64 timestamp = CreateIntegralTimestamp(time); - if (!timestamp) { - numToDelete--; - fs::remove_all(path); - continue; - } - date_sorted_paths.insert({ dateInt + timestamp, path }); - } - } - } - // Start deleting based off timestamp - for (const auto &entry : date_sorted_paths) { - fs::remove_all(entry.second); - } -} - -void Initialize(const std::string_view &logFile) { - // Create directory vars to so we can use fs::path::stem - const fs::path LogDir = GetUserPath(PathType::LogDir); - const fs::path LogFile = LOG_FILE; - const fs::path LogFileStem = LogFile.stem(); - const fs::path LogFileName = LogFile.filename(); - // This is to make string_view happy - const std::string LogFileStemStr = LogFileStem.string(); - const std::string LogFileNameStr = LogFileName.string(); - // Setup filename - const std::string_view filestemBase = logFile.empty() ? LogFileStemStr : logFile; - const std::string_view filenameBase = logFile.empty() ? LogFileNameStr : logFile; - const std::chrono::time_point now = std::chrono::system_clock::now(); - const time_t timeNow = std::chrono::system_clock::to_time_t(now); - const tm *time = std::localtime(&timeNow); - const std::string currentTime = fmt::format("{}-{}-{}", time->tm_hour, time->tm_min, time->tm_sec); - const std::string currentDate = fmt::format("{}-{}-{}", time->tm_mon + 1, time->tm_mday, 1900 + time->tm_year); - const std::string filename = fmt::format("{}_{}_{}.txt", filestemBase, currentDate, currentTime); - CleanupOldLogs(filenameBase, LogDir); - Impl::Initialize(logFile.empty() ? filename : logFile); -} - -bool IsActive() { - return Impl::IsActive(); -} - -void Start() { - Impl::Start(); -} - -void Stop() { - Impl::Stop(); -} - -void SetGlobalFilter(const Filter &filter) { - Impl::Instance().SetGlobalFilter(filter); -} - -void SetColorConsoleBackendEnabled(bool enabled) { - Impl::Instance().SetColorConsoleBackendEnabled(enabled); -} - -void FmtLogMessageImpl(Class logClass, Level logLevel, const char *filename, - u32 lineNum, const char *function, const char *format, - const fmt::format_args &args) { - if (!currentlyInitialising) [[likely]] { - Impl::Instance().PushEntry(logClass, logLevel, filename, lineNum, function, - fmt::vformat(format, args)); - } -} - -void NoFmtMessage(Class logClass, Level logLevel, const std::string &message) { - if (!currentlyInitialising) [[likely]] { - Impl::Instance().PushEntryNoFmt(logClass, logLevel, message); - } -} -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/Backend.h b/src/common/Logging/Backend.h deleted file mode 100644 index abf2964..0000000 --- a/src/common/Logging/Backend.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include - -#include "common/PathUtil.h" - -#include "Filter.h" - -namespace Base { -namespace Log { - -class Filter; - -/// Cleans up logs from previous days, and any logs within the desired limit -void CleanupOldLogs(const std::string_view &logFileBase, const fs::path &logDir, const u16 logLimit = 50); - -/// Initializes the logging system -void Initialize(const std::string_view &logFile = {}); - -bool IsActive(); - -/// Starts the logging threads -void Start(); - -/// Explictily stops the logger thread and flushes the buffers -void Stop(); - -/// The global filter will prevent any messages from even being processed if they are filtered -void SetGlobalFilter(const Filter &filter); - -void SetColorConsoleBackendEnabled(bool enabled); - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/Filter.cpp b/src/common/Logging/Filter.cpp deleted file mode 100644 index d032591..0000000 --- a/src/common/Logging/Filter.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include "Filter.h" -#include - -namespace Base { -namespace Log { - -template -Level GetLevelByName(const It begin, const It end) { - for (u8 i = 0; i < static_cast(Level::Count); ++i) { - const char* level_name = GetLevelName(static_cast(i)); - if (std::string_view(begin, end).compare(level_name) == 0) { - return static_cast(i); - } - } - return Level::Count; -} - -template -Class GetClassByName(const It begin, const It end) { - for (u8 i = 0; i < static_cast(Class::Count); ++i) { - const char* level_name = GetLogClassName(static_cast(i)); - if (std::string_view(begin, end).compare(level_name) == 0) { - return static_cast(i); - } - } - return Class::Count; -} - -template -bool ParseFilterRule(Filter &instance, Iterator begin, Iterator end) { - const auto levelSeparator = std::find(begin, end, ':'); - if (levelSeparator == end) { - // LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string_view(begin, end)); - return false; - } - - const Level level = GetLevelByName(levelSeparator + 1, end); - if (level == Level::Count) { - //LOG_ERROR(Log, "Unknown log level in filter: {}", std::string_view(begin, end)); - return false; - } - - if (std::string_view(begin, levelSeparator).compare("*") == 0) { - instance.ResetAll(level); - return true; - } - - const Class logClass = GetClassByName(begin, levelSeparator); - if (logClass == Class::Count) { - //LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end)); - return false; - } - - instance.SetClassLevel(logClass, level); - return true; -} - -/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. -#define ALL_LOG_CLASSES() \ - CLS(Log) \ - CLS(Base) \ - SUB(Base, Filesystem) \ - CLS(Config) \ - CLS(Debug) \ - CLS(System) \ - CLS(Render) \ - CLS(ARM) \ - CLS(Memory) \ - CLS(PROBE) \ - CLS(MMIO_S1) - -// GetClassName is a macro defined by Windows.h, grrr... -const char* GetLogClassName(Class logClass) { - switch (logClass) { -#define CLS(x) \ - case Class::x: \ - return #x; -#define SUB(x, y) \ - case Class::x##_##y: \ - return #x "." #y; - ALL_LOG_CLASSES() -#undef CLS -#undef SUB - case Class::Count: - default: - break; - } -} - -const char* GetLevelName(Level logLevel) { -#define LVL(x) \ - case Level::x: \ - return #x - switch (logLevel) { - LVL(Trace); - LVL(Debug); - LVL(Info); - LVL(Warning); - LVL(Error); - LVL(Critical); - LVL(Guest); - case Level::Count: - default: - break; - } -#undef LVL -} - -Filter::Filter(Level defaultLevel) { - ResetAll(defaultLevel); -} - -void Filter::ResetAll(Level level) { - classLevels.fill(level); -} - -void Filter::SetClassLevel(Class logClass, Level level) { - classLevels[static_cast(logClass)] = level; -} - -void Filter::ParseFilterString(const std::string_view &filterView) { - auto clause_begin = filterView.cbegin(); - while (clause_begin != filterView.cend()) { - auto clause_end = std::find(clause_begin, filterView.cend(), ' '); - - // If clause isn't empty - if (clause_end != clause_begin) { - ParseFilterRule(*this, clause_begin, clause_end); - } - - if (clause_end != filterView.cend()) { - // Skip over the whitespace - ++clause_end; - } - clause_begin = clause_end; - } -} - -bool Filter::CheckMessage(Class logClass, Level level) const { - return static_cast(level) >= - static_cast(classLevels[static_cast(logClass)]); -} - -bool Filter::IsDebug() const { - return std::any_of(classLevels.begin(), classLevels.end(), [](const Level& l) { - return static_cast(l) <= static_cast(Level::Debug); - }); -} - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/Filter.h b/src/common/Logging/Filter.h deleted file mode 100644 index 2e82720..0000000 --- a/src/common/Logging/Filter.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include - -#include "LogTypes.h" - -namespace Base { -namespace Log { - -/* - * Returns the name of the passed log class as a C-string. Subclasses are separated by periods - * instead of underscores as in the enumeration. - */ -const char* GetLogClassName(Class log_class); - -/* - * Returns the name of the passed log level as a C-string. - */ -const char* GetLevelName(Level log_level); - -/* - * Implements a log message filter which allows different log classes to have different minimum - * severity levels. The filter can be changed at runtime and can be parsed from a string to allow - * editing via the interface or loading from a configuration file. - */ -class Filter { -public: - /// Initializes the filter with all classes having `default_level` as the minimum level. - explicit Filter(Level defaultLevel = Level::Info); - - /// Resets the filter so that all classes have `level` as the minimum displayed level. - void ResetAll(Level level); - - /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`. - void SetClassLevel(Class logClass, Level level); - - /* - * Parses a filter string and applies it to this filter. - * - * A filter string consists of a space-separated list of filter rules, each of the format - * `:`. `` is a log class name, with subclasses separated using periods. - * `*` is allowed as a class name and will reset all filters to the specified level. `` - * a severity level name which will be set as the minimum logging level of the matched classes. - * Rules are applied left to right, with each rule overriding previous ones in the sequence. - * - * A few examples of filter rules: - * - `*:Info` -- Resets the level of all classes to Info. - * - `Service:Info` -- Sets the level of Service to Info. - * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. - */ - void ParseFilterString(const std::string_view &filterView); - - /// Matches class/level combination against the filter, returning true if it passed. - bool CheckMessage(Class logClass, Level level) const; - - /// Returns true if any logging classes are set to debug - bool IsDebug() const; - -private: - std::array(Class::Count)> classLevels; -}; - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/Log.h b/src/common/Logging/Log.h deleted file mode 100644 index 1033b2c..0000000 --- a/src/common/Logging/Log.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include - -#include "common/Config.h" - -#include "LogTypes.h" - -namespace Base { -namespace Log { - -constexpr const char* TrimSourcePath(const std::string_view &source) { - const auto rfind = [source](const std::string_view match) { - return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size()); - }; - const auto idx = std::max({ rfind("/"), rfind("\\") }); - return source.data() + idx; -} - -/// Logs a message to the global logger, using fmt -void FmtLogMessageImpl(Class logClass, Level logLevel, const char *filename, - u32 lineNum, const char *function, const char *format, - const fmt::format_args& args); - -/// Logs a message without any formatting -void NoFmtMessage(Class logClass, Level logLevel, const std::string &message); - -template -void FmtLogMessage(Class logClass, Level logLevel, const char *filename, u32 lineNum, - const char *function, const char *format, const Args&... args) { - FmtLogMessageImpl(logClass, logLevel, filename, lineNum, function, format, - fmt::make_format_args(args...)); -} - -} // namespace Log -} // namespace Base - -// Define the fmt lib macros -#define LOG_GENERIC(logClass, logLevel, ...) \ - Base::Log::FmtLogMessage(logClass, logLevel, Base::Log::TrimSourcePath(__FILE__), \ - __LINE__, __func__, __VA_ARGS__) -#ifdef DEBUG_BUILD -#define LOG_TRACE(logClass, ...) \ -if (Config::log.debugOnly) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Trace, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) -#else -#define LOG_TRACE(logClass, ...) ; -#endif - -#ifdef DEBUG_BUILD -#define LOG_DEBUG(logClass, ...) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Debug, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) -#else -#define LOG_DEBUG(logClass, ...) ; -#endif -#define LOG_INFO(logClass, ...) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Info, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) -#define LOG_WARNING(logClass, ...) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Warning, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) -#define LOG_ERROR(logClass, ...) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Error, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) -#define LOG_CRITICAL(logClass, ...) \ - Base::Log::FmtLogMessage(Base::Log::Class::logClass, Base::Log::Level::Critical, \ - Base::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \ - __VA_ARGS__) diff --git a/src/common/Logging/LogEntry.h b/src/common/Logging/LogEntry.h deleted file mode 100644 index a8852a0..0000000 --- a/src/common/Logging/LogEntry.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include - -#include "LogTypes.h" - -namespace Base { -namespace Log { - -/* - * A log entry. Log entries are store in a structured format to permit more varied output - * formatting on different frontends, as well as facilitating filtering and aggregation. - */ -struct Entry { - std::chrono::microseconds timestamp = {}; - Class logClass = {}; - Level logLevel = {}; - const char *filename = nullptr; - u32 lineNum = 0; - std::string function = {}; - std::string message = {}; - bool formatted = true; -}; - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/LogTypes.h b/src/common/Logging/LogTypes.h deleted file mode 100644 index 6333090..0000000 --- a/src/common/Logging/LogTypes.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include "common/Types.h" - -namespace Base { -namespace Log { - -/// Specifies the severity or level of detail of the log message -enum class Level : const u8 { - Trace, ///< Extremely detailed and repetitive debugging information that is likely to pollute logs - Debug, ///< Less detailed debugging information - Info, ///< Status information from important points during execution - Warning, ///< Minor or potential problems found during execution of a task - Error, ///< Major problems found during execution of a task that prevent it from being completed - Critical, ///< Major problems during execution that threaten the stability of the entire application - Guest, ///< Output from the guest system - Count ///< Total number of logging levels -}; - -/* - * Specifies the sub-system that generated the log message - * - * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in - * filtercpp - */ -enum class Class : const u8 { - Log, // Messages about the log system itself - Base, // System base routines: FS, logging, etc - Base_Filesystem, // Filesystem messages - Config, // Emulator configuration (including commandline) - Debug, // Debugging tools - System, // Base System messages - Render, // OpenGL and Window messages - ARM, - PROBE, - MMIO_S1, - Memory, - Count // Total number of logging classes -}; - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/TextFormatter.cpp b/src/common/Logging/TextFormatter.cpp deleted file mode 100644 index 05b6ae5..0000000 --- a/src/common/Logging/TextFormatter.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include "common/Config.h" - -#include "TextFormatter.h" - -#include "Filter.h" -#include "LogEntry.h" -#include - -namespace Base { -namespace Log { - -std::string FormatLogMessage(const Entry &entry) { - const char *className = GetLogClassName(entry.logClass); - const char *levelName = GetLevelName(entry.logLevel); - - if (Config::isLogAdvanced() && entry.filename) { - return fmt::format("[{}] <{}> {}:{}:{}: {}", className, levelName, entry.filename, - entry.function, entry.lineNum, entry.message); - } else { - return fmt::format("[{}] <{}> {}", className, levelName, entry.message); - } -} - -#define ESC "\x1b" -void PrintMessage(const std::string &color, const Entry &entry) { - std::string msg = entry.formatted ? FormatLogMessage(entry) : entry.message; - const std::string str = color + msg.append(ESC "[0m") + (entry.formatted ? "\n" : ""); - fputs(str.c_str(), stdout); -} - -void PrintColoredMessage(const Entry &entry) { - // NOTE: Custom colors can be achieved - // std::format("\x1b[{};2;{};{};{}m", color.bg ? 48 : 38, color.r, color.g, color.b) - const char *color = ""; - switch (entry.logLevel) { - case Level::Trace: // Grey - color = ESC "[1;30m"; - break; - case Level::Debug: // Cyan - color = ESC "[0;36m"; - break; - case Level::Info: // Bright gray - color = ESC "[0;37m"; - break; - case Level::Warning: // Bright yellow - color = ESC "[1;33m"; - break; - case Level::Error: // Bright red - color = ESC "[1;31m"; - break; - case Level::Critical: // Bright magenta - color = ESC "[1;35m"; - break; - case Level::Guest: // Green - color = ESC "[0;92m"; - break; - case Level::Count: - break; - } - - PrintMessage(color, entry); -} -#undef ESC - -} // namespace Log -} // namespace Base diff --git a/src/common/Logging/TextFormatter.h b/src/common/Logging/TextFormatter.h deleted file mode 100644 index 426c607..0000000 --- a/src/common/Logging/TextFormatter.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include - -namespace Base { -namespace Log { - -struct Entry; - -/// Formats a log entry into the provided text buffer. -std::string FormatLogMessage(const Entry &entry); - -/// Formats and prints a log entry to stderr. -void PrintMessage(const std::string &color, const Entry &entry); - -/// Prints the same message as `PrintMessage`, but colored according to the severity level. -void PrintColoredMessage(const Entry &entry); - -} // namespace Log -} // namespace Base diff --git a/src/common/PathUtil.cpp b/src/common/PathUtil.cpp deleted file mode 100644 index 85e973a..0000000 --- a/src/common/PathUtil.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include "PathUtil.h" -#include "common/Logging/Log.h" - -#include - -#include -#include -#ifdef _WIN32 -#include -#endif // _WIN32 -#ifdef __APPLE__ -#include -#include -#endif // __APPLE__ - -namespace Base -{ -namespace FS -{ - -const fs::path GetBinaryDirectory() -{ - fs::path fspath = {}; -#ifdef _WIN32 - char path[256]; - GetModuleFileNameA(nullptr, path, sizeof(path)); - fspath = path; -#elif __linux__ - fspath = fs::canonical("/proc/self/exe"); -#elif __APPLE__ - pid_t pid = getpid(); - char path[PROC_PIDPATHINFO_MAXSIZE]; - // While this is fine for a raw executable, - // an application bundle is read-only and these files - // should instead be placed in Application Support. - proc_pidpath(pid, path, sizeof(path)); - fspath = path; -#else - // Unknown, just return rootdir - fspath = fs::current_path() / "Pound"; -#endif - return fs::weakly_canonical(fmt::format("{}/..", fspath.string())); -} - -static auto UserPaths = [] -{ - auto currentDir = fs::current_path(); - auto binaryDir = GetBinaryDirectory(); - bool nixos = false; - - std::unordered_map paths; - - const auto insert_path = [&](PathType pound_path, const fs::path& new_path, bool create = true) - { - if (create && !fs::exists(new_path)) - fs::create_directory(new_path); - - paths.insert_or_assign(pound_path, new_path); - }; - - insert_path(PathType::BinaryDir, binaryDir, false); - // If we are in the nix store, it's read-only. Change to currentDir if needed - if (binaryDir.string().find("/nix/store/") != std::string::npos) - { - nixos = true; - } - if (nixos) - { - currentDir /= "files"; - insert_path(PathType::RootDir, currentDir); - insert_path(PathType::FirmwareDir, currentDir / FW_DIR); - insert_path(PathType::LogDir, currentDir / LOG_DIR); - } - else - { - insert_path(PathType::RootDir, currentDir, false); - insert_path(PathType::FirmwareDir, binaryDir / FW_DIR); - insert_path(PathType::LogDir, binaryDir / LOG_DIR); - } - return paths; -}(); - -std::string PathToUTF8String(const fs::path& path) -{ - const auto u8_string = path.u8string(); - return std::string{u8_string.begin(), u8_string.end()}; -} - -const fs::path& GetUserPath(PathType pound_path) -{ - return UserPaths.at(pound_path); -} - -std::string GetUserPathString(PathType pound_path) -{ - return PathToUTF8String(GetUserPath(pound_path)); -} - -std::vector ListFilesFromPath(const fs::path& path) -{ - std::vector fileList; - - fs::path _path = fs::weakly_canonical(path); - - for (auto& entry : fs::directory_iterator{_path}) - { - FileInfo fileInfo; - if (entry.is_directory()) - { - fileInfo.fileSize = 0; - fileInfo.fileType = FileType::Directory; - } - else - { - fileInfo.fileSize = fs::file_size(_path); - fileInfo.fileType = FileType::File; - } - - fileInfo.filePath = entry.path(); - fileInfo.fileName = entry.path().filename(); - fileList.push_back(fileInfo); - } - - return fileList; -} - -void SetUserPath(PathType pound_path, const fs::path& new_path) -{ - UserPaths.insert_or_assign(pound_path, new_path); -} -} // namespace FS -} // namespace Base diff --git a/src/common/PathUtil.h b/src/common/PathUtil.h deleted file mode 100644 index 513f99d..0000000 --- a/src/common/PathUtil.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include - -namespace fs = std::filesystem; - -namespace Base -{ -namespace FS -{ - -enum class PathType -{ - BinaryDir, // Binary Path - FirmwareDir, // Where log files are stored - RootDir, // Execution Path - LogDir, // Where log files are stored -}; - -enum FileType -{ - Directory, - File -}; - -// Represents a given file inside a directory. -typedef struct _FileInfo -{ - fs::path fileName; // The file name and extension - fs::path filePath; // The file path - size_t fileSize; // File size - FileType fileType; // File Type (directory/file) -} FileInfo; - -constexpr auto FW_DIR = "firmware"; - -constexpr auto LOG_DIR = "log"; - -constexpr auto LOG_FILE = "pound_log.txt"; - -// Converts a given fs::path to a UTF8 string. -[[nodiscard]] std::string PathToUTF8String(const fs::path& path); - -// Returns a fs::path object containing the current 'User' path. -[[nodiscard]] const fs::path& GetUserPath(PathType user_path); - -// Returns a string containing the current 'User' path. -[[nodiscard]] std::string GetUserPathString(PathType user_path); - -// Returns a container with a list of the files inside the specified path. -[[nodiscard]] std::vector ListFilesFromPath(const fs::path& path); - -// Sets the current Path for a given PathType. -void SetUserPath(PathType user_path, const fs::path& new_path); - -} // namespace FS -} // namespace Base diff --git a/src/common/PolyfillThread.h b/src/common/PolyfillThread.h deleted file mode 100644 index 2719621..0000000 --- a/src/common/PolyfillThread.h +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -// -// TODO: remove this file when jthread is supported by all compilation targets -// - -#pragma once - -#if defined(__cpp_lib_jthread) || !defined(_MSVC_VER) - -#include -#include -#include -#include -#include -#include - -namespace Base -{ - -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred&& pred) -{ - cv.wait(lk, token, std::forward(pred)); -} - -template -bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) -{ - std::condition_variable_any cv; - std::mutex m; - - // Perform the timed wait. - std::unique_lock lk{m}; - return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); }); -} - -} // namespace Base - -#else - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace std -{ -namespace polyfill -{ - -using stop_state_callback = size_t; - -class stop_state -{ - public: - stop_state() = default; - ~stop_state() = default; - - bool request_stop() - { - unique_lock lk{m_lock}; - - if (m_stop_requested) - { - // Already set, nothing to do. - return false; - } - - // Mark stop requested. - m_stop_requested = true; - - while (!m_callbacks.empty()) - { - // Get an iterator to the first element. - const auto it = m_callbacks.begin(); - - // Move the callback function out of the map. - function f; - swap(it->second, f); - - // Erase the now-empty map element. - m_callbacks.erase(it); - - // Run the callback. - if (f) - { - f(); - } - } - - return true; - } - - bool stop_requested() const - { - unique_lock lk{m_lock}; - return m_stop_requested; - } - - stop_state_callback insert_callback(function f) - { - unique_lock lk{m_lock}; - - if (m_stop_requested) - { - // Stop already requested. Don't insert anything, - // just run the callback synchronously. - if (f) - { - f(); - } - return 0; - } - - // Insert the callback. - stop_state_callback ret = ++m_next_callback; - m_callbacks.emplace(ret, std::move(f)); - return ret; - } - - void remove_callback(stop_state_callback cb) - { - unique_lock lk{m_lock}; - m_callbacks.erase(cb); - } - - private: - mutable recursive_mutex m_lock; - map> m_callbacks; - stop_state_callback m_next_callback{0}; - bool m_stop_requested{false}; -}; - -} // namespace polyfill - -class stop_token; -class stop_source; -struct nostopstate_t -{ - explicit nostopstate_t() = default; -}; -inline constexpr nostopstate_t nostopstate{}; - -template -class stop_callback; - -class stop_token -{ - public: - stop_token() noexcept = default; - - stop_token(const stop_token&) noexcept = default; - stop_token(stop_token&&) noexcept = default; - stop_token& operator=(const stop_token&) noexcept = default; - stop_token& operator=(stop_token&&) noexcept = default; - ~stop_token() = default; - - void swap(stop_token& other) noexcept { m_stop_state.swap(other.m_stop_state); } - - [[nodiscard]] bool stop_requested() const noexcept { return m_stop_state && m_stop_state->stop_requested(); } - [[nodiscard]] bool stop_possible() const noexcept { return m_stop_state != nullptr; } - - private: - friend class stop_source; - template - friend class stop_callback; - stop_token(shared_ptr stop_state) : m_stop_state(std::move(stop_state)) {} - - private: - shared_ptr m_stop_state; -}; - -class stop_source -{ - public: - stop_source() : m_stop_state(make_shared()) {} - explicit stop_source(nostopstate_t) noexcept {} - - stop_source(const stop_source&) noexcept = default; - stop_source(stop_source&&) noexcept = default; - stop_source& operator=(const stop_source&) noexcept = default; - stop_source& operator=(stop_source&&) noexcept = default; - ~stop_source() = default; - void swap(stop_source& other) noexcept { m_stop_state.swap(other.m_stop_state); } - - [[nodiscard]] stop_token get_token() const noexcept { return stop_token(m_stop_state); } - [[nodiscard]] bool stop_possible() const noexcept { return m_stop_state != nullptr; } - [[nodiscard]] bool stop_requested() const noexcept { return m_stop_state && m_stop_state->stop_requested(); } - bool request_stop() noexcept { return m_stop_state && m_stop_state->request_stop(); } - - private: - friend class jthread; - explicit stop_source(shared_ptr stop_state) : m_stop_state(std::move(stop_state)) {} - - private: - shared_ptr m_stop_state; -}; - -template -class stop_callback -{ - static_assert(is_nothrow_destructible_v); - static_assert(is_invocable_v); - - public: - using callback_type = Callback; - - template - requires constructible_from explicit stop_callback(const stop_token& st, C&& cb) noexcept( - is_nothrow_constructible_v) - : m_stop_state(st.m_stop_state) - { - if (m_stop_state) - { - m_callback = m_stop_state->insert_callback(std::move(cb)); - } - } - template - requires constructible_from explicit stop_callback(stop_token&& st, C&& cb) noexcept( - is_nothrow_constructible_v) - : m_stop_state(std::move(st.m_stop_state)) - { - if (m_stop_state) - { - m_callback = m_stop_state->insert_callback(std::move(cb)); - } - } - ~stop_callback() - { - if (m_stop_state && m_callback) - { - m_stop_state->remove_callback(m_callback); - } - } - - stop_callback(const stop_callback&) = delete; - stop_callback(stop_callback&&) = delete; - stop_callback& operator=(const stop_callback&) = delete; - stop_callback& operator=(stop_callback&&) = delete; - - private: - shared_ptr m_stop_state; - polyfill::stop_state_callback m_callback; -}; - -template -stop_callback(stop_token, Callback) -> stop_callback; - -class jthread -{ - public: - using id = thread::id; - using native_handle_type = thread::native_handle_type; - - jthread() noexcept = default; - - template , jthread>>> - explicit jthread(F&& f, Args&&... args) - : m_stop_state(make_shared()), - m_thread(make_thread(std::forward(f), std::forward(args)...)) - { - } - - ~jthread() - { - if (joinable()) - { - request_stop(); - join(); - } - } - - jthread(const jthread&) = delete; - jthread(jthread&&) noexcept = default; - jthread& operator=(const jthread&) = delete; - - jthread& operator=(jthread&& other) noexcept - { - m_thread.swap(other.m_thread); - m_stop_state.swap(other.m_stop_state); - return *this; - } - - void swap(jthread& other) noexcept - { - m_thread.swap(other.m_thread); - m_stop_state.swap(other.m_stop_state); - } - [[nodiscard]] bool joinable() const noexcept { return m_thread.joinable(); } - void join() { m_thread.join(); } - void detach() - { - m_thread.detach(); - m_stop_state.reset(); - } - - [[nodiscard]] id get_id() const noexcept { return m_thread.get_id(); } - [[nodiscard]] native_handle_type native_handle() { return m_thread.native_handle(); } - [[nodiscard]] stop_source get_stop_source() noexcept { return stop_source(m_stop_state); } - [[nodiscard]] stop_token get_stop_token() const noexcept { return stop_source(m_stop_state).get_token(); } - bool request_stop() noexcept { return get_stop_source().request_stop(); } - [[nodiscard]] static u32 hardware_concurrency() noexcept { return thread::hardware_concurrency(); } - - private: - template - thread make_thread(F&& f, Args&&... args) - { - if constexpr (is_invocable_v, stop_token, decay_t...>) - { - return thread(std::forward(f), get_stop_token(), std::forward(args)...); - } - else - { - return thread(std::forward(f), std::forward(args)...); - } - } - - shared_ptr m_stop_state; - thread m_thread; -}; - -} // namespace std - -namespace Base -{ - -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred pred) -{ - if (token.stop_requested()) - { - return; - } - - std::stop_callback callback(token, - [&] - { - { - std::scoped_lock lk2{*lk.mutex()}; - } - cv.notify_all(); - }); - - cv.wait(lk, [&] { return pred() || token.stop_requested(); }); -} - -template -bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) -{ - if (token.stop_requested()) - { - return false; - } - - bool stop_requested = false; - std::condition_variable cv; - std::mutex m; - - std::stop_callback cb(token, - [&] - { - // Wake up the waiting thread. - { - std::scoped_lock lk{m}; - stop_requested = true; - } - cv.notify_one(); - }); - - // Perform the timed wait. - std::unique_lock lk{m}; - return !cv.wait_for(lk, rel_time, [&] { return stop_requested; }); -} - -} // namespace Base - -#endif diff --git a/src/common/StringUtil.cpp b/src/common/StringUtil.cpp deleted file mode 100644 index c7aa797..0000000 --- a/src/common/StringUtil.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include "StringUtil.h" -#include "Types.h" - -namespace Base -{ - -#ifdef _WIN32 -static std::wstring CPToUTF16(u32 code_page, std::string_view input) -{ - const s32 size = ::MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), nullptr, 0); - - if (size == 0) - { - return {}; - } - - std::wstring output(size, L'\0'); - - if (size != ::MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), &output[0], - static_cast(output.size()))) - { - output.clear(); - } - - return output; -} -#endif // ifdef _WIN32 - -std::string UTF16ToUTF8(const std::wstring_view& input) -{ -#ifdef _WIN32 - const s32 size = - ::WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast(input.size()), nullptr, 0, nullptr, nullptr); - if (size == 0) - { - return {}; - } - - std::string output(size, '\0'); - - if (size != ::WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast(input.size()), &output[0], - static_cast(output.size()), nullptr, nullptr)) - { - output.clear(); - } - return output; -#else - // Very hacky way to get cross-platform wstring conversion - return std::filesystem::path(input).string(); -#endif // ifdef _WIN32 -} - -std::wstring UTF8ToUTF16W(const std::string_view& input) -{ -#ifdef _WIN32 - return CPToUTF16(CP_UTF8, input); -#else - // Very hacky way to get cross-platform wstring conversion - return std::filesystem::path(input).wstring(); -#endif // ifdef _WIN32 -} - -std::string ToLower(const std::string& str) -{ - std::string out = str; - std::transform(str.begin(), str.end(), out.data(), ::tolower); - return out; -} - -} // namespace Base diff --git a/src/common/StringUtil.h b/src/common/StringUtil.h deleted file mode 100644 index 5dc8731..0000000 --- a/src/common/StringUtil.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#endif - -namespace Base -{ - -[[nodiscard]] std::string UTF16ToUTF8(const std::wstring_view& input); -[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string_view& str); -[[nodiscard]] std::string ToLower(const std::string& str); - -} // namespace Base diff --git a/src/common/Thread.cpp b/src/common/Thread.cpp deleted file mode 100644 index 36ca65f..0000000 --- a/src/common/Thread.cpp +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#include "Thread.h" -#include "Error.h" -#include "Logging/Log.h" - -#ifdef __APPLE__ -#include -#include -#include -#elif defined(_WIN32) -#include -#include "StringUtil.h" -#else - -#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) -#include -#else -#include -#endif -#include -#endif -#ifndef _WIN32 -#include -#endif -#include -#include - -#ifdef __FreeBSD__ -#define cpu_set_t cpuset_t -#endif - -namespace Base -{ - -#ifdef __APPLE__ - -void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) -{ - // CPU time to grant. - const std::chrono::nanoseconds computation_ns = period_ns / 2; - - // Determine the timebase for converting time to ticks. - struct mach_timebase_info timebase{}; - mach_timebase_info(&timebase); - const auto ticks_per_ns = static_cast(timebase.denom) / static_cast(timebase.numer); - - const auto period_ticks = static_cast(static_cast(period_ns.count()) * ticks_per_ns); - const auto computation_ticks = static_cast(static_cast(computation_ns.count()) * ticks_per_ns); - - thread_time_constraint_policy policy = { - .period = period_ticks, - .computation = computation_ticks, - // Should not matter since preemptible is false, but needs to be >= computation regardless. - .constraint = computation_ticks, - .preemptible = false, - }; - - int ret = thread_policy_set(pthread_mach_thread_np(pthread_self()), THREAD_TIME_CONSTRAINT_POLICY, - reinterpret_cast(&policy), THREAD_TIME_CONSTRAINT_POLICY_COUNT); - if (ret != KERN_SUCCESS) - { - LOG_ERROR(Base, "Could not set thread to real-time with period {} ns: {}", period_ns.count(), ret); - } -} - -#else - -void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) -{ - // Not implemented -} - -#endif - -#ifdef _WIN32 - -void SetCurrentThreadPriority(ThreadPriority new_priority) -{ - const auto handle = GetCurrentThread(); - int windows_priority = 0; - switch (new_priority) - { - case ThreadPriority::Low: - windows_priority = THREAD_PRIORITY_BELOW_NORMAL; - break; - case ThreadPriority::Normal: - windows_priority = THREAD_PRIORITY_NORMAL; - break; - case ThreadPriority::High: - windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; - break; - case ThreadPriority::VeryHigh: - windows_priority = THREAD_PRIORITY_HIGHEST; - break; - case ThreadPriority::Critical: - windows_priority = THREAD_PRIORITY_TIME_CRITICAL; - break; - default: - windows_priority = THREAD_PRIORITY_NORMAL; - break; - } - SetThreadPriority(handle, windows_priority); -} - -static void AccurateSleep(std::chrono::nanoseconds duration) -{ - LARGE_INTEGER interval{ - .QuadPart = -1 * (duration.count() / 100u), - }; - const HANDLE timer = ::CreateWaitableTimer(nullptr, TRUE, nullptr); - SetWaitableTimer(timer, &interval, 0, nullptr, nullptr, 0); - WaitForSingleObject(timer, INFINITE); - ::CloseHandle(timer); -} - -#else - -void SetCurrentThreadPriority(ThreadPriority new_priority) -{ - pthread_t this_thread = pthread_self(); - - constexpr auto scheduling_type = SCHED_OTHER; - const s32 max_prio = sched_get_priority_max(scheduling_type); - const s32 min_prio = sched_get_priority_min(scheduling_type); - const u32 level = std::max(static_cast(new_priority) + 1, 4U); - - struct sched_param params; - if (max_prio > min_prio) - { - params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; - } - else - { - params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; - } - - pthread_setschedparam(this_thread, scheduling_type, ¶ms); -} - -static void AccurateSleep(std::chrono::nanoseconds duration) -{ - std::this_thread::sleep_for(duration); -} - -#endif - -#if defined(_MSC_VER) || defined(__MINGW32__) - -// Sets the debugger-visible name of the current thread. -void SetCurrentThreadName(const std::string_view& name) -{ - SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); -} - -void SetThreadName(void* thread, const std::string_view& name) -{ - const char* nchar = name.data(); - std::string truncated(nchar, std::min(name.size(), static_cast(15))); - SetThreadDescription(thread, UTF8ToUTF16W(name).data()); -} - -#else // !MSVC_VER, so must be POSIX threads - -// MinGW with the POSIX threading model does not support pthread_setname_np -#if !defined(_WIN32) || defined(_MSC_VER) -void SetCurrentThreadName(const std::string_view& name) -{ - const char* nchar = name.data(); -#ifdef __APPLE__ - pthread_setname_np(nchar); -#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(pthread_self(), nchar); -#elif defined(__NetBSD__) - pthread_setname_np(pthread_self(), "%s", (void*)nchar); -#elif defined(__linux__) - // Linux limits thread names to 15 characters and will outright reject any - // attempt to set a longer name with ERANGE. - std::string truncated(nchar, std::min(name.size(), static_cast(15))); - if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) - { - errno = e; - } -#else - pthread_setname_np(pthread_self(), nchar); -#endif -} - -void SetThreadName(void* thread, const std::string_view& name) -{ - // TODO -} -#endif - -#if defined(_WIN32) -void SetCurrentThreadName(const std::string_view& name) -{ - // Do Nothing on MinGW -} - -void SetThreadName(void* thread, const std::string_view& name) -{ - // Do Nothing on MinGW -} -#endif - -#endif - -AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval) : target_interval(target_interval) {} - -void AccurateTimer::Start() -{ - const auto begin_sleep = std::chrono::high_resolution_clock::now(); - if (total_wait.count() > 0) - { - AccurateSleep(total_wait); - } - start_time = std::chrono::high_resolution_clock::now(); - total_wait -= std::chrono::duration_cast(start_time - begin_sleep); -} - -void AccurateTimer::End() -{ - const auto now = std::chrono::high_resolution_clock::now(); - total_wait += target_interval - std::chrono::duration_cast(now - start_time); -} - -} // namespace Base diff --git a/src/common/Thread.h b/src/common/Thread.h deleted file mode 100644 index 3c9a2e7..0000000 --- a/src/common/Thread.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include -#include "Types.h" - -namespace Base -{ - -enum class ThreadPriority : u32 -{ - Low = 0, - Normal = 1, - High = 2, - VeryHigh = 3, - Critical = 4 -}; - -void SetCurrentThreadRealtime(std::chrono::nanoseconds period_ns); - -void SetCurrentThreadPriority(ThreadPriority new_priority); - -void SetCurrentThreadName(const std::string_view& name); - -void SetThreadName(void* thread, const std::string_view& name); - -class AccurateTimer -{ - std::chrono::nanoseconds target_interval{}; - std::chrono::nanoseconds total_wait{}; - - std::chrono::high_resolution_clock::time_point start_time; - - public: - explicit AccurateTimer(std::chrono::nanoseconds target_interval); - - void Start(); - - void End(); - - std::chrono::nanoseconds GetTotalWait() const { return total_wait; } -}; - -} // namespace Base diff --git a/src/common/Types.h b/src/common/Types.h deleted file mode 100644 index c72fa7e..0000000 --- a/src/common/Types.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2025 Xenon Emulator Project. All rights reserved. - -#pragma once - -#include - -#include "Arch.h" - -// Signed -using s8 = signed char; -using s16 = signed short; -using s32 = signed int; -using sl32 = signed long; -using s64 = signed long long; - -// Unsigned -using u8 = unsigned char; -using u16 = unsigned short; -using u32 = unsigned int; -using ul32 = unsigned long; -using u64 = unsigned long long; - -using uptr = uintptr_t; - -// Floating point -using f32 = float; -using f64 = double; - -// Function pointer -template -requires std::is_function_v using fptr = std::add_pointer_t; - -// UDLs for memory size values -[[nodiscard]] constexpr u64 operator""_KB(const u64 x) -{ - return 1000ULL * x; -} -[[nodiscard]] constexpr u64 operator""_KiB(const u64 x) -{ - return 1024ULL * x; -} -[[nodiscard]] constexpr u64 operator""_MB(const u64 x) -{ - return 1000_KB * x; -} -[[nodiscard]] constexpr u64 operator""_MiB(const u64 x) -{ - return 1024_KiB * x; -} -[[nodiscard]] constexpr u64 operator""_GB(const u64 x) -{ - return 1000_MB * x; -} -[[nodiscard]] constexpr u64 operator""_GiB(const u64 x) -{ - return 1024_MiB * x; -} diff --git a/src/common/swap.h b/src/common/swap.h deleted file mode 100644 index ed4f988..0000000 --- a/src/common/swap.h +++ /dev/null @@ -1,673 +0,0 @@ -// SPDX-FileCopyrightText: 2012 PPSSPP Project -// SPDX-FileCopyrightText: 2012 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#pragma once - -#if defined(_MSC_VER) -#include -#endif -#include -#include -#include -#include "common/common_types.h" - -namespace Common -{ - -#ifdef _MSC_VER -[[nodiscard]] inline u16 swap16(u16 data) noexcept -{ - return _byteswap_ushort(data); -} -[[nodiscard]] inline u32 swap32(u32 data) noexcept -{ - return _byteswap_ulong(data); -} -[[nodiscard]] inline u64 swap64(u64 data) noexcept -{ - return _byteswap_uint64(data); -} -#elif defined(__clang__) || defined(__GNUC__) -#if defined(__Bitrig__) || defined(__OpenBSD__) -// redefine swap16, swap32, swap64 as inline functions -#undef swap16 -#undef swap32 -#undef swap64 -#endif -[[nodiscard]] inline u16 swap16(u16 data) noexcept -{ - return __builtin_bswap16(data); -} -[[nodiscard]] inline u32 swap32(u32 data) noexcept -{ - return __builtin_bswap32(data); -} -[[nodiscard]] inline u64 swap64(u64 data) noexcept -{ - return __builtin_bswap64(data); -} -#else -// Generic implementation. -[[nodiscard]] inline u16 swap16(u16 data) noexcept -{ - return (data >> 8) | (data << 8); -} -[[nodiscard]] inline u32 swap32(u32 data) noexcept -{ - return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | ((data & 0x0000FF00U) << 8) | - ((data & 0x000000FFU) << 24); -} -[[nodiscard]] inline u64 swap64(u64 data) noexcept -{ - return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | - ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) | - ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) | - ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56); -} -#endif - -[[nodiscard]] inline float swapf(float f) noexcept -{ - static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); - - u32 value; - std::memcpy(&value, &f, sizeof(u32)); - - value = swap32(value); - std::memcpy(&f, &value, sizeof(u32)); - - return f; -} - -[[nodiscard]] inline double swapd(double f) noexcept -{ - static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); - - u64 value; - std::memcpy(&value, &f, sizeof(u64)); - - value = swap64(value); - std::memcpy(&f, &value, sizeof(u64)); - - return f; -} - -} // Namespace Common - -template -struct swap_struct_t -{ - using swapped_t = swap_struct_t; - - protected: - T value; - - static T swap(T v) { return F::swap(v); } - - public: - T swap() const { return swap(value); } - swap_struct_t() = default; - swap_struct_t(const T& v) : value(swap(v)) {} - - template - swapped_t& operator=(const S& source) - { - value = swap(static_cast(source)); - return *this; - } - - operator s8() const { return static_cast(swap()); } - operator u8() const { return static_cast(swap()); } - operator s16() const { return static_cast(swap()); } - operator u16() const { return static_cast(swap()); } - operator s32() const { return static_cast(swap()); } - operator u32() const { return static_cast(swap()); } - operator s64() const { return static_cast(swap()); } - operator u64() const { return static_cast(swap()); } - operator float() const { return static_cast(swap()); } - operator double() const { return static_cast(swap()); } - - // +v - swapped_t operator+() const { return +swap(); } - // -v - swapped_t operator-() const { return -swap(); } - - // v / 5 - swapped_t operator/(const swapped_t& i) const { return swap() / i.swap(); } - template - swapped_t operator/(const S& i) const - { - return swap() / i; - } - - // v * 5 - swapped_t operator*(const swapped_t& i) const { return swap() * i.swap(); } - template - swapped_t operator*(const S& i) const - { - return swap() * i; - } - - // v + 5 - swapped_t operator+(const swapped_t& i) const { return swap() + i.swap(); } - template - swapped_t operator+(const S& i) const - { - return swap() + static_cast(i); - } - // v - 5 - swapped_t operator-(const swapped_t& i) const { return swap() - i.swap(); } - template - swapped_t operator-(const S& i) const - { - return swap() - static_cast(i); - } - - // v += 5 - swapped_t& operator+=(const swapped_t& i) - { - value = swap(swap() + i.swap()); - return *this; - } - template - swapped_t& operator+=(const S& i) - { - value = swap(swap() + static_cast(i)); - return *this; - } - // v -= 5 - swapped_t& operator-=(const swapped_t& i) - { - value = swap(swap() - i.swap()); - return *this; - } - template - swapped_t& operator-=(const S& i) - { - value = swap(swap() - static_cast(i)); - return *this; - } - - // ++v - swapped_t& operator++() - { - value = swap(swap() + 1); - return *this; - } - // --v - swapped_t& operator--() - { - value = swap(swap() - 1); - return *this; - } - - // v++ - swapped_t operator++(int) - { - swapped_t old = *this; - value = swap(swap() + 1); - return old; - } - // v-- - swapped_t operator--(int) - { - swapped_t old = *this; - value = swap(swap() - 1); - return old; - } - // Comparison - // v == i - bool operator==(const swapped_t& i) const { return swap() == i.swap(); } - template - bool operator==(const S& i) const - { - return swap() == i; - } - - // v != i - bool operator!=(const swapped_t& i) const { return swap() != i.swap(); } - template - bool operator!=(const S& i) const - { - return swap() != i; - } - - // v > i - bool operator>(const swapped_t& i) const { return swap() > i.swap(); } - template - bool operator>(const S& i) const - { - return swap() > i; - } - - // v < i - bool operator<(const swapped_t& i) const { return swap() < i.swap(); } - template - bool operator<(const S& i) const - { - return swap() < i; - } - - // v >= i - bool operator>=(const swapped_t& i) const { return swap() >= i.swap(); } - template - bool operator>=(const S& i) const - { - return swap() >= i; - } - - // v <= i - bool operator<=(const swapped_t& i) const { return swap() <= i.swap(); } - template - bool operator<=(const S& i) const - { - return swap() <= i; - } - - // logical - swapped_t operator!() const { return !swap(); } - - // bitmath - swapped_t operator~() const { return ~swap(); } - - swapped_t operator&(const swapped_t& b) const { return swap() & b.swap(); } - template - swapped_t operator&(const S& b) const - { - return swap() & b; - } - swapped_t& operator&=(const swapped_t& b) - { - value = swap(swap() & b.swap()); - return *this; - } - template - swapped_t& operator&=(const S b) - { - value = swap(swap() & b); - return *this; - } - - swapped_t operator|(const swapped_t& b) const { return swap() | b.swap(); } - template - swapped_t operator|(const S& b) const - { - return swap() | b; - } - swapped_t& operator|=(const swapped_t& b) - { - value = swap(swap() | b.swap()); - return *this; - } - template - swapped_t& operator|=(const S& b) - { - value = swap(swap() | b); - return *this; - } - - swapped_t operator^(const swapped_t& b) const { return swap() ^ b.swap(); } - template - swapped_t operator^(const S& b) const - { - return swap() ^ b; - } - swapped_t& operator^=(const swapped_t& b) - { - value = swap(swap() ^ b.swap()); - return *this; - } - template - swapped_t& operator^=(const S& b) - { - value = swap(swap() ^ b); - return *this; - } - - template - swapped_t operator<<(const S& b) const - { - return swap() << b; - } - template - swapped_t& operator<<=(const S& b) const - { - value = swap(swap() << b); - return *this; - } - - template - swapped_t operator>>(const S& b) const - { - return swap() >> b; - } - template - swapped_t& operator>>=(const S& b) const - { - value = swap(swap() >> b); - return *this; - } - - // Member - /** todo **/ - - // Arithmetic - template - friend S operator+(const S& p, const swapped_t v); - - template - friend S operator-(const S& p, const swapped_t v); - - template - friend S operator/(const S& p, const swapped_t v); - - template - friend S operator*(const S& p, const swapped_t v); - - template - friend S operator%(const S& p, const swapped_t v); - - // Arithmetic + assignments - template - friend S operator+=(const S& p, const swapped_t v); - - template - friend S operator-=(const S& p, const swapped_t v); - - // Bitmath - template - friend S operator&(const S& p, const swapped_t v); - - // Comparison - template - friend bool operator<(const S& p, const swapped_t v); - - template - friend bool operator>(const S& p, const swapped_t v); - - template - friend bool operator<=(const S& p, const swapped_t v); - - template - friend bool operator>=(const S& p, const swapped_t v); - - template - friend bool operator!=(const S& p, const swapped_t v); - - template - friend bool operator==(const S& p, const swapped_t v); -}; - -// Arithmetic -template -S operator+(const S& i, const swap_struct_t v) -{ - return i + v.swap(); -} - -template -S operator-(const S& i, const swap_struct_t v) -{ - return i - v.swap(); -} - -template -S operator/(const S& i, const swap_struct_t v) -{ - return i / v.swap(); -} - -template -S operator*(const S& i, const swap_struct_t v) -{ - return i * v.swap(); -} - -template -S operator%(const S& i, const swap_struct_t v) -{ - return i % v.swap(); -} - -// Arithmetic + assignments -template -S& operator+=(S& i, const swap_struct_t v) -{ - i += v.swap(); - return i; -} - -template -S& operator-=(S& i, const swap_struct_t v) -{ - i -= v.swap(); - return i; -} - -// Logical -template -S operator&(const S& i, const swap_struct_t v) -{ - return i & v.swap(); -} - -// Comparison -template -bool operator<(const S& p, const swap_struct_t v) -{ - return p < v.swap(); -} -template -bool operator>(const S& p, const swap_struct_t v) -{ - return p > v.swap(); -} -template -bool operator<=(const S& p, const swap_struct_t v) -{ - return p <= v.swap(); -} -template -bool operator>=(const S& p, const swap_struct_t v) -{ - return p >= v.swap(); -} -template -bool operator!=(const S& p, const swap_struct_t v) -{ - return p != v.swap(); -} -template -bool operator==(const S& p, const swap_struct_t v) -{ - return p == v.swap(); -} - -template -struct swap_64_t -{ - static T swap(T x) { return static_cast(Common::swap64(x)); } -}; - -template -struct swap_32_t -{ - static T swap(T x) { return static_cast(Common::swap32(x)); } -}; - -template -struct swap_16_t -{ - static T swap(T x) { return static_cast(Common::swap16(x)); } -}; - -template -struct swap_float_t -{ - static T swap(T x) { return static_cast(Common::swapf(x)); } -}; - -template -struct swap_double_t -{ - static T swap(T x) { return static_cast(Common::swapd(x)); } -}; - -template -struct swap_enum_t -{ - static_assert(std::is_enum_v); - using base = std::underlying_type_t; - - public: - swap_enum_t() = default; - swap_enum_t(const T& v) : value(swap(v)) {} - - swap_enum_t& operator=(const T& v) - { - value = swap(v); - return *this; - } - - operator T() const { return swap(value); } - - explicit operator base() const { return static_cast(swap(value)); } - - protected: - T value{}; - // clang-format off - using swap_t = std::conditional_t< - std::is_same_v, swap_16_t, std::conditional_t< - std::is_same_v, swap_16_t, std::conditional_t< - std::is_same_v, swap_32_t, std::conditional_t< - std::is_same_v, swap_32_t, std::conditional_t< - std::is_same_v, swap_64_t, std::conditional_t< - std::is_same_v, swap_64_t, void>>>>>>; - // clang-format on - static T swap(T x) { return static_cast(swap_t::swap(static_cast(x))); } -}; - -struct SwapTag -{ -}; // Use the different endianness from the system -struct KeepTag -{ -}; // Use the same endianness as the system - -template -struct AddEndian; - -// KeepTag specializations - -template -struct AddEndian -{ - using type = T; -}; - -// SwapTag specializations - -template <> -struct AddEndian -{ - using type = u8; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = s8; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template <> -struct AddEndian -{ - using type = swap_struct_t>; -}; - -template -struct AddEndian -{ - static_assert(std::is_enum_v); - using type = swap_enum_t; -}; - -// Alias LETag/BETag as KeepTag/SwapTag depending on the system -using LETag = std::conditional_t; -using BETag = std::conditional_t; - -// Aliases for LE types -using u16_le = AddEndian::type; -using u32_le = AddEndian::type; -using u64_le = AddEndian::type; - -using s16_le = AddEndian::type; -using s32_le = AddEndian::type; -using s64_le = AddEndian::type; - -template -using enum_le = std::enable_if_t, typename AddEndian::type>; - -using float_le = AddEndian::type; -using double_le = AddEndian::type; - -// Aliases for BE types -using u16_be = AddEndian::type; -using u32_be = AddEndian::type; -using u64_be = AddEndian::type; - -using s16_be = AddEndian::type; -using s32_be = AddEndian::type; -using s64_be = AddEndian::type; - -template -using enum_be = std::enable_if_t, typename AddEndian::type>; - -using float_be = AddEndian::type; -using double_be = AddEndian::type;