summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp36
-rw-r--r--src/core/network/network.cpp43
-rw-r--r--src/core/network/network.h5
-rw-r--r--src/core/network/network_interface.cpp113
-rw-r--r--src/core/network/network_interface.h25
-rw-r--r--src/yuzu/CMakeLists.txt6
-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_network.cpp (renamed from src/yuzu/configuration/configure_service.cpp)38
-rw-r--r--src/yuzu/configuration/configure_network.h (renamed from src/yuzu/configuration/configure_service.h)12
-rw-r--r--src/yuzu/configuration/configure_network.ui (renamed from src/yuzu/configuration/configure_service.ui)63
15 files changed, 278 insertions, 90 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index a88ee045d..e1ae72c7a 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -433,9 +433,10 @@ struct Values {
433 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 433 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
434 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 434 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
435 435
436 // Services 436 // Network
437 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; 437 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
438 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; 438 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
439 BasicSetting<std::string> network_interface{std::string(), "network_interface"};
439 440
440 // WebService 441 // WebService
441 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 442 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
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/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 5ef574d20..168053d80 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -179,10 +179,10 @@ private:
179 IPC::ResponseBuilder rb{ctx, 3}; 179 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 180 rb.Push(ResultSuccess);
181 181
182 if (Settings::values.bcat_backend.GetValue() == "none") { 182 if (Network::GetHostIPv4Address().has_value()) {
183 rb.PushEnum(RequestState::NotSubmitted);
184 } else {
185 rb.PushEnum(RequestState::Connected); 183 rb.PushEnum(RequestState::Connected);
184 } else {
185 rb.PushEnum(RequestState::NotSubmitted);
186 } 186 }
187 } 187 }
188 188
@@ -322,12 +322,15 @@ private:
322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
323 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 323 LOG_WARNING(Service_NIFM, "(STUBBED) called");
324 324
325 const auto [ipv4, error] = Network::GetHostIPv4Address(); 325 auto ipv4 = Network::GetHostIPv4Address();
326 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); 326 if (!ipv4) {
327 LOG_CRITICAL(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
328 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
329 }
327 330
328 IPC::ResponseBuilder rb{ctx, 3}; 331 IPC::ResponseBuilder rb{ctx, 3};
329 rb.Push(ResultSuccess); 332 rb.Push(ResultSuccess);
330 rb.PushRaw(ipv4); 333 rb.PushRaw(ipv4.value());
331 } 334 }
332 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 335 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
333 LOG_DEBUG(Service_NIFM, "called"); 336 LOG_DEBUG(Service_NIFM, "called");
@@ -354,13 +357,16 @@ private:
354 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 357 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
355 "IpConfigInfo has incorrect size."); 358 "IpConfigInfo has incorrect size.");
356 359
357 const auto [ipv4, error] = Network::GetHostIPv4Address(); 360 auto ipv4 = Network::GetHostIPv4Address();
358 ASSERT_MSG(error == Network::Errno::SUCCESS, "Couldn't get host IPv4 address"); 361 if (!ipv4) {
362 LOG_CRITICAL(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
363 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
364 }
359 365
360 const IpConfigInfo ip_config_info{ 366 const IpConfigInfo ip_config_info{
361 .ip_address_setting{ 367 .ip_address_setting{
362 .is_automatic{true}, 368 .is_automatic{true},
363 .current_address{ipv4}, 369 .current_address{ipv4.value()},
364 .subnet_mask{255, 255, 255, 0}, 370 .subnet_mask{255, 255, 255, 0},
365 .gateway{192, 168, 1, 1}, 371 .gateway{192, 168, 1, 1},
366 }, 372 },
@@ -387,10 +393,10 @@ private:
387 393
388 IPC::ResponseBuilder rb{ctx, 3}; 394 IPC::ResponseBuilder rb{ctx, 3};
389 rb.Push(ResultSuccess); 395 rb.Push(ResultSuccess);
390 if (Settings::values.bcat_backend.GetValue() == "none") { 396 if (Network::GetHostIPv4Address().has_value()) {
391 rb.Push<u8>(0);
392 } else {
393 rb.Push<u8>(1); 397 rb.Push<u8>(1);
398 } else {
399 rb.Push<u8>(0);
394 } 400 }
395 } 401 }
396 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 402 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -398,10 +404,10 @@ private:
398 404
399 IPC::ResponseBuilder rb{ctx, 3}; 405 IPC::ResponseBuilder rb{ctx, 3};
400 rb.Push(ResultSuccess); 406 rb.Push(ResultSuccess);
401 if (Settings::values.bcat_backend.GetValue() == "none") { 407 if (Network::GetHostIPv4Address().has_value()) {
402 rb.Push<u8>(0);
403 } else {
404 rb.Push<u8>(1); 408 rb.Push<u8>(1);
409 } else {
410 rb.Push<u8>(0);
405 } 411 }
406 } 412 }
407}; 413};
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 7b038041e..67ecf57bd 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,8 +10,8 @@
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 <errno.h> 16#include <errno.h>
17#include <fcntl.h> 17#include <fcntl.h>
@@ -19,6 +19,7 @@
19#include <netinet/in.h> 19#include <netinet/in.h>
20#include <poll.h> 20#include <poll.h>
21#include <sys/socket.h> 21#include <sys/socket.h>
22#include <arpa/inet.h>
22#include <unistd.h> 23#include <unistd.h>
23#else 24#else
24#error "Unimplemented platform" 25#error "Unimplemented platform"
@@ -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 {
@@ -357,27 +360,27 @@ NetworkInstance::~NetworkInstance() {
357 Finalize(); 360 Finalize();
358} 361}
359 362
360std::pair<IPv4Address, Errno> GetHostIPv4Address() { 363std::optional<IPv4Address> GetHostIPv4Address() {
361 std::array<char, 256> name{}; 364 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
362 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 365 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
363 return {IPv4Address{}, GetAndLogLastError()}; 366 ASSERT_MSG(network_interfaces.size() > 0, "GetAvailableNetworkInterfaces returned no interfaces");
364 }
365 367
366 hostent* const ent = gethostbyname(name.data());
367 if (!ent) {
368 return {IPv4Address{}, GetAndLogLastError()};
369 }
370 if (ent->h_addr_list == nullptr) {
371 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
372 return {IPv4Address{}, Errno::SUCCESS};
373 }
374 if (ent->h_length != sizeof(in_addr)) {
375 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
376 }
377 368
378 in_addr addr; 369 const auto res = std::ranges::find_if(network_interfaces,
379 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); 370 [&selected_network_interface](const auto& interface) {
380 return {TranslateIPv4(addr), Errno::SUCCESS}; 371 return interface.name == selected_network_interface;
372 });
373
374 if (res != network_interfaces.end()) {
375 char ip_addr[16];
376 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
377 LOG_INFO(Network, "IP address: {}", ip_addr);
378
379 return TranslateIPv4(res->ip_address);
380 } else {
381 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
382 return {};
383 }
381} 384}
382 385
383std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 386std::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..cfa68d478 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,6 +5,7 @@
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"
@@ -93,7 +94,7 @@ public:
93}; 94};
94 95
95/// @brief Returns host's IPv4 address 96/// @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 97/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
97std::pair<IPv4Address, Errno> GetHostIPv4Address(); 98std::optional<IPv4Address> GetHostIPv4Address();
98 99
99} // namespace Network 100} // namespace Network
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..bba4c8b26
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,113 @@
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 <vector>
6
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10#include "core/network/network_interface.h"
11
12#ifdef _WIN32
13#include <iphlpapi.h>
14#else
15#include <ifaddrs.h>
16#include <net/if.h>
17#include <cerrno>
18#endif
19
20namespace Network {
21
22#ifdef _WIN32
23
24std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
25 std::vector<NetworkInterface> result;
26
27 std::vector<u8> adapter_addresses_raw;
28 auto adapter_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_addresses_raw.data());
29 DWORD ret = ERROR_BUFFER_OVERFLOW;
30 DWORD buf_size = 0;
31
32 // retry up to 5 times
33 for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
34 ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
35 nullptr, adapter_addresses, &buf_size);
36
37 if (ret == ERROR_BUFFER_OVERFLOW) {
38 adapter_addresses_raw.resize(buf_size);
39 adapter_addresses =
40 reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_addresses_raw.data());
41 } else {
42 break;
43 }
44 }
45
46 if (ret == NO_ERROR) {
47 for (auto current_address = adapter_addresses; current_address != nullptr;
48 current_address = current_address->Next) {
49 if (current_address->FirstUnicastAddress == nullptr ||
50 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
51 continue;
52 }
53
54 if (current_address->OperStatus != IfOperStatusUp) {
55 continue;
56 }
57
58 const auto ip_addr = std::bit_cast<struct sockaddr_in>(
59 *current_address->FirstUnicastAddress->Address.lpSockaddr)
60 .sin_addr;
61
62 result.push_back(NetworkInterface{
63 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
64 .ip_address{ip_addr}
65 });
66 }
67 } else {
68 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
69 }
70
71 return result;
72}
73
74#else
75
76std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
77 std::vector<NetworkInterface> result;
78
79 struct ifaddrs* ifaddr = nullptr;
80
81 if (getifaddrs(&ifaddr) != 0) {
82 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
83 std::strerror(errno));
84 return result;
85 }
86
87 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
88 if (ifa->ifa_addr == nullptr) {
89 continue;
90 }
91
92 if (ifa->ifa_addr->sa_family != AF_INET) {
93 continue;
94 }
95
96 if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_flags & IFF_LOOPBACK) {
97 continue;
98 }
99
100 result.push_back(NetworkInterface{
101 .name{ifa->ifa_name},
102 .ip_address{std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}
103 });
104 }
105
106 freeifaddrs(ifaddr);
107
108 return result;
109}
110
111#endif
112
113} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..d7184e14a
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,25 @@
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 <string>
8#include <vector>
9
10#ifdef _WIN32
11#include <winsock2.h>
12#else
13#include <netinet/in.h>
14#endif
15
16namespace Network {
17
18struct NetworkInterface {
19 std::string name;
20 struct in_addr ip_address;
21};
22
23std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
24
25} // namespace Network
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cb4bdcc7e..cf68a95b5 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
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_service.cpp b/src/yuzu/configuration/configure_network.cpp
index 4aa424803..9787d4f1b 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -5,9 +5,10 @@
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 "ui_configure_network.h"
10#include "yuzu/configuration/configure_service.h" 11#include "yuzu/configuration/configure_network.h"
11 12
12#ifdef YUZU_ENABLE_BOXCAT 13#ifdef YUZU_ENABLE_BOXCAT
13namespace { 14namespace {
@@ -35,8 +36,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
35} // Anonymous namespace 36} // Anonymous namespace
36#endif 37#endif
37 38
38ConfigureService::ConfigureService(QWidget* parent) 39ConfigureNetwork::ConfigureNetwork(QWidget* parent)
39 : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { 40 : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
40 ui->setupUi(this); 41 ui->setupUi(this);
41 42
42 ui->bcat_source->addItem(QStringLiteral("None")); 43 ui->bcat_source->addItem(QStringLiteral("None"));
@@ -47,29 +48,42 @@ ConfigureService::ConfigureService(QWidget* parent)
47 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); 48 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
48#endif 49#endif
49 50
51 ui->network_interface->addItem(QStringLiteral("None"));
52 for (const auto& interface : Network::GetAvailableNetworkInterfaces()) {
53 ui->network_interface->addItem(QString::fromStdString(interface.name));
54 }
55
50 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 56 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
51 &ConfigureService::OnBCATImplChanged); 57 &ConfigureNetwork::OnBCATImplChanged);
52 58
53 this->SetConfiguration(); 59 this->SetConfiguration();
54} 60}
55 61
56ConfigureService::~ConfigureService() = default; 62ConfigureNetwork::~ConfigureNetwork() = default;
57 63
58void ConfigureService::ApplyConfiguration() { 64void ConfigureNetwork::ApplyConfiguration() {
59 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); 65 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
66 Settings::values.network_interface = ui->network_interface->currentText().toStdString();
60} 67}
61 68
62void ConfigureService::RetranslateUi() { 69void ConfigureNetwork::RetranslateUi() {
63 ui->retranslateUi(this); 70 ui->retranslateUi(this);
64} 71}
65 72
66void ConfigureService::SetConfiguration() { 73void ConfigureNetwork::SetConfiguration() {
74 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
75
67 const int index = 76 const int index =
68 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); 77 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
69 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); 78 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
79
80 const std::string& network_interface = Settings::values.network_interface.GetValue();
81
82 ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
83 ui->network_interface->setEnabled(runtime_lock);
70} 84}
71 85
72std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { 86std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
73#ifdef YUZU_ENABLE_BOXCAT 87#ifdef YUZU_ENABLE_BOXCAT
74 std::optional<std::string> global; 88 std::optional<std::string> global;
75 std::map<std::string, Service::BCAT::EventStatus> map; 89 std::map<std::string, Service::BCAT::EventStatus> map;
@@ -114,7 +128,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
114#endif 128#endif
115} 129}
116 130
117void ConfigureService::OnBCATImplChanged() { 131void ConfigureNetwork::OnBCATImplChanged() {
118#ifdef YUZU_ENABLE_BOXCAT 132#ifdef YUZU_ENABLE_BOXCAT
119 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 133 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
120 ui->bcat_empty_header->setHidden(!boxcat); 134 ui->bcat_empty_header->setHidden(!boxcat);
@@ -133,7 +147,7 @@ void ConfigureService::OnBCATImplChanged() {
133#endif 147#endif
134} 148}
135 149
136void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { 150void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
137#ifdef YUZU_ENABLE_BOXCAT 151#ifdef YUZU_ENABLE_BOXCAT
138 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 152 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
139 if (boxcat) { 153 if (boxcat) {
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h
index f5c1b703a..d4ef33fbf 100644
--- a/src/yuzu/configuration/configure_service.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -8,16 +8,18 @@
8#include <QFutureWatcher> 8#include <QFutureWatcher>
9#include <QWidget> 9#include <QWidget>
10 10
11#include "core/network/network_interface.h"
12
11namespace Ui { 13namespace Ui {
12class ConfigureService; 14class ConfigureNetwork;
13} 15}
14 16
15class ConfigureService : public QWidget { 17class ConfigureNetwork : public QWidget {
16 Q_OBJECT 18 Q_OBJECT
17 19
18public: 20public:
19 explicit ConfigureService(QWidget* parent = nullptr); 21 explicit ConfigureNetwork(QWidget* parent = nullptr);
20 ~ConfigureService() override; 22 ~ConfigureNetwork() override;
21 23
22 void ApplyConfiguration(); 24 void ApplyConfiguration();
23 void RetranslateUi(); 25 void RetranslateUi();
@@ -29,6 +31,6 @@ private:
29 void OnBCATImplChanged(); 31 void OnBCATImplChanged();
30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); 32 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
31 33
32 std::unique_ptr<Ui::ConfigureService> ui; 34 std::unique_ptr<Ui::ConfigureNetwork> ui;
33 QFutureWatcher<std::pair<QString, QString>> watcher{this}; 35 QFutureWatcher<std::pair<QString, QString>> watcher{this};
34}; 36};
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>