mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #288 from Subv/savedata_stuff
FS_U: Implemented the SaveData archive
This commit is contained in:
		
						commit
						94a103a000
					
				
					 25 changed files with 458 additions and 490 deletions
				
			
		|  | @ -40,6 +40,7 @@ | |||
| #define MAPS_DIR          "maps" | ||||
| #define CACHE_DIR         "cache" | ||||
| #define SDMC_DIR          "sdmc" | ||||
| #define SAVEDATA_DIR      "savedata" | ||||
| #define SYSDATA_DIR       "sysdata" | ||||
| #define SHADERCACHE_DIR   "shader_cache" | ||||
| #define STATESAVES_DIR    "state_saves" | ||||
|  |  | |||
|  | @ -676,6 +676,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|         paths[D_MAPS_IDX]           = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||||
|         paths[D_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||||
|         paths[D_SDMC_IDX]           = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | ||||
|         paths[D_SAVEDATA_IDX]       = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||||
|         paths[D_SYSDATA_IDX]        = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | ||||
|         paths[D_SHADERCACHE_IDX]    = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||||
|         paths[D_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||||
|  | @ -718,6 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
|             paths[D_MAPS_IDX]           = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||||
|             paths[D_CACHE_IDX]          = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||||
|             paths[D_SDMC_IDX]           = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | ||||
|             paths[D_SAVEDATA_IDX]       = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||||
|             paths[D_SHADERCACHE_IDX]    = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||||
|             paths[D_SHADERS_IDX]        = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||||
|             paths[D_STATESAVES_IDX]     = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ enum { | |||
|     D_STATESAVES_IDX, | ||||
|     D_SCREENSHOTS_IDX, | ||||
|     D_SDMC_IDX, | ||||
|     D_SAVEDATA_IDX, | ||||
|     D_SYSDATA_IDX, | ||||
|     D_HIRESTEXTURES_IDX, | ||||
|     D_DUMP_IDX, | ||||
|  |  | |||
|  | @ -18,11 +18,11 @@ set(SRCS | |||
|             arm/skyeye_common/vfp/vfpinstr.cpp | ||||
|             arm/skyeye_common/vfp/vfpsingle.cpp | ||||
|             file_sys/archive_romfs.cpp | ||||
|             file_sys/archive_savedata.cpp | ||||
|             file_sys/archive_sdmc.cpp | ||||
|             file_sys/disk_archive.cpp | ||||
|             file_sys/file_romfs.cpp | ||||
|             file_sys/file_sdmc.cpp | ||||
|             file_sys/directory_romfs.cpp | ||||
|             file_sys/directory_sdmc.cpp | ||||
|             hle/kernel/address_arbiter.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|  | @ -99,13 +99,13 @@ set(HEADERS | |||
|             arm/arm_interface.h | ||||
|             file_sys/archive_backend.h | ||||
|             file_sys/archive_romfs.h | ||||
|             file_sys/archive_savedata.h | ||||
|             file_sys/archive_sdmc.h | ||||
|             file_sys/disk_archive.h | ||||
|             file_sys/file_backend.h | ||||
|             file_sys/file_romfs.h | ||||
|             file_sys/file_sdmc.h | ||||
|             file_sys/directory_backend.h | ||||
|             file_sys/directory_romfs.h | ||||
|             file_sys/directory_sdmc.h | ||||
|             hle/kernel/address_arbiter.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/kernel.h | ||||
|  |  | |||
							
								
								
									
										33
									
								
								src/core/file_sys/archive_savedata.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/core/file_sys/archive_savedata.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2+
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <sys/stat.h> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/archive_savedata.h" | ||||
| #include "core/file_sys/disk_archive.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)  | ||||
|         : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) { | ||||
|     LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | ||||
| } | ||||
| 
 | ||||
| bool Archive_SaveData::Initialize() { | ||||
|     if (!FileUtil::CreateFullPath(mount_point)) { | ||||
|         LOG_ERROR(Service_FS, "Unable to create SaveData path."); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
							
								
								
									
										32
									
								
								src/core/file_sys/archive_savedata.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/file_sys/archive_savedata.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2+
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/file_sys/disk_archive.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| /// File system interface to the SaveData archive
 | ||||
| class Archive_SaveData final : public DiskArchive { | ||||
| public: | ||||
|     Archive_SaveData(const std::string& mount_point, u64 program_id); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Initialize the archive. | ||||
|      * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists, | ||||
|      * Success if it was created properly and Failure if there was any error | ||||
|      */ | ||||
|     bool Initialize(); | ||||
| 
 | ||||
|     std::string GetName() const override { return "SaveData"; } | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | @ -8,8 +8,7 @@ | |||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| #include "core/file_sys/directory_sdmc.h" | ||||
| #include "core/file_sys/file_sdmc.h" | ||||
| #include "core/file_sys/disk_archive.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -17,18 +16,10 @@ | |||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| Archive_SDMC::Archive_SDMC(const std::string& mount_point) { | ||||
|     this->mount_point = mount_point; | ||||
| Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) { | ||||
|     LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); | ||||
| } | ||||
| 
 | ||||
| Archive_SDMC::~Archive_SDMC() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize the archive. | ||||
|  * @return true if it initialized successfully | ||||
|  */ | ||||
| bool Archive_SDMC::Initialize() { | ||||
|     if (!Settings::values.use_virtual_sd) { | ||||
|         LOG_WARNING(Service_FS, "SDMC disabled by config."); | ||||
|  | @ -43,74 +34,4 @@ bool Archive_SDMC::Initialize() { | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 nullptr | ||||
|  */ | ||||
| std::unique_ptr<FileBackend> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const { | ||||
|     LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex); | ||||
|     File_SDMC* file = new File_SDMC(this, path, mode); | ||||
|     if (!file->Open()) | ||||
|         return nullptr; | ||||
|     return std::unique_ptr<FileBackend>(file); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Delete a file specified by its path | ||||
|  * @param path Path relative to the archive | ||||
|  * @return Whether the file could be deleted | ||||
|  */ | ||||
| bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const { | ||||
|     return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool Archive_SDMC::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||||
|     return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Delete a directory specified by its path | ||||
|  * @param path Path relative to the archive | ||||
|  * @return Whether the directory could be deleted | ||||
|  */ | ||||
| bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const { | ||||
|     return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Create a directory specified by its path | ||||
|  * @param path Path relative to the archive | ||||
|  * @return Whether the directory could be created | ||||
|  */ | ||||
| bool Archive_SDMC::CreateDirectory(const Path& path) const { | ||||
|     return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||||
|     return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Open a directory specified by its path | ||||
|  * @param path Path relative to the archive | ||||
|  * @return Opened directory, or nullptr | ||||
|  */ | ||||
| std::unique_ptr<DirectoryBackend> Archive_SDMC::OpenDirectory(const Path& path) const { | ||||
|     LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||||
|     Directory_SDMC* directory = new Directory_SDMC(this, path); | ||||
|     if (!directory->Open()) | ||||
|         return nullptr; | ||||
|     return std::unique_ptr<DirectoryBackend>(directory); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Getter for the path used for this Archive | ||||
|  * @return Mount point of that passthrough archive | ||||
|  */ | ||||
| std::string Archive_SDMC::GetMountPoint() const { | ||||
|     return mount_point; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/file_sys/archive_backend.h" | ||||
| #include "core/file_sys/disk_archive.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -15,10 +15,9 @@ | |||
| namespace FileSys { | ||||
| 
 | ||||
| /// File system interface to the SDMC archive
 | ||||
| class Archive_SDMC final : public ArchiveBackend { | ||||
| class Archive_SDMC final : public DiskArchive { | ||||
| public: | ||||
|     Archive_SDMC(const std::string& mount_point); | ||||
|     ~Archive_SDMC() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Initialize the archive. | ||||
|  | @ -27,67 +26,6 @@ public: | |||
|     bool Initialize(); | ||||
| 
 | ||||
|     std::string GetName() const override { return "SDMC"; } | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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 nullptr | ||||
|      */ | ||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Delete a file specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Whether the file could be deleted | ||||
|      */ | ||||
|     bool DeleteFile(const FileSys::Path& path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Rename a File specified by its path | ||||
|      * @param src_path Source path relative to the archive | ||||
|      * @param dest_path Destination path relative to the archive | ||||
|      * @return Whether rename succeeded | ||||
|      */ | ||||
|     bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Delete a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Whether the directory could be deleted | ||||
|      */ | ||||
|     bool DeleteDirectory(const FileSys::Path& path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Create a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Whether the directory could be created | ||||
|      */ | ||||
|     bool CreateDirectory(const Path& path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Rename a Directory specified by its path | ||||
|      * @param src_path Source path relative to the archive | ||||
|      * @param dest_path Destination path relative to the archive | ||||
|      * @return Whether rename succeeded | ||||
|      */ | ||||
|     bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Open a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Opened directory, or nullptr | ||||
|      */ | ||||
|     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Getter for the path used for this Archive | ||||
|      * @return Mount point of that passthrough archive | ||||
|      */ | ||||
|     std::string GetMountPoint() const; | ||||
| 
 | ||||
| private: | ||||
|     std::string mount_point; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -1,88 +0,0 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <sys/stat.h> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/directory_sdmc.h" | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) { | ||||
|     // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
 | ||||
|     // the root directory we set while opening the archive.
 | ||||
|     // For example, opening /../../usr/bin can give the emulated program your installed programs.
 | ||||
|     this->path = archive->GetMountPoint() + path.AsString(); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| Directory_SDMC::~Directory_SDMC() { | ||||
|     Close(); | ||||
| } | ||||
| 
 | ||||
| bool Directory_SDMC::Open() { | ||||
|     if (!FileUtil::IsDirectory(path)) | ||||
|         return false; | ||||
|     FileUtil::ScanDirectoryTree(path, directory); | ||||
|     children_iterator = directory.children.begin(); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * List files contained in the directory | ||||
|  * @param count Number of entries to return at once in entries | ||||
|  * @param entries Buffer to read data into | ||||
|  * @return Number of entries listed | ||||
|  */ | ||||
| u32 Directory_SDMC::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; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Close the directory | ||||
|  * @return true if the directory closed correctly | ||||
|  */ | ||||
| bool Directory_SDMC::Close() const { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | @ -1,55 +0,0 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/directory_backend.h" | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| class Directory_SDMC final : public DirectoryBackend { | ||||
| public: | ||||
|     Directory_SDMC(); | ||||
|     Directory_SDMC(const Archive_SDMC* archive, const Path& path); | ||||
|     ~Directory_SDMC() override; | ||||
| 
 | ||||
|     /**
 | ||||
|     * Open the directory | ||||
|     * @return true if the directory opened correctly | ||||
|     */ | ||||
|     bool Open() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * List files contained in the directory | ||||
|      * @param count Number of entries to return at once in entries | ||||
|      * @param entries Buffer to read data into | ||||
|      * @return Number of entries listed | ||||
|      */ | ||||
|     u32 Read(const u32 count, Entry* entries) override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Close the directory | ||||
|      * @return true if the directory closed correctly | ||||
|      */ | ||||
|     bool Close() const override; | ||||
| 
 | ||||
| private: | ||||
|     std::string path; | ||||
|     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
 | ||||
							
								
								
									
										167
									
								
								src/core/file_sys/disk_archive.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/core/file_sys/disk_archive.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,167 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <sys/stat.h> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/disk_archive.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | ||||
|     LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | ||||
|     DiskFile* file = new DiskFile(this, path, mode); | ||||
|     if (!file->Open()) | ||||
|         return nullptr; | ||||
|     return std::unique_ptr<FileBackend>(file); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::DeleteFile(const FileSys::Path& path) const { | ||||
|     return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||||
|     return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const { | ||||
|     return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::CreateDirectory(const Path& path) const { | ||||
|     return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||||
|     return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | ||||
|     LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||||
|     DiskDirectory* directory = new DiskDirectory(this, path); | ||||
|     if (!directory->Open()) | ||||
|         return nullptr; | ||||
|     return std::unique_ptr<DirectoryBackend>(directory); | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { | ||||
|     // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
 | ||||
|     // the root directory we set while opening the archive.
 | ||||
|     // For example, opening /../../etc/passwd can give the emulated program your users list.
 | ||||
|     this->path = archive->GetMountPoint() + path.AsString(); | ||||
|     this->mode.hex = mode.hex; | ||||
|     this->archive = archive; | ||||
| } | ||||
| 
 | ||||
| bool DiskFile::Open() { | ||||
|     if (!mode.create_flag && !FileUtil::Exists(path)) { | ||||
|         LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     std::string mode_string; | ||||
|     if (mode.create_flag) | ||||
|         mode_string = "w+"; | ||||
|     else if (mode.write_flag) | ||||
|         mode_string = "r+"; // Files opened with Write access can be read from
 | ||||
|     else if (mode.read_flag) | ||||
|         mode_string = "r"; | ||||
| 
 | ||||
|     // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 | ||||
|     mode_string += "b"; | ||||
| 
 | ||||
|     file = new FileUtil::IOFile(path, mode_string.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { | ||||
|     file->Seek(offset, SEEK_SET); | ||||
|     return file->ReadBytes(buffer, length); | ||||
| } | ||||
| 
 | ||||
| size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||||
|     file->Seek(offset, SEEK_SET); | ||||
|     size_t written = file->WriteBytes(buffer, length); | ||||
|     if (flush) | ||||
|         file->Flush(); | ||||
|     return written; | ||||
| } | ||||
| 
 | ||||
| size_t DiskFile::GetSize() const { | ||||
|     return static_cast<size_t>(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 DiskArchive* archive, const Path& path) { | ||||
|     // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
 | ||||
|     // the root directory we set while opening the archive.
 | ||||
|     // For example, opening /../../usr/bin can give the emulated program your installed programs.
 | ||||
|     this->path = archive->GetMountPoint() + path.AsString(); | ||||
|     this->archive = archive; | ||||
| } | ||||
| 
 | ||||
| bool DiskDirectory::Open() { | ||||
|     if (!FileUtil::IsDirectory(path)) | ||||
|         return false; | ||||
|     FileUtil::ScanDirectoryTree(path, directory); | ||||
|     children_iterator = directory.children.begin(); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
							
								
								
									
										101
									
								
								src/core/file_sys/disk_archive.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/core/file_sys/disk_archive.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/file_sys/archive_backend.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| /**
 | ||||
|  * Helper which implements a backend accessing the host machine's filesystem.  | ||||
|  * This should be subclassed by concrete archive types, which will provide the  | ||||
|  * base directory on the host filesystem and override any required functionality. | ||||
|  */ | ||||
| class DiskArchive : public ArchiveBackend { | ||||
| public: | ||||
|     DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | ||||
| 
 | ||||
|     virtual std::string GetName() const = 0; | ||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||||
|     bool DeleteFile(const FileSys::Path& path) const override; | ||||
|     bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||||
|     bool DeleteDirectory(const FileSys::Path& path) const override; | ||||
|     bool CreateDirectory(const Path& path) const override; | ||||
|     bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||||
|     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Getter for the path used for this Archive | ||||
|      * @return Mount point of that passthrough archive | ||||
|      */ | ||||
|     const std::string& GetMountPoint() const { | ||||
|         return mount_point; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     std::string mount_point; | ||||
| }; | ||||
| 
 | ||||
| class DiskFile : public FileBackend { | ||||
| public: | ||||
|     DiskFile(); | ||||
|     DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); | ||||
|      | ||||
|     ~DiskFile() override { | ||||
|         Close(); | ||||
|     } | ||||
| 
 | ||||
|     bool Open() override; | ||||
|     size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||||
|     size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||||
|     size_t GetSize() const override; | ||||
|     bool SetSize(const u64 size) const override; | ||||
|     bool Close() const override; | ||||
|      | ||||
|     void Flush() const override { | ||||
|         file->Flush(); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     const DiskArchive* archive; | ||||
|     std::string path; | ||||
|     Mode mode; | ||||
|     FileUtil::IOFile* file; | ||||
| }; | ||||
| 
 | ||||
| class DiskDirectory : public DirectoryBackend { | ||||
| public: | ||||
|     DiskDirectory(); | ||||
|     DiskDirectory(const DiskArchive* archive, const Path& path); | ||||
| 
 | ||||
|     ~DiskDirectory() override { | ||||
|         Close(); | ||||
|     } | ||||
| 
 | ||||
|     bool Open() override; | ||||
|     u32 Read(const u32 count, Entry* entries) override; | ||||
| 
 | ||||
|     bool Close() const override { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     const DiskArchive* archive; | ||||
|     std::string path; | ||||
|     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
 | ||||
|  | @ -61,6 +61,11 @@ public: | |||
|      * @return true if the file closed correctly | ||||
|      */ | ||||
|     virtual bool Close() const = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Flushes the file | ||||
|      */ | ||||
|     virtual void Flush() const = 0; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -64,6 +64,8 @@ public: | |||
|      */ | ||||
|     bool Close() const override; | ||||
| 
 | ||||
|     void Flush() const override { } | ||||
| 
 | ||||
| private: | ||||
|     const Archive_RomFS* archive; | ||||
| }; | ||||
|  |  | |||
|  | @ -1,110 +0,0 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <sys/stat.h> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/file_sdmc.h" | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| File_SDMC::File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode) { | ||||
|     // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
 | ||||
|     // the root directory we set while opening the archive.
 | ||||
|     // For example, opening /../../etc/passwd can give the emulated program your users list.
 | ||||
|     this->path = archive->GetMountPoint() + path.AsString(); | ||||
|     this->mode.hex = mode.hex; | ||||
| } | ||||
| 
 | ||||
| File_SDMC::~File_SDMC() { | ||||
|     Close(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Open the file | ||||
|  * @return true if the file opened correctly | ||||
|  */ | ||||
| bool File_SDMC::Open() { | ||||
|     if (!mode.create_flag && !FileUtil::Exists(path)) { | ||||
|         LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     std::string mode_string; | ||||
|     if (mode.create_flag) | ||||
|         mode_string = "w+"; | ||||
|     else if (mode.write_flag) | ||||
|         mode_string = "r+"; // Files opened with Write access can be read from
 | ||||
|     else if (mode.read_flag) | ||||
|         mode_string = "r"; | ||||
|      | ||||
|     // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 | ||||
|     mode_string += "b"; | ||||
| 
 | ||||
|     file = new FileUtil::IOFile(path, mode_string.c_str()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Read data from the file | ||||
|  * @param offset Offset in bytes to start reading data from | ||||
|  * @param length Length in bytes of data to read from file | ||||
|  * @param buffer Buffer to read data into | ||||
|  * @return Number of bytes read | ||||
|  */ | ||||
| size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||||
|     file->Seek(offset, SEEK_SET); | ||||
|     return file->ReadBytes(buffer, length); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Write data to the file | ||||
|  * @param offset Offset in bytes to start writing data to | ||||
|  * @param length Length in bytes of data to write to file | ||||
|  * @param flush The flush parameters (0 == do not flush) | ||||
|  * @param buffer Buffer to read data from | ||||
|  * @return Number of bytes written | ||||
|  */ | ||||
| size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||||
|     file->Seek(offset, SEEK_SET); | ||||
|     size_t written = file->WriteBytes(buffer, length); | ||||
|     if (flush) | ||||
|         file->Flush(); | ||||
|     return written; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the size of the file in bytes | ||||
|  * @return Size of the file in bytes | ||||
|  */ | ||||
| size_t File_SDMC::GetSize() const { | ||||
|     return static_cast<size_t>(file->GetSize()); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the size of the file in bytes | ||||
|  * @param size New size of the file | ||||
|  * @return true if successful | ||||
|  */ | ||||
| bool File_SDMC::SetSize(const u64 size) const { | ||||
|     file->Resize(size); | ||||
|     file->Flush(); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Close the file | ||||
|  * @return true if the file closed correctly | ||||
|  */ | ||||
| bool File_SDMC::Close() const { | ||||
|     return file->Close(); | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | @ -1,75 +0,0 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/file_backend.h" | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
| 
 | ||||
| namespace FileSys { | ||||
| 
 | ||||
| class File_SDMC final : public FileBackend { | ||||
| public: | ||||
|     File_SDMC(); | ||||
|     File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode); | ||||
|     ~File_SDMC() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Open the file | ||||
|      * @return true if the file opened correctly | ||||
|      */ | ||||
|     bool Open() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Read data from the file | ||||
|      * @param offset Offset in bytes to start reading data from | ||||
|      * @param length Length in bytes of data to read from file | ||||
|      * @param buffer Buffer to read data into | ||||
|      * @return Number of bytes read | ||||
|      */ | ||||
|     size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Write data to the file | ||||
|      * @param offset Offset in bytes to start writing data to | ||||
|      * @param length Length in bytes of data to write to file | ||||
|      * @param flush The flush parameters (0 == do not flush) | ||||
|      * @param buffer Buffer to read data from | ||||
|      * @return Number of bytes written | ||||
|      */ | ||||
|     size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the size of the file in bytes | ||||
|      * @return Size of the file in bytes | ||||
|      */ | ||||
|     size_t GetSize() const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set the size of the file in bytes | ||||
|      * @param size New size of the file | ||||
|      * @return true if successful | ||||
|      */ | ||||
|     bool SetSize(const u64 size) const override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Close the file | ||||
|      * @return true if the file closed correctly | ||||
|      */ | ||||
|     bool Close() const override; | ||||
| 
 | ||||
| private: | ||||
|     std::string path; | ||||
|     Mode mode; | ||||
|     FileUtil::IOFile* file; | ||||
| }; | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  | @ -14,6 +14,7 @@ namespace Kernel { | |||
| 
 | ||||
| Handle g_main_thread = 0; | ||||
| ObjectPool g_object_pool; | ||||
| u64 g_program_id = 0; | ||||
| 
 | ||||
| ObjectPool::ObjectPool() { | ||||
|     next_id = INITIAL_NEXT_ID; | ||||
|  |  | |||
|  | @ -151,6 +151,12 @@ private: | |||
| extern ObjectPool g_object_pool; | ||||
| extern Handle g_main_thread; | ||||
| 
 | ||||
| /// The ID code of the currently running game
 | ||||
| /// TODO(Subv): This variable should not be here, 
 | ||||
| /// we need a way to store information about the currently loaded application 
 | ||||
| /// for later query during runtime, maybe using the LDR service?
 | ||||
| extern u64 g_program_id; | ||||
| 
 | ||||
| /// Initialize the kernel
 | ||||
| void Init(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,6 +17,8 @@ | |||
| /// Detailed description of the error. This listing is likely incomplete.
 | ||||
| enum class ErrorDescription : u32 { | ||||
|     Success = 0, | ||||
|     FS_NotFound = 100, | ||||
|     FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
 | ||||
|     InvalidSection = 1000, | ||||
|     TooLarge = 1001, | ||||
|     NotAuthorized = 1002, | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include "common/file_util.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| #include "core/file_sys/archive_savedata.h" | ||||
| #include "core/file_sys/archive_backend.h" | ||||
| #include "core/file_sys/archive_sdmc.h" | ||||
| #include "core/file_sys/directory_backend.h" | ||||
|  | @ -135,6 +136,13 @@ public: | |||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         case FileCommand::Flush: | ||||
|         { | ||||
|             LOG_TRACE(Service_FS, "Flush"); | ||||
|             backend->Flush(); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         // Unknown command...
 | ||||
|         default: | ||||
|             LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||||
|  | @ -220,9 +228,18 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { | |||
| 
 | ||||
|     auto itr = id_code_map.find(id_code); | ||||
|     if (itr == id_code_map.end()) { | ||||
|         if (id_code == ArchiveIdCode::SaveData) { | ||||
|             // When a SaveData archive is created for the first time, it is not yet formatted
 | ||||
|             // and the save file/directory structure expected by the game has not yet been initialized. 
 | ||||
|             // Returning the NotFormatted error code will signal the game to provision the SaveData archive 
 | ||||
|             // with the files and folders that it expects. 
 | ||||
|             // The FormatSaveData service call will create the SaveData archive when it is called.
 | ||||
|             return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||||
|                               ErrorSummary::InvalidState, ErrorLevel::Status); | ||||
|         } | ||||
|         // TODO: Verify error against hardware
 | ||||
|         return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||||
|                 ErrorSummary::NotFound, ErrorLevel::Permanent); | ||||
|                           ErrorSummary::NotFound, ErrorLevel::Permanent); | ||||
|     } | ||||
| 
 | ||||
|     // This should never even happen in the first place with 64-bit handles, 
 | ||||
|  | @ -260,8 +277,8 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy | |||
| 
 | ||||
|     std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | ||||
|     if (backend == nullptr) { | ||||
|         return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||||
|                           ErrorSummary::NotFound, ErrorLevel::Permanent); | ||||
|         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||||
|                           ErrorSummary::NotFound, ErrorLevel::Status); | ||||
|     } | ||||
| 
 | ||||
|     auto file = std::make_unique<File>(std::move(backend), path); | ||||
|  | @ -366,6 +383,28 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F | |||
|     return MakeResult<Handle>(handle); | ||||
| } | ||||
| 
 | ||||
| ResultCode FormatSaveData() { | ||||
|     // TODO(Subv): Actually wipe the savedata folder after creating or opening it
 | ||||
| 
 | ||||
|     // Do not create the archive again if it already exists
 | ||||
|     if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end()) | ||||
|         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
 | ||||
| 
 | ||||
|     // Create the SaveData archive
 | ||||
|     std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | ||||
|     auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory, | ||||
|         Kernel::g_program_id); | ||||
| 
 | ||||
|     if (savedata_archive->Initialize()) { | ||||
|         CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | ||||
|         return RESULT_SUCCESS; | ||||
|     } else { | ||||
|         LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s", | ||||
|             savedata_archive->GetMountPoint().c_str()); | ||||
|         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Initialize archives
 | ||||
| void ArchiveInit() { | ||||
|     next_handle = 1; | ||||
|  | @ -375,9 +414,9 @@ void ArchiveInit() { | |||
|     // archive type is SDMC, so it is the only one getting exposed.
 | ||||
| 
 | ||||
|     std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||||
|     auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | ||||
|     if (archive->Initialize()) | ||||
|         CreateArchive(std::move(archive), ArchiveIdCode::SDMC); | ||||
|     auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | ||||
|     if (sdmc_archive->Initialize()) | ||||
|         CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | ||||
|     else | ||||
|         LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | ||||
| } | ||||
|  |  | |||
|  | @ -109,6 +109,12 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons | |||
|  */ | ||||
| ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a blank SaveData archive. | ||||
|  * @return ResultCode 0 on success or the corresponding code on error | ||||
|  */ | ||||
| ResultCode FormatSaveData(); | ||||
| 
 | ||||
| /// Initialize archives
 | ||||
| void ArchiveInit(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,11 +3,11 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/scope_exit.h" | ||||
| 
 | ||||
| #include "common/string_util.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/service/fs/fs_user.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
|  | @ -50,9 +50,7 @@ static void Initialize(Service::Interface* self) { | |||
| static void OpenFile(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|     // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
 | ||||
|     // 3dmoo's or ctrulib's implementations.  Triple check if it's really the case.
 | ||||
|     Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||
|     auto filename_type    = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     u32 filename_size     = cmd_buff[5]; | ||||
|     FileSys::Mode mode; mode.hex = cmd_buff[6]; | ||||
|  | @ -398,6 +396,36 @@ static void IsSdmcDetected(Service::Interface* self) { | |||
|     LOG_DEBUG(Service_FS, "called"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * FS_User::FormatSaveData service function | ||||
|  *  Inputs: | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void FormatSaveData(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     LOG_DEBUG(Service_FS, "(STUBBED)"); | ||||
| 
 | ||||
|     // TODO(Subv): Find out what the inputs and outputs of this function are
 | ||||
| 
 | ||||
|     cmd_buff[1] = FormatSaveData().raw; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * FS_User::FormatThisUserSaveData service function | ||||
|  *  Inputs: | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void FormatThisUserSaveData(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     LOG_DEBUG(Service_FS, "(STUBBED)"); | ||||
| 
 | ||||
|     // TODO(Subv): Find out what the inputs and outputs of this function are
 | ||||
| 
 | ||||
|     cmd_buff[1] = FormatSaveData().raw; | ||||
| } | ||||
| 
 | ||||
| const FSUserInterface::FunctionInfo FunctionTable[] = { | ||||
|     {0x000100C6, nullptr,               "Dummy1"}, | ||||
|     {0x040100C4, nullptr,               "Control"}, | ||||
|  | @ -415,7 +443,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { | |||
|     {0x080C00C2, OpenArchive,           "OpenArchive"}, | ||||
|     {0x080D0144, nullptr,               "ControlArchive"}, | ||||
|     {0x080E0080, CloseArchive,          "CloseArchive"}, | ||||
|     {0x080F0180, nullptr,               "FormatThisUserSaveData"}, | ||||
|     {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, | ||||
|     {0x08100200, nullptr,               "CreateSystemSaveData"}, | ||||
|     {0x08110040, nullptr,               "DeleteSystemSaveData"}, | ||||
|     {0x08120080, nullptr,               "GetFreeBytes"}, | ||||
|  | @ -476,7 +504,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { | |||
|     {0x08490040, nullptr,               "GetArchiveResource"}, | ||||
|     {0x084A0002, nullptr,               "ExportIntegrityVerificationSeed"}, | ||||
|     {0x084B0002, nullptr,               "ImportIntegrityVerificationSeed"}, | ||||
|     {0x084C0242, nullptr,               "FormatSaveData"}, | ||||
|     {0x084C0242, FormatSaveData,        "FormatSaveData"}, | ||||
|     {0x084D0102, nullptr,               "GetLegacySubBannerData"}, | ||||
|     {0x084E0342, nullptr,               "UpdateSha256Context"}, | ||||
|     {0x084F0102, nullptr,               "ReadSpecialFile"}, | ||||
|  |  | |||
|  | @ -74,6 +74,7 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 
 | ||||
|         // Load application and RomFS
 | ||||
|         if (ResultStatus::Success == app_loader.Load()) { | ||||
|             Kernel::g_program_id = app_loader.GetProgramId(); | ||||
|             Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | ||||
|             return ResultStatus::Success; | ||||
|         } | ||||
|  |  | |||
|  | @ -315,4 +315,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
|     return ResultStatus::Error; | ||||
| } | ||||
| 
 | ||||
| u64 AppLoader_NCCH::GetProgramId() const { | ||||
|     return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]); | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -191,6 +191,12 @@ public: | |||
|      */ | ||||
|     ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Gets the program id from the NCCH header | ||||
|      * @return u64 Program id | ||||
|      */ | ||||
|     u64 GetProgramId() const; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     /**
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue