mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	file_sys: Remove disk_archive, savedata_archive, and title_metadata.
This commit is contained in:
		
							parent
							
								
									7988f02489
								
							
						
					
					
						commit
						1c06c918af
					
				| @ -9,18 +9,12 @@ add_library(core STATIC | ||||
|     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/errors.h | ||||
|     file_sys/file_backend.h | ||||
|     file_sys/path_parser.cpp | ||||
|     file_sys/path_parser.h | ||||
|     file_sys/romfs_archive.cpp | ||||
|     file_sys/romfs_archive.h | ||||
|     file_sys/savedata_archive.cpp | ||||
|     file_sys/savedata_archive.h | ||||
|     file_sys/title_metadata.cpp | ||||
|     file_sys/title_metadata.h | ||||
|     frontend/emu_window.cpp | ||||
|     frontend/emu_window.h | ||||
|     frontend/framebuffer_layout.cpp | ||||
|  | ||||
| @ -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
 | ||||
| @ -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
 | ||||
| @ -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::GetFreeSpaceSize() const { | ||||
|     // TODO: Stubbed to return 1GiB
 | ||||
|     return 1024 * 1024 * 1024; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
| @ -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 GetFreeSpaceSize() const override; | ||||
| 
 | ||||
| protected: | ||||
|     std::string mount_point; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
| @ -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
 | ||||
| @ -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
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei