summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp350
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/settings.h5
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/cpu_manager.cpp40
-rw-r--r--src/core/cpu_manager.h6
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp4
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_process.cpp1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp85
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h36
-rw-r--r--src/core/hle/kernel/kernel.cpp57
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp31
-rw-r--r--src/core/hle/service/nifm/nifm.cpp45
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h3
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp17
-rw-r--r--src/core/hle/service/vi/display/vi_display.h13
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/network/network.cpp64
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp203
-rw-r--r--src/core/network/network_interface.h29
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp8
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.h2
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/yuzu/CMakeLists.txt15
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure.ui10
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp34
-rw-r--r--src/yuzu/configuration/configure_network.cpp (renamed from src/yuzu/configuration/configure_service.cpp)39
-rw-r--r--src/yuzu/configuration/configure_network.h (renamed from src/yuzu/configuration/configure_service.h)10
-rw-r--r--src/yuzu/configuration/configure_network.ui (renamed from src/yuzu/configuration/configure_service.ui)63
-rw-r--r--src/yuzu/debugger/console.cpp11
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu_cmd/yuzu.cpp22
53 files changed, 936 insertions, 571 deletions
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 1ba9b606c..20769d310 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -489,7 +489,7 @@ struct Values {
489 std::chrono::seconds custom_rtc_differential; 489 std::chrono::seconds custom_rtc_differential;
490 490
491 BasicSetting<s32> current_user{0, "current_user"}; 491 BasicSetting<s32> current_user{0, "current_user"};
492 RangedSetting<s32> language_index{1, 0, 16, "language_index"}; 492 RangedSetting<s32> language_index{1, 0, 17, "language_index"};
493 RangedSetting<s32> region_index{1, 0, 6, "region_index"}; 493 RangedSetting<s32> region_index{1, 0, 6, "region_index"};
494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; 495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
@@ -558,9 +558,10 @@ struct Values {
558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
560 560
561 // Services 561 // Network
562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; 562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
563 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"};
564 565
565 // WebService 566 // WebService
566 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 567 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
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 {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5c99c00f5..f5cf5c16a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -636,6 +636,8 @@ add_library(core STATIC
636 memory.h 636 memory.h
637 network/network.cpp 637 network/network.cpp
638 network/network.h 638 network/network.h
639 network/network_interface.cpp
640 network/network_interface.h
639 network/sockets.h 641 network/sockets.h
640 perf_stats.cpp 642 perf_stats.cpp
641 perf_stats.h 643 perf_stats.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d3e84c4ef..b0dc594d4 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
84 84
85} // Anonymous namespace 85} // Anonymous namespace
86 86
87/*static*/ System System::s_instance;
88
89FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 87FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
90 const std::string& path) { 88 const std::string& path) {
91 // To account for split 00+01+etc files. 89 // To account for split 00+01+etc files.
@@ -425,6 +423,13 @@ struct System::Impl {
425System::System() : impl{std::make_unique<Impl>(*this)} {} 423System::System() : impl{std::make_unique<Impl>(*this)} {}
426System::~System() = default; 424System::~System() = default;
427 425
426void System::InitializeGlobalInstance() {
427 if (s_instance) {
428 abort();
429 }
430 s_instance = std::unique_ptr<System>(new System);
431}
432
428CpuManager& System::GetCpuManager() { 433CpuManager& System::GetCpuManager() {
429 return impl->cpu_manager; 434 return impl->cpu_manager;
430} 435}
@@ -494,12 +499,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
494 return impl->kernel.CurrentPhysicalCore().ArmInterface(); 499 return impl->kernel.CurrentPhysicalCore().ArmInterface();
495} 500}
496 501
497std::size_t System::CurrentCoreIndex() const {
498 std::size_t core = impl->kernel.GetCurrentHostThreadID();
499 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
500 return core;
501}
502
503Kernel::PhysicalCore& System::CurrentPhysicalCore() { 502Kernel::PhysicalCore& System::CurrentPhysicalCore() {
504 return impl->kernel.CurrentPhysicalCore(); 503 return impl->kernel.CurrentPhysicalCore();
505} 504}
diff --git a/src/core/core.h b/src/core/core.h
index ea143043c..65b447a1c 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -121,9 +121,14 @@ public:
121 * @returns Reference to the instance of the System singleton class. 121 * @returns Reference to the instance of the System singleton class.
122 */ 122 */
123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { 123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
124 return s_instance; 124 if (!s_instance) {
125 abort();
126 }
127 return *s_instance;
125 } 128 }
126 129
130 static void InitializeGlobalInstance();
131
127 /// Enumeration representing the return values of the System Initialize and Load process. 132 /// Enumeration representing the return values of the System Initialize and Load process.
128 enum class ResultStatus : u32 { 133 enum class ResultStatus : u32 {
129 Success, ///< Succeeded 134 Success, ///< Succeeded
@@ -205,9 +210,6 @@ public:
205 /// Gets an ARM interface to the CPU core that is currently running 210 /// Gets an ARM interface to the CPU core that is currently running
206 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; 211 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
207 212
208 /// Gets the index of the currently running CPU core
209 [[nodiscard]] std::size_t CurrentCoreIndex() const;
210
211 /// Gets the physical core for the CPU core that is currently running 213 /// Gets the physical core for the CPU core that is currently running
212 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); 214 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
213 215
@@ -396,7 +398,7 @@ private:
396 struct Impl; 398 struct Impl;
397 std::unique_ptr<Impl> impl; 399 std::unique_ptr<Impl> impl;
398 400
399 static System s_instance; 401 inline static std::unique_ptr<System> s_instance{};
400}; 402};
401 403
402} // namespace Core 404} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7e195346b..de2e5563e 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -21,34 +21,25 @@ namespace Core {
21CpuManager::CpuManager(System& system_) : system{system_} {} 21CpuManager::CpuManager(System& system_) : system{system_} {}
22CpuManager::~CpuManager() = default; 22CpuManager::~CpuManager() = default;
23 23
24void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { 24void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
25 cpu_manager.RunThread(core); 25 std::size_t core) {
26 cpu_manager.RunThread(stop_token, core);
26} 27}
27 28
28void CpuManager::Initialize() { 29void CpuManager::Initialize() {
29 running_mode = true; 30 running_mode = true;
30 if (is_multicore) { 31 if (is_multicore) {
31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 32 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
32 core_data[core].host_thread = 33 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
33 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
34 } 34 }
35 } else { 35 } else {
36 core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); 36 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
37 } 37 }
38} 38}
39 39
40void CpuManager::Shutdown() { 40void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) {
44 for (auto& data : core_data) {
45 data.host_thread->join();
46 data.host_thread.reset();
47 }
48 } else {
49 core_data[0].host_thread->join();
50 core_data[0].host_thread.reset();
51 }
52} 43}
53 44
54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 45std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
127 physical_core = &kernel.CurrentPhysicalCore(); 118 physical_core = &kernel.CurrentPhysicalCore();
128 } 119 }
129 system.ExitDynarmicProfile(); 120 system.ExitDynarmicProfile();
130 physical_core->ArmInterface().ClearExclusiveState(); 121 {
131 kernel.CurrentScheduler()->RescheduleCurrentCore(); 122 Kernel::KScopedDisableDispatch dd(kernel);
123 physical_core->ArmInterface().ClearExclusiveState();
124 }
132 } 125 }
133} 126}
134 127
135void CpuManager::MultiCoreRunIdleThread() { 128void CpuManager::MultiCoreRunIdleThread() {
136 auto& kernel = system.Kernel(); 129 auto& kernel = system.Kernel();
137 while (true) { 130 while (true) {
138 auto& physical_core = kernel.CurrentPhysicalCore(); 131 Kernel::KScopedDisableDispatch dd(kernel);
139 physical_core.Idle(); 132 kernel.CurrentPhysicalCore().Idle();
140 kernel.CurrentScheduler()->RescheduleCurrentCore();
141 } 133 }
142} 134}
143 135
@@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
145 auto& kernel = system.Kernel(); 137 auto& kernel = system.Kernel();
146 kernel.CurrentScheduler()->OnThreadStart(); 138 kernel.CurrentScheduler()->OnThreadStart();
147 while (true) { 139 while (true) {
148 auto core = kernel.GetCurrentHostThreadID(); 140 auto core = kernel.CurrentPhysicalCoreIndex();
149 auto& scheduler = *kernel.CurrentScheduler(); 141 auto& scheduler = *kernel.CurrentScheduler();
150 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 142 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
151 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 143 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
152 ASSERT(scheduler.ContextSwitchPending()); 144 ASSERT(scheduler.ContextSwitchPending());
153 ASSERT(core == kernel.GetCurrentHostThreadID()); 145 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
154 scheduler.RescheduleCurrentCore(); 146 scheduler.RescheduleCurrentCore();
155 } 147 }
156} 148}
@@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) {
317 } 309 }
318} 310}
319 311
320void CpuManager::RunThread(std::size_t core) { 312void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
321 /// Initialization 313 /// Initialization
322 system.RegisterCoreThread(core); 314 system.RegisterCoreThread(core);
323 std::string name; 315 std::string name;
@@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) {
356 sc_sync_first_use = false; 348 sc_sync_first_use = false;
357 } 349 }
358 350
359 // Abort if emulation was killed before the session really starts 351 // Emulation was stopped
360 if (!system.IsPoweredOn()) { 352 if (stop_token.stop_requested()) {
361 return; 353 return;
362 } 354 }
363 355
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 140263b09..9d92d4af0 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -78,9 +78,9 @@ private:
78 void SingleCoreRunSuspendThread(); 78 void SingleCoreRunSuspendThread();
79 void SingleCorePause(bool paused); 79 void SingleCorePause(bool paused);
80 80
81 static void ThreadStart(CpuManager& cpu_manager, std::size_t core); 81 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
82 82
83 void RunThread(std::size_t core); 83 void RunThread(std::stop_token stop_token, std::size_t core);
84 84
85 struct CoreData { 85 struct CoreData {
86 std::shared_ptr<Common::Fiber> host_context; 86 std::shared_ptr<Common::Fiber> host_context;
@@ -89,7 +89,7 @@ private:
89 std::atomic<bool> is_running; 89 std::atomic<bool> is_running;
90 std::atomic<bool> is_paused; 90 std::atomic<bool> is_paused;
91 std::atomic<bool> initialized; 91 std::atomic<bool> initialized;
92 std::unique_ptr<std::thread> host_thread; 92 std::jthread host_thread;
93 }; 93 };
94 94
95 std::atomic<bool> running_mode{}; 95 std::atomic<bool> running_mode{};
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..6771ef621 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
28 28
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { 29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor(); 30 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex(); 31 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
32 32
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here. 34 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
58 58
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { 59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor(); 60 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex(); 61 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
62 62
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here. 64 // TODO(bunnei): We should call CanAccessAtomic(..) here.
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
170 } 170 }
171 } 171 }
172 172
173 const std::string& GetName() const {
174 return name;
175 }
176
173private: 177private:
174 void RegisterWithKernel(); 178 void RegisterWithKernel();
175 void UnregisterWithKernel(); 179 void UnregisterWithKernel();
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index ef14ad1d2..4174f35fd 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
35bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, 35bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
36 u32 new_orr_mask) { 36 u32 new_orr_mask) {
37 auto& monitor = system.Monitor(); 37 auto& monitor = system.Monitor();
38 const auto current_core = system.CurrentCoreIndex(); 38 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
39 39
40 // Load the value from the address. 40 // Load the value from the address.
41 const auto expected = monitor.ExclusiveRead32(current_core, address); 41 const auto expected = monitor.ExclusiveRead32(current_core, address);
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index 6a420d5b0..d720c2dda 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
13 // Get the table and clear our record of it. 13 // Get the table and clear our record of it.
14 u16 saved_table_size = 0; 14 u16 saved_table_size = 0;
15 { 15 {
16 KScopedDisableDispatch dd(kernel);
16 KScopedSpinLock lk(m_lock); 17 KScopedSpinLock lk(m_lock);
17 18
18 std::swap(m_table_size, saved_table_size); 19 std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
43 // Find the object and free the entry. 44 // Find the object and free the entry.
44 KAutoObject* obj = nullptr; 45 KAutoObject* obj = nullptr;
45 { 46 {
47 KScopedDisableDispatch dd(kernel);
46 KScopedSpinLock lk(m_lock); 48 KScopedSpinLock lk(m_lock);
47 49
48 if (this->IsValidHandle(handle)) { 50 if (this->IsValidHandle(handle)) {
@@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
61} 63}
62 64
63ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 65ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
66 KScopedDisableDispatch dd(kernel);
64 KScopedSpinLock lk(m_lock); 67 KScopedSpinLock lk(m_lock);
65 68
66 // Never exceed our capacity. 69 // Never exceed our capacity.
@@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
83} 86}
84 87
85ResultCode KHandleTable::Reserve(Handle* out_handle) { 88ResultCode KHandleTable::Reserve(Handle* out_handle) {
89 KScopedDisableDispatch dd(kernel);
86 KScopedSpinLock lk(m_lock); 90 KScopedSpinLock lk(m_lock);
87 91
88 // Never exceed our capacity. 92 // Never exceed our capacity.
@@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
93} 97}
94 98
95void KHandleTable::Unreserve(Handle handle) { 99void KHandleTable::Unreserve(Handle handle) {
100 KScopedDisableDispatch dd(kernel);
96 KScopedSpinLock lk(m_lock); 101 KScopedSpinLock lk(m_lock);
97 102
98 // Unpack the handle. 103 // Unpack the handle.
@@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
111} 116}
112 117
113void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 118void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
119 KScopedDisableDispatch dd(kernel);
114 KScopedSpinLock lk(m_lock); 120 KScopedSpinLock lk(m_lock);
115 121
116 // Unpack the handle. 122 // Unpack the handle.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 2ff6aa160..75dcec7df 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -69,6 +69,7 @@ public:
69 template <typename T = KAutoObject> 69 template <typename T = KAutoObject>
70 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { 70 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
71 // Lock and look up in table. 71 // Lock and look up in table.
72 KScopedDisableDispatch dd(kernel);
72 KScopedSpinLock lk(m_lock); 73 KScopedSpinLock lk(m_lock);
73 74
74 if constexpr (std::is_same_v<T, KAutoObject>) { 75 if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -123,6 +124,7 @@ public:
123 size_t num_opened; 124 size_t num_opened;
124 { 125 {
125 // Lock the table. 126 // Lock the table.
127 KScopedDisableDispatch dd(kernel);
126 KScopedSpinLock lk(m_lock); 128 KScopedSpinLock lk(m_lock);
127 for (num_opened = 0; num_opened < num_handles; num_opened++) { 129 for (num_opened = 0; num_opened < num_handles; num_opened++) {
128 // Get the current handle. 130 // Get the current handle.
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 8ead1a769..3d7e6707e 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
59 thread->GetContext64().cpu_registers[0] = 0; 59 thread->GetContext64().cpu_registers[0] = 0;
60 thread->GetContext32().cpu_registers[1] = thread_handle; 60 thread->GetContext32().cpu_registers[1] = thread_handle;
61 thread->GetContext64().cpu_registers[1] = thread_handle; 61 thread->GetContext64().cpu_registers[1] = thread_handle;
62 thread->DisableDispatch();
62 63
63 auto& kernel = system.Kernel(); 64 auto& kernel = system.Kernel();
64 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 65 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..6ddbae52c 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
376} 376}
377 377
378void KScheduler::DisableScheduling(KernelCore& kernel) { 378void KScheduler::DisableScheduling(KernelCore& kernel) {
379 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 379 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
380 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); 380 GetCurrentThreadPointer(kernel)->DisableDispatch();
381 scheduler->GetCurrentThread()->DisableDispatch();
382 }
383} 381}
384 382
385void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { 383void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
386 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 384 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
387 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); 385
388 if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { 386 if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
389 scheduler->GetCurrentThread()->EnableDispatch(); 387 GetCurrentThreadPointer(kernel)->EnableDispatch();
390 } 388 } else {
389 RescheduleCores(kernel, cores_needing_scheduling);
391 } 390 }
392 RescheduleCores(kernel, cores_needing_scheduling);
393} 391}
394 392
395u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { 393u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
617 state.highest_priority_thread = nullptr; 615 state.highest_priority_thread = nullptr;
618} 616}
619 617
620KScheduler::~KScheduler() { 618void KScheduler::Finalize() {
621 if (idle_thread) { 619 if (idle_thread) {
622 idle_thread->Close(); 620 idle_thread->Close();
623 idle_thread = nullptr; 621 idle_thread = nullptr;
624 } 622 }
625} 623}
626 624
625KScheduler::~KScheduler() {
626 ASSERT(!idle_thread);
627}
628
627KThread* KScheduler::GetCurrentThread() const { 629KThread* KScheduler::GetCurrentThread() const {
628 if (auto result = current_thread.load(); result) { 630 if (auto result = current_thread.load(); result) {
629 return result; 631 return result;
@@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() {
642 if (phys_core.IsInterrupted()) { 644 if (phys_core.IsInterrupted()) {
643 phys_core.ClearInterrupt(); 645 phys_core.ClearInterrupt();
644 } 646 }
647
645 guard.Lock(); 648 guard.Lock();
646 if (state.needs_scheduling.load()) { 649 if (state.needs_scheduling.load()) {
647 Schedule(); 650 Schedule();
648 } else { 651 } else {
652 GetCurrentThread()->EnableDispatch();
649 guard.Unlock(); 653 guard.Unlock();
650 } 654 }
651} 655}
@@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() {
655} 659}
656 660
657void KScheduler::Unload(KThread* thread) { 661void KScheduler::Unload(KThread* thread) {
662 ASSERT(thread);
663
658 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); 664 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
659 665
660 if (thread) { 666 if (thread->IsCallingSvc()) {
661 if (thread->IsCallingSvc()) { 667 thread->ClearIsCallingSvc();
662 thread->ClearIsCallingSvc();
663 }
664 if (!thread->IsTerminationRequested()) {
665 prev_thread = thread;
666
667 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
668 cpu_core.SaveContext(thread->GetContext32());
669 cpu_core.SaveContext(thread->GetContext64());
670 // Save the TPIDR_EL0 system register in case it was modified.
671 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
672 cpu_core.ClearExclusiveState();
673 } else {
674 prev_thread = nullptr;
675 }
676 thread->context_guard.Unlock();
677 } 668 }
669
670 auto& physical_core = system.Kernel().PhysicalCore(core_id);
671 if (!physical_core.IsInitialized()) {
672 return;
673 }
674
675 Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
676 cpu_core.SaveContext(thread->GetContext32());
677 cpu_core.SaveContext(thread->GetContext64());
678 // Save the TPIDR_EL0 system register in case it was modified.
679 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
680 cpu_core.ClearExclusiveState();
681
682 if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
683 prev_thread = thread;
684 } else {
685 prev_thread = nullptr;
686 }
687
688 thread->context_guard.Unlock();
678} 689}
679 690
680void KScheduler::Reload(KThread* thread) { 691void KScheduler::Reload(KThread* thread) {
@@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) {
683 if (thread) { 694 if (thread) {
684 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); 695 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
685 696
686 auto* const thread_owner_process = thread->GetOwnerProcess();
687 if (thread_owner_process != nullptr) {
688 system.Kernel().MakeCurrentProcess(thread_owner_process);
689 }
690
691 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 697 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
692 cpu_core.LoadContext(thread->GetContext32()); 698 cpu_core.LoadContext(thread->GetContext32());
693 cpu_core.LoadContext(thread->GetContext64()); 699 cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() {
705} 711}
706 712
707void KScheduler::ScheduleImpl() { 713void KScheduler::ScheduleImpl() {
708 KThread* previous_thread = current_thread.load(); 714 KThread* previous_thread = GetCurrentThread();
709 KThread* next_thread = state.highest_priority_thread; 715 KThread* next_thread = state.highest_priority_thread;
710 716
711 state.needs_scheduling = false; 717 state.needs_scheduling = false;
@@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() {
717 723
718 // If we're not actually switching thread, there's nothing to do. 724 // If we're not actually switching thread, there's nothing to do.
719 if (next_thread == current_thread.load()) { 725 if (next_thread == current_thread.load()) {
726 previous_thread->EnableDispatch();
720 guard.Unlock(); 727 guard.Unlock();
721 return; 728 return;
722 } 729 }
723 730
731 if (next_thread->GetCurrentCore() != core_id) {
732 next_thread->SetCurrentCore(core_id);
733 }
734
724 current_thread.store(next_thread); 735 current_thread.store(next_thread);
725 736
726 KProcess* const previous_process = system.Kernel().CurrentProcess(); 737 KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() {
731 Unload(previous_thread); 742 Unload(previous_thread);
732 743
733 std::shared_ptr<Common::Fiber>* old_context; 744 std::shared_ptr<Common::Fiber>* old_context;
734 if (previous_thread != nullptr) { 745 old_context = &previous_thread->GetHostContext();
735 old_context = &previous_thread->GetHostContext();
736 } else {
737 old_context = &idle_thread->GetHostContext();
738 }
739 guard.Unlock(); 746 guard.Unlock();
740 747
741 Common::Fiber::YieldTo(*old_context, *switch_fiber); 748 Common::Fiber::YieldTo(*old_context, *switch_fiber);
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 12cfae919..516e0cdba 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -33,6 +33,8 @@ public:
33 explicit KScheduler(Core::System& system_, s32 core_id_); 33 explicit KScheduler(Core::System& system_, s32 core_id_);
34 ~KScheduler(); 34 ~KScheduler();
35 35
36 void Finalize();
37
36 /// Reschedules to the next available thread (call after current thread is suspended) 38 /// Reschedules to the next available thread (call after current thread is suspended)
37 void RescheduleCurrentCore(); 39 void RescheduleCurrentCore();
38 40
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 9f1d3156b..0f6808ade 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -14,6 +14,7 @@
14#include "common/fiber.h" 14#include "common/fiber.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/scope_exit.h" 16#include "common/scope_exit.h"
17#include "common/settings.h"
17#include "common/thread_queue_list.h" 18#include "common/thread_queue_list.h"
18#include "core/core.h" 19#include "core/core.h"
19#include "core/cpu_manager.h" 20#include "core/cpu_manager.h"
@@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
188 // Setup the stack parameters. 189 // Setup the stack parameters.
189 StackParameters& sp = GetStackParameters(); 190 StackParameters& sp = GetStackParameters();
190 sp.cur_thread = this; 191 sp.cur_thread = this;
191 sp.disable_count = 1; 192 sp.disable_count = 0;
192 SetInExceptionHandler(); 193 SetInExceptionHandler();
193 194
194 // Set thread ID. 195 // Set thread ID.
@@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
215 // Initialize the thread. 216 // Initialize the thread.
216 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); 217 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
217 218
218 // Initialize host context. 219 // Initialize emulation parameters.
219 thread->host_context = 220 thread->host_context =
220 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); 221 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
222 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
221 223
222 return ResultSuccess; 224 return ResultSuccess;
223} 225}
@@ -970,6 +972,9 @@ ResultCode KThread::Run() {
970 972
971 // Set our state and finish. 973 // Set our state and finish.
972 SetState(ThreadState::Runnable); 974 SetState(ThreadState::Runnable);
975
976 DisableDispatch();
977
973 return ResultSuccess; 978 return ResultSuccess;
974 } 979 }
975} 980}
@@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1054 return GetCurrentThread(kernel).GetCurrentCore(); 1059 return GetCurrentThread(kernel).GetCurrentCore();
1055} 1060}
1056 1061
1062KScopedDisableDispatch::~KScopedDisableDispatch() {
1063 if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
1064 auto scheduler = kernel.CurrentScheduler();
1065
1066 if (scheduler) {
1067 scheduler->RescheduleCurrentCore();
1068 }
1069 } else {
1070 GetCurrentThread(kernel).EnableDispatch();
1071 }
1072}
1073
1057} // namespace Kernel 1074} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..e4c4c877d 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -450,16 +450,39 @@ public:
450 sleeping_queue = q; 450 sleeping_queue = q;
451 } 451 }
452 452
453 [[nodiscard]] bool IsKernelThread() const {
454 return GetActiveCore() == 3;
455 }
456
457 [[nodiscard]] bool IsDispatchTrackingDisabled() const {
458 return is_single_core || IsKernelThread();
459 }
460
453 [[nodiscard]] s32 GetDisableDispatchCount() const { 461 [[nodiscard]] s32 GetDisableDispatchCount() const {
462 if (IsDispatchTrackingDisabled()) {
463 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
464 return 1;
465 }
466
454 return this->GetStackParameters().disable_count; 467 return this->GetStackParameters().disable_count;
455 } 468 }
456 469
457 void DisableDispatch() { 470 void DisableDispatch() {
471 if (IsDispatchTrackingDisabled()) {
472 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
473 return;
474 }
475
458 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); 476 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
459 this->GetStackParameters().disable_count++; 477 this->GetStackParameters().disable_count++;
460 } 478 }
461 479
462 void EnableDispatch() { 480 void EnableDispatch() {
481 if (IsDispatchTrackingDisabled()) {
482 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
483 return;
484 }
485
463 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); 486 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
464 this->GetStackParameters().disable_count--; 487 this->GetStackParameters().disable_count--;
465 } 488 }
@@ -708,6 +731,7 @@ private:
708 731
709 // For emulation 732 // For emulation
710 std::shared_ptr<Common::Fiber> host_context{}; 733 std::shared_ptr<Common::Fiber> host_context{};
734 bool is_single_core{};
711 735
712 // For debugging 736 // For debugging
713 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 737 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +776,16 @@ public:
752 } 776 }
753}; 777};
754 778
779class KScopedDisableDispatch {
780public:
781 [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
782 GetCurrentThread(kernel).DisableDispatch();
783 }
784
785 ~KScopedDisableDispatch();
786
787private:
788 KernelCore& kernel;
789};
790
755} // namespace Kernel 791} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index bea945301..8fdab44e4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -85,8 +85,9 @@ struct KernelCore::Impl {
85 } 85 }
86 86
87 void InitializeCores() { 87 void InitializeCores() {
88 for (auto& core : cores) { 88 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
89 core.Initialize(current_process->Is64BitProcess()); 89 cores[core_id].Initialize(current_process->Is64BitProcess());
90 system.Memory().SetCurrentPageTable(*current_process, core_id);
90 } 91 }
91 } 92 }
92 93
@@ -131,15 +132,6 @@ struct KernelCore::Impl {
131 next_user_process_id = KProcess::ProcessIDMin; 132 next_user_process_id = KProcess::ProcessIDMin;
132 next_thread_id = 1; 133 next_thread_id = 1;
133 134
134 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
135 if (suspend_threads[core_id]) {
136 suspend_threads[core_id]->Close();
137 suspend_threads[core_id] = nullptr;
138 }
139
140 schedulers[core_id].reset();
141 }
142
143 cores.clear(); 135 cores.clear();
144 136
145 global_handle_table->Finalize(); 137 global_handle_table->Finalize();
@@ -167,6 +159,16 @@ struct KernelCore::Impl {
167 CleanupObject(time_shared_mem); 159 CleanupObject(time_shared_mem);
168 CleanupObject(system_resource_limit); 160 CleanupObject(system_resource_limit);
169 161
162 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
163 if (suspend_threads[core_id]) {
164 suspend_threads[core_id]->Close();
165 suspend_threads[core_id] = nullptr;
166 }
167
168 schedulers[core_id]->Finalize();
169 schedulers[core_id].reset();
170 }
171
170 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 172 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
171 next_host_thread_id = Core::Hardware::NUM_CPU_CORES; 173 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
172 174
@@ -257,14 +259,6 @@ struct KernelCore::Impl {
257 259
258 void MakeCurrentProcess(KProcess* process) { 260 void MakeCurrentProcess(KProcess* process) {
259 current_process = process; 261 current_process = process;
260 if (process == nullptr) {
261 return;
262 }
263
264 const u32 core_id = GetCurrentHostThreadID();
265 if (core_id < Core::Hardware::NUM_CPU_CORES) {
266 system.Memory().SetCurrentPageTable(*process, core_id);
267 }
268 } 262 }
269 263
270 static inline thread_local u32 host_thread_id = UINT32_MAX; 264 static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -827,16 +821,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
827 return impl->cores[id]; 821 return impl->cores[id];
828} 822}
829 823
824size_t KernelCore::CurrentPhysicalCoreIndex() const {
825 const u32 core_id = impl->GetCurrentHostThreadID();
826 if (core_id >= Core::Hardware::NUM_CPU_CORES) {
827 return Core::Hardware::NUM_CPU_CORES - 1;
828 }
829 return core_id;
830}
831
830Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { 832Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
831 u32 core_id = impl->GetCurrentHostThreadID(); 833 return impl->cores[CurrentPhysicalCoreIndex()];
832 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
833 return impl->cores[core_id];
834} 834}
835 835
836const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { 836const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
837 u32 core_id = impl->GetCurrentHostThreadID(); 837 return impl->cores[CurrentPhysicalCoreIndex()];
838 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
839 return impl->cores[core_id];
840} 838}
841 839
842Kernel::KScheduler* KernelCore::CurrentScheduler() { 840Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1029,6 +1027,9 @@ void KernelCore::Suspend(bool in_suspention) {
1029 impl->suspend_threads[core_id]->SetState(state); 1027 impl->suspend_threads[core_id]->SetState(state);
1030 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1028 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1031 ThreadWaitReasonForDebugging::Suspended); 1029 ThreadWaitReasonForDebugging::Suspended);
1030 if (!should_suspend) {
1031 impl->suspend_threads[core_id]->DisableDispatch();
1032 }
1032 } 1033 }
1033 } 1034 }
1034} 1035}
@@ -1043,13 +1044,11 @@ void KernelCore::ExceptionalExit() {
1043} 1044}
1044 1045
1045void KernelCore::EnterSVCProfile() { 1046void KernelCore::EnterSVCProfile() {
1046 std::size_t core = impl->GetCurrentHostThreadID(); 1047 impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1047 impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1048} 1048}
1049 1049
1050void KernelCore::ExitSVCProfile() { 1050void KernelCore::ExitSVCProfile() {
1051 std::size_t core = impl->GetCurrentHostThreadID(); 1051 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
1052 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
1053} 1052}
1054 1053
1055std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { 1054std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3a6db0b1c..57535433b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -146,6 +146,9 @@ public:
146 /// Gets the an instance of the respective physical CPU core. 146 /// Gets the an instance of the respective physical CPU core.
147 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 147 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
148 148
149 /// Gets the current physical core index for the running host thread.
150 std::size_t CurrentPhysicalCoreIndex() const;
151
149 /// Gets the sole instance of the Scheduler at the current running core. 152 /// Gets the sole instance of the Scheduler at the current running core.
150 Kernel::KScheduler* CurrentScheduler(); 153 Kernel::KScheduler* CurrentScheduler();
151 154
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2eb532472..890c52198 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
877 const u64 thread_ticks = current_thread->GetCpuTime(); 877 const u64 thread_ticks = current_thread->GetCpuTime();
878 878
879 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 879 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
880 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 880 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
881 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 881 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
882 } 882 }
883 883
@@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { 1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
1080 current = true; 1080 current = true;
1081 break;
1081 } 1082 }
1082 break;
1083 } 1083 }
1084 1084
1085 // If the thread is current, retry until it isn't. 1085 // If the thread is current, retry until it isn't.
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index ef6854d62..36a4aa9cd 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -16,6 +16,30 @@
16 16
17namespace Service::AM::Applets { 17namespace Service::AM::Applets {
18 18
19struct ErrorCode {
20 u32 error_category{};
21 u32 error_number{};
22
23 static constexpr ErrorCode FromU64(u64 error_code) {
24 return {
25 .error_category{static_cast<u32>(error_code >> 32)},
26 .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
27 };
28 }
29
30 static constexpr ErrorCode FromResultCode(ResultCode result) {
31 return {
32 .error_category{2000 + static_cast<u32>(result.module.Value())},
33 .error_number{result.description.Value()},
34 };
35 }
36
37 constexpr ResultCode ToResultCode() const {
38 return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
39 }
40};
41static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
42
19#pragma pack(push, 4) 43#pragma pack(push, 4)
20struct ShowError { 44struct ShowError {
21 u8 mode; 45 u8 mode;
@@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
76} 100}
77 101
78ResultCode Decode64BitError(u64 error) { 102ResultCode Decode64BitError(u64 error) {
79 const auto description = (error >> 32) & 0x1FFF; 103 return ErrorCode::FromU64(error).ToResultCode();
80 auto module = error & 0x3FF;
81 if (module >= 2000)
82 module -= 2000;
83 module &= 0x1FF;
84 return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
85} 104}
86 105
87} // Anonymous namespace 106} // Anonymous namespace
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e742db48f..0a53c0c81 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/service/nifm/nifm.h" 11#include "core/hle/service/nifm/nifm.h"
12#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
13#include "core/network/network.h" 13#include "core/network/network.h"
14#include "core/network/network_interface.h"
14 15
15namespace Service::NIFM { 16namespace Service::NIFM {
16 17
@@ -179,10 +180,10 @@ private:
179 IPC::ResponseBuilder rb{ctx, 3}; 180 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
181 182
182 if (Settings::values.bcat_backend.GetValue() == "none") { 183 if (Network::GetHostIPv4Address().has_value()) {
183 rb.PushEnum(RequestState::NotSubmitted);
184 } else {
185 rb.PushEnum(RequestState::Connected); 184 rb.PushEnum(RequestState::Connected);
185 } else {
186 rb.PushEnum(RequestState::NotSubmitted);
186 } 187 }
187 } 188 }
188 189
@@ -322,12 +323,15 @@ private:
322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 323 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
323 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 324 LOG_WARNING(Service_NIFM, "(STUBBED) called");
324 325
325 const auto [ipv4, error] = Network::GetHostIPv4Address(); 326 auto ipv4 = Network::GetHostIPv4Address();
326 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); 327 if (!ipv4) {
328 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
329 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
330 }
327 331
328 IPC::ResponseBuilder rb{ctx, 3}; 332 IPC::ResponseBuilder rb{ctx, 3};
329 rb.Push(ResultSuccess); 333 rb.Push(ResultSuccess);
330 rb.PushRaw(ipv4); 334 rb.PushRaw(*ipv4);
331 } 335 }
332 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 336 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
333 LOG_DEBUG(Service_NIFM, "called"); 337 LOG_DEBUG(Service_NIFM, "called");
@@ -354,10 +358,10 @@ private:
354 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 358 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
355 "IpConfigInfo has incorrect size."); 359 "IpConfigInfo has incorrect size.");
356 360
357 const IpConfigInfo ip_config_info{ 361 IpConfigInfo ip_config_info{
358 .ip_address_setting{ 362 .ip_address_setting{
359 .is_automatic{true}, 363 .is_automatic{true},
360 .current_address{192, 168, 1, 100}, 364 .current_address{0, 0, 0, 0},
361 .subnet_mask{255, 255, 255, 0}, 365 .subnet_mask{255, 255, 255, 0},
362 .gateway{192, 168, 1, 1}, 366 .gateway{192, 168, 1, 1},
363 }, 367 },
@@ -368,6 +372,19 @@ private:
368 }, 372 },
369 }; 373 };
370 374
375 const auto iface = Network::GetSelectedNetworkInterface();
376 if (iface) {
377 ip_config_info.ip_address_setting =
378 IpAddressSetting{.is_automatic{true},
379 .current_address{Network::TranslateIPv4(iface->ip_address)},
380 .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
381 .gateway{Network::TranslateIPv4(iface->gateway)}};
382
383 } else {
384 LOG_ERROR(Service_NIFM,
385 "Couldn't get host network configuration info, using default values");
386 }
387
371 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 388 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
372 rb.Push(ResultSuccess); 389 rb.Push(ResultSuccess);
373 rb.PushRaw<IpConfigInfo>(ip_config_info); 390 rb.PushRaw<IpConfigInfo>(ip_config_info);
@@ -384,10 +401,10 @@ private:
384 401
385 IPC::ResponseBuilder rb{ctx, 3}; 402 IPC::ResponseBuilder rb{ctx, 3};
386 rb.Push(ResultSuccess); 403 rb.Push(ResultSuccess);
387 if (Settings::values.bcat_backend.GetValue() == "none") { 404 if (Network::GetHostIPv4Address().has_value()) {
388 rb.Push<u8>(0);
389 } else {
390 rb.Push<u8>(1); 405 rb.Push<u8>(1);
406 } else {
407 rb.Push<u8>(0);
391 } 408 }
392 } 409 }
393 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 410 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -395,10 +412,10 @@ private:
395 412
396 IPC::ResponseBuilder rb{ctx, 3}; 413 IPC::ResponseBuilder rb{ctx, 3};
397 rb.Push(ResultSuccess); 414 rb.Push(ResultSuccess);
398 if (Settings::values.bcat_backend.GetValue() == "none") { 415 if (Network::GetHostIPv4Address().has_value()) {
399 rb.Push<u8>(0);
400 } else {
401 rb.Push<u8>(1); 416 rb.Push<u8>(1);
417 } else {
418 rb.Push<u8>(0);
402 } 419 }
403 } 420 }
404}; 421};
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 59ddf6298..b4c3a6099 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,17 +9,20 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h" 10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 13#include "core/hle/service/nvflinger/buffer_queue.h"
13 14
14namespace Service::NVFlinger { 15namespace Service::NVFlinger {
15 16
16BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) 17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
17 : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} { 18 KernelHelpers::ServiceContext& service_context_)
18 Kernel::KAutoObject::Create(std::addressof(buffer_wait_event)); 19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
19 buffer_wait_event.Initialize("BufferQueue:WaitEvent"); 20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
20} 21}
21 22
22BufferQueue::~BufferQueue() = default; 23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
23 26
24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
25 ASSERT(slot < buffer_slots); 28 ASSERT(slot < buffer_slots);
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
41 .multi_fence = {}, 44 .multi_fence = {},
42 }; 45 };
43 46
44 buffer_wait_event.GetWritableEvent().Signal(); 47 buffer_wait_event->GetWritableEvent().Signal();
45} 48}
46 49
47std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, 50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
@@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
119 } 122 }
120 free_buffers_condition.notify_one(); 123 free_buffers_condition.notify_one();
121 124
122 buffer_wait_event.GetWritableEvent().Signal(); 125 buffer_wait_event->GetWritableEvent().Signal();
123} 126}
124 127
125std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { 128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
154 } 157 }
155 free_buffers_condition.notify_one(); 158 free_buffers_condition.notify_one();
156 159
157 buffer_wait_event.GetWritableEvent().Signal(); 160 buffer_wait_event->GetWritableEvent().Signal();
158} 161}
159 162
160void BufferQueue::Connect() { 163void BufferQueue::Connect() {
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
169 std::unique_lock lock{queue_sequence_mutex}; 172 std::unique_lock lock{queue_sequence_mutex};
170 queue_sequence.clear(); 173 queue_sequence.clear();
171 } 174 }
172 buffer_wait_event.GetWritableEvent().Signal(); 175 buffer_wait_event->GetWritableEvent().Signal();
173 is_connect = false; 176 is_connect = false;
174 free_buffers_condition.notify_one(); 177 free_buffers_condition.notify_one();
175} 178}
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
189} 192}
190 193
191Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { 194Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
192 return buffer_wait_event.GetWritableEvent(); 195 return buffer_wait_event->GetWritableEvent();
193} 196}
194 197
195Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { 198Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
196 return buffer_wait_event.GetReadableEvent(); 199 return buffer_wait_event->GetReadableEvent();
197} 200}
198 201
199} // namespace Service::NVFlinger 202} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 61e337ac5..759247eb0 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -24,6 +24,10 @@ class KReadableEvent;
24class KWritableEvent; 24class KWritableEvent;
25} // namespace Kernel 25} // namespace Kernel
26 26
27namespace Service::KernelHelpers {
28class ServiceContext;
29} // namespace Service::KernelHelpers
30
27namespace Service::NVFlinger { 31namespace Service::NVFlinger {
28 32
29constexpr u32 buffer_slots = 0x40; 33constexpr u32 buffer_slots = 0x40;
@@ -54,7 +58,8 @@ public:
54 NativeWindowFormat = 2, 58 NativeWindowFormat = 2,
55 }; 59 };
56 60
57 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); 61 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
62 KernelHelpers::ServiceContext& service_context_);
58 ~BufferQueue(); 63 ~BufferQueue();
59 64
60 enum class BufferTransformFlags : u32 { 65 enum class BufferTransformFlags : u32 {
@@ -130,12 +135,14 @@ private:
130 std::list<u32> free_buffers; 135 std::list<u32> free_buffers;
131 std::array<Buffer, buffer_slots> buffers; 136 std::array<Buffer, buffer_slots> buffers;
132 std::list<u32> queue_sequence; 137 std::list<u32> queue_sequence;
133 Kernel::KEvent buffer_wait_event; 138 Kernel::KEvent* buffer_wait_event{};
134 139
135 std::mutex free_buffers_mutex; 140 std::mutex free_buffers_mutex;
136 std::condition_variable free_buffers_condition; 141 std::condition_variable free_buffers_condition;
137 142
138 std::mutex queue_sequence_mutex; 143 std::mutex queue_sequence_mutex;
144
145 KernelHelpers::ServiceContext& service_context;
139}; 146};
140 147
141} // namespace Service::NVFlinger 148} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 941748970..00bff8caf 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() {
61 } 61 }
62} 62}
63 63
64NVFlinger::NVFlinger(Core::System& system_) : system(system_) { 64NVFlinger::NVFlinger(Core::System& system_)
65 displays.emplace_back(0, "Default", system); 65 : system(system_), service_context(system_, "nvflinger") {
66 displays.emplace_back(1, "External", system); 66 displays.emplace_back(0, "Default", service_context, system);
67 displays.emplace_back(2, "Edid", system); 67 displays.emplace_back(1, "External", service_context, system);
68 displays.emplace_back(3, "Internal", system); 68 displays.emplace_back(2, "Edid", service_context, system);
69 displays.emplace_back(4, "Null", system); 69 displays.emplace_back(3, "Internal", service_context, system);
70 displays.emplace_back(4, "Null", service_context, system);
70 guard = std::make_shared<std::mutex>(); 71 guard = std::make_shared<std::mutex>();
71 72
72 // Schedule the screen composition events 73 // Schedule the screen composition events
@@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
146void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 147void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
147 const u32 buffer_queue_id = next_buffer_queue_id++; 148 const u32 buffer_queue_id = next_buffer_queue_id++;
148 buffer_queues.emplace_back( 149 buffer_queues.emplace_back(
149 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); 150 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
150 display.CreateLayer(layer_id, *buffer_queues.back()); 151 display.CreateLayer(layer_id, *buffer_queues.back());
151} 152}
152 153
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index d80fd07ef..6d84cafb4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,6 +15,7 @@
15#include <vector> 15#include <vector>
16 16
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "core/hle/service/kernel_helpers.h"
18 19
19namespace Common { 20namespace Common {
20class Event; 21class Event;
@@ -135,6 +136,8 @@ private:
135 std::unique_ptr<std::thread> vsync_thread; 136 std::unique_ptr<std::thread> vsync_thread;
136 std::unique_ptr<Common::Event> wait_event; 137 std::unique_ptr<Common::Event> wait_event;
137 std::atomic<bool> is_running{}; 138 std::atomic<bool> is_running{};
139
140 KernelHelpers::ServiceContext service_context;
138}; 141};
139 142
140} // namespace Service::NVFlinger 143} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 0dd342dbf..b7705c02a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,18 +12,21 @@
12#include "core/hle/kernel/k_event.h" 12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/vi/display/vi_display.h" 16#include "core/hle/service/vi/display/vi_display.h"
16#include "core/hle/service/vi/layer/vi_layer.h" 17#include "core/hle/service/vi/layer/vi_layer.h"
17 18
18namespace Service::VI { 19namespace Service::VI {
19 20
20Display::Display(u64 id, std::string name_, Core::System& system) 21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
21 : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} { 22 Core::System& system_)
22 Kernel::KAutoObject::Create(std::addressof(vsync_event)); 23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
23 vsync_event.Initialize(fmt::format("Display VSync Event {}", id)); 24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
24} 25}
25 26
26Display::~Display() = default; 27Display::~Display() {
28 service_context.CloseEvent(vsync_event);
29}
27 30
28Layer& Display::GetLayer(std::size_t index) { 31Layer& Display::GetLayer(std::size_t index) {
29 return *layers.at(index); 32 return *layers.at(index);
@@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const {
34} 37}
35 38
36Kernel::KReadableEvent& Display::GetVSyncEvent() { 39Kernel::KReadableEvent& Display::GetVSyncEvent() {
37 return vsync_event.GetReadableEvent(); 40 return vsync_event->GetReadableEvent();
38} 41}
39 42
40void Display::SignalVSyncEvent() { 43void Display::SignalVSyncEvent() {
41 vsync_event.GetWritableEvent().Signal(); 44 vsync_event->GetWritableEvent().Signal();
42} 45}
43 46
44void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 166f2a4cc..0979fc421 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -18,6 +18,9 @@ class KEvent;
18namespace Service::NVFlinger { 18namespace Service::NVFlinger {
19class BufferQueue; 19class BufferQueue;
20} 20}
21namespace Service::KernelHelpers {
22class ServiceContext;
23} // namespace Service::KernelHelpers
21 24
22namespace Service::VI { 25namespace Service::VI {
23 26
@@ -31,10 +34,13 @@ class Display {
31public: 34public:
32 /// Constructs a display with a given unique ID and name. 35 /// Constructs a display with a given unique ID and name.
33 /// 36 ///
34 /// @param id The unique ID for this display. 37 /// @param id The unique ID for this display.
38 /// @param service_context_ The ServiceContext for the owning service.
35 /// @param name_ The name for this display. 39 /// @param name_ The name for this display.
40 /// @param system_ The global system instance.
36 /// 41 ///
37 Display(u64 id, std::string name_, Core::System& system); 42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
43 Core::System& system_);
38 ~Display(); 44 ~Display();
39 45
40 /// Gets the unique ID assigned to this display. 46 /// Gets the unique ID assigned to this display.
@@ -98,9 +104,10 @@ public:
98private: 104private:
99 u64 display_id; 105 u64 display_id;
100 std::string name; 106 std::string name;
107 KernelHelpers::ServiceContext& service_context;
101 108
102 std::vector<std::shared_ptr<Layer>> layers; 109 std::vector<std::shared_ptr<Layer>> layers;
103 Kernel::KEvent vsync_event; 110 Kernel::KEvent* vsync_event{};
104}; 111};
105 112
106} // namespace Service::VI 113} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 3e5949d52..8e8fc40ca 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1158,7 +1158,7 @@ private:
1158 1158
1159 const auto layer_id = nv_flinger.CreateLayer(display_id); 1159 const auto layer_id = nv_flinger.CreateLayer(display_id);
1160 if (!layer_id) { 1160 if (!layer_id) {
1161 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1161 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
1162 IPC::ResponseBuilder rb{ctx, 2}; 1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ERR_NOT_FOUND); 1163 rb.Push(ERR_NOT_FOUND);
1164 return; 1164 return;
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 375bc79ec..4732d4485 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,9 +10,10 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11 11
12#ifdef _WIN32 12#ifdef _WIN32
13#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
14#include <winsock2.h> 13#include <winsock2.h>
14#include <ws2tcpip.h>
15#elif YUZU_UNIX 15#elif YUZU_UNIX
16#include <arpa/inet.h>
16#include <errno.h> 17#include <errno.h>
17#include <fcntl.h> 18#include <fcntl.h>
18#include <netdb.h> 19#include <netdb.h>
@@ -27,7 +28,9 @@
27#include "common/assert.h" 28#include "common/assert.h"
28#include "common/common_types.h" 29#include "common/common_types.h"
29#include "common/logging/log.h" 30#include "common/logging/log.h"
31#include "common/settings.h"
30#include "core/network/network.h" 32#include "core/network/network.h"
33#include "core/network/network_interface.h"
31#include "core/network/sockets.h" 34#include "core/network/sockets.h"
32 35
33namespace Network { 36namespace Network {
@@ -47,11 +50,6 @@ void Finalize() {
47 WSACleanup(); 50 WSACleanup();
48} 51}
49 52
50constexpr IPv4Address TranslateIPv4(in_addr addr) {
51 auto& bytes = addr.S_un.S_un_b;
52 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
53}
54
55sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 53sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
56 sockaddr_in result; 54 sockaddr_in result;
57 55
@@ -138,12 +136,6 @@ void Initialize() {}
138 136
139void Finalize() {} 137void Finalize() {}
140 138
141constexpr IPv4Address TranslateIPv4(in_addr addr) {
142 const u32 bytes = addr.s_addr;
143 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
144 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
145}
146
147sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 139sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
148 sockaddr_in result; 140 sockaddr_in result;
149 141
@@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
182} 174}
183 175
184bool EnableNonBlock(int fd, bool enable) { 176bool EnableNonBlock(int fd, bool enable) {
185 int flags = fcntl(fd, F_GETFD); 177 int flags = fcntl(fd, F_GETFL);
186 if (flags == -1) { 178 if (flags == -1) {
187 return false; 179 return false;
188 } 180 }
@@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) {
191 } else { 183 } else {
192 flags &= ~O_NONBLOCK; 184 flags &= ~O_NONBLOCK;
193 } 185 }
194 return fcntl(fd, F_SETFD, flags) == 0; 186 return fcntl(fd, F_SETFL, flags) == 0;
195} 187}
196 188
197Errno TranslateNativeError(int e) { 189Errno TranslateNativeError(int e) {
@@ -227,8 +219,12 @@ Errno GetAndLogLastError() {
227#else 219#else
228 int e = errno; 220 int e = errno;
229#endif 221#endif
222 const Errno err = TranslateNativeError(e);
223 if (err == Errno::AGAIN) {
224 return err;
225 }
230 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); 226 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
231 return TranslateNativeError(e); 227 return err;
232} 228}
233 229
234int TranslateDomain(Domain domain) { 230int TranslateDomain(Domain domain) {
@@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() {
353 Finalize(); 349 Finalize();
354} 350}
355 351
356std::pair<IPv4Address, Errno> GetHostIPv4Address() { 352std::optional<IPv4Address> GetHostIPv4Address() {
357 std::array<char, 256> name{}; 353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
358 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
359 return {IPv4Address{}, GetAndLogLastError()}; 355 if (network_interfaces.size() == 0) {
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {};
360 } 358 }
361 359
362 hostent* const ent = gethostbyname(name.data()); 360 const auto res =
363 if (!ent) { 361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
364 return {IPv4Address{}, GetAndLogLastError()}; 362 return iface.name == selected_network_interface;
365 } 363 });
366 if (ent->h_addr_list == nullptr) { 364
367 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); 365 if (res != network_interfaces.end()) {
368 return {IPv4Address{}, Errno::SUCCESS}; 366 char ip_addr[16] = {};
369 } 367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
370 if (ent->h_length != sizeof(in_addr)) { 368 LOG_INFO(Network, "IP address: {}", ip_addr);
371 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
372 }
373 369
374 in_addr addr; 370 return TranslateIPv4(res->ip_address);
375 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); 371 } else {
376 return {TranslateIPv4(addr), Errno::SUCCESS}; 372 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
373 return {};
374 }
377} 375}
378 376
379std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 377std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index bd30f1899..fdd3e4655 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <utility> 9#include <utility>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
14#ifdef _WIN32
15#include <winsock2.h>
16#elif YUZU_UNIX
17#include <netinet/in.h>
18#endif
19
13namespace Network { 20namespace Network {
14 21
15class Socket; 22class Socket;
@@ -92,8 +99,21 @@ public:
92 ~NetworkInstance(); 99 ~NetworkInstance();
93}; 100};
94 101
102#ifdef _WIN32
103constexpr IPv4Address TranslateIPv4(in_addr addr) {
104 auto& bytes = addr.S_un.S_un_b;
105 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
106}
107#elif YUZU_UNIX
108constexpr IPv4Address TranslateIPv4(in_addr addr) {
109 const u32 bytes = addr.s_addr;
110 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
111 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
112}
113#endif
114
95/// @brief Returns host's IPv4 address 115/// @brief Returns host's IPv4 address
96/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code 116/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
97std::pair<IPv4Address, Errno> GetHostIPv4Address(); 117std::optional<IPv4Address> GetHostIPv4Address();
98 118
99} // namespace Network 119} // namespace Network
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..cecc9aa11
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,203 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <fstream>
7#include <sstream>
8#include <vector>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/string_util.h"
15#include "core/network/network_interface.h"
16
17#ifdef _WIN32
18#include <iphlpapi.h>
19#else
20#include <cerrno>
21#include <ifaddrs.h>
22#include <net/if.h>
23#endif
24
25namespace Network {
26
27#ifdef _WIN32
28
29std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
30 std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses;
31 DWORD ret = ERROR_BUFFER_OVERFLOW;
32 DWORD buf_size = 0;
33
34 // retry up to 5 times
35 for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
36 ret = GetAdaptersAddresses(
37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
38 nullptr, adapter_addresses.data(), &buf_size);
39
40 if (ret == ERROR_BUFFER_OVERFLOW) {
41 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
42 } else {
43 break;
44 }
45 }
46
47 if (ret == NO_ERROR) {
48 std::vector<NetworkInterface> result;
49
50 for (auto current_address = adapter_addresses.data(); current_address != nullptr;
51 current_address = current_address->Next) {
52 if (current_address->FirstUnicastAddress == nullptr ||
53 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
54 continue;
55 }
56
57 if (current_address->OperStatus != IfOperStatusUp) {
58 continue;
59 }
60
61 const auto ip_addr = Common::BitCast<struct sockaddr_in>(
62 *current_address->FirstUnicastAddress->Address.lpSockaddr)
63 .sin_addr;
64
65 ULONG mask = 0;
66 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
67 &mask) != NO_ERROR) {
68 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
69 continue;
70 }
71
72 struct in_addr gateway = {.S_un{.S_addr{0}}};
73 if (current_address->FirstGatewayAddress != nullptr &&
74 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
75 gateway = Common::BitCast<struct sockaddr_in>(
76 *current_address->FirstGatewayAddress->Address.lpSockaddr)
77 .sin_addr;
78 }
79
80 result.push_back(NetworkInterface{
81 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
82 .ip_address{ip_addr},
83 .subnet_mask = in_addr{.S_un{.S_addr{mask}}},
84 .gateway = gateway});
85 }
86
87 return result;
88 } else {
89 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
90 return {};
91 }
92}
93
94#else
95
96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
97 std::vector<NetworkInterface> result;
98
99 struct ifaddrs* ifaddr = nullptr;
100
101 if (getifaddrs(&ifaddr) != 0) {
102 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
103 std::strerror(errno));
104 return result;
105 }
106
107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
109 continue;
110 }
111
112 if (ifa->ifa_addr->sa_family != AF_INET) {
113 continue;
114 }
115
116 if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
117 continue;
118 }
119
120 std::uint32_t gateway{0};
121 std::ifstream file{"/proc/net/route"};
122 if (file.is_open()) {
123
124 // ignore header
125 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
126
127 bool gateway_found = false;
128
129 for (std::string line; std::getline(file, line);) {
130 std::istringstream iss{line};
131
132 std::string iface_name{};
133 iss >> iface_name;
134 if (iface_name != ifa->ifa_name) {
135 continue;
136 }
137
138 iss >> std::hex;
139
140 std::uint32_t dest{0};
141 iss >> dest;
142 if (dest != 0) {
143 // not the default route
144 continue;
145 }
146
147 iss >> gateway;
148
149 std::uint16_t flags{0};
150 iss >> flags;
151
152 // flag RTF_GATEWAY (defined in <linux/route.h>)
153 if ((flags & 0x2) == 0) {
154 continue;
155 }
156
157 gateway_found = true;
158 break;
159 }
160
161 if (!gateway_found) {
162 gateway = 0;
163 }
164 } else {
165 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
166 }
167
168 result.push_back(NetworkInterface{
169 .name{ifa->ifa_name},
170 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
171 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
172 .gateway{in_addr{.s_addr = gateway}}});
173 }
174
175 freeifaddrs(ifaddr);
176
177 return result;
178}
179
180#endif
181
182std::optional<NetworkInterface> GetSelectedNetworkInterface() {
183 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
184 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
185 if (network_interfaces.size() == 0) {
186 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
187 return {};
188 }
189
190 const auto res =
191 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
192 return iface.name == selected_network_interface;
193 });
194
195 if (res != network_interfaces.end()) {
196 return *res;
197 } else {
198 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
199 return {};
200 }
201}
202
203} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..980edb2f5
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,29 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8#include <string>
9#include <vector>
10
11#ifdef _WIN32
12#include <winsock2.h>
13#else
14#include <netinet/in.h>
15#endif
16
17namespace Network {
18
19struct NetworkInterface {
20 std::string name;
21 struct in_addr ip_address;
22 struct in_addr subnet_mask;
23 struct in_addr gateway;
24};
25
26std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
27std::optional<NetworkInterface> GetSelectedNetworkInterface();
28
29} // namespace Network
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index fb8c02a77..14c77f162 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
298 if (IR::IsGeneric(attr)) { 298 if (IR::IsGeneric(attr)) {
299 const u32 index{IR::GenericAttributeIndex(attr)}; 299 const u32 index{IR::GenericAttributeIndex(attr)};
300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; 300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
301 if (!type) { 301 if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
302 // Attribute is disabled 302 // Attribute is disabled or varying component is not written
303 return ctx.Const(element == 3 ? 1.0f : 0.0f); 303 return ctx.Const(element == 3 ? 1.0f : 0.0f);
304 } 304 }
305 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
306 // Varying component is not written
307 return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
308 }
309 const Id generic_id{ctx.input_generics.at(index)}; 305 const Id generic_id{ctx.input_generics.at(index)};
310 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; 306 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
311 const Id value{ctx.OpLoad(type->id, pointer)}; 307 const Id value{ctx.OpLoad(type->id, pointer)};
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index 4c0f9654f..e31ca3544 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -4,11 +4,13 @@
4 4
5#include <catch2/catch.hpp> 5#include <catch2/catch.hpp>
6#include <math.h> 6#include <math.h>
7#include "common/logging/backend.h"
7#include "common/param_package.h" 8#include "common/param_package.h"
8 9
9namespace Common { 10namespace Common {
10 11
11TEST_CASE("ParamPackage", "[common]") { 12TEST_CASE("ParamPackage", "[common]") {
13 Common::Log::DisableLoggingInTests();
12 ParamPackage original{ 14 ParamPackage original{
13 {"abc", "xyz"}, 15 {"abc", "xyz"},
14 {"def", "42"}, 16 {"def", "42"},
diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h
index 7f50ac2f8..d03d480b4 100644
--- a/src/video_core/macro/macro_jit_x64.h
+++ b/src/video_core/macro/macro_jit_x64.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/x64/xbyak_abi.h" 12#include "common/x64/xbyak_abi.h"
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 1a9399455..7994cb859 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
159 return {raw, raw}; 159 return {raw, raw};
160 } else { 160 } else {
161 const Tegra::Texture::TextureHandle handle{raw}; 161 const Tegra::Texture::TextureHandle handle{raw};
162 return {handle.tic_id, via_header_index ? handle.tic_id : handle.tsc_id}; 162 return {handle.tic_id, handle.tsc_id};
163 } 163 }
164} 164}
165 165
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cb4bdcc7e..19ba0dbba 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -102,9 +102,9 @@ add_executable(yuzu
102 configuration/configure_profile_manager.cpp 102 configuration/configure_profile_manager.cpp
103 configuration/configure_profile_manager.h 103 configuration/configure_profile_manager.h
104 configuration/configure_profile_manager.ui 104 configuration/configure_profile_manager.ui
105 configuration/configure_service.cpp 105 configuration/configure_network.cpp
106 configuration/configure_service.h 106 configuration/configure_network.h
107 configuration/configure_service.ui 107 configuration/configure_network.ui
108 configuration/configure_system.cpp 108 configuration/configure_system.cpp
109 configuration/configure_system.h 109 configuration/configure_system.h
110 configuration/configure_system.ui 110 configuration/configure_system.ui
@@ -182,7 +182,14 @@ if (ENABLE_QT_TRANSLATION)
182 # Update source TS file if enabled 182 # Update source TS file if enabled
183 if (GENERATE_QT_TRANSLATION) 183 if (GENERATE_QT_TRANSLATION)
184 get_target_property(SRCS yuzu SOURCES) 184 get_target_property(SRCS yuzu SOURCES)
185 qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts) 185 qt5_create_translation(QM_FILES
186 ${SRCS}
187 ${UIS}
188 ${YUZU_QT_LANGUAGES}/en.ts
189 OPTIONS
190 -source-language en_US
191 -target-language en_US
192 )
186 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) 193 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts)
187 endif() 194 endif()
188 195
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 848801cec..8fc0c5a36 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; 438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; 439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button; 440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
441 initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; 441 initialize_parameters.disable_cancel_button = appear_parameters.disable_cancel_button;
442 442
443 SetKeyboardType(); 443 SetKeyboardType();
444 SetControllerImage(); 444 SetControllerImage();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 380379eb4..377795326 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -692,6 +692,7 @@ void Config::ReadServiceValues() {
692 qt_config->beginGroup(QStringLiteral("Services")); 692 qt_config->beginGroup(QStringLiteral("Services"));
693 ReadBasicSetting(Settings::values.bcat_backend); 693 ReadBasicSetting(Settings::values.bcat_backend);
694 ReadBasicSetting(Settings::values.bcat_boxcat_local); 694 ReadBasicSetting(Settings::values.bcat_boxcat_local);
695 ReadBasicSetting(Settings::values.network_interface);
695 qt_config->endGroup(); 696 qt_config->endGroup();
696} 697}
697 698
@@ -1144,7 +1145,7 @@ void Config::SaveValues() {
1144 SaveDataStorageValues(); 1145 SaveDataStorageValues();
1145 SaveDebuggingValues(); 1146 SaveDebuggingValues();
1146 SaveDisabledAddOnValues(); 1147 SaveDisabledAddOnValues();
1147 SaveServiceValues(); 1148 SaveNetworkValues();
1148 SaveUIValues(); 1149 SaveUIValues();
1149 SaveWebServiceValues(); 1150 SaveWebServiceValues();
1150 SaveMiscellaneousValues(); 1151 SaveMiscellaneousValues();
@@ -1238,11 +1239,12 @@ void Config::SaveDebuggingValues() {
1238 qt_config->endGroup(); 1239 qt_config->endGroup();
1239} 1240}
1240 1241
1241void Config::SaveServiceValues() { 1242void Config::SaveNetworkValues() {
1242 qt_config->beginGroup(QStringLiteral("Services")); 1243 qt_config->beginGroup(QStringLiteral("Services"));
1243 1244
1244 WriteBasicSetting(Settings::values.bcat_backend); 1245 WriteBasicSetting(Settings::values.bcat_backend);
1245 WriteBasicSetting(Settings::values.bcat_boxcat_local); 1246 WriteBasicSetting(Settings::values.bcat_boxcat_local);
1247 WriteBasicSetting(Settings::values.network_interface);
1246 1248
1247 qt_config->endGroup(); 1249 qt_config->endGroup();
1248} 1250}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index c1d7feb9f..9555f4498 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -88,7 +88,7 @@ private:
88 void SaveCoreValues(); 88 void SaveCoreValues();
89 void SaveDataStorageValues(); 89 void SaveDataStorageValues();
90 void SaveDebuggingValues(); 90 void SaveDebuggingValues();
91 void SaveServiceValues(); 91 void SaveNetworkValues();
92 void SaveDisabledAddOnValues(); 92 void SaveDisabledAddOnValues();
93 void SaveMiscellaneousValues(); 93 void SaveMiscellaneousValues();
94 void SavePathValues(); 94 void SavePathValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fca9aed5f..6258dcf20 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -147,12 +147,12 @@
147 <string>Web</string> 147 <string>Web</string>
148 </attribute> 148 </attribute>
149 </widget> 149 </widget>
150 <widget class="ConfigureService" name="serviceTab"> 150 <widget class="ConfigureNetwork" name="networkTab">
151 <property name="accessibleName"> 151 <property name="accessibleName">
152 <string>Services</string> 152 <string>Network</string>
153 </property> 153 </property>
154 <attribute name="title"> 154 <attribute name="title">
155 <string>Services</string> 155 <string>Network</string>
156 </attribute> 156 </attribute>
157 </widget> 157 </widget>
158 </widget> 158 </widget>
@@ -242,9 +242,9 @@
242 <container>1</container> 242 <container>1</container>
243 </customwidget> 243 </customwidget>
244 <customwidget> 244 <customwidget>
245 <class>ConfigureService</class> 245 <class>ConfigureNetwork</class>
246 <extends>QWidget</extends> 246 <extends>QWidget</extends>
247 <header>configuration/configure_service.h</header> 247 <header>configuration/configure_network.h</header>
248 <container>1</container> 248 <container>1</container>
249 </customwidget> 249 </customwidget>
250 <customwidget> 250 <customwidget>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index bc009b6b3..fe4186157 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
67 ui->audioTab->ApplyConfiguration(); 67 ui->audioTab->ApplyConfiguration();
68 ui->debugTab->ApplyConfiguration(); 68 ui->debugTab->ApplyConfiguration();
69 ui->webTab->ApplyConfiguration(); 69 ui->webTab->ApplyConfiguration();
70 ui->serviceTab->ApplyConfiguration(); 70 ui->networkTab->ApplyConfiguration();
71 Core::System::GetInstance().ApplySettings(); 71 Core::System::GetInstance().ApplySettings();
72 Settings::LogSettings(); 72 Settings::LogSettings();
73} 73}
@@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
103void ConfigureDialog::PopulateSelectionList() { 103void ConfigureDialog::PopulateSelectionList() {
104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, 105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}},
107 {tr("CPU"), {ui->cpuTab}}, 107 {tr("CPU"), {ui->cpuTab}},
108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
109 {tr("Audio"), {ui->audioTab}}, 109 {tr("Audio"), {ui->audioTab}},
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index cd633e45f..9c890ed5d 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -647,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
647 // Face buttons 647 // Face buttons
648 p.setPen(colors.outline); 648 p.setPen(colors.outline);
649 button_color = colors.button; 649 button_color = colors.button;
650 DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); 650 DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius);
651 DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); 651 DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius);
652 DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); 652 DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius);
653 DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); 653 DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius);
654 654
655 // Face buttons text 655 // Face buttons text
656 p.setPen(colors.transparent); 656 p.setPen(colors.transparent);
657 p.setBrush(colors.font); 657 p.setBrush(colors.font);
658 DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); 658 DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size);
659 DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); 659 DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size);
660 DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); 660 DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size);
661 DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); 661 DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size);
662 662
663 // D-pad constants 663 // D-pad constants
664 const QPointF dpad_center = center + QPoint(-171, 8); 664 const QPointF dpad_center = center + QPoint(-171, 8);
@@ -669,18 +669,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
669 // D-pad buttons 669 // D-pad buttons
670 p.setPen(colors.outline); 670 p.setPen(colors.outline);
671 button_color = colors.button; 671 button_color = colors.button;
672 DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); 672 DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight],
673 DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); 673 dpad_radius);
674 DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); 674 DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius);
675 DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); 675 DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius);
676 DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft],
677 dpad_radius);
676 678
677 // D-pad arrows 679 // D-pad arrows
678 p.setPen(colors.font2); 680 p.setPen(colors.font2);
679 p.setBrush(colors.font2); 681 p.setBrush(colors.font2);
680 DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); 682 DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size);
681 DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); 683 DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size);
682 DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); 684 DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size);
683 DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); 685 DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
684 686
685 // ZL and ZR buttons 687 // ZL and ZR buttons
686 p.setPen(colors.outline); 688 p.setPen(colors.outline);
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_network.cpp
index 4aa424803..ae22f1018 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -5,9 +5,11 @@
5#include <QGraphicsItem> 5#include <QGraphicsItem>
6#include <QtConcurrent/QtConcurrent> 6#include <QtConcurrent/QtConcurrent>
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h"
8#include "core/hle/service/bcat/backend/boxcat.h" 9#include "core/hle/service/bcat/backend/boxcat.h"
9#include "ui_configure_service.h" 10#include "core/network/network_interface.h"
10#include "yuzu/configuration/configure_service.h" 11#include "ui_configure_network.h"
12#include "yuzu/configuration/configure_network.h"
11 13
12#ifdef YUZU_ENABLE_BOXCAT 14#ifdef YUZU_ENABLE_BOXCAT
13namespace { 15namespace {
@@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
35} // Anonymous namespace 37} // Anonymous namespace
36#endif 38#endif
37 39
38ConfigureService::ConfigureService(QWidget* parent) 40ConfigureNetwork::ConfigureNetwork(QWidget* parent)
39 : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { 41 : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
40 ui->setupUi(this); 42 ui->setupUi(this);
41 43
42 ui->bcat_source->addItem(QStringLiteral("None")); 44 ui->bcat_source->addItem(QStringLiteral("None"));
@@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent)
47 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); 49 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
48#endif 50#endif
49 51
52 ui->network_interface->addItem(tr("None"));
53 for (const auto& iface : Network::GetAvailableNetworkInterfaces()) {
54 ui->network_interface->addItem(QString::fromStdString(iface.name));
55 }
56
50 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 57 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
51 &ConfigureService::OnBCATImplChanged); 58 &ConfigureNetwork::OnBCATImplChanged);
52 59
53 this->SetConfiguration(); 60 this->SetConfiguration();
54} 61}
55 62
56ConfigureService::~ConfigureService() = default; 63ConfigureNetwork::~ConfigureNetwork() = default;
57 64
58void ConfigureService::ApplyConfiguration() { 65void ConfigureNetwork::ApplyConfiguration() {
59 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); 66 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
67 Settings::values.network_interface = ui->network_interface->currentText().toStdString();
60} 68}
61 69
62void ConfigureService::RetranslateUi() { 70void ConfigureNetwork::RetranslateUi() {
63 ui->retranslateUi(this); 71 ui->retranslateUi(this);
64} 72}
65 73
66void ConfigureService::SetConfiguration() { 74void ConfigureNetwork::SetConfiguration() {
75 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
76
67 const int index = 77 const int index =
68 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); 78 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
69 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); 79 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
80
81 const std::string& network_interface = Settings::values.network_interface.GetValue();
82
83 ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
84 ui->network_interface->setEnabled(runtime_lock);
70} 85}
71 86
72std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { 87std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
73#ifdef YUZU_ENABLE_BOXCAT 88#ifdef YUZU_ENABLE_BOXCAT
74 std::optional<std::string> global; 89 std::optional<std::string> global;
75 std::map<std::string, Service::BCAT::EventStatus> map; 90 std::map<std::string, Service::BCAT::EventStatus> map;
@@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
114#endif 129#endif
115} 130}
116 131
117void ConfigureService::OnBCATImplChanged() { 132void ConfigureNetwork::OnBCATImplChanged() {
118#ifdef YUZU_ENABLE_BOXCAT 133#ifdef YUZU_ENABLE_BOXCAT
119 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 134 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
120 ui->bcat_empty_header->setHidden(!boxcat); 135 ui->bcat_empty_header->setHidden(!boxcat);
@@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() {
133#endif 148#endif
134} 149}
135 150
136void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { 151void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
137#ifdef YUZU_ENABLE_BOXCAT 152#ifdef YUZU_ENABLE_BOXCAT
138 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 153 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
139 if (boxcat) { 154 if (boxcat) {
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h
index f5c1b703a..442b68e6b 100644
--- a/src/yuzu/configuration/configure_service.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -9,15 +9,15 @@
9#include <QWidget> 9#include <QWidget>
10 10
11namespace Ui { 11namespace Ui {
12class ConfigureService; 12class ConfigureNetwork;
13} 13}
14 14
15class ConfigureService : public QWidget { 15class ConfigureNetwork : public QWidget {
16 Q_OBJECT 16 Q_OBJECT
17 17
18public: 18public:
19 explicit ConfigureService(QWidget* parent = nullptr); 19 explicit ConfigureNetwork(QWidget* parent = nullptr);
20 ~ConfigureService() override; 20 ~ConfigureNetwork() override;
21 21
22 void ApplyConfiguration(); 22 void ApplyConfiguration();
23 void RetranslateUi(); 23 void RetranslateUi();
@@ -29,6 +29,6 @@ private:
29 void OnBCATImplChanged(); 29 void OnBCATImplChanged();
30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); 30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
31 31
32 std::unique_ptr<Ui::ConfigureService> ui; 32 std::unique_ptr<Ui::ConfigureNetwork> ui;
33 QFutureWatcher<std::pair<QString, QString>> watcher{this}; 33 QFutureWatcher<std::pair<QString, QString>> watcher{this};
34}; 34};
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui
index 9668dd557..5f9b7e97b 100644
--- a/src/yuzu/configuration/configure_service.ui
+++ b/src/yuzu/configuration/configure_network.ui
@@ -1,7 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureService</class> 3 <class>ConfigureNetwork</class>
4 <widget class="QWidget" name="ConfigureService"> 4 <widget class="QWidget" name="ConfigureNetwork">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
@@ -17,21 +17,37 @@
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3"> 18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item> 19 <item>
20 <widget class="QGroupBox" name="groupBox_2">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout_2">
25 <item row="1" column="1">
26 <widget class="QComboBox" name="network_interface"/>
27 </item>
28 <item row="1" column="0">
29 <widget class="QLabel" name="label_4">
30 <property name="text">
31 <string>Network Interface</string>
32 </property>
33 </widget>
34 </item>
35 </layout>
36 </widget>
37 </item>
38 <item>
20 <widget class="QGroupBox" name="groupBox"> 39 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 40 <property name="title">
22 <string>BCAT</string> 41 <string>BCAT</string>
23 </property> 42 </property>
24 <layout class="QGridLayout" name="gridLayout"> 43 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="1" colspan="2"> 44 <item row="3" column="0">
26 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="bcat_empty_header">
27 <property name="maximumSize">
28 <size>
29 <width>260</width>
30 <height>16777215</height>
31 </size>
32 </property>
33 <property name="text"> 46 <property name="text">
34 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> 47 <string/>
48 </property>
49 <property name="alignment">
50 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
35 </property> 51 </property>
36 <property name="wordWrap"> 52 <property name="wordWrap">
37 <bool>true</bool> 53 <bool>true</bool>
@@ -51,11 +67,8 @@
51 </property> 67 </property>
52 </widget> 68 </widget>
53 </item> 69 </item>
54 <item row="3" column="1" colspan="2"> 70 <item row="1" column="1" colspan="2">
55 <widget class="QLabel" name="bcat_empty_label"> 71 <widget class="QLabel" name="label_2">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="maximumSize"> 72 <property name="maximumSize">
60 <size> 73 <size>
61 <width>260</width> 74 <width>260</width>
@@ -63,10 +76,7 @@
63 </size> 76 </size>
64 </property> 77 </property>
65 <property name="text"> 78 <property name="text">
66 <string/> 79 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
67 </property>
68 <property name="alignment">
69 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
70 </property> 80 </property>
71 <property name="wordWrap"> 81 <property name="wordWrap">
72 <bool>true</bool> 82 <bool>true</bool>
@@ -86,8 +96,17 @@
86 <item row="0" column="1" colspan="2"> 96 <item row="0" column="1" colspan="2">
87 <widget class="QComboBox" name="bcat_source"/> 97 <widget class="QComboBox" name="bcat_source"/>
88 </item> 98 </item>
89 <item row="3" column="0"> 99 <item row="3" column="1" colspan="2">
90 <widget class="QLabel" name="bcat_empty_header"> 100 <widget class="QLabel" name="bcat_empty_label">
101 <property name="enabled">
102 <bool>true</bool>
103 </property>
104 <property name="maximumSize">
105 <size>
106 <width>260</width>
107 <height>16777215</height>
108 </size>
109 </property>
91 <property name="text"> 110 <property name="text">
92 <string/> 111 <string/>
93 </property> 112 </property>
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index 22ca1285d..f89ea8ea7 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -21,6 +21,7 @@ void ToggleConsole() {
21 console_shown = UISettings::values.show_console.GetValue(); 21 console_shown = UISettings::values.show_console.GetValue();
22 } 22 }
23 23
24 using namespace Common::Log;
24#if defined(_WIN32) && !defined(_DEBUG) 25#if defined(_WIN32) && !defined(_DEBUG)
25 FILE* temp; 26 FILE* temp;
26 if (UISettings::values.show_console) { 27 if (UISettings::values.show_console) {
@@ -29,24 +30,20 @@ void ToggleConsole() {
29 freopen_s(&temp, "CONIN$", "r", stdin); 30 freopen_s(&temp, "CONIN$", "r", stdin);
30 freopen_s(&temp, "CONOUT$", "w", stdout); 31 freopen_s(&temp, "CONOUT$", "w", stdout);
31 freopen_s(&temp, "CONOUT$", "w", stderr); 32 freopen_s(&temp, "CONOUT$", "w", stderr);
32 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); 33 SetColorConsoleBackendEnabled(true);
33 } 34 }
34 } else { 35 } else {
35 if (FreeConsole()) { 36 if (FreeConsole()) {
36 // In order to close the console, we have to also detach the streams on it. 37 // In order to close the console, we have to also detach the streams on it.
37 // Just redirect them to NUL if there is no console window 38 // Just redirect them to NUL if there is no console window
38 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); 39 SetColorConsoleBackendEnabled(false);
39 freopen_s(&temp, "NUL", "r", stdin); 40 freopen_s(&temp, "NUL", "r", stdin);
40 freopen_s(&temp, "NUL", "w", stdout); 41 freopen_s(&temp, "NUL", "w", stdout);
41 freopen_s(&temp, "NUL", "w", stderr); 42 freopen_s(&temp, "NUL", "w", stderr);
42 } 43 }
43 } 44 }
44#else 45#else
45 if (UISettings::values.show_console) { 46 SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
46 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
47 } else {
48 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
49 }
50#endif 47#endif
51} 48}
52} // namespace Debugger 49} // namespace Debugger
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5940e0cfd..1bae1489f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() {
175 175
176const int GMainWindow::max_recent_files_item; 176const int GMainWindow::max_recent_files_item;
177 177
178static void InitializeLogging() {
179 using namespace Common;
180
181 Log::Filter log_filter;
182 log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
183 Log::SetGlobalFilter(log_filter);
184
185 const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
186 void(FS::CreateDir(log_dir));
187 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
188#ifdef _WIN32
189 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
190#endif
191}
192
193static void RemoveCachedContents() { 178static void RemoveCachedContents() {
194 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); 179 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
195 const auto offline_fonts = cache_dir / "fonts"; 180 const auto offline_fonts = cache_dir / "fonts";
@@ -207,8 +192,6 @@ GMainWindow::GMainWindow()
207 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 192 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
208 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 193 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
209 provider{std::make_unique<FileSys::ManualContentProvider>()} { 194 provider{std::make_unique<FileSys::ManualContentProvider>()} {
210 InitializeLogging();
211
212 LoadTranslation(); 195 LoadTranslation();
213 196
214 setAcceptDrops(true); 197 setAcceptDrops(true);
@@ -3398,6 +3381,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
3398#endif 3381#endif
3399 3382
3400int main(int argc, char* argv[]) { 3383int main(int argc, char* argv[]) {
3384 Common::Log::Initialize();
3401 Common::DetachedTasks detached_tasks; 3385 Common::DetachedTasks detached_tasks;
3402 MicroProfileOnThreadCreate("Frontend"); 3386 MicroProfileOnThreadCreate("Frontend");
3403 SCOPE_EXIT({ MicroProfileShutdown(); }); 3387 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -3437,6 +3421,7 @@ int main(int argc, char* argv[]) {
3437 // generating shaders 3421 // generating shaders
3438 setlocale(LC_ALL, "C"); 3422 setlocale(LC_ALL, "C");
3439 3423
3424 Core::System::InitializeGlobalInstance();
3440 GMainWindow main_window; 3425 GMainWindow main_window;
3441 // After settings have been loaded by GMainWindow, apply the filter 3426 // After settings have been loaded by GMainWindow, apply the filter
3442 main_window.show(); 3427 main_window.show();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c10093820..ba2c993ba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,31 +74,14 @@ static void PrintVersion() {
74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; 74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
75} 75}
76 76
77static void InitializeLogging() {
78 using namespace Common;
79
80 Log::Filter log_filter(Log::Level::Debug);
81 log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
82 Log::SetGlobalFilter(log_filter);
83
84 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
85
86 const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
87 void(FS::CreateDir(log_dir));
88 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
89#ifdef _WIN32
90 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
91#endif
92}
93
94/// Application entry point 77/// Application entry point
95int main(int argc, char** argv) { 78int main(int argc, char** argv) {
79 Common::Log::Initialize();
80 Common::Log::SetColorConsoleBackendEnabled(true);
96 Common::DetachedTasks detached_tasks; 81 Common::DetachedTasks detached_tasks;
97 Config config; 82 Config config;
98 83
99 int option_index = 0; 84 int option_index = 0;
100
101 InitializeLogging();
102#ifdef _WIN32 85#ifdef _WIN32
103 int argc_w; 86 int argc_w;
104 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); 87 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@@ -163,6 +146,7 @@ int main(int argc, char** argv) {
163 return -1; 146 return -1;
164 } 147 }
165 148
149 Core::System::InitializeGlobalInstance();
166 auto& system{Core::System::GetInstance()}; 150 auto& system{Core::System::GetInstance()};
167 InputCommon::InputSubsystem input_subsystem; 151 InputCommon::InputSubsystem input_subsystem;
168 152