Remove lots more 3DS-specific code.

This commit is contained in:
bunnei 2017-10-12 21:21:49 -04:00
parent 0906de9a14
commit 72b03025ac
50 changed files with 8 additions and 6976 deletions

View file

@ -1,245 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/disk_archive.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/path_parser.h"
#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/**
* A modified version of DiskFile for fixed-size file used by ExtSaveData
* The file size can't be changed by SetSize or Write.
*/
class FixSizeDiskFile : public DiskFile {
public:
FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) {
size = GetSize();
}
bool SetSize(u64 size) const override {
return false;
}
ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
const u8* buffer) const override {
if (offset > size) {
return ERR_WRITE_BEYOND_END;
} else if (offset == size) {
return MakeResult<size_t>(0);
}
if (offset + length > size) {
length = size - offset;
}
return DiskFile::Write(offset, length, flush, buffer);
}
private:
u64 size{};
};
/**
* Archive backend for general extsave data archive type.
* The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for
* - file size can't be changed once created (thus creating zero-size file and openning with create
* flag are prohibited);
* - always open a file with read+write permission.
*/
class ExtSaveDataArchive : public SaveDataArchive {
public:
explicit ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {}
std::string GetName() const override {
return "ExtSaveDataArchive: " + mount_point;
}
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const override {
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
if (mode.hex == 0) {
LOG_ERROR(Service_FS, "Empty open mode");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
if (mode.create_flag) {
LOG_ERROR(Service_FS, "Create flag is not supported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::PathNotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_PATH_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::DirectoryFound:
LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
case PathParser::NotFound:
LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
return ERROR_FILE_NOT_FOUND;
case PathParser::FileFound:
break; // Expected 'success' case
}
FileUtil::IOFile file(full_path, "r+b");
if (!file.IsOpen()) {
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
return ERROR_FILE_NOT_FOUND;
}
Mode rwmode;
rwmode.write_flag.Assign(1);
rwmode.read_flag.Assign(1);
auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode);
return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
}
ResultCode CreateFile(const Path& path, u64 size) const override {
if (size == 0) {
LOG_ERROR(Service_FS, "Zero-size file is not supported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
return SaveDataArchive::CreateFile(path, size);
}
};
std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
std::vector<u8> vec_data = path.AsBinary();
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
u32 save_low = data[1];
u32 save_high = data[2];
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_high, save_low);
}
std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) {
if (shared)
return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID);
return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(), SYSTEM_ID,
SDCARD_ID);
}
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
std::vector<u8> binary_path;
binary_path.reserve(12);
// Append each word byte by byte
// The first word is the media type
for (unsigned i = 0; i < 4; ++i)
binary_path.push_back((media_type >> (8 * i)) & 0xFF);
// Next is the low word
for (unsigned i = 0; i < 4; ++i)
binary_path.push_back((low >> (8 * i)) & 0xFF);
// Next is the high word
for (unsigned i = 0; i < 4; ++i)
binary_path.push_back((high >> (8 * i)) & 0xFF);
return {binary_path};
}
ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location,
bool shared)
: shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
LOG_DEBUG(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
}
bool ArchiveFactory_ExtSaveData::Initialize() {
if (!FileUtil::CreateFullPath(mount_point)) {
LOG_ERROR(Service_FS, "Unable to create ExtSaveData base path.");
return false;
}
return true;
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) {
std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/";
if (!FileUtil::Exists(fullpath)) {
// TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
// ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
if (!shared) {
return ERR_NOT_FOUND_INVALID_STATE;
} else {
return ERR_NOT_FORMATTED;
}
}
auto archive = std::make_unique<ExtSaveDataArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
// These folders are always created with the ExtSaveData
std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/";
std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/";
FileUtil::CreateFullPath(user_path);
FileUtil::CreateFullPath(boss_path);
// Write the format metadata
std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
FileUtil::IOFile file(metadata_path, "wb");
if (!file.IsOpen()) {
// TODO(Subv): Find the correct error code
return ResultCode(-1);
}
file.WriteBytes(&format_info, sizeof(format_info));
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const {
std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
FileUtil::IOFile file(metadata_path, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
// TODO(Subv): Verify error code
return ERR_NOT_FORMATTED;
}
ArchiveFormatInfo info = {};
file.ReadBytes(&info, sizeof(info));
return MakeResult<ArchiveFormatInfo>(info);
}
void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data,
size_t icon_size) {
std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
FileUtil::IOFile icon_file(game_path + "icon", "wb");
icon_file.WriteBytes(icon_data, icon_size);
}
} // namespace FileSys

View file

@ -1,89 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the ExtSaveData archive
class ArchiveFactory_ExtSaveData final : public ArchiveFactory {
public:
ArchiveFactory_ExtSaveData(const std::string& mount_point, bool shared);
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Initialize();
std::string GetName() const override {
return "ExtSaveData";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
const std::string& GetMountPoint() const {
return mount_point;
}
/**
* Writes the SMDH icon of the ExtSaveData to file
* @param path Path of this ExtSaveData
* @param icon_data Binary data of the icon
* @param icon_size Size of the icon data
*/
void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
private:
bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData
/// archive
/**
* This holds the full directory path for this archive, it is only set after a successful call
* to Open, this is formed as `<base extsavedatapath>/<type>/<high>/<low>`.
* See GetExtSaveDataPath for the code that extracts this data from an archive path.
*/
std::string mount_point;
};
/**
* Constructs a path to the concrete ExtData archive in the host filesystem based on the
* input Path and base mount point.
* @param mount_point The base mount point of the ExtSaveData archives.
* @param path The path that identifies the requested concrete ExtSaveData archive.
* @returns The complete path to the specified extdata archive in the host filesystem
*/
std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path);
/**
* Constructs a path to the base folder to hold concrete ExtSaveData archives in the host file
* system.
* @param mount_point The base folder where this folder resides, ie. SDMC or NAND.
* @param shared Whether this ExtSaveData container is for SharedExtSaveDatas or not.
* @returns The path to the base ExtSaveData archives' folder in the host file system
*/
std::string GetExtDataContainerPath(const std::string& mount_point, bool shared);
/**
* Constructs a FileSys::Path object that refers to the ExtData archive identified by
* the specified media type, high save id and low save id.
* @param media_type The media type where the archive is located (NAND / SDMC)
* @param high The high word of the save id for the archive
* @param low The low word of the save id for the archive
* @returns A FileSys::Path to the wanted archive
*/
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low);
} // namespace FileSys

View file

@ -1,114 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/archive_ncch.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/ivfc_archive.h"
#include "core/file_sys/ncch_container.h"
#include "core/file_sys/title_metadata.h"
#include "core/hle/service/fs/archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
static std::string GetNCCHContainerPath(const std::string& nand_directory) {
return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID);
}
static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
u32 content_id = 0;
// TODO(shinyquagsire23): Title database should be doing this path lookup
std::string content_path =
Common::StringFromFormat("%s%08x/%08x/content/", mount_point.c_str(), high, low);
std::string tmd_path = content_path + "00000000.tmd";
TitleMetadata tmd(tmd_path);
if (tmd.Load() == Loader::ResultStatus::Success) {
content_id = tmd.GetBootContentID();
}
return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id);
}
ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
: mount_point(GetNCCHContainerPath(nand_directory)) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
auto vec = path.AsBinary();
const u32* data = reinterpret_cast<u32*>(vec.data());
u32 high = data[1];
u32 low = data[0];
std::string file_path = GetNCCHPath(mount_point, high, low);
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 romfs_offset = 0;
u64 romfs_size = 0;
auto ncch_container = NCCHContainer(file_path);
if (ncch_container.ReadRomFS(romfs_file, romfs_offset, romfs_size) !=
Loader::ResultStatus::Success) {
// High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list).
constexpr u32 shared_data_archive = 0x0004009B;
constexpr u32 system_data_archive = 0x000400DB;
// Low Title IDs.
constexpr u32 mii_data = 0x00010202;
constexpr u32 region_manifest = 0x00010402;
constexpr u32 ng_word_list = 0x00010302;
LOG_DEBUG(Service_FS, "Full Path: %s. Category: 0x%X. Path: 0x%X.", path.DebugStr().c_str(),
high, low);
if (high == shared_data_archive) {
if (low == mii_data) {
LOG_ERROR(Service_FS, "Failed to get a handle for shared data archive: Mii data. ");
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
"Mii data");
} else if (low == region_manifest) {
LOG_ERROR(Service_FS,
"Failed to get a handle for shared data archive: region manifest.");
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
"Region manifest");
}
} else if (high == system_data_archive) {
if (low == ng_word_list) {
LOG_ERROR(Service_FS,
"Failed to get a handle for system data archive: NG bad word list.");
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
"NG bad word list");
}
}
return ERROR_NOT_FOUND;
}
auto archive = std::make_unique<IVFCArchive>(romfs_file, romfs_offset, romfs_size);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveFactory_NCCH::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
ErrorLevel::Permanent);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
}
} // namespace FileSys

View file

@ -1,34 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the NCCH archive
class ArchiveFactory_NCCH final : public ArchiveFactory {
public:
explicit ArchiveFactory_NCCH(const std::string& mount_point);
std::string GetName() const override {
return "NCCH";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
};
} // namespace FileSys

View file

@ -1,145 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <tuple>
#include "core/file_sys/archive_other_savedata.h"
#include "core/file_sys/errors.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
// TODO(wwylele): The storage info in exheader should be checked before accessing these archives
using Service::FS::MediaType;
namespace {
template <typename T>
ResultVal<std::tuple<MediaType, u64>> ParsePath(const Path& path, T program_id_reader) {
if (path.GetType() != Binary) {
LOG_ERROR(Service_FS, "Wrong path type %d", static_cast<int>(path.GetType()));
return ERROR_INVALID_PATH;
}
std::vector<u8> vec_data = path.AsBinary();
if (vec_data.size() != 12) {
LOG_ERROR(Service_FS, "Wrong path length %zu", vec_data.size());
return ERROR_INVALID_PATH;
}
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
auto media_type = static_cast<MediaType>(data[0]);
if (media_type != MediaType::SDMC && media_type != MediaType::GameCard) {
LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast<u32>(media_type));
// Note: this is strange, but the error code was verified with a real 3DS
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
return MakeResult<std::tuple<MediaType, u64>>(media_type, program_id_reader(data));
}
ResultVal<std::tuple<MediaType, u64>> ParsePathPermitted(const Path& path) {
return ParsePath(path,
[](const u32* data) -> u64 { return (data[1] << 8) | 0x0004000000000000ULL; });
}
ResultVal<std::tuple<MediaType, u64>> ParsePathGeneral(const Path& path) {
return ParsePath(
path, [](const u32* data) -> u64 { return data[1] | (static_cast<u64>(data[2]) << 32); });
}
} // namespace
ArchiveFactory_OtherSaveDataPermitted::ArchiveFactory_OtherSaveDataPermitted(
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
: sd_savedata_source(sd_savedata) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataPermitted::Open(
const Path& path) {
MediaType media_type;
u64 program_id;
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
if (media_type == MediaType::GameCard) {
LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
return ERROR_GAMECARD_NOT_INSERTED;
}
return sd_savedata_source->Open(program_id);
}
ResultCode ArchiveFactory_OtherSaveDataPermitted::Format(
const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
LOG_ERROR(Service_FS, "Attempted to format a OtherSaveDataPermitted archive.");
return ERROR_INVALID_PATH;
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataPermitted::GetFormatInfo(
const Path& path) const {
MediaType media_type;
u64 program_id;
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
if (media_type == MediaType::GameCard) {
LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
return ERROR_GAMECARD_NOT_INSERTED;
}
return sd_savedata_source->GetFormatInfo(program_id);
}
ArchiveFactory_OtherSaveDataGeneral::ArchiveFactory_OtherSaveDataGeneral(
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
: sd_savedata_source(sd_savedata) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataGeneral::Open(
const Path& path) {
MediaType media_type;
u64 program_id;
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
if (media_type == MediaType::GameCard) {
LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
return ERROR_GAMECARD_NOT_INSERTED;
}
return sd_savedata_source->Open(program_id);
}
ResultCode ArchiveFactory_OtherSaveDataGeneral::Format(
const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
MediaType media_type;
u64 program_id;
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
if (media_type == MediaType::GameCard) {
LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
return ERROR_GAMECARD_NOT_INSERTED;
}
return sd_savedata_source->Format(program_id, format_info);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataGeneral::GetFormatInfo(
const Path& path) const {
MediaType media_type;
u64 program_id;
CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
if (media_type == MediaType::GameCard) {
LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
return ERROR_GAMECARD_NOT_INSERTED;
}
return sd_savedata_source->GetFormatInfo(program_id);
}
} // namespace FileSys

View file

@ -1,52 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/archive_source_sd_savedata.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the OtherSaveDataPermitted archive
class ArchiveFactory_OtherSaveDataPermitted final : public ArchiveFactory {
public:
explicit ArchiveFactory_OtherSaveDataPermitted(
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
std::string GetName() const override {
return "OtherSaveDataPermitted";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
};
/// File system interface to the OtherSaveDataGeneral archive
class ArchiveFactory_OtherSaveDataGeneral final : public ArchiveFactory {
public:
explicit ArchiveFactory_OtherSaveDataGeneral(
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
std::string GetName() const override {
return "OtherSaveDataGeneral";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
};
} // namespace FileSys

View file

@ -1,33 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/archive_savedata.h"
#include "core/hle/kernel/process.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
ArchiveFactory_SaveData::ArchiveFactory_SaveData(
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
: sd_savedata_source(sd_savedata) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) {
UNIMPLEMENTED();
return {}; //sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id);
}
ResultCode ArchiveFactory_SaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
UNIMPLEMENTED();
return RESULT_SUCCESS; //sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
UNIMPLEMENTED();
return {}; //sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id);
}
} // namespace FileSys

View file

@ -1,33 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/archive_source_sd_savedata.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the SaveData archive
class ArchiveFactory_SaveData final : public ArchiveFactory {
public:
explicit ArchiveFactory_SaveData(std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
std::string GetName() const override {
return "SaveData";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
};
} // namespace FileSys

View file

@ -1,379 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/file_sys/disk_archive.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/path_parser.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
const Mode& mode) const {
Mode modified_mode;
modified_mode.hex = mode.hex;
// SDMC archive always opens a file with at least read permission
modified_mode.read_flag.Assign(1);
return OpenFileBase(path, modified_mode);
}
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& path,
const Mode& mode) const {
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
if (mode.hex == 0) {
LOG_ERROR(Service_FS, "Empty open mode");
return ERROR_INVALID_OPEN_FLAGS;
}
if (mode.create_flag && !mode.write_flag) {
LOG_ERROR(Service_FS, "Create flag set but write flag not set");
return ERROR_INVALID_OPEN_FLAGS;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::DirectoryFound:
LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
case PathParser::NotFound:
if (!mode.create_flag) {
LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
full_path.c_str());
return ERROR_NOT_FOUND;
} else {
// Create the file
FileUtil::CreateEmptyFile(full_path);
}
break;
case PathParser::FileFound:
break; // Expected 'success' case
}
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
if (!file.IsOpen()) {
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
return ERROR_NOT_FOUND;
}
auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
}
ResultCode SDMCArchive::DeleteFile(const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::FileInPath:
case PathParser::NotFound:
LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::DirectoryFound:
LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
case PathParser::FileFound:
break; // Expected 'success' case
}
if (FileUtil::Delete(full_path)) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
return ERROR_NOT_FOUND;
}
ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
const PathParser path_parser_src(src_path);
// TODO: Verify these return codes with HW
if (!path_parser_src.IsValid()) {
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const PathParser path_parser_dest(dest_path);
if (!path_parser_dest.IsValid()) {
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}
// TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
// exist or similar. Verify.
return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
ErrorSummary::NothingHappened, ErrorLevel::Status);
}
template <typename T>
static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
T deleter) {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
if (path_parser.IsRootDirectory())
return ERROR_NOT_FOUND;
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::NotFound:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::FileInPath:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
case PathParser::DirectoryFound:
break; // Expected 'success' case
}
if (deleter(full_path)) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
}
ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
}
ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
return DeleteDirectoryHelper(
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
}
ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::DirectoryFound:
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
case PathParser::FileFound:
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
return ERROR_ALREADY_EXISTS;
case PathParser::NotFound:
break; // Expected 'success' case
}
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
FileUtil::IOFile file(full_path, "wb");
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
// We do this by seeking to the right size, then writing a single null byte.
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Too large file");
return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
ErrorLevel::Info);
}
ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::DirectoryFound:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
return ERROR_ALREADY_EXISTS;
case PathParser::NotFound:
break; // Expected 'success' case
}
if (FileUtil::CreateDir(mount_point + path.AsString())) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
ErrorLevel::Status);
}
ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
const PathParser path_parser_src(src_path);
// TODO: Verify these return codes with HW
if (!path_parser_src.IsValid()) {
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const PathParser path_parser_dest(dest_path);
if (!path_parser_dest.IsValid()) {
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}
// TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
// exist or similar. Verify.
return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
ErrorSummary::NothingHappened, ErrorLevel::Status);
}
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
const PathParser path_parser(path);
if (!path_parser.IsValid()) {
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
return ERROR_INVALID_PATH;
}
const auto full_path = path_parser.BuildHostPath(mount_point);
switch (path_parser.GetHostStatus(mount_point)) {
case PathParser::InvalidMountPoint:
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
return ERROR_NOT_FOUND;
case PathParser::PathNotFound:
case PathParser::NotFound:
case PathParser::FileFound:
LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
return ERROR_NOT_FOUND;
case PathParser::FileInPath:
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
case PathParser::DirectoryFound:
break; // Expected 'success' case
}
auto directory = std::make_unique<DiskDirectory>(full_path);
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
}
u64 SDMCArchive::GetFreeBytes() const {
// TODO: Stubbed to return 1GiB
return 1024 * 1024 * 1024;
}
ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
: sdmc_directory(sdmc_directory) {
LOG_DEBUG(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
}
bool ArchiveFactory_SDMC::Initialize() {
if (!Settings::values.use_virtual_sd) {
LOG_WARNING(Service_FS, "SDMC disabled by config.");
return false;
}
if (!FileUtil::CreateFullPath(sdmc_directory)) {
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}
return true;
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveFactory_SDMC::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
// This is kind of an undesirable operation, so let's just ignore it. :)
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
}
} // namespace FileSys

View file

@ -1,66 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// Archive backend for SDMC archive
class SDMCArchive : public ArchiveBackend {
public:
explicit SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
std::string GetName() const override {
return "SDMCArchive: " + mount_point;
}
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const override;
ResultCode DeleteFile(const Path& path) const override;
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const Path& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeBytes() const override;
protected:
ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
std::string mount_point;
};
/// File system interface to the SDMC archive
class ArchiveFactory_SDMC final : public ArchiveFactory {
public:
explicit ArchiveFactory_SDMC(const std::string& mount_point);
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Initialize();
std::string GetName() const override {
return "SDMC";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string sdmc_directory;
};
} // namespace FileSys

View file

@ -1,70 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/file_util.h"
#include "core/file_sys/archive_sdmcwriteonly.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/file_backend.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
const Mode& mode) const {
if (mode.read_flag) {
LOG_ERROR(Service_FS, "Read flag is not supported");
return ERROR_INVALID_READ_FLAG;
}
return SDMCArchive::OpenFileBase(path, mode);
}
ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
const Path& path) const {
LOG_ERROR(Service_FS, "Not supported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
: sdmc_directory(mount_point) {
LOG_DEBUG(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
}
bool ArchiveFactory_SDMCWriteOnly::Initialize() {
if (!Settings::values.use_virtual_sd) {
LOG_WARNING(Service_FS, "SDMC disabled by config.");
return false;
}
if (!FileUtil::CreateFullPath(sdmc_directory)) {
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}
return true;
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) {
auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
// TODO(wwylele): hwtest this
LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
}
} // namespace FileSys

View file

@ -1,57 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/archive_sdmc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/**
* Archive backend for SDMC write-only archive.
* The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for
* - OpenDirectory is unsupported;
* - OpenFile with read flag is unsupported.
*/
class SDMCWriteOnlyArchive : public SDMCArchive {
public:
explicit SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {}
std::string GetName() const override {
return "SDMCWriteOnlyArchive: " + mount_point;
}
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
const Mode& mode) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
};
/// File system interface to the SDMC write-only archive
class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory {
public:
explicit ArchiveFactory_SDMCWriteOnly(const std::string& mount_point);
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Initialize();
std::string GetName() const override {
return "SDMCWriteOnly";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
std::string sdmc_directory;
};
} // namespace FileSys

View file

@ -1,297 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cinttypes>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/file_sys/archive_selfncch.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/ivfc_archive.h"
#include "core/hle/kernel/process.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
enum class SelfNCCHFilePathType : u32 {
RomFS = 0,
Code = 1, // This is not supported by SelfNCCHArchive but by archive 0x2345678E
ExeFS = 2,
UpdateRomFS = 5, // This is presumably for accessing the RomFS of the update patch.
};
struct SelfNCCHFilePath {
u32_le type;
std::array<char, 8> exefs_filename;
};
static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!");
// A read-only file created from a block of data. It only allows you to read the entire file at
// once, in a single read operation.
class ExeFSSectionFile final : public FileBackend {
public:
explicit ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_) : data(std::move(data_)) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override {
if (offset != 0) {
LOG_ERROR(Service_FS, "offset must be zero!");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
if (length != data->size()) {
LOG_ERROR(Service_FS, "size must match the file size!");
return ERROR_INCORRECT_EXEFS_READ_SIZE;
}
std::memcpy(buffer, data->data(), data->size());
return MakeResult<size_t>(data->size());
}
ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
const u8* buffer) const override {
LOG_ERROR(Service_FS, "The file is read-only!");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
u64 GetSize() const override {
return data->size();
}
bool SetSize(u64 size) const override {
return false;
}
bool Close() const override {
return true;
}
void Flush() const override {}
private:
std::shared_ptr<std::vector<u8>> data;
};
// SelfNCCHArchive represents the running application itself. From this archive the application can
// open RomFS and ExeFS, excluding the .code section.
class SelfNCCHArchive final : public ArchiveBackend {
public:
explicit SelfNCCHArchive(const NCCHData& ncch_data_) : ncch_data(ncch_data_) {}
std::string GetName() const override {
return "SelfNCCHArchive";
}
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override {
// Note: SelfNCCHArchive doesn't check the open mode.
if (path.GetType() != LowPathType::Binary) {
LOG_ERROR(Service_FS, "Path need to be Binary");
return ERROR_INVALID_PATH;
}
std::vector<u8> binary = path.AsBinary();
if (binary.size() != sizeof(SelfNCCHFilePath)) {
LOG_ERROR(Service_FS, "Wrong path size %zu", binary.size());
return ERROR_INVALID_PATH;
}
SelfNCCHFilePath file_path;
std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath));
switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
case SelfNCCHFilePathType::UpdateRomFS:
return OpenUpdateRomFS();
case SelfNCCHFilePathType::RomFS:
return OpenRomFS();
case SelfNCCHFilePathType::Code:
LOG_ERROR(Service_FS, "Reading the code section is not supported!");
return ERROR_COMMAND_NOT_ALLOWED;
case SelfNCCHFilePathType::ExeFS: {
const auto& raw = file_path.exefs_filename;
auto end = std::find(raw.begin(), raw.end(), '\0');
std::string filename(raw.begin(), end);
return OpenExeFS(filename);
}
default:
LOG_ERROR(Service_FS, "Unknown file type %u!", static_cast<u32>(file_path.type));
return ERROR_INVALID_PATH;
}
}
ResultCode DeleteFile(const Path& path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode DeleteDirectory(const Path& path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode DeleteDirectoryRecursively(const Path& path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode CreateFile(const Path& path, u64 size) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode CreateDirectory(const Path& path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override {
LOG_ERROR(Service_FS, "Unsupported");
return ERROR_UNSUPPORTED_OPEN_FLAGS;
}
u64 GetFreeBytes() const override {
return 0;
}
private:
ResultVal<std::unique_ptr<FileBackend>> OpenRomFS() const {
if (ncch_data.romfs_file) {
return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
ncch_data.romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size));
} else {
LOG_INFO(Service_FS, "Unable to read RomFS");
return ERROR_ROMFS_NOT_FOUND;
}
}
ResultVal<std::unique_ptr<FileBackend>> OpenUpdateRomFS() const {
if (ncch_data.update_romfs_file) {
return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
ncch_data.update_romfs_file, ncch_data.update_romfs_offset,
ncch_data.update_romfs_size));
} else {
LOG_INFO(Service_FS, "Unable to read update RomFS");
return ERROR_ROMFS_NOT_FOUND;
}
}
ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
if (filename == "icon") {
if (ncch_data.icon) {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<ExeFSSectionFile>(ncch_data.icon));
}
LOG_WARNING(Service_FS, "Unable to read icon");
return ERROR_EXEFS_SECTION_NOT_FOUND;
}
if (filename == "logo") {
if (ncch_data.logo) {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<ExeFSSectionFile>(ncch_data.logo));
}
LOG_WARNING(Service_FS, "Unable to read logo");
return ERROR_EXEFS_SECTION_NOT_FOUND;
}
if (filename == "banner") {
if (ncch_data.banner) {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<ExeFSSectionFile>(ncch_data.banner));
}
LOG_WARNING(Service_FS, "Unable to read banner");
return ERROR_EXEFS_SECTION_NOT_FOUND;
}
LOG_ERROR(Service_FS, "Unknown ExeFS section %s!", filename.c_str());
return ERROR_INVALID_PATH;
}
NCCHData ncch_data;
};
void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
u64 program_id = 0;
if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
LOG_WARNING(
Service_FS,
"Could not read program id when registering with SelfNCCH, this might be a 3dsx file");
}
LOG_DEBUG(Service_FS, "Registering program %016" PRIX64 " with the SelfNCCH archive factory",
program_id);
if (ncch_data.find(program_id) != ncch_data.end()) {
LOG_WARNING(Service_FS, "Registering program %016" PRIX64
" with SelfNCCH will override existing mapping",
program_id);
}
NCCHData& data = ncch_data[program_id];
std::shared_ptr<FileUtil::IOFile> romfs_file_;
if (Loader::ResultStatus::Success ==
app_loader.ReadRomFS(romfs_file_, data.romfs_offset, data.romfs_size)) {
data.romfs_file = std::move(romfs_file_);
}
std::shared_ptr<FileUtil::IOFile> update_romfs_file;
if (Loader::ResultStatus::Success ==
app_loader.ReadUpdateRomFS(update_romfs_file, data.update_romfs_offset,
data.update_romfs_size)) {
data.update_romfs_file = std::move(update_romfs_file);
}
std::vector<u8> buffer;
if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
buffer.clear();
if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
buffer.clear();
if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
//auto archive = std::make_unique<SelfNCCHArchive>(
// ncch_data[Kernel::g_current_process->codeset->program_id]);
//return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
return {};
}
ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) {
LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive.");
return ERROR_INVALID_PATH;
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&) const {
LOG_ERROR(Service_FS, "Attempted to get format info of a SelfNCCH archive");
return ERROR_INVALID_PATH;
}
} // namespace FileSys

View file

@ -1,54 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
struct NCCHData {
std::shared_ptr<std::vector<u8>> icon;
std::shared_ptr<std::vector<u8>> logo;
std::shared_ptr<std::vector<u8>> banner;
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 romfs_offset = 0;
u64 romfs_size = 0;
std::shared_ptr<FileUtil::IOFile> update_romfs_file;
u64 update_romfs_offset = 0;
u64 update_romfs_size = 0;
};
/// File system interface to the SelfNCCH archive
class ArchiveFactory_SelfNCCH final : public ArchiveFactory {
public:
ArchiveFactory_SelfNCCH() = default;
/// Registers a loaded application so that we can open its SelfNCCH archive when requested.
void Register(Loader::AppLoader& app_loader);
std::string GetName() const override {
return "SelfNCCH";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
/// Mapping of ProgramId -> NCCHData
std::unordered_map<u64, NCCHData> ncch_data;
};
} // namespace FileSys

View file

@ -1,97 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/archive_source_sd_savedata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
namespace {
std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
SYSTEM_ID, SDCARD_ID);
}
std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
u32 high = static_cast<u32>(program_id >> 32);
u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high,
low);
}
std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
u32 high = static_cast<u32>(program_id >> 32);
u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(),
high, low);
}
} // namespace
ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory)
: mount_point(GetSaveDataContainerPath(sdmc_directory)) {
LOG_DEBUG(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
if (!FileUtil::Exists(concrete_mount_point)) {
// When a SaveData archive is created for the first time, it is not yet formatted and the
// save file/directory structure expected by the game has not yet been initialized.
// Returning the NotFormatted error code will signal the game to provision the SaveData
// archive with the files and folders that it expects.
return ERR_NOT_FORMATTED;
}
auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveSource_SDSaveData::Format(u64 program_id,
const FileSys::ArchiveFormatInfo& format_info) {
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
FileUtil::DeleteDirRecursively(concrete_mount_point);
FileUtil::CreateFullPath(concrete_mount_point);
// Write the format metadata
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
FileUtil::IOFile file(metadata_path, "wb");
if (file.IsOpen()) {
file.WriteBytes(&format_info, sizeof(format_info));
return RESULT_SUCCESS;
}
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program_id) const {
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
FileUtil::IOFile file(metadata_path, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
// TODO(Subv): Verify error code
return ERR_NOT_FORMATTED;
}
ArchiveFormatInfo info = {};
file.ReadBytes(&info, sizeof(info));
return MakeResult<ArchiveFormatInfo>(info);
}
std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point,
u64 program_id) {
return GetSaveDataPath(GetSaveDataContainerPath(mount_point), program_id);
}
} // namespace FileSys

View file

@ -1,32 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// A common source of SD save data archive
class ArchiveSource_SDSaveData {
public:
explicit ArchiveSource_SDSaveData(const std::string& mount_point);
ResultVal<std::unique_ptr<ArchiveBackend>> Open(u64 program_id);
ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info);
ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const;
static std::string GetSaveDataPathFor(const std::string& mount_point, u64 program_id);
private:
std::string mount_point;
};
} // namespace FileSys

View file

@ -1,77 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
std::vector<u8> vec_data = path.AsBinary();
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
u32 save_low = data[1];
u32 save_high = data[0];
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
}
std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID);
}
Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
std::vector<u8> binary_path;
binary_path.reserve(8);
// Append each word byte by byte
// First is the high word
for (unsigned i = 0; i < 4; ++i)
binary_path.push_back((high >> (8 * i)) & 0xFF);
// Next is the low word
for (unsigned i = 0; i < 4; ++i)
binary_path.push_back((low >> (8 * i)) & 0xFF);
return {binary_path};
}
ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
: base_path(GetSystemSaveDataContainerPath(nand_path)) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path) {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
if (!FileUtil::Exists(fullpath)) {
// TODO(Subv): Check error code, this one is probably wrong
return ERR_NOT_FORMATTED;
}
auto archive = std::make_unique<SaveDataArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
std::string fullpath = GetSystemSaveDataPath(base_path, path);
FileUtil::DeleteDirRecursively(fullpath);
FileUtil::CreateFullPath(fullpath);
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
}
} // namespace FileSys

View file

@ -1,61 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the SystemSaveData archive
class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
public:
explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
std::string GetName() const override {
return "SystemSaveData";
}
private:
std::string base_path;
};
/**
* Constructs a path to the concrete SystemSaveData archive in the host filesystem based on the
* input Path and base mount point.
* @param mount_point The base mount point of the SystemSaveData archives.
* @param path The path that identifies the requested concrete SystemSaveData archive.
* @returns The complete path to the specified SystemSaveData archive in the host filesystem
*/
std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path);
/**
* Constructs a path to the base folder to hold concrete SystemSaveData archives in the host file
* system.
* @param mount_point The base folder where this folder resides, ie. SDMC or NAND.
* @returns The path to the base SystemSaveData archives' folder in the host file system
*/
std::string GetSystemSaveDataContainerPath(const std::string& mount_point);
/**
* Constructs a FileSys::Path object that refers to the SystemSaveData archive identified by
* the specified high save id and low save id.
* @param high The high word of the save id for the archive
* @param low The low word of the save id for the archive
* @returns A FileSys::Path to the wanted archive
*/
Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low);
} // namespace FileSys