Skip to content

Commit

Permalink
Artic Base: Implement DLC support and other fixes (#173)
Browse files Browse the repository at this point in the history
* Artic Base: Implement DLC support and other fixes

* Fix per game settings not working with artic loader

* Fix compilation error
  • Loading branch information
PabloMK7 authored Jul 9, 2024
1 parent 1e2be72 commit 4780a71
Show file tree
Hide file tree
Showing 16 changed files with 991 additions and 235 deletions.
9 changes: 8 additions & 1 deletion src/citra_qt/configuration/configure_per_game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,14 @@ void ConfigurePerGame::LoadConfiguration() {
ui->display_title_id->setText(
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;
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
Expand Down
17 changes: 14 additions & 3 deletions src/citra_qt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorArticDisconnected:
QMessageBox::critical(
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;
default:
QMessageBox::critical(
Expand All @@ -1238,6 +1241,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
}

void GMainWindow::BootGame(const QString& filename) {
if (emu_thread) {
ShutdownGame();
}

const bool is_artic = filename.startsWith(QString::fromStdString("articbase://"));

if (!is_artic && filename.endsWith(QStringLiteral(".cia"))) {
Expand Down Expand Up @@ -2640,10 +2647,12 @@ void GMainWindow::UpdateStatusBar() {
const bool 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);
static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 4>
static const std::array<std::pair<Core::PerfStats::PerfArticEventBits, QString>, 5>
perf_events = {
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_SHARED_EXT_DATA,
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,
tr("(Accessing BossExtData)")),
std::make_pair(Core::PerfStats::PerfArticEventBits::ARTIC_EXT_DATA,
Expand Down Expand Up @@ -2868,7 +2877,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
error_severity_icon = QMessageBox::Icon::Warning;
} else if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
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;
can_continue = false;
} else {
Expand Down
81 changes: 72 additions & 9 deletions src/core/file_sys/archive_systemsavedata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "common/archives.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_artic.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/savedata_archive.h"
Expand Down Expand Up @@ -52,24 +53,45 @@ Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& 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,
u64 program_id) {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
if (!FileUtil::Exists(fullpath)) {
// TODO(Subv): Check error code, this one is probably wrong
return ResultNotFound;
if (IsUsingArtic() && AllowArticSystemSaveData(path)) {
EnsureCacheCreated();
return ArticArchive::Open(artic_client, Service::FS::ArchiveIdCode::SystemSaveData, path,
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,
const FileSys::ArchiveFormatInfo& format_info,
u64 program_id, u32 directory_buckets,
u32 file_buckets) {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
FileUtil::DeleteDirRecursively(fullpath);
FileUtil::CreateFullPath(fullpath);
return ResultSuccess;
const std::vector<u8> vec_data = path.AsBinary();
u32 save_low;
u32 save_high;
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,
Expand All @@ -79,4 +101,45 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const
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
23 changes: 22 additions & 1 deletion src/core/file_sys/archive_systemsavedata.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/artic_cache.h"
#include "core/hle/result.h"
#include "core/hle/service/fs/archive.h"
#include "network/artic_base/artic_base_client.h"

namespace FileSys {

/// File system interface to the SystemSaveData archive
class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
class ArchiveFactory_SystemSaveData final : public ArchiveFactory, public ArticCacheProvider {
public:
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);

Expand All @@ -24,13 +27,31 @@ class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
u32 directory_buckets, u32 file_buckets) 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 {
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:
std::string base_path;

std::shared_ptr<Network::ArticBase::Client> artic_client = nullptr;

ArchiveFactory_SystemSaveData() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
Expand Down
Loading

0 comments on commit 4780a71

Please sign in to comment.