mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 12:34:47 +00:00
ncm: fix fs use, implement more of < 4.0.0 for-initialize/safemode
This commit is contained in:
parent
2194f04fa7
commit
7c973acb0c
20 changed files with 590 additions and 132 deletions
|
@ -19,6 +19,7 @@
|
|||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
#include <stratosphere/fs/impl/fs_filesystem_proxy_type.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_registrar.hpp>
|
||||
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
||||
#include <stratosphere/fs/fs_readonly_filesystem_adapter.hpp>
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include <stratosphere/fs/impl/fs_data.hpp>
|
||||
#include <stratosphere/fs/fs_system_data.hpp>
|
||||
#include <stratosphere/fs/fs_bis.hpp>
|
||||
#include <stratosphere/fs/fs_content.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||
#include <stratosphere/fs/fs_game_card.hpp>
|
||||
#include <stratosphere/fs/fs_sd_card.hpp>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
enum ContentType {
|
||||
ContentType_Meta = 0,
|
||||
ContentType_Control = 1,
|
||||
ContentType_Manual = 2,
|
||||
ContentType_Logo = 3,
|
||||
ContentType_Data = 4,
|
||||
};
|
||||
|
||||
Result MountContent(const char *name, const char *path, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type);
|
||||
|
||||
}
|
|
@ -14,163 +14,217 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "fs_common.hpp"
|
||||
#include "impl/fs_newable.hpp"
|
||||
#include "fsa/fs_ifile.hpp"
|
||||
#include "fsa/fs_idirectory.hpp"
|
||||
#include "fsa/fs_ifilesystem.hpp"
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||
#include <stratosphere/fs/fs_query_range.hpp>
|
||||
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
class RemoteFile : public fsa::IFile, public impl::Newable {
|
||||
private:
|
||||
std::unique_ptr<::FsFile, impl::Deleter> base_file;
|
||||
::FsFile base_file;
|
||||
public:
|
||||
RemoteFile(::FsFile &f) {
|
||||
this->base_file = impl::MakeUnique<::FsFile>();
|
||||
*this->base_file = f;
|
||||
}
|
||||
RemoteFile(const ::FsFile &f) : base_file(f) { /* ... */ }
|
||||
|
||||
virtual ~RemoteFile() { fsFileClose(this->base_file.get()); }
|
||||
virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); }
|
||||
public:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||
return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out);
|
||||
return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out);
|
||||
}
|
||||
|
||||
virtual Result GetSizeImpl(s64 *out) override final {
|
||||
return fsFileGetSize(this->base_file.get(), out);
|
||||
return fsFileGetSize(std::addressof(this->base_file), out);
|
||||
}
|
||||
|
||||
virtual Result FlushImpl() override final {
|
||||
return fsFileFlush(this->base_file.get());
|
||||
return fsFileFlush(std::addressof(this->base_file));
|
||||
}
|
||||
|
||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value);
|
||||
return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value);
|
||||
}
|
||||
|
||||
virtual Result SetSizeImpl(s64 size) override final {
|
||||
return fsFileSetSize(this->base_file.get(), size);
|
||||
return fsFileSetSize(std::addressof(this->base_file), size);
|
||||
}
|
||||
|
||||
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
/* TODO: How should this be handled? */
|
||||
return fs::ResultNotImplemented();
|
||||
R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA());
|
||||
R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
return fsFileOperateRange(std::addressof(this->base_file), static_cast<::FsOperationId>(op_id), offset, size, reinterpret_cast<::FsRangeInfo *>(dst));
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)};
|
||||
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_file.s)))};
|
||||
}
|
||||
};
|
||||
|
||||
class RemoteDirectory : public fsa::IDirectory, public impl::Newable {
|
||||
private:
|
||||
std::unique_ptr<::FsDir, impl::Deleter> base_dir;
|
||||
::FsDir base_dir;
|
||||
public:
|
||||
RemoteDirectory(::FsDir &d) {
|
||||
this->base_dir = impl::MakeUnique<::FsDir>();
|
||||
*this->base_dir = d;
|
||||
}
|
||||
RemoteDirectory(const ::FsDir &d) : base_dir(d) { /* ... */ }
|
||||
|
||||
virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); }
|
||||
virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); }
|
||||
public:
|
||||
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||
return fsDirRead(this->base_dir.get(), out_count, max_entries, out_entries);
|
||||
return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries);
|
||||
}
|
||||
|
||||
virtual Result GetEntryCountImpl(s64 *out) override final {
|
||||
return fsDirGetEntryCount(this->base_dir.get(), out);
|
||||
return fsDirGetEntryCount(std::addressof(this->base_dir), out);
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)};
|
||||
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_dir.s)))};
|
||||
}
|
||||
};
|
||||
|
||||
class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable {
|
||||
private:
|
||||
std::unique_ptr<::FsFileSystem, impl::Deleter> base_fs;
|
||||
::FsFileSystem base_fs;
|
||||
public:
|
||||
RemoteFileSystem(::FsFileSystem &fs) {
|
||||
this->base_fs = impl::MakeUnique<::FsFileSystem>();
|
||||
*this->base_fs = fs;
|
||||
}
|
||||
RemoteFileSystem(const ::FsFileSystem &fs) : base_fs(fs) { /* ... */ }
|
||||
|
||||
virtual ~RemoteFileSystem() { fsFsClose(this->base_fs.get()); }
|
||||
virtual ~RemoteFileSystem() { fsFsClose(std::addressof(this->base_fs)); }
|
||||
private:
|
||||
Result GetPathForServiceObject(fssrv::sf::Path *out_path, const char *path) {
|
||||
/* Copy and null terminate. */
|
||||
std::strncpy(out_path->str, path, sizeof(out_path->str) - 1);
|
||||
out_path->str[sizeof(out_path->str) - 1] = '\x00';
|
||||
|
||||
/* Replace directory separators. */
|
||||
Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator);
|
||||
|
||||
/* Get lengths. */
|
||||
const auto mount_name_len = PathTool::IsWindowsAbsolutePath(path) ? 2 : 0;
|
||||
const auto rel_path = out_path->str + mount_name_len;
|
||||
const auto max_len = fs::EntryNameLengthMax - mount_name_len;
|
||||
return VerifyPath(rel_path, max_len, max_len);
|
||||
}
|
||||
public:
|
||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||
return fsFsCreateFile(this->base_fs.get(), path, size, flags);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags);
|
||||
}
|
||||
|
||||
virtual Result DeleteFileImpl(const char *path) override final {
|
||||
return fsFsDeleteFile(this->base_fs.get(), path);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||
return fsFsCreateDirectory(this->base_fs.get(), path);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||
return fsFsDeleteDirectory(this->base_fs.get(), path);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||
return fsFsRenameFile(this->base_fs.get(), old_path, new_path);
|
||||
fssrv::sf::Path old_sf_path;
|
||||
fssrv::sf::Path new_sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||
return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||
return fsFsRenameDirectory(this->base_fs.get(), old_path, new_path);
|
||||
fssrv::sf::Path old_sf_path;
|
||||
fssrv::sf::Path new_sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||
return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
|
||||
static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType));
|
||||
return fsFsGetEntryType(this->base_fs.get(), path, reinterpret_cast<::FsDirEntryType *>(out));
|
||||
return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out));
|
||||
}
|
||||
|
||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||
FsFile f;
|
||||
R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f));
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
|
||||
*out_file = std::make_unique<RemoteFile>(f);
|
||||
FsFile f;
|
||||
R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f));
|
||||
|
||||
std::unique_ptr<RemoteFile> file(new RemoteFile(f));
|
||||
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
*out_file = std::move(file);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||
FsDir d;
|
||||
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d));
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
|
||||
*out_dir = std::make_unique<RemoteDirectory>(d);
|
||||
FsDir d;
|
||||
R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d));
|
||||
|
||||
std::unique_ptr<RemoteDirectory> dir(new RemoteDirectory(d));
|
||||
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
*out_dir = std::move(dir);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result CommitImpl() override final {
|
||||
return fsFsCommit(this->base_fs.get());
|
||||
return fsFsCommit(std::addressof(this->base_fs));
|
||||
}
|
||||
|
||||
|
||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
||||
return fsFsGetFreeSpace(this->base_fs.get(), path, out);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||
}
|
||||
|
||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
||||
return fsFsGetTotalSpace(this->base_fs.get(), path, out);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||
}
|
||||
|
||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
||||
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path);
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||
}
|
||||
|
||||
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
|
||||
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path, reinterpret_cast<::FsTimeStampRaw *>(out));
|
||||
return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out));
|
||||
}
|
||||
|
||||
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
||||
return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast<FsFileSystemQueryId>(query));
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||
return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast<FsFileSystemQueryId>(query));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
enum FileSystemProxyType {
|
||||
FileSystemProxyType_Code = 0,
|
||||
FileSystemProxyType_Rom = 1,
|
||||
FileSystemProxyType_Logo = 2,
|
||||
FileSystemProxyType_Control = 3,
|
||||
FileSystemProxyType_Manual = 4,
|
||||
FileSystemProxyType_Meta = 5,
|
||||
FileSystemProxyType_Data = 6,
|
||||
FileSystemProxyType_Package = 7,
|
||||
FileSystemProxyType_UpdatePartition = 8,
|
||||
};
|
||||
|
||||
}
|
|
@ -14,9 +14,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "../fs/fs_common.hpp"
|
||||
#include "../fs/fs_directory.hpp"
|
||||
#include "../sf/sf_buffer_tags.hpp"
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fs_directory.hpp>
|
||||
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||
|
||||
namespace ams::fssrv::sf {
|
||||
|
||||
|
|
|
@ -25,4 +25,5 @@
|
|||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||
#include <stratosphere/ncm/ncm_api.hpp>
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace ams::ncm {
|
|||
return this->id_offset;
|
||||
}
|
||||
|
||||
static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs) {
|
||||
static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs = 0) {
|
||||
const u32 size_low = size & 0xFFFFFFFFu;
|
||||
const u16 size_high = static_cast<u16>(size >> 32);
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/ncm/ncm_content_meta.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
class ContentMetaDatabaseBuilder {
|
||||
private:
|
||||
ContentMetaDatabase *db;
|
||||
private:
|
||||
Result BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info);
|
||||
public:
|
||||
explicit ContentMetaDatabaseBuilder(ContentMetaDatabase *d) : db(d) { /* ... */ }
|
||||
|
||||
Result BuildFromStorage(ContentStorage *storage);
|
||||
|
||||
Result Cleanup();
|
||||
};
|
||||
|
||||
}
|
|
@ -19,11 +19,19 @@
|
|||
namespace ams::ncm {
|
||||
|
||||
struct ContentManagerConfig {
|
||||
bool import_database_from_system;
|
||||
bool build_system_database;
|
||||
bool import_database_from_system_on_sd;
|
||||
|
||||
constexpr bool HasAnyImport() const {
|
||||
return this->import_database_from_system || this->import_database_from_system_on_sd;
|
||||
bool HasAnyConfig() const {
|
||||
return this->ShouldBuildDatabase() || this->import_database_from_system_on_sd;
|
||||
}
|
||||
|
||||
bool ShouldBuildDatabase() const {
|
||||
return hos::GetVersion() < hos::Version_400 || this->build_system_database;
|
||||
}
|
||||
|
||||
bool ShouldImportDatabaseFromSignedSystemPartitionOnSd() const {
|
||||
return this->import_database_from_system_on_sd;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_bounded_map.hpp>
|
||||
#include <stratosphere/ncm/ncm_rights_id_cache.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_management_utils.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_memory_key_value_store.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
@ -93,7 +95,9 @@ namespace ams::ncm {
|
|||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
|
||||
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
|
||||
|
||||
Result ImportContentMetaDatabase(StorageId storage_id, const char *import_mount_name, const char *path);
|
||||
Result BuildContentMetaDatabase(StorageId storage_id);
|
||||
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);
|
||||
Result ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path);
|
||||
|
||||
Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const;
|
||||
public:
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace ams::ncm {
|
|||
return lc;
|
||||
}
|
||||
|
||||
ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits<u64>::min(), u64 max = std::numeric_limits<u64>::max(), ContentInstallType install_type = ContentInstallType::Full) {
|
||||
ListCount ListContentMeta(ContentMetaKey *dst, size_t dst_size, ContentMetaType type = ContentMetaType::Unknown, ApplicationId app_id = InvalidApplicationId, u64 min = std::numeric_limits<u64>::min(), u64 max = std::numeric_limits<u64>::max(), ContentInstallType install_type = ContentInstallType::Full) {
|
||||
ListCount lc = {};
|
||||
R_ABORT_UNLESS(this->interface->List(std::addressof(lc.total), std::addressof(lc.written), sf::OutArray<ContentMetaKey>(dst, dst_size), type, app_id, min, max, install_type));
|
||||
return lc;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/ncm/ncm_auto_buffer.hpp>
|
||||
#include <stratosphere/ncm/ncm_storage_id.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_key.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
Result ReadContentMetaPath(AutoBuffer *out, const char *path);
|
||||
|
||||
}
|
74
libraries/libstratosphere/source/fs/fs_content.cpp
Normal file
74
libraries/libstratosphere/source/fs/fs_content.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
impl::FileSystemProxyType ConvertToFileSystemProxyType(ContentType content_type) {
|
||||
switch (content_type) {
|
||||
case ContentType_Control: return impl::FileSystemProxyType_Control;
|
||||
case ContentType_Manual: return impl::FileSystemProxyType_Manual;
|
||||
case ContentType_Meta: return impl::FileSystemProxyType_Meta;
|
||||
case ContentType_Data: return impl::FileSystemProxyType_Data;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result MountContentImpl(const char *name, const char *path, u64 id, impl::FileSystemProxyType type) {
|
||||
/* Open a filesystem using libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), id, static_cast<::FsFileSystemType>(type), path));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa(new RemoteFileSystem(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, u64 id, ContentType content_type) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
|
||||
/* Validate the path. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Mount the content. */
|
||||
return MountContentImpl(name, path, id, ConvertToFileSystemProxyType(content_type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ContentType content_type) {
|
||||
/* This API only supports mounting Manual content. */
|
||||
AMS_ABORT_UNLESS(content_type == ContentType_Manual);
|
||||
|
||||
return MountContent(name, path, ncm::InvalidProgramId, content_type);
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type) {
|
||||
return MountContent(name, path, id.value, content_type);
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type) {
|
||||
return MountContent(name, path, id.value, content_type);
|
||||
}
|
||||
|
||||
}
|
|
@ -176,7 +176,7 @@ namespace ams::fs {
|
|||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
std::unique_ptr<impl::FileAccessor> file_accessor(new impl::FileAccessor(std::move(file), nullptr, static_cast<OpenMode>(mode)));
|
||||
R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInUserFileSystem());
|
||||
R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew());
|
||||
out->handle = file_accessor.release();
|
||||
|
||||
return ResultSuccess();
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
|
||||
Result ContentMetaDatabaseBuilder::BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info) {
|
||||
AMS_ABORT();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContentMetaDatabaseBuilder::BuildFromStorage(ContentStorage *storage) {
|
||||
/* Get the total count of contents. */
|
||||
s32 total_count;
|
||||
R_TRY(storage->GetContentCount(std::addressof(total_count)));
|
||||
|
||||
/* Loop over all contents, looking for a package we can build from. */
|
||||
const size_t MaxContentIds = 64;
|
||||
ContentId content_ids[MaxContentIds];
|
||||
for (s32 offset = 0; offset < total_count; /* ... */) {
|
||||
/* List contents at the current offset. */
|
||||
s32 count;
|
||||
R_TRY(storage->ListContentId(std::addressof(count), content_ids, MaxContentIds, offset));
|
||||
|
||||
/* Loop the contents we listed, looking for a correct one. */
|
||||
for (s32 i = 0; i < count; i++) {
|
||||
/* Get the path for this content id. */
|
||||
auto &content_id = content_ids[i];
|
||||
ncm::Path path;
|
||||
storage->GetPath(std::addressof(path), content_id);
|
||||
|
||||
/* Read the content meta path, and build. */
|
||||
ncm::AutoBuffer package_meta;
|
||||
if (R_SUCCEEDED(ncm::ReadContentMetaPath(std::addressof(package_meta), path.str))) {
|
||||
/* Get the size of the content. */
|
||||
s64 size;
|
||||
R_TRY(storage->GetSize(std::addressof(size), content_id));
|
||||
|
||||
/* Build. */
|
||||
R_TRY(this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(content_id, size, ContentType::Meta)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
offset += count;
|
||||
}
|
||||
|
||||
/* Commit our changes. */
|
||||
return this->db->Commit();
|
||||
}
|
||||
|
||||
Result ContentMetaDatabaseBuilder::Cleanup() {
|
||||
/* This cleans up the content meta by removing all entries. */
|
||||
while (true) {
|
||||
/* List as many keys as we can. */
|
||||
constexpr s32 MaxKeys = 64;
|
||||
ContentMetaKey keys[MaxKeys];
|
||||
auto list_count = this->db->ListContentMeta(keys, MaxKeys);
|
||||
|
||||
/* Remove the listed keys. */
|
||||
for (auto i = 0; i < list_count.written; i++) {
|
||||
R_TRY(this->db->Remove(keys[i]));
|
||||
}
|
||||
|
||||
/* If there aren't more keys to read, we're done. */
|
||||
if (list_count.written < MaxKeys) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit our deletions. */
|
||||
return this->db->Commit();
|
||||
}
|
||||
|
||||
}
|
|
@ -108,25 +108,22 @@ namespace ams::ncm {
|
|||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ShouldPerformImport(const ContentManagerConfig &config, const char *bis_mount_name) {
|
||||
AMS_ASSERT(config.HasAnyImport());
|
||||
if (config.import_database_from_system) {
|
||||
/* If we're importing from system, just do the import. */
|
||||
return true;
|
||||
} else /* if (config.import_database_from_system_on_sd) */ {
|
||||
/* If we're importing from system on SD, make sure that the signed system partition is valid. */
|
||||
const auto version = hos::GetVersion();
|
||||
if (version >= hos::Version_800 || version < hos::Version_400) {
|
||||
/* On >= 8.0.0, a simpler method was added to check validity. */
|
||||
/* This also works on < 4.0.0 (though the system partition will never be on-sd there), */
|
||||
/* and so this will always return false. */
|
||||
char path[fs::MountNameLengthMax + 2 /* :/ */ + 1];
|
||||
std::snprintf(path, sizeof(path), "%s:/", bis_mount_name);
|
||||
return fs::IsSignedSystemPartitionOnSdCardValid(path);
|
||||
} else {
|
||||
/* On 4.0.0-7.0.1, use the remote command to validate the system partition. */
|
||||
return fs::IsSignedSystemPartitionOnSdCardValidDeprecated();
|
||||
}
|
||||
ALWAYS_INLINE bool IsSignedSystemPartitionOnSdCardValid(const char *bis_mount_name) {
|
||||
/* Signed system partition should never be checked on < 4.0.0, as it did not exist before then. */
|
||||
AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_400);
|
||||
|
||||
/* If we're importing from system on SD, make sure that the signed system partition is valid. */
|
||||
const auto version = hos::GetVersion();
|
||||
if (version >= hos::Version_800) {
|
||||
/* On >= 8.0.0, a simpler method was added to check validity. */
|
||||
/* This also works on < 4.0.0 (though the system partition will never be on-sd there), */
|
||||
/* and so this will always return false. */
|
||||
char path[fs::MountNameLengthMax + 2 /* :/ */ + 1];
|
||||
std::snprintf(path, sizeof(path), "%s:/", bis_mount_name);
|
||||
return fs::IsSignedSystemPartitionOnSdCardValid(path);
|
||||
} else {
|
||||
/* On 4.0.0-7.0.1, use the remote command to validate the system partition. */
|
||||
return fs::IsSignedSystemPartitionOnSdCardValidDeprecated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +237,7 @@ namespace ams::ncm {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, const char *import_mount_name, const char *path) {
|
||||
Result ContentManagerImpl::ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path) {
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
/* Obtain the content meta database root. */
|
||||
|
@ -269,6 +266,46 @@ namespace ams::ncm {
|
|||
return fs::CommitSaveData(root->mount_name);
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::BuildContentMetaDatabase(StorageId storage_id) {
|
||||
if (hos::GetVersion() <= hos::Version_400) {
|
||||
/* Temporarily activate the database. */
|
||||
R_TRY(this->ActivateContentMetaDatabase(storage_id));
|
||||
ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); };
|
||||
|
||||
/* Open the content meta database and storage. */
|
||||
ContentMetaDatabase meta_db;
|
||||
ContentStorage storage;
|
||||
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id));
|
||||
R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id));
|
||||
|
||||
/* Create a builder, and build. */
|
||||
ContentMetaDatabaseBuilder builder(std::addressof(meta_db));
|
||||
return builder.BuildFromStorage(std::addressof(storage));
|
||||
} else {
|
||||
/* On 5.0.0+, building just performs an import. */
|
||||
return this->ImportContentMetaDatabase(storage_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition) {
|
||||
/* Only support importing BuiltInSystem. */
|
||||
AMS_ABORT_UNLESS(storage_id == StorageId::BuiltInSystem);
|
||||
|
||||
/* Get a mount name for the system partition. */
|
||||
auto bis_mount_name = impl::CreateUniqueMountName();
|
||||
|
||||
/* Mount the BIS partition that contains the database we're importing. */
|
||||
R_TRY(fs::MountBis(bis_mount_name.str, fs::BisPartitionId::System));
|
||||
ON_SCOPE_EXIT { fs::Unmount(bis_mount_name.str); };
|
||||
|
||||
/* If we're not importing from a signed partition (or the partition signature is valid), import. */
|
||||
if (!from_signed_partition || IsSignedSystemPartitionOnSdCardValid(bis_mount_name.str)) {
|
||||
R_TRY(this->ImportContentMetaDatabaseImpl(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc"));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) {
|
||||
std::scoped_lock lk(this->mutex);
|
||||
|
||||
|
@ -297,22 +334,15 @@ namespace ams::ncm {
|
|||
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
|
||||
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
|
||||
/* NOTE: Nintendo added support for building/importing in 4.0.0. */
|
||||
/* However, there's no reason to restrict this on a per-version basis. */
|
||||
|
||||
/* If we should import the database from system, do so and verify it. */
|
||||
if (config.HasAnyImport()) {
|
||||
/* Get a mount name for the system partition. */
|
||||
auto bis_mount_name = impl::CreateUniqueMountName();
|
||||
|
||||
/* Mount the BIS partition that contains the database we're importing. */
|
||||
R_TRY(fs::MountBis(bis_mount_name.str, fs::BisPartitionId::System));
|
||||
ON_SCOPE_EXIT { fs::Unmount(bis_mount_name.str); };
|
||||
|
||||
if (ShouldPerformImport(config, bis_mount_name.str)) {
|
||||
R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, bis_mount_name.str, "cnmtdb.arc"));
|
||||
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
}
|
||||
/* Try to build or import a database, depending on our configuration. */
|
||||
if (config.ShouldBuildDatabase()) {
|
||||
/* If we should build the database, do so. */
|
||||
R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
} else if (config.ShouldImportDatabaseFromSignedSystemPartitionOnSd()) {
|
||||
/* Otherwise if we should import the database from the SD, do so. */
|
||||
R_TRY(this->ImportContentMetaDatabase(StorageId::BuiltInSystem, true));
|
||||
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ncm_fs_utils.hpp"
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
namespace {
|
||||
|
||||
using FilePathString = kvdb::BoundedString<64>;
|
||||
|
||||
bool IsContentMetaFileName(const char *name) {
|
||||
return impl::PathView(name).HasSuffix(".cnmt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ReadContentMetaPath(AutoBuffer *out, const char *path) {
|
||||
/* Mount the content. */
|
||||
auto mount_name = impl::CreateUniqueMountName();
|
||||
R_TRY(fs::MountContent(mount_name.str, path, fs::ContentType_Meta));
|
||||
ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
|
||||
|
||||
/* Open the root directory. */
|
||||
auto root_path = impl::GetRootDirectoryPath(mount_name);
|
||||
fs::DirectoryHandle dir;
|
||||
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
/* Loop directory reading until we find the entry we're looking for. */
|
||||
while (true) {
|
||||
/* Read one entry, and finish when we fail to read. */
|
||||
fs::DirectoryEntry entry;
|
||||
s64 num_read;
|
||||
R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1));
|
||||
if (num_read == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If this is the content meta file, parse it. */
|
||||
if (IsContentMetaFileName(entry.name)) {
|
||||
/* Create the file path. */
|
||||
FilePathString file_path(root_path.str);
|
||||
file_path.Append(entry.name);
|
||||
|
||||
/* Open the content meta file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), file_path, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Get the meta size. */
|
||||
s64 file_size;
|
||||
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
const size_t meta_size = static_cast<size_t>(file_size);
|
||||
|
||||
/* Create a buffer for the meta. */
|
||||
R_TRY(out->Initialize(meta_size));
|
||||
|
||||
/* Read the meta into the buffer. */
|
||||
return fs::ReadFile(file, 0, out->Get(), meta_size);
|
||||
}
|
||||
}
|
||||
|
||||
return ncm::ResultContentMetaNotFound();
|
||||
}
|
||||
|
||||
}
|
|
@ -155,21 +155,4 @@ namespace ams::ncm::impl {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetSaveDataFlags(u32 *out_flags, u64 save_id) {
|
||||
FsSaveDataExtraData extra_data;
|
||||
|
||||
R_TRY(fsReadSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), save_id));
|
||||
*out_flags = extra_data.flags;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SetSaveDataFlags(u64 save_id, FsSaveDataSpaceId space_id, u32 flags) {
|
||||
FsSaveDataExtraData extra_data;
|
||||
|
||||
R_TRY(fsReadSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), save_id));
|
||||
extra_data.flags = flags;
|
||||
R_TRY(fsWriteSaveDataFileSystemExtraData(&extra_data, sizeof(FsSaveDataExtraData), space_id, save_id));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInCodeA, 3218);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInContentA, 3219);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222);
|
||||
|
@ -79,7 +81,7 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInUserFileSystem, 3420);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999);
|
||||
|
||||
|
@ -235,16 +237,17 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||
|
||||
|
|
|
@ -189,9 +189,9 @@ namespace {
|
|||
|
||||
/* Compile-time configuration. */
|
||||
#ifdef NCM_BUILD_FOR_INTITIALIZE
|
||||
constexpr inline bool ImportSystemDatabase = true;
|
||||
constexpr inline bool BuildSystemDatabase = true;
|
||||
#else
|
||||
constexpr inline bool ImportSystemDatabase = false;
|
||||
constexpr inline bool BuildSystemDatabase = false;
|
||||
#endif
|
||||
|
||||
#ifdef NCM_BUILD_FOR_SAFEMODE
|
||||
|
@ -200,9 +200,9 @@ namespace {
|
|||
constexpr inline bool ImportSystemDatabaseFromSignedSystemPartitionOnSdCard = false;
|
||||
#endif
|
||||
|
||||
static_assert(!(ImportSystemDatabase && ImportSystemDatabaseFromSignedSystemPartitionOnSdCard), "Invalid NCM build configuration!");
|
||||
static_assert(!(BuildSystemDatabase && ImportSystemDatabaseFromSignedSystemPartitionOnSdCard), "Invalid NCM build configuration!");
|
||||
|
||||
constexpr inline ncm::ContentManagerConfig ManagerConfig = { ImportSystemDatabase, ImportSystemDatabaseFromSignedSystemPartitionOnSdCard };
|
||||
constexpr inline ncm::ContentManagerConfig ManagerConfig = { BuildSystemDatabase, ImportSystemDatabaseFromSignedSystemPartitionOnSdCard };
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue