mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-31 06:46:40 +08:00 
			
		
		
		
	Merge pull request #123 from bunnei/fs
Initial implementation of RomFS filesystem and fsp-srv
This commit is contained in:
		
						commit
						ab8525705b
					
				| @ -6,21 +6,17 @@ add_library(core STATIC | |||||||
|     core.h |     core.h | ||||||
|     core_timing.cpp |     core_timing.cpp | ||||||
|     core_timing.h |     core_timing.h | ||||||
|     file_sys/archive_backend.cpp |     file_sys/directory.h | ||||||
|     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/errors.h | ||||||
|     file_sys/file_backend.h |     file_sys/filesystem.cpp | ||||||
|     file_sys/ivfc_archive.cpp |     file_sys/filesystem.h | ||||||
|     file_sys/ivfc_archive.h |  | ||||||
|     file_sys/path_parser.cpp |     file_sys/path_parser.cpp | ||||||
|     file_sys/path_parser.h |     file_sys/path_parser.h | ||||||
|     file_sys/savedata_archive.cpp |     file_sys/romfs_factory.cpp | ||||||
|     file_sys/savedata_archive.h |     file_sys/romfs_factory.h | ||||||
|     file_sys/title_metadata.cpp |     file_sys/romfs_filesystem.cpp | ||||||
|     file_sys/title_metadata.h |     file_sys/romfs_filesystem.h | ||||||
|  |     file_sys/storage.h | ||||||
|     frontend/emu_window.cpp |     frontend/emu_window.cpp | ||||||
|     frontend/emu_window.h |     frontend/emu_window.h | ||||||
|     frontend/framebuffer_layout.cpp |     frontend/framebuffer_layout.cpp | ||||||
| @ -101,6 +97,10 @@ add_library(core STATIC | |||||||
|     hle/service/audio/audio.h |     hle/service/audio/audio.h | ||||||
|     hle/service/audio/audout_u.cpp |     hle/service/audio/audout_u.cpp | ||||||
|     hle/service/audio/audout_u.h |     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.cpp | ||||||
|     hle/service/hid/hid.h |     hle/service/hid/hid.h | ||||||
|     hle/service/lm/lm.cpp |     hle/service/lm/lm.cpp | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2014 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -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,4 +1,4 @@ | |||||||
| // Copyright 2015 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -7,7 +7,7 @@ | |||||||
| #include <sstream> | #include <sstream> | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "core/file_sys/archive_backend.h" | #include "core/file_sys/filesystem.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2014 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -15,7 +15,7 @@ | |||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| class FileBackend; | class StorageBackend; | ||||||
| class DirectoryBackend; | class DirectoryBackend; | ||||||
| 
 | 
 | ||||||
| // Path string type
 | // Path string type
 | ||||||
| @ -71,9 +71,9 @@ struct ArchiveFormatInfo { | |||||||
| }; | }; | ||||||
| static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | ||||||
| 
 | 
 | ||||||
| class ArchiveBackend : NonCopyable { | class FileSystemBackend : NonCopyable { | ||||||
| public: | public: | ||||||
|     virtual ~ArchiveBackend() {} |     virtual ~FileSystemBackend() {} | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||||||
| @ -81,13 +81,12 @@ public: | |||||||
|     virtual std::string GetName() const = 0; |     virtual std::string GetName() const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Open a file specified by its path, using the specified mode |      * Create a file specified by its path | ||||||
|      * @param path Path relative to the archive |      * @param path Path relative to the Archive | ||||||
|      * @param mode Mode to open the file with |      * @param size The size of the new file, filled with zeroes | ||||||
|      * @return Opened file, or error code |      * @return Result of the operation | ||||||
|      */ |      */ | ||||||
|     virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, |     virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; | ||||||
|                                                              const Mode& mode) const = 0; |  | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Delete a file specified by its path |      * Delete a file specified by its path | ||||||
| @ -97,12 +96,11 @@ public: | |||||||
|     virtual ResultCode DeleteFile(const Path& path) const = 0; |     virtual ResultCode DeleteFile(const Path& path) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Rename a File specified by its path |      * Create a directory specified by its path | ||||||
|      * @param src_path Source path relative to the archive |      * @param path Path relative to the archive | ||||||
|      * @param dest_path Destination path relative to the archive |  | ||||||
|      * @return Result of the operation |      * @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 |      * Delete a directory specified by its path | ||||||
| @ -119,19 +117,12 @@ public: | |||||||
|     virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; |     virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Create a file specified by its path |      * Rename a File specified by its path | ||||||
|      * @param path Path relative to the Archive |      * @param src_path Source path relative to the archive | ||||||
|      * @param size The size of the new file, filled with zeroes |      * @param dest_path Destination path relative to the archive | ||||||
|      * @return Result of the operation |      * @return Result of the operation | ||||||
|      */ |      */ | ||||||
|     virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; |     virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Create a directory specified by its path |  | ||||||
|      * @param path Path relative to the archive |  | ||||||
|      * @return Result of the operation |  | ||||||
|      */ |  | ||||||
|     virtual ResultCode CreateDirectory(const Path& path) const = 0; |  | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Rename a Directory specified by its path |      * Rename a Directory specified by its path | ||||||
| @ -141,6 +132,15 @@ public: | |||||||
|      */ |      */ | ||||||
|     virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; |     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 |      * Open a directory specified by its path | ||||||
|      * @param path Path relative to the archive |      * @param path Path relative to the archive | ||||||
| @ -152,12 +152,12 @@ public: | |||||||
|      * Get the free space |      * Get the free space | ||||||
|      * @return The number of free bytes in the archive |      * @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: | public: | ||||||
|     virtual ~ArchiveFactory() {} |     virtual ~FileSystemFactory() {} | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||||||
| @ -169,7 +169,7 @@ public: | |||||||
|      * @param path Path to the archive |      * @param path Path to the archive | ||||||
|      * @return An ArchiveBackend corresponding operating specified archive path. |      * @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 |      * Deletes the archive contents and then re-creates the base folder | ||||||
| @ -6,7 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "core/file_sys/archive_backend.h" | #include "core/file_sys/filesystem.h" | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								src/core/file_sys/romfs_factory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/core/file_sys/romfs_factory.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
							
								
								
									
										35
									
								
								src/core/file_sys/romfs_factory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/core/file_sys/romfs_factory.h
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2014 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -6,84 +6,80 @@ | |||||||
| #include <memory> | #include <memory> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/file_sys/ivfc_archive.h" | #include "core/file_sys/romfs_filesystem.h" | ||||||
| 
 |  | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // FileSys namespace
 |  | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| std::string IVFCArchive::GetName() const { | std::string RomFS_FileSystem::GetName() const { | ||||||
|     return "IVFC"; |     return "RomFS"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, | ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, | ||||||
|                                                               const Mode& mode) const { |                                                                       const Mode& mode) const { | ||||||
|     return MakeResult<std::unique_ptr<FileBackend>>( |     return MakeResult<std::unique_ptr<StorageBackend>>( | ||||||
|         std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); |         std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::DeleteFile(const Path& path) const { | ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(bunnei): Use correct error code
 |     // TODO(bunnei): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(wwylele): Use correct error code
 |     // TODO(wwylele): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { | ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(wwylele): Use correct error code
 |     // TODO(wwylele): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { | ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(wwylele): Use correct error code
 |     // TODO(wwylele): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(bunnei): Use correct error code
 |     // TODO(bunnei): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::CreateDirectory(const Path& path) const { | ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(wwylele): Use correct error code
 |     // TODO(wwylele): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", |     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", | ||||||
|                  GetName().c_str()); |                  GetName().c_str()); | ||||||
|     // TODO(wwylele): Use correct error code
 |     // TODO(wwylele): Use correct error code
 | ||||||
|     return ResultCode(-1); |     return ResultCode(-1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { | ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( | ||||||
|     return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); |     const Path& path) const { | ||||||
|  |     return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 IVFCArchive::GetFreeBytes() const { | u64 RomFS_FileSystem::GetFreeSpaceSize() const { | ||||||
|     LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive"); |     LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||||||
| 
 |  | ||||||
| ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { |  | ||||||
|     LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); |     LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); | ||||||
|     romfs_file->Seek(data_offset + offset, SEEK_SET); |     romfs_file->Seek(data_offset + offset, SEEK_SET); | ||||||
|     size_t read_length = (size_t)std::min((u64)length, data_size - offset); |     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)); |     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, | ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||||||
|                                   const u8* buffer) const { |                                        const u8* buffer) const { | ||||||
|     LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); |     LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); | ||||||
|     // TODO(Subv): Find error code
 |     // TODO(Subv): Find error code
 | ||||||
|     return MakeResult<size_t>(0); |     return MakeResult<size_t>(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 IVFCFile::GetSize() const { | u64 RomFS_Storage::GetSize() const { | ||||||
|     return data_size; |     return data_size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IVFCFile::SetSize(const u64 size) const { | bool RomFS_Storage::SetSize(const u64 size) const { | ||||||
|     LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file"); |     LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2014 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -10,30 +10,27 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "core/file_sys/archive_backend.h" | #include "core/file_sys/directory.h" | ||||||
| #include "core/file_sys/directory_backend.h" | #include "core/file_sys/filesystem.h" | ||||||
| #include "core/file_sys/file_backend.h" | #include "core/file_sys/storage.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // FileSys namespace
 |  | ||||||
| 
 |  | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Helper which implements an interface to deal with IVFC images used in some archives |  * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some | ||||||
|  * This should be subclassed by concrete archive types, which will provide the |  * archives This should be subclassed by concrete archive types, which will provide the input data | ||||||
|  * input data (load the raw IVFC archive) and override any required methods |  * (load the raw ROMFS archive) and override any required methods | ||||||
|  */ |  */ | ||||||
| class IVFCArchive : public ArchiveBackend { | class RomFS_FileSystem : public FileSystemBackend { | ||||||
| public: | 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) {} |         : romfs_file(file), data_offset(offset), data_size(size) {} | ||||||
| 
 | 
 | ||||||
|     std::string GetName() const override; |     std::string GetName() const override; | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, |     ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, | ||||||
|                                                      const Mode& mode) const override; |                                                         const Mode& mode) const override; | ||||||
|     ResultCode DeleteFile(const Path& path) const override; |     ResultCode DeleteFile(const Path& path) const override; | ||||||
|     ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; |     ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; | ||||||
|     ResultCode DeleteDirectory(const Path& path) const override; |     ResultCode DeleteDirectory(const Path& path) const override; | ||||||
| @ -42,7 +39,7 @@ public: | |||||||
|     ResultCode CreateDirectory(const Path& path) const override; |     ResultCode CreateDirectory(const Path& path) const override; | ||||||
|     ResultCode RenameDirectory(const Path& src_path, const Path& dest_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; |     ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; | ||||||
|     u64 GetFreeBytes() const override; |     u64 GetFreeSpaceSize() const override; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     std::shared_ptr<FileUtil::IOFile> romfs_file; |     std::shared_ptr<FileUtil::IOFile> romfs_file; | ||||||
| @ -50,9 +47,9 @@ protected: | |||||||
|     u64 data_size; |     u64 data_size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IVFCFile : public FileBackend { | class RomFS_Storage : public StorageBackend { | ||||||
| public: | 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) {} |         : romfs_file(file), data_offset(offset), data_size(size) {} | ||||||
| 
 | 
 | ||||||
|     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; |     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||||||
| @ -70,7 +67,7 @@ private: | |||||||
|     u64 data_size; |     u64 data_size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IVFCDirectory : public DirectoryBackend { | class ROMFSDirectory : public DirectoryBackend { | ||||||
| public: | public: | ||||||
|     u32 Read(const u32 count, Entry* entries) override { |     u32 Read(const u32 count, Entry* entries) override { | ||||||
|         return 0; |         return 0; | ||||||
| @ -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
 |  | ||||||
| @ -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
 |  | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright 2014 Citra Emulator Project
 | // Copyright 2018 yuzu emulator team
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| @ -8,15 +8,12 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // FileSys namespace
 |  | ||||||
| 
 |  | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| class FileBackend : NonCopyable { | class StorageBackend : NonCopyable { | ||||||
| public: | public: | ||||||
|     FileBackend() {} |     StorageBackend() {} | ||||||
|     virtual ~FileBackend() {} |     virtual ~StorageBackend() {} | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Read data from the file |      * Read data from the file | ||||||
| @ -39,10 +36,9 @@ public: | |||||||
|                                     const u8* buffer) const = 0; |                                     const u8* buffer) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Get the size of the file in bytes |      * Flushes the file | ||||||
|      * @return Size of the file in bytes |  | ||||||
|      */ |      */ | ||||||
|     virtual u64 GetSize() const = 0; |     virtual void Flush() const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Set the size of the file in bytes |      * Set the size of the file in bytes | ||||||
| @ -51,16 +47,17 @@ public: | |||||||
|      */ |      */ | ||||||
|     virtual bool SetSize(u64 size) const = 0; |     virtual bool SetSize(u64 size) const = 0; | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Get the size of the file in bytes | ||||||
|  |      * @return Size of the file in bytes | ||||||
|  |      */ | ||||||
|  |     virtual u64 GetSize() const = 0; | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Close the file |      * Close the file | ||||||
|      * @return true if the file closed correctly |      * @return true if the file closed correctly | ||||||
|      */ |      */ | ||||||
|     virtual bool Close() const = 0; |     virtual bool Close() const = 0; | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Flushes the file |  | ||||||
|      */ |  | ||||||
|     virtual void Flush() const = 0; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace FileSys
 | } // 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
 |  | ||||||
| @ -304,6 +304,11 @@ inline u64 RequestParser::Pop() { | |||||||
|     return msw << 32 | lsw; |     return msw << 32 | lsw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template <> | ||||||
|  | inline s64 RequestParser::Pop() { | ||||||
|  |     return static_cast<s64>(Pop<u64>()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template <> | template <> | ||||||
| inline bool RequestParser::Pop() { | inline bool RequestParser::Pop() { | ||||||
|     return Pop<u8>() != 0; |     return Pop<u8>() != 0; | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ | |||||||
| enum class ErrorDescription : u32 { | enum class ErrorDescription : u32 { | ||||||
|     Success = 0, |     Success = 0, | ||||||
|     RemoteProcessDead = 301, |     RemoteProcessDead = 301, | ||||||
|  |     InvalidOffset = 6061, | ||||||
|  |     InvalidLength = 6062, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | |||||||
							
								
								
									
										54
									
								
								src/core/hle/service/filesystem/filesystem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/core/hle/service/filesystem/filesystem.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
							
								
								
									
										50
									
								
								src/core/hle/service/filesystem/filesystem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/core/hle/service/filesystem/filesystem.h
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
							
								
								
									
										138
									
								
								src/core/hle/service/filesystem/fsp_srv.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/core/hle/service/filesystem/fsp_srv.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
							
								
								
									
										34
									
								
								src/core/hle/service/filesystem/fsp_srv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/core/hle/service/filesystem/fsp_srv.h
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
| @ -19,6 +19,7 @@ | |||||||
| #include "core/hle/service/aoc/aoc_u.h" | #include "core/hle/service/aoc/aoc_u.h" | ||||||
| #include "core/hle/service/apm/apm.h" | #include "core/hle/service/apm/apm.h" | ||||||
| #include "core/hle/service/audio/audio.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/hid/hid.h" | ||||||
| #include "core/hle/service/lm/lm.h" | #include "core/hle/service/lm/lm.h" | ||||||
| #include "core/hle/service/nvdrv/nvdrv.h" | #include "core/hle/service/nvdrv/nvdrv.h" | ||||||
| @ -172,6 +173,7 @@ void Init() { | |||||||
|     AOC::InstallInterfaces(*SM::g_service_manager); |     AOC::InstallInterfaces(*SM::g_service_manager); | ||||||
|     APM::InstallInterfaces(*SM::g_service_manager); |     APM::InstallInterfaces(*SM::g_service_manager); | ||||||
|     Audio::InstallInterfaces(*SM::g_service_manager); |     Audio::InstallInterfaces(*SM::g_service_manager); | ||||||
|  |     FileSystem::InstallInterfaces(*SM::g_service_manager); | ||||||
|     HID::InstallInterfaces(*SM::g_service_manager); |     HID::InstallInterfaces(*SM::g_service_manager); | ||||||
|     LM::InstallInterfaces(*SM::g_service_manager); |     LM::InstallInterfaces(*SM::g_service_manager); | ||||||
|     Nvidia::InstallInterfaces(*SM::g_service_manager); |     Nvidia::InstallInterfaces(*SM::g_service_manager); | ||||||
|  | |||||||
| @ -7,14 +7,44 @@ | |||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
|  | #include "core/file_sys/romfs_factory.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.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/deconstructed_rom_directory.h" | ||||||
| #include "core/loader/nso.h" | #include "core/loader/nso.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| namespace Loader { | 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, | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, | ||||||
|                                                                          std::string filepath) |                                                                          std::string filepath) | ||||||
|     : AppLoader(std::move(file)), filepath(std::move(filepath)) {} |     : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||||||
| @ -79,10 +109,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||||||
| 
 | 
 | ||||||
|     // Load NSO modules
 |     // Load NSO modules
 | ||||||
|     VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; |     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", |     for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | ||||||
|                                "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |                                "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | ||||||
|         const std::string path = |         const std::string path = directory + DIR_SEP + module; | ||||||
|             filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP + module; |  | ||||||
|         const VAddr load_addr = next_load_addr; |         const VAddr load_addr = next_load_addr; | ||||||
|         next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); |         next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); | ||||||
|         if (next_load_addr) { |         if (next_load_addr) { | ||||||
| @ -98,8 +128,43 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||||||
|         Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |         Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||||||
|     process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); |     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; |     is_loaded = true; | ||||||
|     return ResultStatus::Success; |     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
 | } // namespace Loader
 | ||||||
|  | |||||||
| @ -35,7 +35,11 @@ public: | |||||||
| 
 | 
 | ||||||
|     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||||||
| 
 | 
 | ||||||
|  |     ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||||||
|  |                            u64& size) override; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|  |     std::string filepath_romfs; | ||||||
|     std::string filepath; |     std::string filepath; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user