summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/assert.h8
-rw-r--r--src/common/fs/fs_util.cpp4
-rw-r--r--src/common/fs/fs_util.h11
-rw-r--r--src/common/hex_util.h2
-rw-r--r--src/common/host_memory.cpp9
-rw-r--r--src/common/logging/backend.cpp350
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/settings.h194
-rw-r--r--src/common/threadsafe_queue.h10
-rw-r--r--src/common/uuid.cpp56
-rw-r--r--src/common/uuid.h30
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
13 files changed, 501 insertions, 290 deletions
diff --git a/src/common/assert.h b/src/common/assert.h
index b3ba35c0f..33060d865 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -52,8 +52,12 @@ assert_noinline_call(const Fn& fn) {
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 52#define DEBUG_ASSERT(_a_) ASSERT(_a_)
53#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__) 53#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
54#else // not debug 54#else // not debug
55#define DEBUG_ASSERT(_a_) 55#define DEBUG_ASSERT(_a_) \
56#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) 56 do { \
57 } while (0)
58#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) \
59 do { \
60 } while (0)
57#endif 61#endif
58 62
59#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!") 63#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 357cf5855..9f8671982 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) {
20 return std::string{u8_string.begin(), u8_string.end()}; 20 return std::string{u8_string.begin(), u8_string.end()};
21} 21}
22 22
23std::string BufferToUTF8String(std::span<const u8> buffer) {
24 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
25}
26
23std::string PathToUTF8String(const std::filesystem::path& path) { 27std::string PathToUTF8String(const std::filesystem::path& path) {
24 return ToUTF8String(path.u8string()); 28 return ToUTF8String(path.u8string());
25} 29}
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index ec9950ee7..1ec82eb35 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -47,6 +47,17 @@ concept IsChar = std::same_as<T, char>;
47[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string); 47[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
48 48
49/** 49/**
50 * Converts a buffer of bytes to a UTF8-encoded std::string.
51 * This converts from the start of the buffer until the first encountered null-terminator.
52 * If no null-terminator is found, this converts the entire buffer instead.
53 *
54 * @param buffer Buffer of bytes
55 *
56 * @returns UTF-8 encoded std::string.
57 */
58[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
59
60/**
50 * Converts a filesystem path to a UTF-8 encoded std::string. 61 * Converts a filesystem path to a UTF-8 encoded std::string.
51 * 62 *
52 * @param path Filesystem path 63 * @param path Filesystem path
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index f5f9e4507..5e9b6ef8b 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -61,7 +61,7 @@ template <typename ContiguousContainer>
61 return out; 61 return out;
62} 62}
63 63
64[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) { 64[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[33]) {
65 return HexStringToArray<16>(data); 65 return HexStringToArray<16>(data);
66} 66}
67 67
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 2a5a7596c..6661244cf 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -6,7 +6,7 @@
6#include <windows.h> 6#include <windows.h>
7#include "common/dynamic_library.h" 7#include "common/dynamic_library.h"
8 8
9#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 9#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
10 10
11#ifndef _GNU_SOURCE 11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE 12#define _GNU_SOURCE
@@ -343,7 +343,7 @@ private:
343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset 343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
344}; 344};
345 345
346#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 346#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
347 347
348class HostMemory::Impl { 348class HostMemory::Impl {
349public: 349public:
@@ -357,7 +357,12 @@ public:
357 }); 357 });
358 358
359 // Backing memory initialization 359 // Backing memory initialization
360#if defined(__FreeBSD__) && __FreeBSD__ < 13
361 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
362 fd = shm_open(SHM_ANON, O_RDWR, 0600);
363#else
360 fd = memfd_create("HostMemory", 0); 364 fd = memfd_create("HostMemory", 0);
365#endif
361 if (fd == -1) { 366 if (fd == -1) {
362 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); 367 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
363 throw std::bad_alloc{}; 368 throw std::bad_alloc{};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 61dddab3f..13edda9c9 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,13 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <atomic> 5#include <atomic>
7#include <chrono> 6#include <chrono>
8#include <climits> 7#include <climits>
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12#include <thread> 8#include <thread>
13#include <vector> 9#include <vector>
14 10
@@ -16,104 +12,229 @@
16#include <windows.h> // For OutputDebugStringW 12#include <windows.h> // For OutputDebugStringW
17#endif 13#endif
18 14
19#include "common/assert.h"
20#include "common/fs/file.h" 15#include "common/fs/file.h"
21#include "common/fs/fs.h" 16#include "common/fs/fs.h"
17#include "common/fs/fs_paths.h"
18#include "common/fs/path_util.h"
22#include "common/literals.h" 19#include "common/literals.h"
23 20
24#include "common/logging/backend.h" 21#include "common/logging/backend.h"
25#include "common/logging/log.h" 22#include "common/logging/log.h"
26#include "common/logging/text_formatter.h" 23#include "common/logging/text_formatter.h"
27#include "common/settings.h" 24#include "common/settings.h"
25#ifdef _WIN32
28#include "common/string_util.h" 26#include "common/string_util.h"
27#endif
29#include "common/threadsafe_queue.h" 28#include "common/threadsafe_queue.h"
30 29
31namespace Common::Log { 30namespace Common::Log {
32 31
32namespace {
33
33/** 34/**
34 * Static state as a singleton. 35 * Interface for logging backends.
35 */ 36 */
36class Impl { 37class Backend {
37public: 38public:
38 static Impl& Instance() { 39 virtual ~Backend() = default;
39 static Impl backend; 40
40 return backend; 41 virtual void Write(const Entry& entry) = 0;
42
43 virtual void EnableForStacktrace() = 0;
44
45 virtual void Flush() = 0;
46};
47
48/**
49 * Backend that writes to stderr and with color
50 */
51class ColorConsoleBackend final : public Backend {
52public:
53 explicit ColorConsoleBackend() = default;
54
55 ~ColorConsoleBackend() override = default;
56
57 void Write(const Entry& entry) override {
58 if (enabled.load(std::memory_order_relaxed)) {
59 PrintColoredMessage(entry);
60 }
41 } 61 }
42 62
43 Impl(const Impl&) = delete; 63 void Flush() override {
44 Impl& operator=(const Impl&) = delete; 64 // stderr shouldn't be buffered
65 }
45 66
46 Impl(Impl&&) = delete; 67 void EnableForStacktrace() override {
47 Impl& operator=(Impl&&) = delete; 68 enabled = true;
69 }
48 70
49 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, 71 void SetEnabled(bool enabled_) {
50 const char* function, std::string message) { 72 enabled = enabled_;
51 message_queue.Push( 73 }
52 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); 74
75private:
76 std::atomic_bool enabled{false};
77};
78
79/**
80 * Backend that writes to a file passed into the constructor
81 */
82class FileBackend final : public Backend {
83public:
84 explicit FileBackend(const std::filesystem::path& filename) {
85 auto old_filename = filename;
86 old_filename += ".old.txt";
87
88 // Existence checks are done within the functions themselves.
89 // We don't particularly care if these succeed or not.
90 static_cast<void>(FS::RemoveFile(old_filename));
91 static_cast<void>(FS::RenameFile(filename, old_filename));
92
93 file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
94 FS::FileType::TextFile);
95 }
96
97 ~FileBackend() override = default;
98
99 void Write(const Entry& entry) override {
100 if (!enabled) {
101 return;
102 }
103
104 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
105
106 using namespace Common::Literals;
107 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
108 const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
109 const bool write_limit_exceeded = bytes_written > write_limit;
110 if (entry.log_level >= Level::Error || write_limit_exceeded) {
111 if (write_limit_exceeded) {
112 // Stop writing after the write limit is exceeded.
113 // Don't close the file so we can print a stacktrace if necessary
114 enabled = false;
115 }
116 file->Flush();
117 }
118 }
119
120 void Flush() override {
121 file->Flush();
122 }
123
124 void EnableForStacktrace() override {
125 enabled = true;
126 bytes_written = 0;
53 } 127 }
54 128
55 void AddBackend(std::unique_ptr<Backend> backend) { 129private:
56 std::lock_guard lock{writing_mutex}; 130 std::unique_ptr<FS::IOFile> file;
57 backends.push_back(std::move(backend)); 131 bool enabled = true;
132 std::size_t bytes_written = 0;
133};
134
135/**
136 * Backend that writes to Visual Studio's output window
137 */
138class DebuggerBackend final : public Backend {
139public:
140 explicit DebuggerBackend() = default;
141
142 ~DebuggerBackend() override = default;
143
144 void Write(const Entry& entry) override {
145#ifdef _WIN32
146 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
147#endif
58 } 148 }
59 149
60 void RemoveBackend(std::string_view backend_name) { 150 void Flush() override {}
61 std::lock_guard lock{writing_mutex}; 151
152 void EnableForStacktrace() override {}
153};
154
155bool initialization_in_progress_suppress_logging = false;
62 156
63 std::erase_if(backends, [&backend_name](const auto& backend) { 157/**
64 return backend_name == backend->GetName(); 158 * Static state as a singleton.
65 }); 159 */
160class Impl {
161public:
162 static Impl& Instance() {
163 if (!instance) {
164 abort();
165 }
166 return *instance;
66 } 167 }
67 168
68 const Filter& GetGlobalFilter() const { 169 static void Initialize() {
69 return filter; 170 if (instance) {
171 abort();
172 }
173 using namespace Common::FS;
174 initialization_in_progress_suppress_logging = true;
175 const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
176 void(CreateDir(log_dir));
177 Filter filter;
178 filter.ParseFilterString(Settings::values.log_filter.GetValue());
179 instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
180 Deleter);
181 initialization_in_progress_suppress_logging = false;
70 } 182 }
71 183
184 Impl(const Impl&) = delete;
185 Impl& operator=(const Impl&) = delete;
186
187 Impl(Impl&&) = delete;
188 Impl& operator=(Impl&&) = delete;
189
72 void SetGlobalFilter(const Filter& f) { 190 void SetGlobalFilter(const Filter& f) {
73 filter = f; 191 filter = f;
74 } 192 }
75 193
76 Backend* GetBackend(std::string_view backend_name) { 194 void SetColorConsoleBackendEnabled(bool enabled) {
77 const auto it = 195 color_console_backend.SetEnabled(enabled);
78 std::find_if(backends.begin(), backends.end(), 196 }
79 [&backend_name](const auto& i) { return backend_name == i->GetName(); }); 197
80 if (it == backends.end()) 198 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
81 return nullptr; 199 const char* function, std::string message) {
82 return it->get(); 200 if (!filter.CheckMessage(log_class, log_level))
201 return;
202 const Entry& entry =
203 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
204 message_queue.Push(entry);
83 } 205 }
84 206
85private: 207private:
86 Impl() { 208 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
87 backend_thread = std::thread([&] { 209 : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
88 Entry entry; 210 Common::SetCurrentThreadName("yuzu:Log");
89 auto write_logs = [&](Entry& e) { 211 Entry entry;
90 std::lock_guard lock{writing_mutex}; 212 const auto write_logs = [this, &entry]() {
91 for (const auto& backend : backends) { 213 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
92 backend->Write(e); 214 };
93 } 215 while (true) {
94 }; 216 entry = message_queue.PopWait();
95 while (true) { 217 if (entry.final_entry) {
96 entry = message_queue.PopWait(); 218 break;
97 if (entry.final_entry) { 219 }
98 break; 220 write_logs();
99 } 221 }
100 write_logs(entry); 222 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
101 } 223 // case where a system is repeatedly spamming logs even on close.
224 int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
225 while (max_logs_to_write-- && message_queue.Pop(entry)) {
226 write_logs();
227 }
228 })} {}
102 229
103 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a 230 ~Impl() {
104 // case where a system is repeatedly spamming logs even on close. 231 StopBackendThread();
105 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
106 int logs_written = 0;
107 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
108 write_logs(entry);
109 }
110 });
111 } 232 }
112 233
113 ~Impl() { 234 void StopBackendThread() {
114 Entry entry; 235 Entry stop_entry{};
115 entry.final_entry = true; 236 stop_entry.final_entry = true;
116 message_queue.Push(entry); 237 message_queue.Push(stop_entry);
117 backend_thread.join(); 238 backend_thread.join();
118 } 239 }
119 240
@@ -135,100 +256,51 @@ private:
135 }; 256 };
136 } 257 }
137 258
138 std::mutex writing_mutex; 259 void ForEachBackend(auto lambda) {
139 std::thread backend_thread; 260 lambda(static_cast<Backend&>(debugger_backend));
140 std::vector<std::unique_ptr<Backend>> backends; 261 lambda(static_cast<Backend&>(color_console_backend));
141 MPSCQueue<Entry> message_queue; 262 lambda(static_cast<Backend&>(file_backend));
142 Filter filter; 263 }
143 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
144};
145
146ConsoleBackend::~ConsoleBackend() = default;
147
148void ConsoleBackend::Write(const Entry& entry) {
149 PrintMessage(entry);
150}
151
152ColorConsoleBackend::~ColorConsoleBackend() = default;
153
154void ColorConsoleBackend::Write(const Entry& entry) {
155 PrintColoredMessage(entry);
156}
157
158FileBackend::FileBackend(const std::filesystem::path& filename) {
159 auto old_filename = filename;
160 old_filename += ".old.txt";
161
162 // Existence checks are done within the functions themselves.
163 // We don't particularly care if these succeed or not.
164 FS::RemoveFile(old_filename);
165 void(FS::RenameFile(filename, old_filename));
166
167 file =
168 std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
169}
170
171FileBackend::~FileBackend() = default;
172 264
173void FileBackend::Write(const Entry& entry) { 265 static void Deleter(Impl* ptr) {
174 if (!file->IsOpen()) { 266 delete ptr;
175 return;
176 } 267 }
177 268
178 using namespace Common::Literals; 269 static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
179 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
180 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
181 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
182 270
183 const bool write_limit_exceeded = 271 Filter filter;
184 bytes_written > MAX_BYTES_WRITTEN_EXTENDED || 272 DebuggerBackend debugger_backend{};
185 (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); 273 ColorConsoleBackend color_console_backend{};
274 FileBackend file_backend;
186 275
187 // Close the file after the write limit is exceeded. 276 std::thread backend_thread;
188 if (write_limit_exceeded) { 277 MPSCQueue<Entry> message_queue{};
189 file->Close(); 278 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
190 return; 279};
191 } 280} // namespace
192 281
193 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); 282void Initialize() {
194 if (entry.log_level >= Level::Error) { 283 Impl::Initialize();
195 file->Flush();
196 }
197} 284}
198 285
199DebuggerBackend::~DebuggerBackend() = default; 286void DisableLoggingInTests() {
200 287 initialization_in_progress_suppress_logging = true;
201void DebuggerBackend::Write(const Entry& entry) {
202#ifdef _WIN32
203 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
204#endif
205} 288}
206 289
207void SetGlobalFilter(const Filter& filter) { 290void SetGlobalFilter(const Filter& filter) {
208 Impl::Instance().SetGlobalFilter(filter); 291 Impl::Instance().SetGlobalFilter(filter);
209} 292}
210 293
211void AddBackend(std::unique_ptr<Backend> backend) { 294void SetColorConsoleBackendEnabled(bool enabled) {
212 Impl::Instance().AddBackend(std::move(backend)); 295 Impl::Instance().SetColorConsoleBackendEnabled(enabled);
213}
214
215void RemoveBackend(std::string_view backend_name) {
216 Impl::Instance().RemoveBackend(backend_name);
217}
218
219Backend* GetBackend(std::string_view backend_name) {
220 return Impl::Instance().GetBackend(backend_name);
221} 296}
222 297
223void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 298void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
224 unsigned int line_num, const char* function, const char* format, 299 unsigned int line_num, const char* function, const char* format,
225 const fmt::format_args& args) { 300 const fmt::format_args& args) {
226 auto& instance = Impl::Instance(); 301 if (!initialization_in_progress_suppress_logging) {
227 const auto& filter = instance.GetGlobalFilter(); 302 Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
228 if (!filter.CheckMessage(log_class, log_level)) 303 fmt::vformat(format, args));
229 return; 304 }
230
231 instance.PushEntry(log_class, log_level, filename, line_num, function,
232 fmt::vformat(format, args));
233} 305}
234} // namespace Common::Log 306} // namespace Common::Log
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 4b9a910c1..cb7839ee9 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -5,120 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <filesystem> 7#include <filesystem>
8#include <memory>
9#include <string>
10#include <string_view>
11#include "common/logging/filter.h" 8#include "common/logging/filter.h"
12#include "common/logging/log.h"
13
14namespace Common::FS {
15class IOFile;
16}
17 9
18namespace Common::Log { 10namespace Common::Log {
19 11
20class Filter; 12class Filter;
21 13
22/** 14/// Initializes the logging system. This should be the first thing called in main.
23 * Interface for logging backends. As loggers can be created and removed at runtime, this can be 15void Initialize();
24 * used by a frontend for adding a custom logging backend as needed
25 */
26class Backend {
27public:
28 virtual ~Backend() = default;
29
30 virtual void SetFilter(const Filter& new_filter) {
31 filter = new_filter;
32 }
33 virtual const char* GetName() const = 0;
34 virtual void Write(const Entry& entry) = 0;
35
36private:
37 Filter filter;
38};
39
40/**
41 * Backend that writes to stderr without any color commands
42 */
43class ConsoleBackend : public Backend {
44public:
45 ~ConsoleBackend() override;
46
47 static const char* Name() {
48 return "console";
49 }
50 const char* GetName() const override {
51 return Name();
52 }
53 void Write(const Entry& entry) override;
54};
55
56/**
57 * Backend that writes to stderr and with color
58 */
59class ColorConsoleBackend : public Backend {
60public:
61 ~ColorConsoleBackend() override;
62
63 static const char* Name() {
64 return "color_console";
65 }
66
67 const char* GetName() const override {
68 return Name();
69 }
70 void Write(const Entry& entry) override;
71};
72 16
73/** 17void DisableLoggingInTests();
74 * Backend that writes to a file passed into the constructor
75 */
76class FileBackend : public Backend {
77public:
78 explicit FileBackend(const std::filesystem::path& filename);
79 ~FileBackend() override;
80
81 static const char* Name() {
82 return "file";
83 }
84
85 const char* GetName() const override {
86 return Name();
87 }
88
89 void Write(const Entry& entry) override;
90
91private:
92 std::unique_ptr<FS::IOFile> file;
93 std::size_t bytes_written = 0;
94};
95
96/**
97 * Backend that writes to Visual Studio's output window
98 */
99class DebuggerBackend : public Backend {
100public:
101 ~DebuggerBackend() override;
102
103 static const char* Name() {
104 return "debugger";
105 }
106 const char* GetName() const override {
107 return Name();
108 }
109 void Write(const Entry& entry) override;
110};
111
112void AddBackend(std::unique_ptr<Backend> backend);
113
114void RemoveBackend(std::string_view backend_name);
115
116Backend* GetBackend(std::string_view backend_name);
117 18
118/** 19/**
119 * The global filter will prevent any messages from even being processed if they are filtered. Each 20 * The global filter will prevent any messages from even being processed if they are filtered.
120 * backend can have a filter, but if the level is lower than the global filter, the backend will
121 * never get the message
122 */ 21 */
123void SetGlobalFilter(const Filter& filter); 22void SetGlobalFilter(const Filter& filter);
124} // namespace Common::Log \ No newline at end of file 23
24void SetColorConsoleBackendEnabled(bool enabled);
25} // namespace Common::Log
diff --git a/src/common/settings.h b/src/common/settings.h
index d8730f515..20769d310 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <array> 8#include <array>
8#include <atomic> 9#include <atomic>
9#include <chrono> 10#include <chrono>
@@ -42,6 +43,11 @@ enum class CPUAccuracy : u32 {
42 Unsafe = 2, 43 Unsafe = 2,
43}; 44};
44 45
46enum class FullscreenMode : u32 {
47 Borderless = 0,
48 Exclusive = 1,
49};
50
45/** The BasicSetting class is a simple resource manager. It defines a label and default value 51/** The BasicSetting class is a simple resource manager. It defines a label and default value
46 * alongside the actual value of the setting for simpler and less-error prone use with frontend 52 * alongside the actual value of the setting for simpler and less-error prone use with frontend
47 * configurations. Setting a default value and label is required, though subclasses may deviate from 53 * configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -69,14 +75,14 @@ public:
69 */ 75 */
70 explicit BasicSetting(const Type& default_val, const std::string& name) 76 explicit BasicSetting(const Type& default_val, const std::string& name)
71 : default_value{default_val}, global{default_val}, label{name} {} 77 : default_value{default_val}, global{default_val}, label{name} {}
72 ~BasicSetting() = default; 78 virtual ~BasicSetting() = default;
73 79
74 /** 80 /**
75 * Returns a reference to the setting's value. 81 * Returns a reference to the setting's value.
76 * 82 *
77 * @returns A reference to the setting 83 * @returns A reference to the setting
78 */ 84 */
79 [[nodiscard]] const Type& GetValue() const { 85 [[nodiscard]] virtual const Type& GetValue() const {
80 return global; 86 return global;
81 } 87 }
82 88
@@ -85,7 +91,7 @@ public:
85 * 91 *
86 * @param value The desired value 92 * @param value The desired value
87 */ 93 */
88 void SetValue(const Type& value) { 94 virtual void SetValue(const Type& value) {
89 Type temp{value}; 95 Type temp{value};
90 std::swap(global, temp); 96 std::swap(global, temp);
91 } 97 }
@@ -115,7 +121,7 @@ public:
115 * 121 *
116 * @returns A reference to the setting 122 * @returns A reference to the setting
117 */ 123 */
118 const Type& operator=(const Type& value) { 124 virtual const Type& operator=(const Type& value) {
119 Type temp{value}; 125 Type temp{value};
120 std::swap(global, temp); 126 std::swap(global, temp);
121 return global; 127 return global;
@@ -126,7 +132,7 @@ public:
126 * 132 *
127 * @returns A reference to the setting 133 * @returns A reference to the setting
128 */ 134 */
129 explicit operator const Type&() const { 135 explicit virtual operator const Type&() const {
130 return global; 136 return global;
131 } 137 }
132 138
@@ -137,6 +143,51 @@ protected:
137}; 143};
138 144
139/** 145/**
146 * BasicRangedSetting class is intended for use with quantifiable settings that need a more
147 * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
148 * simply used to sanitize SetValue and the assignment overload.
149 */
150template <typename Type>
151class BasicRangedSetting : virtual public BasicSetting<Type> {
152public:
153 /**
154 * Sets a default value, minimum value, maximum value, and label.
155 *
156 * @param default_val Intial value of the setting, and default value of the setting
157 * @param min_val Sets the minimum allowed value of the setting
158 * @param max_val Sets the maximum allowed value of the setting
159 * @param name Label for the setting
160 */
161 explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
162 const std::string& name)
163 : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
164 virtual ~BasicRangedSetting() = default;
165
166 /**
167 * Like BasicSetting's SetValue, except value is clamped to the range of the setting.
168 *
169 * @param value The desired value
170 */
171 void SetValue(const Type& value) override {
172 this->global = std::clamp(value, minimum, maximum);
173 }
174
175 /**
176 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
177 *
178 * @param value The desired value
179 * @returns A reference to the setting's value
180 */
181 const Type& operator=(const Type& value) override {
182 this->global = std::clamp(value, minimum, maximum);
183 return this->global;
184 }
185
186 const Type minimum; ///< Minimum allowed value of the setting
187 const Type maximum; ///< Maximum allowed value of the setting
188};
189
190/**
140 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a 191 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a
141 * custom setting to switch to when a guest application specifically requires it. The effect is that 192 * custom setting to switch to when a guest application specifically requires it. The effect is that
142 * other components of the emulator can access the setting's intended value without any need for the 193 * other components of the emulator can access the setting's intended value without any need for the
@@ -147,7 +198,7 @@ protected:
147 * Like the BasicSetting, this requires setting a default value and label to use. 198 * Like the BasicSetting, this requires setting a default value and label to use.
148 */ 199 */
149template <typename Type> 200template <typename Type>
150class Setting final : public BasicSetting<Type> { 201class Setting : virtual public BasicSetting<Type> {
151public: 202public:
152 /** 203 /**
153 * Sets a default value, label, and setting value. 204 * Sets a default value, label, and setting value.
@@ -157,7 +208,7 @@ public:
157 */ 208 */
158 explicit Setting(const Type& default_val, const std::string& name) 209 explicit Setting(const Type& default_val, const std::string& name)
159 : BasicSetting<Type>(default_val, name) {} 210 : BasicSetting<Type>(default_val, name) {}
160 ~Setting() = default; 211 virtual ~Setting() = default;
161 212
162 /** 213 /**
163 * Tells this setting to represent either the global or custom setting when other member 214 * Tells this setting to represent either the global or custom setting when other member
@@ -186,7 +237,13 @@ public:
186 * 237 *
187 * @returns The required value of the setting 238 * @returns The required value of the setting
188 */ 239 */
189 [[nodiscard]] const Type& GetValue(bool need_global = false) const { 240 [[nodiscard]] virtual const Type& GetValue() const override {
241 if (use_global) {
242 return this->global;
243 }
244 return custom;
245 }
246 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
190 if (use_global || need_global) { 247 if (use_global || need_global) {
191 return this->global; 248 return this->global;
192 } 249 }
@@ -198,7 +255,7 @@ public:
198 * 255 *
199 * @param value The new value 256 * @param value The new value
200 */ 257 */
201 void SetValue(const Type& value) { 258 void SetValue(const Type& value) override {
202 Type temp{value}; 259 Type temp{value};
203 if (use_global) { 260 if (use_global) {
204 std::swap(this->global, temp); 261 std::swap(this->global, temp);
@@ -214,7 +271,7 @@ public:
214 * 271 *
215 * @returns A reference to the current setting value 272 * @returns A reference to the current setting value
216 */ 273 */
217 const Type& operator=(const Type& value) { 274 const Type& operator=(const Type& value) override {
218 Type temp{value}; 275 Type temp{value};
219 if (use_global) { 276 if (use_global) {
220 std::swap(this->global, temp); 277 std::swap(this->global, temp);
@@ -229,19 +286,88 @@ public:
229 * 286 *
230 * @returns A reference to the current setting value 287 * @returns A reference to the current setting value
231 */ 288 */
232 explicit operator const Type&() const { 289 virtual explicit operator const Type&() const override {
233 if (use_global) { 290 if (use_global) {
234 return this->global; 291 return this->global;
235 } 292 }
236 return custom; 293 return custom;
237 } 294 }
238 295
239private: 296protected:
240 bool use_global{true}; ///< The setting's global state 297 bool use_global{true}; ///< The setting's global state
241 Type custom{}; ///< The custom value of the setting 298 Type custom{}; ///< The custom value of the setting
242}; 299};
243 300
244/** 301/**
302 * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
303 * for use with quantifiable settings.
304 */
305template <typename Type>
306class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
307public:
308 /**
309 * Sets a default value, minimum value, maximum value, and label.
310 *
311 * @param default_val Intial value of the setting, and default value of the setting
312 * @param min_val Sets the minimum allowed value of the setting
313 * @param max_val Sets the maximum allowed value of the setting
314 * @param name Label for the setting
315 */
316 explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
317 const std::string& name)
318 : BasicSetting<Type>{default_val, name},
319 BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
320 name} {}
321 virtual ~RangedSetting() = default;
322
323 // The following are needed to avoid a MSVC bug
324 // (source: https://stackoverflow.com/questions/469508)
325 [[nodiscard]] const Type& GetValue() const override {
326 return Setting<Type>::GetValue();
327 }
328 [[nodiscard]] const Type& GetValue(bool need_global) const override {
329 return Setting<Type>::GetValue(need_global);
330 }
331 explicit operator const Type&() const override {
332 if (this->use_global) {
333 return this->global;
334 }
335 return this->custom;
336 }
337
338 /**
339 * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
340 * appropriate value depending on the global state.
341 *
342 * @param value The desired value
343 */
344 void SetValue(const Type& value) override {
345 const Type temp = std::clamp(value, this->minimum, this->maximum);
346 if (this->use_global) {
347 this->global = temp;
348 }
349 this->custom = temp;
350 }
351
352 /**
353 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
354 * Uses the appropriate value depending on the global state.
355 *
356 * @param value The desired value
357 * @returns A reference to the setting's value
358 */
359 const Type& operator=(const Type& value) override {
360 const Type temp = std::clamp(value, this->minimum, this->maximum);
361 if (this->use_global) {
362 this->global = temp;
363 return this->global;
364 }
365 this->custom = temp;
366 return this->custom;
367 }
368};
369
370/**
245 * The InputSetting class allows for getting a reference to either the global or custom members. 371 * The InputSetting class allows for getting a reference to either the global or custom members.
246 * This is required as we cannot easily modify the values of user-defined types within containers 372 * This is required as we cannot easily modify the values of user-defined types within containers
247 * using the SetValue() member function found in the Setting class. The primary purpose of this 373 * using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -284,13 +410,14 @@ struct Values {
284 BasicSetting<std::string> sink_id{"auto", "output_engine"}; 410 BasicSetting<std::string> sink_id{"auto", "output_engine"};
285 BasicSetting<bool> audio_muted{false, "audio_muted"}; 411 BasicSetting<bool> audio_muted{false, "audio_muted"};
286 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"}; 412 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
287 Setting<u8> volume{100, "volume"}; 413 RangedSetting<u8> volume{100, 0, 100, "volume"};
288 414
289 // Core 415 // Core
290 Setting<bool> use_multi_core{true, "use_multi_core"}; 416 Setting<bool> use_multi_core{true, "use_multi_core"};
291 417
292 // Cpu 418 // Cpu
293 Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"}; 419 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
420 CPUAccuracy::Unsafe, "cpu_accuracy"};
294 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 421 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
295 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 422 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
296 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 423 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -312,8 +439,10 @@ struct Values {
312 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 439 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
313 440
314 // Renderer 441 // Renderer
315 Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; 442 RangedSetting<RendererBackend> renderer_backend{
443 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
316 BasicSetting<bool> renderer_debug{false, "debug"}; 444 BasicSetting<bool> renderer_debug{false, "debug"};
445 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
317 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 446 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
318 BasicSetting<bool> disable_shader_loop_safety_checks{false, 447 BasicSetting<bool> disable_shader_loop_safety_checks{false,
319 "disable_shader_loop_safety_checks"}; 448 "disable_shader_loop_safety_checks"};
@@ -322,26 +451,28 @@ struct Values {
322 Setting<u16> resolution_factor{1, "resolution_factor"}; 451 Setting<u16> resolution_factor{1, "resolution_factor"};
323 // *nix platforms may have issues with the borderless windowed fullscreen mode. 452 // *nix platforms may have issues with the borderless windowed fullscreen mode.
324 // Default to exclusive fullscreen on these platforms for now. 453 // Default to exclusive fullscreen on these platforms for now.
325 Setting<int> fullscreen_mode{ 454 RangedSetting<FullscreenMode> fullscreen_mode{
326#ifdef _WIN32 455#ifdef _WIN32
327 0, 456 FullscreenMode::Borderless,
328#else 457#else
329 1, 458 FullscreenMode::Exclusive,
330#endif 459#endif
331 "fullscreen_mode"}; 460 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
332 Setting<int> aspect_ratio{0, "aspect_ratio"}; 461 RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
333 Setting<int> max_anisotropy{0, "max_anisotropy"}; 462 RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
334 Setting<bool> use_speed_limit{true, "use_speed_limit"}; 463 Setting<bool> use_speed_limit{true, "use_speed_limit"};
335 Setting<u16> speed_limit{100, "speed_limit"}; 464 RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
336 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 465 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
337 Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; 466 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
467 GPUAccuracy::Extreme, "gpu_accuracy"};
338 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 468 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
339 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 469 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
340 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 470 Setting<bool> accelerate_astc{true, "accelerate_astc"};
341 Setting<bool> use_vsync{true, "use_vsync"}; 471 Setting<bool> use_vsync{true, "use_vsync"};
342 BasicSetting<u16> fps_cap{1000, "fps_cap"}; 472 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
343 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 473 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
344 Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"}; 474 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
475 ShaderBackend::SPIRV, "shader_backend"};
345 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 476 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
346 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 477 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
347 Setting<bool> use_caches_gc{false, "use_caches_gc"}; 478 Setting<bool> use_caches_gc{false, "use_caches_gc"};
@@ -358,10 +489,10 @@ struct Values {
358 std::chrono::seconds custom_rtc_differential; 489 std::chrono::seconds custom_rtc_differential;
359 490
360 BasicSetting<s32> current_user{0, "current_user"}; 491 BasicSetting<s32> current_user{0, "current_user"};
361 Setting<s32> language_index{1, "language_index"}; 492 RangedSetting<s32> language_index{1, 0, 17, "language_index"};
362 Setting<s32> region_index{1, "region_index"}; 493 RangedSetting<s32> region_index{1, 0, 6, "region_index"};
363 Setting<s32> time_zone_index{0, "time_zone_index"}; 494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
364 Setting<s32> sound_index{1, "sound_index"}; 495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
365 496
366 // Controls 497 // Controls
367 InputSetting<std::array<PlayerInput, 10>> players; 498 InputSetting<std::array<PlayerInput, 10>> players;
@@ -378,7 +509,7 @@ struct Values {
378 "udp_input_servers"}; 509 "udp_input_servers"};
379 510
380 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 511 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
381 BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"}; 512 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
382 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 513 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
383 std::string mouse_device; 514 std::string mouse_device;
384 MouseButtonsRaw mouse_buttons; 515 MouseButtonsRaw mouse_buttons;
@@ -427,9 +558,10 @@ struct Values {
427 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
428 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
429 560
430 // Services 561 // Network
431 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; 562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
432 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; 563 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
564 BasicSetting<std::string> network_interface{std::string(), "network_interface"};
433 565
434 // WebService 566 // WebService
435 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 567 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index ad04df8ca..8430b9778 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -46,15 +46,13 @@ public:
46 ElementPtr* new_ptr = new ElementPtr(); 46 ElementPtr* new_ptr = new ElementPtr();
47 write_ptr->next.store(new_ptr, std::memory_order_release); 47 write_ptr->next.store(new_ptr, std::memory_order_release);
48 write_ptr = new_ptr; 48 write_ptr = new_ptr;
49 ++size;
49 50
50 const size_t previous_size{size++}; 51 // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
51 52 // line before cv.wait
52 // Acquire the mutex and then immediately release it as a fence.
53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. 53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. 54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
55 if (previous_size == 0) { 55 std::lock_guard lock{cv_mutex};
56 std::lock_guard lock{cv_mutex};
57 }
58 cv.notify_one(); 56 cv.notify_one();
59 } 57 }
60 58
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 26db03fba..d7435a6e9 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -6,10 +6,64 @@
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8 8
9#include "common/assert.h"
9#include "common/uuid.h" 10#include "common/uuid.h"
10 11
11namespace Common { 12namespace Common {
12 13
14namespace {
15
16bool IsHexDigit(char c) {
17 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
18}
19
20u8 HexCharToByte(char c) {
21 if (c >= '0' && c <= '9') {
22 return static_cast<u8>(c - '0');
23 }
24 if (c >= 'a' && c <= 'f') {
25 return static_cast<u8>(c - 'a' + 10);
26 }
27 if (c >= 'A' && c <= 'F') {
28 return static_cast<u8>(c - 'A' + 10);
29 }
30 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
31 return u8{0};
32}
33
34} // Anonymous namespace
35
36u128 HexStringToU128(std::string_view hex_string) {
37 const size_t length = hex_string.length();
38
39 // Detect "0x" prefix.
40 const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
41 const size_t offset = has_0x_prefix ? 2 : 0;
42
43 // Check length.
44 if (length > 32 + offset) {
45 ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
46 return INVALID_UUID;
47 }
48
49 u64 lo = 0;
50 u64 hi = 0;
51 for (size_t i = 0; i < length - offset; ++i) {
52 const char c = hex_string[length - 1 - i];
53 if (!IsHexDigit(c)) {
54 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
55 return INVALID_UUID;
56 }
57 if (i < 16) {
58 lo |= u64{HexCharToByte(c)} << (i * 4);
59 }
60 if (i >= 16) {
61 hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
62 }
63 }
64 return u128{lo, hi};
65}
66
13UUID UUID::Generate() { 67UUID UUID::Generate() {
14 std::random_device device; 68 std::random_device device;
15 std::mt19937 gen(device()); 69 std::mt19937 gen(device());
@@ -18,7 +72,7 @@ UUID UUID::Generate() {
18} 72}
19 73
20std::string UUID::Format() const { 74std::string UUID::Format() const {
21 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 75 return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
22} 76}
23 77
24std::string UUID::FormatSwitch() const { 78std::string UUID::FormatSwitch() const {
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 0ffa37e7c..2353179d8 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <string_view>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -12,12 +13,30 @@ namespace Common {
12 13
13constexpr u128 INVALID_UUID{{0, 0}}; 14constexpr u128 INVALID_UUID{{0, 0}};
14 15
16/**
17 * Converts a hex string to a 128-bit unsigned integer.
18 *
19 * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
20 *
21 * This function will assert and return INVALID_UUID under the following conditions:
22 * - If the hex string is more than 32 characters long
23 * - If the hex string contains non-hexadecimal characters
24 *
25 * @param hex_string Hexadecimal string
26 *
27 * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
28 */
29[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
30
15struct UUID { 31struct UUID {
16 // UUIDs which are 0 are considered invalid! 32 // UUIDs which are 0 are considered invalid!
17 u128 uuid; 33 u128 uuid;
18 UUID() = default; 34 UUID() = default;
19 constexpr explicit UUID(const u128& id) : uuid{id} {} 35 constexpr explicit UUID(const u128& id) : uuid{id} {}
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 36 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
37 explicit UUID(std::string_view hex_string) {
38 uuid = HexStringToU128(hex_string);
39 }
21 40
22 [[nodiscard]] constexpr explicit operator bool() const { 41 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid != INVALID_UUID; 42 return uuid != INVALID_UUID;
@@ -50,3 +69,14 @@ struct UUID {
50static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 69static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
51 70
52} // namespace Common 71} // namespace Common
72
73namespace std {
74
75template <>
76struct hash<Common::UUID> {
77 size_t operator()(const Common::UUID& uuid) const noexcept {
78 return uuid.uuid[1] ^ uuid.uuid[0];
79 }
80};
81
82} // namespace std
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index c2c9b6134..0ddf9b83e 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -6,7 +6,7 @@
6 6
7#include <bitset> 7#include <bitset>
8#include <initializer_list> 8#include <initializer_list>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/assert.h" 10#include "common/assert.h"
11 11
12namespace Common::X64 { 12namespace Common::X64 {
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index df17f8cbe..44d2558f1 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <type_traits> 7#include <type_traits>
8#include <xbyak.h> 8#include <xbyak/xbyak.h>
9#include "common/x64/xbyak_abi.h" 9#include "common/x64/xbyak_abi.h"
10 10
11namespace Common::X64 { 11namespace Common::X64 {