early-access version 1992

This commit is contained in:
pineappleEA 2021-08-13 07:06:50 +02:00
parent 792cb23ddd
commit df8f235e87
18 changed files with 552 additions and 56 deletions

View file

@ -768,7 +768,7 @@ if (APPLE)
elseif (WIN32) elseif (WIN32)
# WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
set(PLATFORM_LIBRARIES winmm ws2_32) set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
if (MINGW) if (MINGW)
# PSAPI is the Process Status API # PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1991. This is the source code for early-access 1992.
## Legal Notice ## Legal Notice

View file

@ -569,9 +569,10 @@ struct Values {
BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
// Services // Network
BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
BasicSetting<std::string> network_interface{std::string(), "network_interface"};
// WebService // WebService
BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};

View file

@ -636,6 +636,8 @@ add_library(core STATIC
memory.h memory.h
network/network.cpp network/network.cpp
network/network.h network/network.h
network/network_interface.cpp
network/network_interface.h
network/sockets.h network/sockets.h
perf_stats.cpp perf_stats.cpp
perf_stats.h perf_stats.h

View file

@ -179,10 +179,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") { if (Network::GetHostIPv4Address().has_value()) {
rb.PushEnum(RequestState::NotSubmitted);
} else {
rb.PushEnum(RequestState::Connected); rb.PushEnum(RequestState::Connected);
} else {
rb.PushEnum(RequestState::NotSubmitted);
} }
} }
@ -322,12 +322,15 @@ private:
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called"); LOG_WARNING(Service_NIFM, "(STUBBED) called");
const auto [ipv4, error] = Network::GetHostIPv4Address(); auto ipv4 = Network::GetHostIPv4Address();
UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw(ipv4); rb.PushRaw(*ipv4);
} }
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called"); LOG_DEBUG(Service_NIFM, "called");
@ -354,10 +357,16 @@ private:
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size."); "IpConfigInfo has incorrect size.");
auto ipv4 = Network::GetHostIPv4Address();
if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
const IpConfigInfo ip_config_info{ const IpConfigInfo ip_config_info{
.ip_address_setting{ .ip_address_setting{
.is_automatic{true}, .is_automatic{true},
.current_address{192, 168, 1, 100}, .current_address{*ipv4},
.subnet_mask{255, 255, 255, 0}, .subnet_mask{255, 255, 255, 0},
.gateway{192, 168, 1, 1}, .gateway{192, 168, 1, 1},
}, },
@ -384,10 +393,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") { if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(0);
} else {
rb.Push<u8>(1); rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
} }
} }
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@ -395,10 +404,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") { if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(0);
} else {
rb.Push<u8>(1); rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
} }
} }
}; };

View file

@ -10,9 +10,10 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#ifdef _WIN32 #ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h>
#elif YUZU_UNIX #elif YUZU_UNIX
#include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <netdb.h> #include <netdb.h>
@ -27,7 +28,9 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h"
#include "core/network/network.h" #include "core/network/network.h"
#include "core/network/network_interface.h"
#include "core/network/sockets.h" #include "core/network/sockets.h"
namespace Network { namespace Network {
@ -182,7 +185,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
} }
bool EnableNonBlock(int fd, bool enable) { bool EnableNonBlock(int fd, bool enable) {
int flags = fcntl(fd, F_GETFD); int flags = fcntl(fd, F_GETFL);
if (flags == -1) { if (flags == -1) {
return false; return false;
} }
@ -191,7 +194,7 @@ bool EnableNonBlock(int fd, bool enable) {
} else { } else {
flags &= ~O_NONBLOCK; flags &= ~O_NONBLOCK;
} }
return fcntl(fd, F_SETFD, flags) == 0; return fcntl(fd, F_SETFL, flags) == 0;
} }
Errno TranslateNativeError(int e) { Errno TranslateNativeError(int e) {
@ -227,8 +230,12 @@ Errno GetAndLogLastError() {
#else #else
int e = errno; int e = errno;
#endif #endif
const Errno err = TranslateNativeError(e);
if (err == Errno::AGAIN) {
return err;
}
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
return TranslateNativeError(e); return err;
} }
int TranslateDomain(Domain domain) { int TranslateDomain(Domain domain) {
@ -353,27 +360,27 @@ NetworkInstance::~NetworkInstance() {
Finalize(); Finalize();
} }
std::pair<IPv4Address, Errno> GetHostIPv4Address() { std::optional<IPv4Address> GetHostIPv4Address() {
std::array<char, 256> name{}; const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
return {IPv4Address{}, GetAndLogLastError()}; ASSERT_MSG(network_interfaces.size() > 0,
} "GetAvailableNetworkInterfaces returned no interfaces");
hostent* const ent = gethostbyname(name.data()); const auto res = std::ranges::find_if(network_interfaces,
if (!ent) { [&selected_network_interface](const auto& interface) {
return {IPv4Address{}, GetAndLogLastError()}; return interface.name == selected_network_interface;
} });
if (ent->h_addr_list == nullptr) {
UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
return {IPv4Address{}, Errno::SUCCESS};
}
if (ent->h_length != sizeof(in_addr)) {
UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
}
in_addr addr; if (res != network_interfaces.end()) {
std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); char ip_addr[16] = {};
return {TranslateIPv4(addr), Errno::SUCCESS}; ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
LOG_INFO(Network, "IP address: {}", ip_addr);
return TranslateIPv4(res->ip_address);
} else {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return {};
}
} }
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <optional>
#include <utility> #include <utility>
#include "common/common_funcs.h" #include "common/common_funcs.h"
@ -93,7 +94,7 @@ public:
}; };
/// @brief Returns host's IPv4 address /// @brief Returns host's IPv4 address
/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
std::pair<IPv4Address, Errno> GetHostIPv4Address(); std::optional<IPv4Address> GetHostIPv4Address();
} // namespace Network } // namespace Network

View file

@ -0,0 +1,113 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>
#else
#include <cerrno>
#include <ifaddrs.h>
#include <net/if.h>
#endif
namespace Network {
#ifdef _WIN32
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<u8> adapter_addresses_raw;
auto adapter_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_addresses_raw.data());
DWORD ret = ERROR_BUFFER_OVERFLOW;
DWORD buf_size = 0;
// retry up to 5 times
for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
nullptr, adapter_addresses, &buf_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
adapter_addresses_raw.resize(buf_size);
adapter_addresses =
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_addresses_raw.data());
} else {
break;
}
}
if (ret == NO_ERROR) {
std::vector<NetworkInterface> result;
for (auto current_address = adapter_addresses; current_address != nullptr;
current_address = current_address->Next) {
if (current_address->FirstUnicastAddress == nullptr ||
current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
continue;
}
if (current_address->OperStatus != IfOperStatusUp) {
continue;
}
const auto ip_addr = Common::BitCast<struct sockaddr_in>(
*current_address->FirstUnicastAddress->Address.lpSockaddr)
.sin_addr;
result.push_back(NetworkInterface{
.name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
.ip_address{ip_addr}});
}
return result;
} else {
LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
return {};
}
}
#else
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> result;
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
std::strerror(errno));
return result;
}
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) {
continue;
}
if (ifa->ifa_addr->sa_family != AF_INET) {
continue;
}
if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
continue;
}
result.push_back(NetworkInterface{
.name{ifa->ifa_name},
.ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}});
}
freeifaddrs(ifaddr);
return result;
}
#endif
} // namespace Network

View file

@ -0,0 +1,25 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <vector>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
namespace Network {
struct NetworkInterface {
std::string name;
struct in_addr ip_address;
};
std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
} // namespace Network

View file

@ -95,8 +95,8 @@ const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
// TODO (ameerj): Find a way to compute this number, it seems to differ depending on the stream // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
writer.WriteUe(14); // Max number of reference frames writer.WriteUe(6); // Max number of reference frames
writer.WriteBit(false); writer.WriteBit(false);
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1); writer.WriteUe(pic_height - 1);

View file

@ -102,9 +102,9 @@ add_executable(yuzu
configuration/configure_profile_manager.cpp configuration/configure_profile_manager.cpp
configuration/configure_profile_manager.h configuration/configure_profile_manager.h
configuration/configure_profile_manager.ui configuration/configure_profile_manager.ui
configuration/configure_service.cpp configuration/configure_network.cpp
configuration/configure_service.h configuration/configure_network.h
configuration/configure_service.ui configuration/configure_network.ui
configuration/configure_system.cpp configuration/configure_system.cpp
configuration/configure_system.h configuration/configure_system.h
configuration/configure_system.ui configuration/configure_system.ui

View file

@ -707,6 +707,7 @@ void Config::ReadServiceValues() {
qt_config->beginGroup(QStringLiteral("Services")); qt_config->beginGroup(QStringLiteral("Services"));
ReadBasicSetting(Settings::values.bcat_backend); ReadBasicSetting(Settings::values.bcat_backend);
ReadBasicSetting(Settings::values.bcat_boxcat_local); ReadBasicSetting(Settings::values.bcat_boxcat_local);
ReadBasicSetting(Settings::values.network_interface);
qt_config->endGroup(); qt_config->endGroup();
} }
@ -1159,7 +1160,7 @@ void Config::SaveValues() {
SaveDataStorageValues(); SaveDataStorageValues();
SaveDebuggingValues(); SaveDebuggingValues();
SaveDisabledAddOnValues(); SaveDisabledAddOnValues();
SaveServiceValues(); SaveNetworkValues();
SaveUIValues(); SaveUIValues();
SaveWebServiceValues(); SaveWebServiceValues();
SaveMiscellaneousValues(); SaveMiscellaneousValues();
@ -1262,11 +1263,12 @@ void Config::SaveDebuggingValues() {
qt_config->endGroup(); qt_config->endGroup();
} }
void Config::SaveServiceValues() { void Config::SaveNetworkValues() {
qt_config->beginGroup(QStringLiteral("Services")); qt_config->beginGroup(QStringLiteral("Services"));
WriteBasicSetting(Settings::values.bcat_backend); WriteBasicSetting(Settings::values.bcat_backend);
WriteBasicSetting(Settings::values.bcat_boxcat_local); WriteBasicSetting(Settings::values.bcat_boxcat_local);
WriteBasicSetting(Settings::values.network_interface);
qt_config->endGroup(); qt_config->endGroup();
} }

View file

@ -88,7 +88,7 @@ private:
void SaveCoreValues(); void SaveCoreValues();
void SaveDataStorageValues(); void SaveDataStorageValues();
void SaveDebuggingValues(); void SaveDebuggingValues();
void SaveServiceValues(); void SaveNetworkValues();
void SaveDisabledAddOnValues(); void SaveDisabledAddOnValues();
void SaveMiscellaneousValues(); void SaveMiscellaneousValues();
void SavePathValues(); void SavePathValues();

View file

@ -147,12 +147,12 @@
<string>Web</string> <string>Web</string>
</attribute> </attribute>
</widget> </widget>
<widget class="ConfigureService" name="serviceTab"> <widget class="ConfigureNetwork" name="networkTab">
<property name="accessibleName"> <property name="accessibleName">
<string>Services</string> <string>Network</string>
</property> </property>
<attribute name="title"> <attribute name="title">
<string>Services</string> <string>Network</string>
</attribute> </attribute>
</widget> </widget>
</widget> </widget>
@ -242,9 +242,9 @@
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>ConfigureService</class> <class>ConfigureNetwork</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>configuration/configure_service.h</header> <header>configuration/configure_network.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget> <customwidget>

View file

@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
ui->audioTab->ApplyConfiguration(); ui->audioTab->ApplyConfiguration();
ui->debugTab->ApplyConfiguration(); ui->debugTab->ApplyConfiguration();
ui->webTab->ApplyConfiguration(); ui->webTab->ApplyConfiguration();
ui->serviceTab->ApplyConfiguration(); ui->networkTab->ApplyConfiguration();
Core::System::GetInstance().ApplySettings(); Core::System::GetInstance().ApplySettings();
Settings::LogSettings(); Settings::LogSettings();
} }
@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() { void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
{{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab}}, {tr("CPU"), {ui->cpuTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}}, {tr("Audio"), {ui->audioTab}},

View file

@ -0,0 +1,159 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QGraphicsItem>
#include <QtConcurrent/QtConcurrent>
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/service/bcat/backend/boxcat.h"
#include "core/network/network_interface.h"
#include "ui_configure_network.h"
#include "yuzu/configuration/configure_network.h"
#ifdef YUZU_ENABLE_BOXCAT
namespace {
QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
QString out;
if (status.header.has_value()) {
out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.header));
}
if (status.events.size() == 1) {
out += QStringLiteral("%1<br>").arg(QString::fromStdString(status.events.front()));
} else {
for (const auto& event : status.events) {
out += QStringLiteral("- %1<br>").arg(QString::fromStdString(event));
}
}
if (status.footer.has_value()) {
out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.footer));
}
return out;
}
} // Anonymous namespace
#endif
ConfigureNetwork::ConfigureNetwork(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
ui->setupUi(this);
ui->bcat_source->addItem(QStringLiteral("None"));
ui->bcat_empty_label->setHidden(true);
ui->bcat_empty_header->setHidden(true);
#ifdef YUZU_ENABLE_BOXCAT
ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
#endif
ui->network_interface->addItem(tr("None"));
for (const auto& interface : Network::GetAvailableNetworkInterfaces()) {
ui->network_interface->addItem(QString::fromStdString(interface.name));
}
connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureNetwork::OnBCATImplChanged);
this->SetConfiguration();
}
ConfigureNetwork::~ConfigureNetwork() = default;
void ConfigureNetwork::ApplyConfiguration() {
Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
Settings::values.network_interface = ui->network_interface->currentText().toStdString();
}
void ConfigureNetwork::RetranslateUi() {
ui->retranslateUi(this);
}
void ConfigureNetwork::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
const int index =
ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
const std::string& network_interface = Settings::values.network_interface.GetValue();
ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
ui->network_interface->setEnabled(runtime_lock);
}
std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
#ifdef YUZU_ENABLE_BOXCAT
std::optional<std::string> global;
std::map<std::string, Service::BCAT::EventStatus> map;
const auto res = Service::BCAT::Boxcat::GetStatus(global, map);
switch (res) {
case Service::BCAT::Boxcat::StatusResult::Success:
break;
case Service::BCAT::Boxcat::StatusResult::Offline:
return {QString{},
tr("The boxcat service is offline or you are not connected to the internet.")};
case Service::BCAT::Boxcat::StatusResult::ParseError:
return {QString{},
tr("There was an error while processing the boxcat event data. Contact the yuzu "
"developers.")};
case Service::BCAT::Boxcat::StatusResult::BadClientVersion:
return {QString{},
tr("The version of yuzu you are using is either too new or too old for the server. "
"Try updating to the latest official release of yuzu.")};
}
if (map.empty()) {
return {QStringLiteral("Current Boxcat Events"),
tr("There are currently no events on boxcat.")};
}
QString out;
if (global.has_value()) {
out += QStringLiteral("%1<br>").arg(QString::fromStdString(*global));
}
for (const auto& [key, value] : map) {
out += QStringLiteral("%1<b>%2</b><br>%3")
.arg(out.isEmpty() ? QString{} : QStringLiteral("<br>"))
.arg(QString::fromStdString(key))
.arg(FormatEventStatusString(value));
}
return {tr("Current Boxcat Events"), std::move(out)};
#else
return {tr("Current Boxcat Events"), tr("There are currently no events on boxcat.")};
#endif
}
void ConfigureNetwork::OnBCATImplChanged() {
#ifdef YUZU_ENABLE_BOXCAT
const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
ui->bcat_empty_header->setHidden(!boxcat);
ui->bcat_empty_label->setHidden(!boxcat);
ui->bcat_empty_header->setText(QString{});
ui->bcat_empty_label->setText(tr("Yuzu is retrieving the latest boxcat status..."));
if (!boxcat)
return;
const auto future = QtConcurrent::run([this] { return BCATDownloadEvents(); });
watcher.setFuture(future);
connect(&watcher, &QFutureWatcher<std::pair<QString, QString>>::finished, this,
[this] { OnUpdateBCATEmptyLabel(watcher.result()); });
#endif
}
void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
#ifdef YUZU_ENABLE_BOXCAT
const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
if (boxcat) {
ui->bcat_empty_header->setText(string.first);
ui->bcat_empty_label->setText(string.second);
}
#endif
}

View file

@ -0,0 +1,34 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QFutureWatcher>
#include <QWidget>
namespace Ui {
class ConfigureNetwork;
}
class ConfigureNetwork : public QWidget {
Q_OBJECT
public:
explicit ConfigureNetwork(QWidget* parent = nullptr);
~ConfigureNetwork() override;
void ApplyConfiguration();
void RetranslateUi();
private:
void SetConfiguration();
std::pair<QString, QString> BCATDownloadEvents();
void OnBCATImplChanged();
void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
std::unique_ptr<Ui::ConfigureNetwork> ui;
QFutureWatcher<std::pair<QString, QString>> watcher{this};
};

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureNetwork</class>
<widget class="QWidget" name="ConfigureNetwork">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>433</width>
<height>561</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QComboBox" name="network_interface"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Network Interface</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>BCAT</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QLabel" name="bcat_empty_header">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>BCAT Backend</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>260</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="bcat_source"/>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="bcat_empty_label">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>260</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>