More Overhaul

This commit is contained in:
Zach Hilman
2018-06-25 18:20:34 -04:00
parent af1f6a94f7
commit 00a1a5f376
23 changed files with 317 additions and 403 deletions

View File

@@ -4,8 +4,8 @@
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
#include "vfs_offset.h"
// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
@@ -14,18 +14,18 @@ constexpr u64 SECTION_HEADER_SIZE = 0x200;
constexpr u64 SECTION_HEADER_OFFSET = 0x400;
namespace FileSys {
enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
struct NcaSectionHeaderBlock {
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NcaSectionFilesystemType filesystem_type;
NCASectionFilesystemType filesystem_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
struct Pfs0Superblock {
NcaSectionHeaderBlock header_block;
struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_PADDING_BYTES(4);
@@ -35,40 +35,39 @@ struct Pfs0Superblock {
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
Loader::ResultStatus Nca::Load(v_file file_) {
file = std::move(file_);
if (sizeof(NcaHeader) != file->ReadObject(&header))
NCA::NCA(v_file file_) : file(file_) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
if (!IsValidNca(header))
return Loader::ResultStatus::ErrorInvalidFormat;
if (!IsValidNCA(header)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
int number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
for (int i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
NcaSectionHeaderBlock block{};
if (sizeof(NcaSectionHeaderBlock) !=
NCASectionHeaderBlock block{};
if (sizeof(NCASectionHeaderBlock) !=
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
const size_t romfs_size =
header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
entries.emplace_back(std::make_shared<RomFs>(
std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)));
romfs = entries.back();
} else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
Pfs0Superblock sb{};
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
romfs = files.back();
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
PFS0Superblock sb{};
// Seek back to beginning of this section.
if (sizeof(Pfs0Superblock) !=
if (sizeof(PFS0Superblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
NGLOG_CRITICAL(Loader, "File reader errored out during header read.");
@@ -77,50 +76,63 @@ Loader::ResultStatus Nca::Load(v_file file_) {
sb.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
FileSys::PartitionFilesystem npfs{};
Loader::ResultStatus status =
npfs.Load(std::make_shared<OffsetVfsFile>(file, size, offset));
auto npfs = std::make_shared<PartitionFilesystem>(
std::make_shared<OffsetVfsFile>(file, size, offset));
if (status == Loader::ResultStatus::Success) {
entries.emplace_back(npfs);
if (IsDirectoryExeFs(entries.back()))
exefs = entries.back();
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.emplace_back(npfs);
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
}
}
return Loader::ResultStatus::Success;
status = Loader::ResultStatus::Success;
}
std::vector<std::shared_ptr<VfsFile>> Nca::GetFiles() const {
return {};
Loader::ResultStatus NCA::GetStatus() const {
return status;
}
std::vector<std::shared_ptr<VfsDirectory>> Nca::GetSubdirectories() const {
return entries;
std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const {
if (status != Loader::ResultStatus::Success)
return {};
return files;
}
std::string Nca::GetName() const {
std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const {
if (status != Loader::ResultStatus::Success)
return {};
return dirs;
}
std::string NCA::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> Nca::GetParentDirectory() const {
std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const {
return file->GetContainingDirectory();
}
NcaContentType Nca::GetType() const {
NCAContentType NCA::GetType() const {
return header.content_type;
}
u64 Nca::GetTitleId() const {
u64 NCA::GetTitleId() const {
if (status != Loader::ResultStatus::Success)
return {};
return header.title_id;
}
v_dir Nca::GetRomFs() const {
v_file NCA::GetRomFS() const {
return romfs;
}
v_dir Nca::GetExeFs() const {
v_dir NCA::GetExeFS() const {
return exefs;
}
bool NCA::ReplaceFileWithSubdirectory(v_file file, v_dir dir) {
return false;
}
} // namespace FileSys

View File

@@ -11,21 +11,21 @@
namespace FileSys {
enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
struct NcaSectionTableEntry {
struct NCASectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size.");
struct NcaHeader {
struct NCAHeader {
std::array<u8, 0x100> rsa_signature_1;
std::array<u8, 0x100> rsa_signature_2;
u32_le magic;
u8 is_system;
NcaContentType content_type;
NCAContentType content_type;
u8 crypto_type;
u8 key_index;
u64_le size;
@@ -35,46 +35,52 @@ struct NcaHeader {
u8 crypto_type_2;
INSERT_PADDING_BYTES(15);
std::array<u8, 0x10> rights_id;
std::array<NcaSectionTableEntry, 0x4> section_tables;
std::array<NCASectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
static bool IsDirectoryExeFs(std::shared_ptr<FileSys::VfsDirectory> pfs) {
static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) {
// According to switchbrew, an exefs must only contain these two files:
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.ndpm") != nullptr;
}
static bool IsValidNca(const NcaHeader& header) {
static bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
class Nca : public ReadOnlyVfsDirectory {
std::vector<v_dir> entries;
std::vector<u64> entry_offset;
class NCA : public ReadOnlyVfsDirectory {
std::vector<v_dir> dirs{};
std::vector<v_file> files{};
v_dir romfs = nullptr;
v_file romfs = nullptr;
v_dir exefs = nullptr;
v_file file;
NcaHeader header{};
NCAHeader header{};
Loader::ResultStatus status{};
public:
Loader::ResultStatus Load(v_file file);
explicit NCA(v_file file);
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
NcaContentType GetType() const;
NCAContentType GetType() const;
u64 GetTitleId() const;
v_dir GetRomFs() const;
v_dir GetExeFs() const;
v_file GetRomFS() const;
v_dir GetExeFS() const;
protected:
bool ReplaceFileWithSubdirectory(v_file file, v_dir dir) override;
};
} // namespace FileSys

View File

@@ -66,44 +66,4 @@ private:
std::u16string u16str;
};
/// Parameters of the archive, as specified in the Create or Format call.
struct ArchiveFormatInfo {
u32_le total_size; ///< The pre-defined size of the archive.
u32_le number_directories; ///< The pre-defined number of directories in the archive.
u32_le number_files; ///< The pre-defined number of files in the archive.
u8 duplicate_data; ///< Whether the archive should duplicate the data.
};
static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
class FileSystemFactory : NonCopyable {
public:
virtual ~FileSystemFactory() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
virtual std::string GetName() const = 0;
/**
* Tries to open the archive of this type with the specified path
* @param path Path to the archive
* @return An ArchiveBackend corresponding operating specified archive path.
*/
virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
* @return ResultCode of the operation, 0 on success
*/
virtual ResultCode Format(const Path& path) = 0;
/**
* Retrieves the format info about the archive with the specified path
* @param path Path to the archive
* @return Format information about the archive or error code
*/
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
};
} // namespace FileSys

View File

@@ -11,20 +11,25 @@
namespace FileSys {
Loader::ResultStatus PartitionFilesystem::Load(std::shared_ptr<VfsFile> file) {
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
// At least be as large as the header
if (file->GetSize() < sizeof(Header))
return Loader::ResultStatus::Error;
if (file->GetSize() < sizeof(Header)) {
status = Loader::ResultStatus::Error;
return;
}
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
Header pfs_header;
if (!file->ReadObject(&pfs_header))
return Loader::ResultStatus::Error;
if (!file->ReadObject(&pfs_header)) {
status = Loader::ResultStatus::Error;
return;
}
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
@@ -36,17 +41,22 @@ Loader::ResultStatus PartitionFilesystem::Load(std::shared_ptr<VfsFile> file) {
// Actually read in now...
std::vector<u8> file_data = file->ReadBytes(metadata_size);
if (file_data.size() != metadata_size)
return Loader::ResultStatus::Error;
if (file_data.size() != metadata_size) {
status = Loader::ResultStatus::Error;
return;
}
size_t total_size = file_data.size();
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
if (total_size < sizeof(Header)) {
status = Loader::ResultStatus::Error;
return;
}
memcpy(&pfs_header, &file_data, sizeof(Header));
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
@@ -65,7 +75,11 @@ Loader::ResultStatus PartitionFilesystem::Load(std::shared_ptr<VfsFile> file) {
std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name));
}
return Loader::ResultStatus::Success;
status = Loader::ResultStatus::Success;
}
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
}
std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {

View File

@@ -24,7 +24,8 @@ namespace FileSys {
*/
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
Loader::ResultStatus Load(std::shared_ptr<VfsFile> file);
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
@@ -72,6 +73,8 @@ private:
#pragma pack(pop)
Loader::ResultStatus status;
Header pfs_header;
bool is_hfs;
size_t content_offset;

View File

@@ -14,13 +14,19 @@ Loader::ResultStatus ProgramMetadata::Load(v_file file) {
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
if (sizeof(Header) != file->ReadObject(&npdm_header))
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
if (sizeof(Header) != npdm_header_data.size())
return Loader::ResultStatus::Error;
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::Error;
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::Error;
if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset))
return Loader::ResultStatus::Error;
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
return Loader::ResultStatus::Error;

View File

@@ -51,6 +51,7 @@ public:
void Print() const;
private:
// TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -77,6 +78,7 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
// TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;

View File

@@ -1,110 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/romfs_filesystem.h"
namespace FileSys {
std::string RomFS_FileSystem::GetName() const {
return "RomFS";
}
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
}
ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
const std::string& path) const {
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
}
u64 RomFS_FileSystem::GetFreeSpaceSize() const {
LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
return 0;
}
ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path);
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
}
ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
// TODO(Subv): Find error code
return MakeResult<size_t>(0);
}
u64 RomFS_Storage::GetSize() const {
return data_size;
}
bool RomFS_Storage::SetSize(const u64 size) const {
LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
return false;
}
} // namespace FileSys

View File

@@ -1,86 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys {
/**
* Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
* archives This should be subclassed by concrete archive types, which will provide the input data
* (load the raw ROMFS archive) and override any required methods
*/
class RomFS_FileSystem : public FileSystemBackend {
public:
RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
std::string GetName() const override;
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const override;
ResultCode DeleteFile(const std::string& path) const override;
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const std::string& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
class RomFS_Storage : public StorageBackend {
public:
RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override {
return false;
}
void Flush() const override {}
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
class ROMFSDirectory : public DirectoryBackend {
public:
u64 Read(const u64 count, Entry* entries) override {
return 0;
}
u64 GetEntryCount() const override {
return 0;
}
bool Close() const override {
return false;
}
};
} // namespace FileSys

View File

@@ -125,4 +125,16 @@ bool RealVfsDirectory::Rename(const std::string& name) {
return FileUtil::Rename(path.string(), (path / name).string());
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(v_file file, v_dir dir) {
auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
files[iter - files.begin()] = files.back();
files.pop_back();
subdirectories.emplace_back(dir);
return true;
}
} // namespace FileSys

View File

@@ -36,7 +36,6 @@ struct RealVfsDirectory : public VfsDirectory {
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;
bool IsRoot() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
@@ -45,6 +44,9 @@ struct RealVfsDirectory : public VfsDirectory {
bool DeleteFile(const std::string& name) override;
bool Rename(const std::string& name) override;
protected:
bool ReplaceFileWithSubdirectory(v_file file, v_dir dir) override;
private:
filesystem::path path;
filesystem::perms perms;

View File

@@ -5,21 +5,26 @@
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
ResultVal<v_file> OpenRomFS() {
return romfs;
}
/**
* Map of registered file systems, identified by type. Once an file system is registered here, it
* is never removed until UnregisterFileSystems is called.
*/
static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
static boost::container::flat_map<Type, v_dir> filesystem_map;
static v_file filesystem_romfs;
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
auto result = filesystem_map.emplace(type, std::move(factory));
ResultCode RegisterFileSystem(v_dir factory, Type type) {
auto result = filesystem_map.emplace(type, factory);
bool inserted = result.second;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
@@ -30,9 +35,18 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
return RESULT_SUCCESS;
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path) {
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
ResultCode RegisterRomFS(v_file filesystem) {
bool inserted = filesystem_romfs == nullptr;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
filesystem_romfs = filesystem;
NGLOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}",
filesystem->GetName(), static_cast<u32>(Type::RomFS));
return RESULT_SUCCESS;
}
ResultVal<v_dir> OpenFileSystem(Type type) {
NGLOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
@@ -40,7 +54,7 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
return ResultCode(-1);
}
return itr->second->Open(path);
return MakeResult(itr->second);
}
ResultCode FormatFileSystem(Type type) {
@@ -52,8 +66,9 @@ ResultCode FormatFileSystem(Type type) {
return ResultCode(-1);
}
FileSys::Path unused;
return itr->second->Format(unused);
return itr->second->GetParentDirectory()->DeleteSubdirectory(itr->second->GetName())
? RESULT_SUCCESS
: ResultCode(-1);
}
void RegisterFileSystems() {
@@ -62,10 +77,11 @@ void RegisterFileSystems() {
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
auto savedata =
std::make_unique<FileSys::RealVfsDirectory>(nand_directory, filesystem::perms::all);
RegisterFileSystem(std::move(savedata), Type::SaveData);
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
auto sdcard = std::make_unique<FileSys::RealVfsDirectory>(sd_directory, filesystem::perms::all);
RegisterFileSystem(std::move(sdcard), Type::SDMC);
}

View File

@@ -6,6 +6,7 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
@@ -29,12 +30,109 @@ enum class Type {
SDMC = 3,
};
class VfsDirectoryServiceWrapper {
v_dir backing;
public:
VfsDirectoryServiceWrapper(v_dir backing);
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
std::string GetName() const;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
ResultCode CreateFile(const std::string& path, u64 size) const;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteFile(const std::string& path) const;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode CreateDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteDirectoryRecursively(const std::string& path) const;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
ResultVal<v_file> OpenFile(const std::string& path, FileSys::Mode mode) const;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or error code
*/
ResultVal<v_dir> OpenDirectory(const std::string& path) const;
/**
* Get the free space
* @return The number of free bytes in the archive
*/
u64 GetFreeSpaceSize() const;
/**
* Get the type of the specified path
* @return The type of the specified path or error code
*/
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
};
class VfsFileServiceWrapper {
v_file backing;
};
/**
* Registers a FileSystem, instances of which can later be opened using its IdCode.
* @param factory FileSystem backend interface to use
* @param type Type used to access this type of FileSystem
*/
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
ResultCode RegisterFileSystem(v_dir fs, Type type);
ResultCode RegisterRomFS(v_file fs);
/**
* Opens a file system
@@ -42,8 +140,9 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
* @param path Path to the file system, used with Binary paths
* @return FileSys::FileSystemBackend interface to the file system
*/
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
ResultVal<v_dir> OpenFileSystem(Type type);
ResultVal<v_file> OpenRomFS();
/**
* Formats a file system

View File

@@ -19,8 +19,7 @@ namespace Service::FileSystem {
class IStorage final : public ServiceFramework<IStorage> {
public:
IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IStorage"), backend(std::move(backend)) {
IStorage(v_file backend_) : ServiceFramework("IStorage"), backend(backend_) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
@@ -29,7 +28,7 @@ public:
}
private:
std::unique_ptr<FileSys::StorageBackend> backend;
v_file backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -51,8 +50,8 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
std::vector<u8> output = backend->ReadBytes(length, offset);
auto res = MakeResult<size_t>(output.size());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -69,8 +68,7 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IFile"), backend(std::move(backend)) {
explicit IFile(v_file backend_) : ServiceFramework("IFile"), backend(backend_) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
{2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
@@ -80,7 +78,7 @@ public:
}
private:
std::unique_ptr<FileSys::StorageBackend> backend;
v_file backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -103,8 +101,8 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
std::vector<u8> output = backend->ReadBytes(length, offset);
auto res = MakeResult<size_t>(output.size());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -140,8 +138,7 @@ private:
}
// Write the data to the Storage backend
std::vector<u8> data = ctx.ReadBuffer();
ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
auto res = MakeResult<size_t>(backend->WriteBytes(ctx.ReadBuffer(), length, offset));
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -153,8 +150,9 @@ private:
}
void Flush(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
backend->Flush();
NGLOG_DEBUG(Service_FS, "called");
// Exists for SDK compatibiltity -- No need to flush file.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -163,7 +161,7 @@ private:
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
backend->SetSize(size);
backend->Resize(size);
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -232,7 +230,7 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
explicit IFileSystem(v_dir backend)
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
@@ -267,7 +265,12 @@ public:
LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateFile(name, size));
auto b1 = backend->CreateFile(name);
if (b1 == nullptr) {
}
auto b2 = b1->Res
rb.Push(? RESULT_SUCCESS : ResultCode(-1));
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
@@ -291,7 +294,8 @@ public:
LOG_DEBUG(Service_FS, "called directory {}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateDirectory(name));
auto dir = backend->CreateSubdirectory(name);
rb.Push(dir == nullptr ? -1 : 0);
}
void RenameFile(Kernel::HLERequestContext& ctx) {
@@ -389,7 +393,7 @@ public:
}
private:
std::unique_ptr<FileSys::FileSystemBackend> backend;
v_dir backend;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -490,10 +494,9 @@ void FSP_SRV::TryLoadRomFS() {
if (romfs) {
return;
}
FileSys::Path unused;
auto res = OpenFileSystem(Type::RomFS, unused);
auto res = OpenRomFS();
if (res.Succeeded()) {
romfs = std::move(res.Unwrap());
romfs = res.Unwrap();
}
}
@@ -507,8 +510,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
auto filesystem = OpenFileSystem(Type::SDMC).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -559,15 +561,6 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
// Attempt to open a StorageBackend interface to the RomFS
auto storage = romfs->OpenFile({}, {});
if (storage.Failed()) {
LOG_CRITICAL(Service_FS, "no storage interface available!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(storage.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));

View File

@@ -29,7 +29,7 @@ private:
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);
std::unique_ptr<FileSys::FileSystemBackend> romfs;
v_file romfs;
};
} // namespace Service::FileSystem

View File

@@ -4,12 +4,10 @@
#include <cinttypes>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -51,7 +49,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(v_file
: AppLoader(file) {}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(v_file file) {
if (FileSys::IsDirectoryExeFs(file->GetContainingDirectory())) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
}
@@ -110,26 +108,19 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
});
// TODO(DarkLordZach): Identify RomFS if its a subdirectory.
romfs = std::make_shared<RomFs>((romfs_iter == dir->GetFiles().end()) ? nullptr : *romfs_iter);
// Register the RomFS if a ".romfs" file was found
if (romfs != nullptr) {
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
}
romfs = (romfs_iter == dir->GetFiles().end()) ? nullptr : *romfs_iter;
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(v_dir& dir) {
if (filepath_romfs.empty()) {
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(v_file& file) {
if (romfs == nullptr) {
LOG_DEBUG(Loader, "No RomFS available");
return ResultStatus::ErrorNotUsed;
}
dir = romfs;
file = romfs;
return ResultStatus::Success;
}

View File

@@ -35,10 +35,10 @@ public:
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(v_dir& dir) override;
ResultStatus ReadRomFS(v_file& file) override;
private:
v_dir romfs;
v_file romfs;
FileSys::ProgramMetadata metadata;
};

View File

@@ -158,7 +158,7 @@ public:
* @param file The file containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadRomFS(v_dir& dir) {
virtual ResultStatus ReadRomFS(v_file& dir) {
return ResultStatus::ErrorNotImplemented;
}
@@ -168,7 +168,7 @@ public:
* @param file The file containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadUpdateRomFS(v_dir& file) {
virtual ResultStatus ReadUpdateRomFS(v_file& file) {
return ResultStatus::ErrorNotImplemented;
}

View File

@@ -4,14 +4,11 @@
#include <vector>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -21,15 +18,15 @@
namespace Loader {
AppLoader_NCA::AppLoader_NCA(v_file file) : AppLoader(std::move(file)) {}
AppLoader_NCA::AppLoader_NCA(v_file file) : AppLoader(file) {}
FileType AppLoader_NCA::IdentifyType(v_file file) {
// TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
FileSys::NcaHeader header{};
if (sizeof(FileSys::NcaHeader) != file->ReadObject(&header))
FileSys::NCAHeader header{};
if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header))
return FileType::Error;
if (IsValidNca(header) && header.content_type == FileSys::NcaContentType::Program)
if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program)
return FileType::NCA;
return FileType::Error;
@@ -40,16 +37,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::ErrorAlreadyLoaded;
}
nca = std::make_unique<FileSys::Nca>();
ResultStatus result = nca->Load(file);
nca = std::make_unique<FileSys::NCA>(file);
ResultStatus result = nca->GetStatus();
if (result != ResultStatus::Success) {
return result;
}
if (nca->GetType() != FileSys::NcaContentType::Program)
if (nca->GetType() != FileSys::NCAContentType::Program)
return ResultStatus::ErrorInvalidFormat;
auto exefs = nca->GetExeFs();
auto exefs = nca->GetExeFS();
if (exefs == nullptr)
return ResultStatus::ErrorInvalidFormat;
@@ -85,23 +82,19 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
if (nca->GetRomFs() != nullptr)
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadRomFS(v_dir& dir) {
const auto romfs = nca->GetRomFs();
ResultStatus AppLoader_NCA::ReadRomFS(v_file& file) {
const auto romfs = nca->GetRomFS();
if (romfs == nullptr) {
NGLOG_DEBUG(Loader, "No RomFS available");
return ResultStatus::ErrorNotUsed;
}
dir = romfs;
file = romfs;
return ResultStatus::Success;
}

View File

@@ -6,7 +6,7 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
@@ -31,14 +31,14 @@ public:
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(v_dir& dir) override;
ResultStatus ReadRomFS(v_file& file) override;
~AppLoader_NCA();
private:
FileSys::ProgramMetadata metadata;
std::unique_ptr<FileSys::Nca> nca;
std::unique_ptr<FileSys::NCA> nca;
};
} // namespace Loader

View File

@@ -37,6 +37,7 @@ struct NsoHeader {
std::array<u32_le, 3> segments_compressed_size;
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
static_assert(std::is_trivially_copyable<NsoHeader>::value, "NsoHeader isn't trivially copyable.");
struct ModHeader {
u32_le magic;
@@ -99,7 +100,7 @@ VAddr AppLoader_NSO::LoadModule(v_file file, VAddr load_base) {
return {};
NsoHeader nso_header{};
if (sizeof(NsoHeader) != file->ReadObject(&file))
if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
return {};
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))