artic base: Implement DLC support and other fixes

- Artic Base: Implement DLC support and other fixes
- Fix per game settings not working with artic loader
- Fix compilation error
This commit is contained in:
PabloMK7 2024-07-10 00:22:34 +01:00 committed by OpenSauce
parent 10d93555c3
commit 550e06e1c9
16 changed files with 992 additions and 236 deletions

View File

@ -10,6 +10,7 @@
#include "common/archives.h" #include "common/archives.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "core/file_sys/archive_artic.h"
#include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/file_sys/savedata_archive.h" #include "core/file_sys/savedata_archive.h"
@ -52,24 +53,45 @@ Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path) ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
: base_path(GetSystemSaveDataContainerPath(nand_path)) {} : base_path(GetSystemSaveDataContainerPath(nand_path)) {}
static bool AllowArticSystemSaveData(const Path& path) {
constexpr u32 APP_SYSTEM_SAVE_DATA_MASK = 0x00020000;
if (path.GetType() == FileSys::LowPathType::Binary) {
std::vector<u8> path_data = path.AsBinary();
return path_data.size() == 8 &&
(*reinterpret_cast<u32*>(path_data.data() + 4) & APP_SYSTEM_SAVE_DATA_MASK) != 0;
}
return false;
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path, ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path,
u64 program_id) { u64 program_id) {
std::string fullpath = GetSystemSaveDataPath(base_path, path); if (IsUsingArtic() && AllowArticSystemSaveData(path)) {
if (!FileUtil::Exists(fullpath)) { EnsureCacheCreated();
// TODO(Subv): Check error code, this one is probably wrong return ArticArchive::Open(artic_client, Service::FS::ArchiveIdCode::SystemSaveData, path,
return ResultNotFound; Core::PerfStats::PerfArticEventBits::ARTIC_SYSTEM_SAVE_DATA,
*this, false);
} else {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
if (!FileUtil::Exists(fullpath)) {
// TODO(Subv): Check error code, this one is probably wrong
return ResultNotFound;
}
return std::make_unique<SaveDataArchive>(fullpath);
} }
return std::make_unique<SaveDataArchive>(fullpath);
} }
Result ArchiveFactory_SystemSaveData::Format(const Path& path, Result ArchiveFactory_SystemSaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info, const FileSys::ArchiveFormatInfo& format_info,
u64 program_id, u32 directory_buckets, u64 program_id, u32 directory_buckets,
u32 file_buckets) { u32 file_buckets) {
std::string fullpath = GetSystemSaveDataPath(base_path, path); const std::vector<u8> vec_data = path.AsBinary();
FileUtil::DeleteDirRecursively(fullpath); u32 save_low;
FileUtil::CreateFullPath(fullpath); u32 save_high;
return ResultSuccess; std::memcpy(&save_low, &vec_data[4], sizeof(u32));
std::memcpy(&save_high, &vec_data[0], sizeof(u32));
return FormatAsSysData(save_high, save_low, format_info.total_size, 0x1000,
format_info.number_directories, format_info.number_files,
directory_buckets, file_buckets, format_info.duplicate_data);
} }
ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path, ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path,
@ -79,4 +101,45 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const
return ResultUnknown; return ResultUnknown;
} }
Result ArchiveFactory_SystemSaveData::FormatAsSysData(u32 high, u32 low, u32 total_size,
u32 block_size, u32 number_directories,
u32 number_files,
u32 number_directory_buckets,
u32 number_file_buckets, u8 duplicate_data) {
if (IsUsingArtic() &&
AllowArticSystemSaveData(FileSys::ConstructSystemSaveDataBinaryPath(high, low))) {
auto req = artic_client->NewRequest("FSUSER_CreateSysSaveData");
req.AddParameterU32(high);
req.AddParameterU32(low);
req.AddParameterU32(total_size);
req.AddParameterU32(block_size);
req.AddParameterU32(number_directories);
req.AddParameterU32(number_files);
req.AddParameterU32(number_directory_buckets);
req.AddParameterU32(number_file_buckets);
req.AddParameterU8(duplicate_data);
auto resp = artic_client->Send(req);
if (!resp.has_value() || !resp->Succeeded()) {
return ResultUnknown;
}
Result res(static_cast<u32>(resp->GetMethodResult()));
return res;
} else {
// Construct the binary path to the archive first
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
if (!FileUtil::CreateFullPath(systemsavedata_path)) {
return ResultUnknown; // TODO(Subv): Find the right error code
}
return ResultSuccess;
}
}
} // namespace FileSys } // namespace FileSys

View File

@ -10,12 +10,15 @@
#include <boost/serialization/string.hpp> #include <boost/serialization/string.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_backend.h"
#include "core/file_sys/artic_cache.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/fs/archive.h"
#include "network/artic_base/artic_base_client.h"
namespace FileSys { namespace FileSys {
/// File system interface to the SystemSaveData archive /// File system interface to the SystemSaveData archive
class ArchiveFactory_SystemSaveData final : public ArchiveFactory { class ArchiveFactory_SystemSaveData final : public ArchiveFactory, public ArticCacheProvider {
public: public:
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point); explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
@ -24,13 +27,31 @@ public:
u32 directory_buckets, u32 file_buckets) override; u32 directory_buckets, u32 file_buckets) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override; ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
Result FormatAsSysData(u32 high, u32 low, u32 total_size, u32 block_size,
u32 number_directories, u32 number_files, u32 number_directory_buckets,
u32 number_file_buckets, u8 duplicate_data);
std::string GetName() const override { std::string GetName() const override {
return "SystemSaveData"; return "SystemSaveData";
} }
bool IsSlow() override {
return IsUsingArtic();
}
void RegisterArtic(std::shared_ptr<Network::ArticBase::Client>& client) {
artic_client = client;
}
bool IsUsingArtic() const {
return artic_client.get() != nullptr;
}
private: private:
std::string base_path; std::string base_path;
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
ArchiveFactory_SystemSaveData() = default; ArchiveFactory_SystemSaveData() = default;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int) { void serialize(Archive& ar, const unsigned int) {

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "network/artic_base/artic_base_client.h"
namespace Core { namespace Core {
class System; class System;
@ -216,7 +217,13 @@ public:
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session); Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
~Interface(); ~Interface();
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
artic_client = client;
}
protected: protected:
void GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool ignore_platform);
/** /**
* AM::GetNumPrograms service function * AM::GetNumPrograms service function
* Gets the number of installed titles in the requested media type * Gets the number of installed titles in the requested media type
@ -704,6 +711,9 @@ public:
protected: protected:
std::shared_ptr<Module> am; std::shared_ptr<Module> am;
// Placed on the interface level so that only am:net and am:app have it.
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
}; };
private: private:

View File

@ -251,7 +251,7 @@ void Module::Interface::GetTransferableId(Kernel::HLERequestContext& ctx) {
std::array<u8, 12> buffer; std::array<u8, 12> buffer;
const Result result = const Result result =
cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::SystemRead, buffer.data()); cfg->GetConfigBlock(ConsoleUniqueID2BlockID, 8, AccessFlag::Global, buffer.data());
rb.Push(result); rb.Push(result);
if (result.IsSuccess()) { if (result.IsSuccess()) {
std::memcpy(&buffer[8], &app_id_salt, sizeof(u32)); std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
@ -438,11 +438,42 @@ ResultVal<void*> Module::GetConfigBlockPointer(u32 block_id, u32 size, AccessFla
} }
Result Module::GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output) { Result Module::GetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, void* output) {
void* pointer = nullptr; bool get_from_artic =
CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag)); block_id == ConsoleUniqueID2BlockID &&
std::memcpy(output, pointer, size); (static_cast<u16>(accesss_flag) & static_cast<u16>(AccessFlag::UserRead)) != 0;
return ResultSuccess; if (get_from_artic && artic_client.get()) {
auto req = artic_client->NewRequest("CFGU_GetConfigInfoBlk2");
req.AddParameterS32(block_id);
req.AddParameterU32(size);
auto resp = artic_client->Send(req);
if (!resp.has_value() || !resp->Succeeded())
return Result(-1);
auto res = Result(static_cast<u32>(resp->GetMethodResult()));
if (res.IsError())
return res;
auto buff = resp->GetResponseBuffer(0);
if (!buff.has_value())
return Result(-1);
size_t actually_read = buff->second;
if (actually_read > size)
return Result(-1);
memcpy(output, buff->first, actually_read);
return ResultSuccess;
} else {
void* pointer = nullptr;
CASCADE_RESULT(pointer, GetConfigBlockPointer(block_id, size, accesss_flag));
std::memcpy(output, pointer, size);
return ResultSuccess;
}
} }
Result Module::SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input) { Result Module::SetConfigBlock(u32 block_id, u32 size, AccessFlag accesss_flag, const void* input) {

View File

@ -11,6 +11,7 @@
#include <utility> #include <utility>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "network/artic_base/artic_base_client.h"
namespace FileSys { namespace FileSys {
class ArchiveBackend; class ArchiveBackend;
@ -188,6 +189,10 @@ public:
std::shared_ptr<Module> GetModule() const; std::shared_ptr<Module> GetModule() const;
void UseArticClient(std::shared_ptr<Network::ArticBase::Client>& client) {
GetModule()->artic_client = client;
}
/** /**
* CFG::GetCountryCodeString service function * CFG::GetCountryCodeString service function
* Inputs: * Inputs:
@ -593,6 +598,8 @@ private:
bool preferred_region_chosen = false; bool preferred_region_chosen = false;
MCUData mcu_data{}; MCUData mcu_data{};
std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int); void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access; friend class boost::serialization::access;

View File

@ -298,18 +298,22 @@ Result ArchiveManager::DeleteSystemSaveData(u32 high, u32 low) {
return ResultSuccess; return ResultSuccess;
} }
Result ArchiveManager::CreateSystemSaveData(u32 high, u32 low) { Result ArchiveManager::CreateSystemSaveData(u32 high, u32 low, u32 total_size, u32 block_size,
// Construct the binary path to the archive first u32 number_directories, u32 number_files,
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low); u32 number_directory_buckets, u32 number_file_buckets,
u8 duplicate_data) {
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); auto archive = id_code_map.find(ArchiveIdCode::SystemSaveData);
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path); if (archive == id_code_map.end()) {
if (!FileUtil::CreateFullPath(systemsavedata_path)) { return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
return ResultUnknown; // TODO(Subv): Find the right error code
} }
return ResultSuccess; auto sys_savedata = static_cast<FileSys::ArchiveFactory_SystemSaveData*>(archive->second.get());
return sys_savedata->FormatAsSysData(high, low, total_size, block_size, number_directories,
number_files, number_directory_buckets,
number_file_buckets, duplicate_data);
} }
ResultVal<ArchiveResource> ArchiveManager::GetArchiveResource(MediaType media_type) const { ResultVal<ArchiveResource> ArchiveManager::GetArchiveResource(MediaType media_type) const {
@ -454,6 +458,16 @@ void ArchiveManager::RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Clien
reinterpret_cast<FileSys::ArchiveFactory_NCCH*>(itr->second.get())->RegisterArtic(client); reinterpret_cast<FileSys::ArchiveFactory_NCCH*>(itr->second.get())->RegisterArtic(client);
} }
void ArchiveManager::RegisterArticSystemSaveData(
std::shared_ptr<Network::ArticBase::Client>& client) {
auto itr = id_code_map.find(ArchiveIdCode::SystemSaveData);
if (itr == id_code_map.end() || itr->second.get() == nullptr) {
return;
}
reinterpret_cast<FileSys::ArchiveFactory_SystemSaveData*>(itr->second.get())
->RegisterArtic(client);
}
ArchiveManager::ArchiveManager(Core::System& system) : system(system) { ArchiveManager::ArchiveManager(Core::System& system) : system(system) {
RegisterArchiveTypes(); RegisterArchiveTypes();
} }

View File

@ -264,11 +264,12 @@ public:
/** /**
* Creates the SystemSaveData archive folder for the specified save data id * Creates the SystemSaveData archive folder for the specified save data id
* @param high The high word of the SystemSaveData archive to create
* @param low The low word of the SystemSaveData archive to create
* @return Result 0 on success or the corresponding code on error * @return Result 0 on success or the corresponding code on error
*/ */
Result CreateSystemSaveData(u32 high, u32 low); Result CreateSystemSaveData(u32 high, u32 low, u32 total_size, u32 block_size,
u32 number_directories, u32 number_files,
u32 number_directory_buckets, u32 number_file_buckets,
u8 duplicate_data);
/** /**
* Returns capacity and free space information about the given media type. * Returns capacity and free space information about the given media type.
@ -296,6 +297,8 @@ public:
void RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Client>& client); void RegisterArticNCCH(std::shared_ptr<Network::ArticBase::Client>& client);
void RegisterArticSystemSaveData(std::shared_ptr<Network::ArticBase::Client>& client);
private: private:
Core::System& system; Core::System& system;

View File

@ -1026,7 +1026,9 @@ void FS_USER::CreateSystemSaveData(Kernel::HLERequestContext& ctx) {
file_buckets, duplicate); file_buckets, duplicate);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(archives.CreateSystemSaveData(savedata_high, savedata_low)); rb.Push(archives.CreateSystemSaveData(savedata_high, savedata_low, total_size, block_size,
directories, files, directory_buckets, file_buckets,
duplicate ? 1 : 0));
} }
void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) { void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) {
@ -1048,7 +1050,8 @@ void FS_USER::CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
// With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND) // With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND)
rb.Push(archives.CreateSystemSaveData(0, savedata_id)); rb.Push(archives.CreateSystemSaveData(0, savedata_id, total_size, block_size, directories,
files, directory_buckets, file_buckets, duplicate));
} }
void FS_USER::InitializeWithSdkVersion(Kernel::HLERequestContext& ctx) { void FS_USER::InitializeWithSdkVersion(Kernel::HLERequestContext& ctx) {

View File

@ -21,7 +21,10 @@
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_app.h"
#include "core/hle/service/am/am_net.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_u.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/hle/service/fs/fs_user.h" #include "core/hle/service/fs/fs_user.h"
#include "core/loader/artic.h" #include "core/loader/artic.h"
@ -335,9 +338,28 @@ ResultStatus Apploader_Artic::Load(std::shared_ptr<Kernel::Process>& process) {
system.ArchiveManager().RegisterArticSaveDataSource(client); system.ArchiveManager().RegisterArticSaveDataSource(client);
system.ArchiveManager().RegisterArticExtData(client); system.ArchiveManager().RegisterArticExtData(client);
system.ArchiveManager().RegisterArticNCCH(client); system.ArchiveManager().RegisterArticNCCH(client);
system.ArchiveManager().RegisterArticSystemSaveData(client);
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER"); auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
fs_user->RegisterSecureValueBackend(std::make_shared<FileSys::ArticSecureValueBackend>(client)); if (fs_user.get()) {
fs_user->RegisterSecureValueBackend(
std::make_shared<FileSys::ArticSecureValueBackend>(client));
}
auto cfg = system.ServiceManager().GetService<Service::CFG::CFG_U>("cfg:u");
if (cfg.get()) {
cfg->UseArticClient(client);
}
auto amnet = system.ServiceManager().GetService<Service::AM::AM_NET>("am:net");
if (amnet.get()) {
amnet->UseArticClient(client);
}
auto amapp = system.ServiceManager().GetService<Service::AM::AM_APP>("am:app");
if (amapp.get()) {
amapp->UseArticClient(client);
}
ParseRegionLockoutInfo(ncch_program_id); ParseRegionLockoutInfo(ncch_program_id);

View File

@ -21,8 +21,9 @@ public:
Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port) Apploader_Artic(Core::System& system_, const std::string& server_addr, u16 server_port)
: AppLoader(system_, FileUtil::IOFile()) { : AppLoader(system_, FileUtil::IOFile()) {
client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port); client = std::make_shared<Network::ArticBase::Client>(server_addr, server_port);
client->SetCommunicationErrorCallback([&system_]() { client->SetCommunicationErrorCallback([&system_](const std::string& msg) {
system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected); system_.SetStatus(Core::System::ResultStatus::ErrorArticDisconnected,
msg.empty() ? nullptr : msg.c_str());
}); });
client->SetArticReportTrafficCallback( client->SetArticReportTrafficCallback(
[&system_](u32 bytes) { system_.ReportArticTraffic(bytes); }); [&system_](u32 bytes) { system_.ReportArticTraffic(bytes); });

View File

@ -32,6 +32,7 @@ public:
ARTIC_EXT_DATA = (1 << 1), ARTIC_EXT_DATA = (1 << 1),
ARTIC_BOSS_EXT_DATA = (1 << 2), ARTIC_BOSS_EXT_DATA = (1 << 2),
ARTIC_SHARED_EXT_DATA = (1 << 3), ARTIC_SHARED_EXT_DATA = (1 << 3),
ARTIC_SYSTEM_SAVE_DATA = (1 << 4),
}; };
union PerfArticEvents { union PerfArticEvents {
u32 raw{}; u32 raw{};

View File

@ -143,7 +143,14 @@ void ConfigurePerGame::LoadConfiguration() {
ui->display_title_id->setText( ui->display_title_id->setText(
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
const auto loader = Loader::GetLoader(filename); std::unique_ptr<Loader::AppLoader> loader_ptr;
Loader::AppLoader* loader;
if (system.IsPoweredOn()) {
loader = &system.GetAppLoader();
} else {
loader_ptr = Loader::GetLoader(filename);
loader = loader_ptr.get();
}
std::string title; std::string title;
if (loader->ReadTitle(title) == Loader::ResultStatus::Success) if (loader->ReadTitle(title) == Loader::ResultStatus::Success)

View File

@ -1242,7 +1242,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorArticDisconnected: case Core::System::ResultStatus::ErrorArticDisconnected:
QMessageBox::critical( QMessageBox::critical(
this, tr("Artic Base Server"), this, tr("Artic Base Server"),
tr("An error has occurred whilst communicating with the Artic Base Server.")); tr(fmt::format(
"An error has occurred whilst communicating with the Artic Base Server.\n{}",
system.GetStatusDetails())
.c_str()));
break; break;
default: default:
QMessageBox::critical( QMessageBox::critical(
@ -1271,6 +1274,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
} }
void GMainWindow::BootGame(const QString& filename) { void GMainWindow::BootGame(const QString& filename) {
if (emu_thread) {
ShutdownGame();
}
const bool is_artic = filename.startsWith(QString::fromStdString("articbase://")); const bool is_artic = filename.startsWith(QString::fromStdString("articbase://"));
if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) { if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
@ -2924,10 +2931,12 @@ void GMainWindow::UpdateStatusBar() {
const bool do_mb = results.artic_transmitted >= (1000.0 * 1000.0); const bool do_mb = results.artic_transmitted >= (1000.0 * 1000.0);
const double value = do_mb ? (results.artic_transmitted / (1000.0 * 1000.0)) const double value = do_mb ? (results.artic_transmitted / (1000.0 * 1000.0))
: (results.artic_transmitted / 1000.0); : (results.artic_transmitted / 1000.0);
static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 4> static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 5>
perf_events = { perf_events = {
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA, std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA,
tr("(Accessing SharedExtData)")), tr("(Accessing SharedExtData)")),
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SYSTEM_SAVE_DATA,
tr("(Accessing SystemSaveData)")),
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_BOSS_EXT_DATA, std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_BOSS_EXT_DATA,
tr("(Accessing BossExtData)")), tr("(Accessing BossExtData)")),
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA, std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA,
@ -3152,7 +3161,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
error_severity_icon = QMessageBox::Icon::Warning; error_severity_icon = QMessageBox::Icon::Warning;
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) { } else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
title = tr("Artic Base Server"); title = tr("Artic Base Server");
message = tr("A communication error has occurred. The game will quit."); message =
tr(fmt::format("A communication error has occurred. The game will quit.\n{}", details)
.c_str());
error_severity_icon = QMessageBox::Icon::Critical; error_severity_icon = QMessageBox::Icon::Critical;
can_continue = false; can_continue = false;
} else { } else {

View File

@ -197,7 +197,8 @@ bool Client::Connect() {
shutdown(main_socket, SHUT_RDWR); shutdown(main_socket, SHUT_RDWR);
closesocket(main_socket); closesocket(main_socket);
LOG_ERROR(Network, "Incompatible server version: {}", version_value); LOG_ERROR(Network, "Incompatible server version: {}", version_value);
SignalCommunicationError(); SignalCommunicationError("\nIncompatible Artic Base Server version.\nCheck for updates "
"to Artic Base Server or Citra.");
return false; return false;
} }
} else { } else {
@ -369,11 +370,11 @@ std::optional<Client::Response> Client::Send(Request& request) {
return std::optional<Client::Response>(std::move(resp.response)); return std::optional<Client::Response>(std::move(resp.response));
} }
void Client::SignalCommunicationError() { void Client::SignalCommunicationError(const std::string& msg) {
StopImpl(true); StopImpl(true);
LOG_CRITICAL(Network, "Communication error"); LOG_CRITICAL(Network, "Communication error");
if (communication_error_callback) if (communication_error_callback)
communication_error_callback(); communication_error_callback(msg);
} }
void Client::PingFunction() { void Client::PingFunction() {

View File

@ -80,7 +80,7 @@ public:
StopImpl(false); StopImpl(false);
} }
void SetCommunicationErrorCallback(const std::function<void()>& callback) { void SetCommunicationErrorCallback(const std::function<void(const std::string&)>& callback) {
communication_error_callback = callback; communication_error_callback = callback;
} }
@ -98,7 +98,7 @@ public:
} }
private: private:
static constexpr const int SERVER_VERSION = 0; static constexpr const int SERVER_VERSION = 1;
std::string address; std::string address;
u16 port; u16 port;
@ -109,8 +109,8 @@ private:
return currRequestID++; return currRequestID++;
} }
void SignalCommunicationError(); void SignalCommunicationError(const std::string& msg = "");
std::function<void()> communication_error_callback; std::function<void(const std::string&)> communication_error_callback;
std::function<void(u64)> report_artic_event_callback; std::function<void(u64)> report_artic_event_callback;