diff options
Diffstat (limited to '')
25 files changed, 535 insertions, 947 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7153c4f3f..433e7e596 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,21 +6,17 @@ add_library(core STATIC core.h core_timing.cpp core_timing.h - file_sys/archive_backend.cpp - file_sys/archive_backend.h - file_sys/directory_backend.h - file_sys/disk_archive.cpp - file_sys/disk_archive.h + file_sys/directory.h file_sys/errors.h - file_sys/file_backend.h - file_sys/ivfc_archive.cpp - file_sys/ivfc_archive.h + file_sys/filesystem.cpp + file_sys/filesystem.h file_sys/path_parser.cpp file_sys/path_parser.h - file_sys/savedata_archive.cpp - file_sys/savedata_archive.h - file_sys/title_metadata.cpp - file_sys/title_metadata.h + file_sys/romfs_factory.cpp + file_sys/romfs_factory.h + file_sys/romfs_filesystem.cpp + file_sys/romfs_filesystem.h + file_sys/storage.h frontend/emu_window.cpp frontend/emu_window.h frontend/framebuffer_layout.cpp @@ -101,6 +97,10 @@ add_library(core STATIC hle/service/audio/audio.h hle/service/audio/audout_u.cpp hle/service/audio/audout_u.h + hle/service/filesystem/filesystem.cpp + hle/service/filesystem/filesystem.h + hle/service/filesystem/fsp_srv.cpp + hle/service/filesystem/fsp_srv.h hle/service/hid/hid.cpp hle/service/hid/hid.h hle/service/lm/lm.cpp diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory.h index 0c93f2074..5a40bf472 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp deleted file mode 100644 index 98d80aabc..000000000 --- a/src/core/file_sys/disk_archive.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstdio> -#include <memory> -#include "common/common_types.h" -#include "common/file_util.h" -#include "common/logging/log.h" -#include "core/file_sys/disk_archive.h" -#include "core/file_sys/errors.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { - if (!mode.read_flag) - return ERROR_INVALID_OPEN_FLAGS; - - file->Seek(offset, SEEK_SET); - return MakeResult<size_t>(file->ReadBytes(buffer, length)); -} - -ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, - const u8* buffer) const { - if (!mode.write_flag) - return ERROR_INVALID_OPEN_FLAGS; - - file->Seek(offset, SEEK_SET); - size_t written = file->WriteBytes(buffer, length); - if (flush) - file->Flush(); - return MakeResult<size_t>(written); -} - -u64 DiskFile::GetSize() const { - return file->GetSize(); -} - -bool DiskFile::SetSize(const u64 size) const { - file->Resize(size); - file->Flush(); - return true; -} - -bool DiskFile::Close() const { - return file->Close(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -DiskDirectory::DiskDirectory(const std::string& path) : directory() { - unsigned size = FileUtil::ScanDirectoryTree(path, directory); - directory.size = size; - directory.isDirectory = true; - children_iterator = directory.children.begin(); -} - -u32 DiskDirectory::Read(const u32 count, Entry* entries) { - u32 entries_read = 0; - - while (entries_read < count && children_iterator != directory.children.cend()) { - const FileUtil::FSTEntry& file = *children_iterator; - const std::string& filename = file.virtualName; - Entry& entry = entries[entries_read]; - - LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, - file.isDirectory); - - // TODO(Link Mauve): use a proper conversion to UTF-16. - for (size_t j = 0; j < FILENAME_LENGTH; ++j) { - entry.filename[j] = filename[j]; - if (!filename[j]) - break; - } - - FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); - - entry.is_directory = file.isDirectory; - entry.is_hidden = (filename[0] == '.'); - entry.is_read_only = 0; - entry.file_size = file.size; - - // We emulate a SD card where the archive bit has never been cleared, as it would be on - // most user SD cards. - // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a - // file bit. - entry.is_archive = !file.isDirectory; - - ++entries_read; - ++children_iterator; - } - return entries_read; -} - -} // namespace FileSys diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h deleted file mode 100644 index eb9166df6..000000000 --- a/src/core/file_sys/disk_archive.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// 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/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" -#include "core/hle/result.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -class DiskFile : public FileBackend { -public: - DiskFile(FileUtil::IOFile&& file_, const Mode& mode_) - : file(new FileUtil::IOFile(std::move(file_))) { - mode.hex = mode_.hex; - } - - 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; - - void Flush() const override { - file->Flush(); - } - -protected: - Mode mode; - std::unique_ptr<FileUtil::IOFile> file; -}; - -class DiskDirectory : public DirectoryBackend { -public: - DiskDirectory(const std::string& path); - - ~DiskDirectory() override { - Close(); - } - - u32 Read(const u32 count, Entry* entries) override; - - bool Close() const override { - return true; - } - -protected: - u32 total_entries_in_directory; - FileUtil::FSTEntry directory; - - // We need to remember the last entry we returned, so a subsequent call to Read will continue - // from the next one. This iterator will always point to the next unread entry. - std::vector<FileUtil::FSTEntry>::iterator children_iterator; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/filesystem.cpp index fc472b44f..82fdb3c46 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/filesystem.cpp @@ -1,4 +1,4 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,7 +7,7 @@ #include <sstream> #include "common/logging/log.h" #include "common/string_util.h" -#include "core/file_sys/archive_backend.h" +#include "core/file_sys/filesystem.h" #include "core/memory.h" namespace FileSys { diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/filesystem.h index 58f6c150c..02705506b 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/filesystem.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -15,7 +15,7 @@ namespace FileSys { -class FileBackend; +class StorageBackend; class DirectoryBackend; // Path string type @@ -71,9 +71,9 @@ struct ArchiveFormatInfo { }; static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); -class ArchiveBackend : NonCopyable { +class FileSystemBackend : NonCopyable { public: - virtual ~ArchiveBackend() {} + virtual ~FileSystemBackend() {} /** * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) @@ -81,13 +81,12 @@ public: virtual std::string GetName() const = 0; /** - * 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 + * 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 */ - virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode& mode) const = 0; + virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; /** * Delete a file specified by its path @@ -97,12 +96,11 @@ public: virtual ResultCode DeleteFile(const Path& path) const = 0; /** - * 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 + * Create a directory specified by its path + * @param path Path relative to the archive * @return Result of the operation */ - virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; + virtual ResultCode CreateDirectory(const Path& path) const = 0; /** * Delete a directory specified by its path @@ -119,19 +117,12 @@ public: virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; /** - * 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 - */ - virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; - - /** - * Create a directory specified by its path - * @param path Path relative to the archive + * 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 */ - virtual ResultCode CreateDirectory(const Path& path) const = 0; + virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; /** * Rename a Directory specified by its path @@ -142,6 +133,15 @@ public: virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; /** + * 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 + */ + virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, + const Mode& mode) const = 0; + + /** * Open a directory specified by its path * @param path Path relative to the archive * @return Opened directory, or error code @@ -152,12 +152,12 @@ public: * Get the free space * @return The number of free bytes in the archive */ - virtual u64 GetFreeBytes() const = 0; + virtual u64 GetFreeSpaceSize() const = 0; }; -class ArchiveFactory : NonCopyable { +class FileSystemFactory : NonCopyable { public: - virtual ~ArchiveFactory() {} + virtual ~FileSystemFactory() {} /** * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) @@ -169,7 +169,7 @@ public: * @param path Path to the archive * @return An ArchiveBackend corresponding operating specified archive path. */ - virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0; + virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0; /** * Deletes the archive contents and then re-creates the base folder diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h index b9f52f65d..184f59d55 100644 --- a/src/core/file_sys/path_parser.h +++ b/src/core/file_sys/path_parser.h @@ -6,7 +6,7 @@ #include <string> #include <vector> -#include "core/file_sys/archive_backend.h" +#include "core/file_sys/filesystem.h" namespace FileSys { diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp new file mode 100644 index 000000000..e0de49f05 --- /dev/null +++ b/src/core/file_sys/romfs_factory.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <memory> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/romfs_filesystem.h" + +namespace FileSys { + +RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { + // Load the RomFS from the app + if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { + LOG_ERROR(Service_FS, "Unable to read RomFS!"); + } +} + +ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { + auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); + return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); +} + +ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { + LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +} // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h new file mode 100644 index 000000000..10ea13966 --- /dev/null +++ b/src/core/file_sys/romfs_factory.h @@ -0,0 +1,35 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> +#include <vector> +#include "common/common_types.h" +#include "core/file_sys/filesystem.h" +#include "core/hle/result.h" +#include "core/loader/loader.h" + +namespace FileSys { + +/// File system interface to the RomFS archive +class RomFS_Factory final : public FileSystemFactory { +public: + explicit RomFS_Factory(Loader::AppLoader& app_loader); + + std::string GetName() const override { + return "ArchiveFactory_RomFS"; + } + ResultVal<std::unique_ptr<FileSystemBackend>> 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::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/romfs_filesystem.cpp index b3c3f2c6f..ca1463d7c 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,84 +6,80 @@ #include <memory> #include "common/common_types.h" #include "common/logging/log.h" -#include "core/file_sys/ivfc_archive.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace +#include "core/file_sys/romfs_filesystem.h" namespace FileSys { -std::string IVFCArchive::GetName() const { - return "IVFC"; +std::string RomFS_FileSystem::GetName() const { + return "RomFS"; } -ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, - const Mode& mode) const { - return MakeResult<std::unique_ptr<FileBackend>>( - std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); +ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, + const Mode& mode) const { + return MakeResult<std::unique_ptr<StorageBackend>>( + std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); } -ResultCode IVFCArchive::DeleteFile(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", +ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { + LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { - LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", +ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const { + LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::CreateDirectory(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", +ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", +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 (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { - return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); +ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( + const Path& path) const { + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); } -u64 IVFCArchive::GetFreeBytes() const { - LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive"); +u64 RomFS_FileSystem::GetFreeSpaceSize() const { + LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); return 0; } -//////////////////////////////////////////////////////////////////////////////////////////////////// - -ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { +ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); romfs_file->Seek(data_offset + offset, SEEK_SET); size_t read_length = (size_t)std::min((u64)length, data_size - offset); @@ -91,19 +87,19 @@ ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buff return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); } -ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, - const u8* buffer) const { - LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); +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 IVFCFile::GetSize() const { +u64 RomFS_Storage::GetSize() const { return data_size; } -bool IVFCFile::SetSize(const u64 size) const { - LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file"); +bool RomFS_Storage::SetSize(const u64 size) const { + LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); return false; } diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/romfs_filesystem.h index e6fbdfb1f..900ea567a 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/romfs_filesystem.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -10,30 +10,27 @@ #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "core/file_sys/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" +#include "core/file_sys/directory.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - namespace FileSys { /** - * Helper which implements an interface to deal with IVFC images used in some archives - * This should be subclassed by concrete archive types, which will provide the - * input data (load the raw IVFC archive) and override any required methods + * 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 IVFCArchive : public ArchiveBackend { +class RomFS_FileSystem : public FileSystemBackend { public: - IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + 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<FileBackend>> OpenFile(const Path& path, - const Mode& mode) const override; + ResultVal<std::unique_ptr<StorageBackend>> 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; @@ -42,7 +39,7 @@ public: 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; + u64 GetFreeSpaceSize() const override; protected: std::shared_ptr<FileUtil::IOFile> romfs_file; @@ -50,9 +47,9 @@ protected: u64 data_size; }; -class IVFCFile : public FileBackend { +class RomFS_Storage : public StorageBackend { public: - IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + 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; @@ -70,7 +67,7 @@ private: u64 data_size; }; -class IVFCDirectory : public DirectoryBackend { +class ROMFSDirectory : public DirectoryBackend { public: u32 Read(const u32 count, Entry* entries) override { return 0; diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp deleted file mode 100644 index d7b012f6e..000000000 --- a/src/core/file_sys/savedata_archive.cpp +++ /dev/null @@ -1,330 +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 "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" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(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_UNSUPPORTED_OPEN_FLAGS; - } - - if (mode.create_flag && !mode.write_flag) { - LOG_ERROR(Service_FS, "Create flag set but write flag not set"); - 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: - 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_FILE_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_FILE_NOT_FOUND; - } - - auto disk_file = std::make_unique<DiskFile>(std::move(file), mode); - return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); -} - -ResultCode SaveDataArchive::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_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: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "File not found %s", full_path.c_str()); - return ERROR_FILE_NOT_FOUND; - 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_FILE_NOT_FOUND; -} - -ResultCode SaveDataArchive::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(bunnei): Use correct error code - return ResultCode(-1); -} - -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_DIRECTORY_NOT_EMPTY; - - 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_PATH_NOT_FOUND; - case PathParser::PathNotFound: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - 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_DIRECTORY_NOT_EMPTY; -} - -ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const { - return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); -} - -ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const { - return DeleteDirectoryHelper( - path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); -} - -ResultCode SaveDataArchive::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_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: - LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); - return ERROR_FILE_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"); - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultCode SaveDataArchive::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_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: - LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); - return ERROR_DIRECTORY_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()); - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultCode SaveDataArchive::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(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::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_FILE_NOT_FOUND; - case PathParser::PathNotFound: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_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; - 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 SaveDataArchive::GetFreeBytes() const { - // TODO: Stubbed to return 1GiB - return 1024 * 1024 * 1024; -} - -} // namespace FileSys diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h deleted file mode 100644 index 176d35710..000000000 --- a/src/core/file_sys/savedata_archive.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> -#include "core/file_sys/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" -#include "core/hle/result.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -/// Archive backend for general save data archive type (SaveData and SystemSaveData) -class SaveDataArchive : public ArchiveBackend { -public: - explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} - - std::string GetName() const override { - return "SaveDataArchive: " + 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: - std::string mount_point; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/storage.h index 5e7c2bab4..2a6811831 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/storage.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -8,15 +8,12 @@ #include "common/common_types.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - namespace FileSys { -class FileBackend : NonCopyable { +class StorageBackend : NonCopyable { public: - FileBackend() {} - virtual ~FileBackend() {} + StorageBackend() {} + virtual ~StorageBackend() {} /** * Read data from the file @@ -39,10 +36,9 @@ public: const u8* buffer) const = 0; /** - * Get the size of the file in bytes - * @return Size of the file in bytes + * Flushes the file */ - virtual u64 GetSize() const = 0; + virtual void Flush() const = 0; /** * Set the size of the file in bytes @@ -52,15 +48,16 @@ public: virtual bool SetSize(u64 size) const = 0; /** - * Close the file - * @return true if the file closed correctly + * Get the size of the file in bytes + * @return Size of the file in bytes */ - virtual bool Close() const = 0; + virtual u64 GetSize() const = 0; /** - * Flushes the file + * Close the file + * @return true if the file closed correctly */ - virtual void Flush() const = 0; + virtual bool Close() const = 0; }; } // namespace FileSys diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp deleted file mode 100644 index e29ba6064..000000000 --- a/src/core/file_sys/title_metadata.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cinttypes> -#include "common/alignment.h" -#include "common/file_util.h" -#include "common/logging/log.h" -#include "core/file_sys/title_metadata.h" -#include "core/loader/loader.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -static u32 GetSignatureSize(u32 signature_type) { - switch (signature_type) { - case Rsa4096Sha1: - case Rsa4096Sha256: - return 0x200; - - case Rsa2048Sha1: - case Rsa2048Sha256: - return 0x100; - - case EllipticSha1: - case EcdsaSha256: - return 0x3C; - } -} - -Loader::ResultStatus TitleMetadata::Load() { - FileUtil::IOFile file(filepath, "rb"); - if (!file.IsOpen()) - return Loader::ResultStatus::Error; - - if (!file.ReadBytes(&signature_type, sizeof(u32_be))) - return Loader::ResultStatus::Error; - - // Signature lengths are variable, and the body follows the signature - u32 signature_size = GetSignatureSize(signature_type); - - tmd_signature.resize(signature_size); - if (!file.ReadBytes(&tmd_signature[0], signature_size)) - return Loader::ResultStatus::Error; - - // The TMD body start position is rounded to the nearest 0x40 after the signature - size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); - file.Seek(body_start, SEEK_SET); - - // Read our TMD body, then load the amount of ContentChunks specified - if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body)) - return Loader::ResultStatus::Error; - - for (u16 i = 0; i < tmd_body.content_count; i++) { - ContentChunk chunk; - if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) { - tmd_chunks.push_back(chunk); - } else { - LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!", - filepath.c_str(), i); - return Loader::ResultStatus::ErrorInvalidFormat; - } - } - - return Loader::ResultStatus::Success; -} - -Loader::ResultStatus TitleMetadata::Save() { - UNIMPLEMENTED(); - return Loader::ResultStatus::Success; -} - -u64 TitleMetadata::GetTitleID() const { - return tmd_body.title_id; -} - -u32 TitleMetadata::GetTitleType() const { - return tmd_body.title_type; -} - -u16 TitleMetadata::GetTitleVersion() const { - return tmd_body.title_version; -} - -u64 TitleMetadata::GetSystemVersion() const { - return tmd_body.system_version; -} - -size_t TitleMetadata::GetContentCount() const { - return tmd_chunks.size(); -} - -u32 TitleMetadata::GetBootContentID() const { - return tmd_chunks[TMDContentIndex::Main].id; -} - -u32 TitleMetadata::GetManualContentID() const { - return tmd_chunks[TMDContentIndex::Manual].id; -} - -u32 TitleMetadata::GetDLPContentID() const { - return tmd_chunks[TMDContentIndex::DLP].id; -} - -void TitleMetadata::SetTitleID(u64 title_id) { - tmd_body.title_id = title_id; -} - -void TitleMetadata::SetTitleType(u32 type) { - tmd_body.title_type = type; -} - -void TitleMetadata::SetTitleVersion(u16 version) { - tmd_body.title_version = version; -} - -void TitleMetadata::SetSystemVersion(u64 version) { - tmd_body.system_version = version; -} - -void TitleMetadata::AddContentChunk(const ContentChunk& chunk) { - tmd_chunks.push_back(chunk); -} - -void TitleMetadata::Print() const { - LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(), - static_cast<u32>(tmd_body.content_count)); - - // Content info describes ranges of content chunks - LOG_DEBUG(Service_FS, "Content info:"); - for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { - if (tmd_body.contentinfo[i].command_count == 0) - break; - - LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X", - static_cast<u32>(tmd_body.contentinfo[i].index), - static_cast<u32>(tmd_body.contentinfo[i].command_count)); - } - - // For each content info, print their content chunk range - for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { - u16 index = static_cast<u16>(tmd_body.contentinfo[i].index); - u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count); - - if (count == 0) - continue; - - LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i); - for (u16 j = index; j < index + count; j++) { - // Don't attempt to print content we don't have - if (j > tmd_body.content_count) - break; - - const ContentChunk& chunk = tmd_chunks[j]; - LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64, - static_cast<u32>(chunk.id), static_cast<u32>(chunk.index), - static_cast<u32>(chunk.type), static_cast<u64>(chunk.size)); - } - } -} -} // namespace FileSys diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h deleted file mode 100644 index a4c7d1089..000000000 --- a/src/core/file_sys/title_metadata.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <string> -#include <vector> -#include "common/common_types.h" -#include "common/swap.h" - -namespace Loader { -enum class ResultStatus; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -enum TMDSignatureType : u32 { - Rsa4096Sha1 = 0x10000, - Rsa2048Sha1 = 0x10001, - EllipticSha1 = 0x10002, - Rsa4096Sha256 = 0x10003, - Rsa2048Sha256 = 0x10004, - EcdsaSha256 = 0x10005 -}; - -enum TMDContentTypeFlag : u16 { - Encrypted = 1 << 1, - Disc = 1 << 2, - CFM = 1 << 3, - Optional = 1 << 14, - Shared = 1 << 15 -}; - -/** - * Helper which implements an interface to read and write Title Metadata (TMD) files. - * If a file path is provided and the file exists, it can be parsed and used, otherwise - * it must be created. The TMD file can then be interpreted, modified and/or saved. - */ -class TitleMetadata { -public: - struct ContentChunk { - u32_be id; - u16_be index; - u16_be type; - u64_be size; - std::array<u8, 0x20> hash; - }; - - static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong"); - - struct ContentInfo { - u16_be index; - u16_be command_count; - std::array<u8, 0x20> hash; - }; - - static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong"); - -#pragma pack(push, 1) - - struct Body { - std::array<u8, 0x40> issuer; - u8 version; - u8 ca_crl_version; - u8 signer_crl_version; - u8 reserved; - u64_be system_version; - u64_be title_id; - u32_be title_type; - u16_be group_id; - u32_be savedata_size; - u32_be srl_private_savedata_size; - std::array<u8, 4> reserved_2; - u8 srl_flag; - std::array<u8, 0x31> reserved_3; - u32_be access_rights; - u16_be title_version; - u16_be content_count; - u16_be boot_content; - std::array<u8, 2> reserved_4; - std::array<u8, 0x20> contentinfo_hash; - std::array<ContentInfo, 64> contentinfo; - }; - - static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong"); - -#pragma pack(pop) - - explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {} - Loader::ResultStatus Load(); - Loader::ResultStatus Save(); - - u64 GetTitleID() const; - u32 GetTitleType() const; - u16 GetTitleVersion() const; - u64 GetSystemVersion() const; - size_t GetContentCount() const; - u32 GetBootContentID() const; - u32 GetManualContentID() const; - u32 GetDLPContentID() const; - - void SetTitleID(u64 title_id); - void SetTitleType(u32 type); - void SetTitleVersion(u16 version); - void SetSystemVersion(u64 version); - void AddContentChunk(const ContentChunk& chunk); - - void Print() const; - -private: - enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 }; - - Body tmd_body; - u32_be signature_type; - std::vector<u8> tmd_signature; - std::vector<ContentChunk> tmd_chunks; - - std::string filepath; -}; - -} // namespace FileSys diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 4c9b0de28..a27cfbc2d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -305,6 +305,11 @@ inline u64 RequestParser::Pop() { } template <> +inline s64 RequestParser::Pop() { + return static_cast<s64>(Pop<u64>()); +} + +template <> inline bool RequestParser::Pop() { return Pop<u8>() != 0; } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 10ddc4feb..656e1b4a7 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -19,6 +19,8 @@ enum class ErrorDescription : u32 { Success = 0, RemoteProcessDead = 301, + InvalidOffset = 6061, + InvalidLength = 6062, }; /** diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp new file mode 100644 index 000000000..4b47548fd --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -0,0 +1,54 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/container/flat_map.hpp> +#include "core/file_sys/filesystem.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +/** + * 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; + +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { + auto result = filesystem_map.emplace(type, std::move(factory)); + + bool inserted = result.second; + ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); + + auto& filesystem = result.first->second; + LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X", + filesystem->GetName().c_str(), static_cast<u32>(type)); + return RESULT_SUCCESS; +} + +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path) { + LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + return itr->second->Open(path); +} + +void UnregisterFileSystems() { + filesystem_map.clear(); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + UnregisterFileSystems(); + std::make_shared<FSP_SRV>()->InstallAsService(service_manager); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h new file mode 100644 index 000000000..a674c9493 --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace FileSys { +class FileSystemBackend; +class FileSystemFactory; +class Path; +} // namespace FileSys + +namespace Service { + +namespace SM { +class ServiceManager; +} // namespace SM + +namespace FileSystem { + +/// Supported FileSystem types +enum class Type { + RomFS = 1, +}; + +/** + * 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); + +/** + * Opens a file system + * @param type Type of the file system to open + * @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); + +/// Registers all Filesystem services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp new file mode 100644 index 000000000..ef1915e5a --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -0,0 +1,138 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IStorage"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + const auto& descriptor = ctx.BufferDescriptorB()[0]; + + LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); + + // Error checking + ASSERT_MSG(length == descriptor.Size(), "unexpected size difference"); + if (length < 0) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size()); + + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + static const FunctionInfo functions[] = { + {1, &FSP_SRV::Initalize, "Initalize"}, + {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, + {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, + {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, + }; + RegisterHandlers(functions); +} + +void FSP_SRV::TryLoadRomFS() { + if (romfs) { + return; + } + FileSys::Path unused; + auto res = OpenFileSystem(Type::RomFS, unused); + if (res.Succeeded()) { + romfs = std::move(res.Unwrap()); + } +} + +void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::RequestBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(5); +} + +void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + TryLoadRomFS(); + if (!romfs) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + 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::RequestBuilder rb{ctx, 2}; + rb.Push(storage.Code()); + return; + } + + IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); +} + +void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); + OpenDataStorageByCurrentProcess(ctx); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h new file mode 100644 index 000000000..15be8edc1 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/service/service.h" + +namespace FileSys { +class FileSystemBackend; +} + +namespace Service { +namespace FileSystem { + +class FSP_SRV final : public ServiceFramework<FSP_SRV> { +public: + explicit FSP_SRV(); + ~FSP_SRV() = default; + +private: + void TryLoadRomFS(); + + void Initalize(Kernel::HLERequestContext& ctx); + void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OpenRomStorage(Kernel::HLERequestContext& ctx); + + std::unique_ptr<FileSys::FileSystemBackend> romfs; +}; + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 19213a2f4..3f5ce56c6 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -19,6 +19,7 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -172,6 +173,7 @@ void Init() { AOC::InstallInterfaces(*SM::g_service_manager); APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); + FileSystem::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 4bee5fb86..37030683b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -7,14 +7,44 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.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" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/nso.h" #include "core/memory.h" namespace Loader { +static std::string FindRomFS(const std::string& directory) { + std::string filepath_romfs; + const auto callback = [&filepath_romfs](unsigned*, const std::string& directory, + const std::string& virtual_name) -> bool { + const std::string physical_name = directory + virtual_name; + if (FileUtil::IsDirectory(physical_name)) { + // Skip directories + return true; + } + + // Verify extension + const std::string extension = physical_name.substr(physical_name.find_last_of(".") + 1); + if (Common::ToLower(extension) != "istorage") { + return true; + } + + // Found it - we are done + filepath_romfs = std::move(physical_name); + return false; + }; + + // Search the specified directory recursively, looking for the first .istorage file, which will + // be used for the RomFS + FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); + + return filepath_romfs; +} + AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath) : AppLoader(std::move(file)), filepath(std::move(filepath)) {} @@ -79,10 +109,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( // Load NSO modules VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; + const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { - const std::string path = - filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP + module; + const std::string path = directory + DIR_SEP + module; const VAddr load_addr = next_load_addr; next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); if (next_load_addr) { @@ -98,8 +128,43 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + // Find the RomFS by searching for a ".istorage" file in this directory + filepath_romfs = FindRomFS(directory); + + // Register the RomFS if a ".istorage" file was found + if (!filepath_romfs.empty()) { + Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), + Service::FileSystem::Type::RomFS); + } + is_loaded = true; return ResultStatus::Success; } +ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( + std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { + + if (filepath_romfs.empty()) { + LOG_DEBUG(Loader, "No RomFS available"); + return ResultStatus::ErrorNotUsed; + } + + // We reopen the file, to allow its position to be independent + romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); + if (!romfs_file->IsOpen()) { + return ResultStatus::Error; + } + + offset = 0; + size = romfs_file->GetSize(); + + LOG_DEBUG(Loader, "RomFS offset: 0x%08X", offset); + LOG_DEBUG(Loader, "RomFS size: 0x%08X", size); + + // Reset read pointer + file.Seek(0, SEEK_SET); + + return ResultStatus::Success; +} + } // namespace Loader diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 162541d54..26493de5e 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -35,7 +35,11 @@ public: ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, + u64& size) override; + private: + std::string filepath_romfs; std::string filepath; }; |