mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #1302 from Subv/save_fix
HLE/FS: Fixed many corner cases in our file handling
This commit is contained in:
		
						commit
						b83e95727f
					
				
					 24 changed files with 400 additions and 143 deletions
				
			
		|  | @ -11,6 +11,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
| 
 | 
 | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
|  | @ -62,6 +63,14 @@ private: | ||||||
|     std::u16string u16str; |     std::u16string u16str; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct ArchiveFormatInfo { | ||||||
|  |     u32_le total_size; ///< The pre-defined size of the archive, as specified in the Create or Format call
 | ||||||
|  |     u32_le number_directories; ///< The pre-defined number of directories in the archive, as specified in the Create or Format call
 | ||||||
|  |     u32_le number_files; ///< The pre-defined number of files in the archive, as specified in the Create or Format call
 | ||||||
|  |     u8 duplicate_data; ///< Whether the archive should duplicate the data, as specified in the Create or Format call
 | ||||||
|  | }; | ||||||
|  | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | ||||||
|  | 
 | ||||||
| class ArchiveBackend : NonCopyable { | class ArchiveBackend : NonCopyable { | ||||||
| public: | public: | ||||||
|     virtual ~ArchiveBackend() { |     virtual ~ArchiveBackend() { | ||||||
|  | @ -76,16 +85,16 @@ public: | ||||||
|      * Open a file specified by its path, using the specified mode |      * Open a file specified by its path, using the specified mode | ||||||
|      * @param path Path relative to the archive |      * @param path Path relative to the archive | ||||||
|      * @param mode Mode to open the file with |      * @param mode Mode to open the file with | ||||||
|      * @return Opened file, or nullptr |      * @return Opened file, or error code | ||||||
|      */ |      */ | ||||||
|     virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; |     virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Delete a file specified by its path |      * Delete a file specified by its path | ||||||
|      * @param path Path relative to the archive |      * @param path Path relative to the archive | ||||||
|      * @return Whether the file could be deleted |      * @return Result of the operation | ||||||
|      */ |      */ | ||||||
|     virtual bool DeleteFile(const Path& path) const = 0; |     virtual ResultCode DeleteFile(const Path& path) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Rename a File specified by its path |      * Rename a File specified by its path | ||||||
|  | @ -108,7 +117,7 @@ public: | ||||||
|      * @param size The size of the new file, filled with zeroes |      * @param size The size of the new file, filled with zeroes | ||||||
|      * @return File creation result code |      * @return File creation result code | ||||||
|      */ |      */ | ||||||
|     virtual ResultCode CreateFile(const Path& path, u32 size) const = 0; |     virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Create a directory specified by its path |      * Create a directory specified by its path | ||||||
|  | @ -159,9 +168,17 @@ public: | ||||||
|     /**
 |     /**
 | ||||||
|      * Deletes the archive contents and then re-creates the base folder |      * Deletes the archive contents and then re-creates the base folder | ||||||
|      * @param path Path to the archive |      * @param path Path to the archive | ||||||
|  |      * @param format_info Format information for the new archive | ||||||
|      * @return ResultCode of the operation, 0 on success |      * @return ResultCode of the operation, 0 on success | ||||||
|      */ |      */ | ||||||
|     virtual ResultCode Format(const Path& path) = 0; |     virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Retrieves the format info about the archive with the specified path | ||||||
|  |      * @param path Path to the archive | ||||||
|  |      * @return Format information about the archive or error code | ||||||
|  |      */ | ||||||
|  |     virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared) | ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared) | ||||||
|         : mount_point(GetExtDataContainerPath(mount_location, shared)) { |         : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) { | ||||||
|     LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); |     LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -74,21 +74,59 @@ bool ArchiveFactory_ExtSaveData::Initialize() { | ||||||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) { | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) { | ||||||
|     std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/"; |     std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/"; | ||||||
|     if (!FileUtil::Exists(fullpath)) { |     if (!FileUtil::Exists(fullpath)) { | ||||||
|         // TODO(Subv): Check error code, this one is probably wrong
 |         // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
 | ||||||
|  |         // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
 | ||||||
|  |         if (!shared) { | ||||||
|  |             return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||||||
|  |                               ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  |         } else { | ||||||
|             return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, |             return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||||||
|                               ErrorSummary::InvalidState, ErrorLevel::Status); |                               ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|     auto archive = Common::make_unique<DiskArchive>(fullpath); |     auto archive = Common::make_unique<DiskArchive>(fullpath); | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path) { | ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     // These folders are always created with the ExtSaveData
 |     // These folders are always created with the ExtSaveData
 | ||||||
|     std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/"; |     std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/"; | ||||||
|     std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/"; |     std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/"; | ||||||
|     FileUtil::CreateFullPath(user_path); |     FileUtil::CreateFullPath(user_path); | ||||||
|     FileUtil::CreateFullPath(boss_path); |     FileUtil::CreateFullPath(boss_path); | ||||||
|  | 
 | ||||||
|  |     // Write the format metadata
 | ||||||
|  |     std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata"; | ||||||
|  |     FileUtil::IOFile file(metadata_path, "wb"); | ||||||
|  | 
 | ||||||
|  |     if (!file.IsOpen()) { | ||||||
|  |         // TODO(Subv): Find the correct error code
 | ||||||
|  |         return ResultCode(-1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     file.WriteBytes(&format_info, sizeof(format_info)); | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const { | ||||||
|  |     std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata"; | ||||||
|  |     FileUtil::IOFile file(metadata_path, "rb"); | ||||||
|  | 
 | ||||||
|  |     if (!file.IsOpen()) { | ||||||
|  |         LOG_ERROR(Service_FS, "Could not open metadata information for archive"); | ||||||
|  |         // TODO(Subv): Verify error code
 | ||||||
|  |         return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ArchiveFormatInfo info = {}; | ||||||
|  |     file.ReadBytes(&info, sizeof(info)); | ||||||
|  |     return MakeResult<ArchiveFormatInfo>(info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data, size_t icon_size) { | ||||||
|  |     std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path); | ||||||
|  |     FileUtil::IOFile icon_file(game_path + "icon", "wb"); | ||||||
|  |     icon_file.WriteBytes(icon_data, icon_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -31,10 +31,19 @@ public: | ||||||
|     std::string GetName() const override { return "ExtSaveData"; } |     std::string GetName() const override { return "ExtSaveData"; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
|     const std::string& GetMountPoint() const { return mount_point; } |     const std::string& GetMountPoint() const { return mount_point; } | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Writes the SMDH icon of the ExtSaveData to file | ||||||
|  |      * @param path Path of this ExtSaveData | ||||||
|  |      * @param icon_data Binary data of the icon | ||||||
|  |      * @param icon_size Size of the icon data | ||||||
|  |      */ | ||||||
|  |     void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     /**
 |     /**
 | ||||||
|      * This holds the full directory path for this archive, it is only set after a successful call |      * This holds the full directory path for this archive, it is only set after a successful call | ||||||
|  | @ -42,6 +51,7 @@ private: | ||||||
|      * See GetExtSaveDataPath for the code that extracts this data from an archive path. |      * See GetExtSaveDataPath for the code that extracts this data from an archive path. | ||||||
|      */ |      */ | ||||||
|     std::string mount_point; |     std::string mount_point; | ||||||
|  |     bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -29,11 +29,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_RomFS::Format(const Path& path) { | ResultCode ArchiveFactory_RomFS::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     LOG_ERROR(Service_FS, "Attempted to format a RomFS archive."); |     LOG_ERROR(Service_FS, "Attempted to format a RomFS archive."); | ||||||
|     // TODO: Verify error code
 |     // TODO: Verify error code
 | ||||||
|     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, |     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||||||
|             ErrorSummary::NotSupported, ErrorLevel::Permanent); |             ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_RomFS::GetFormatInfo(const Path& path) const { | ||||||
|  |     // TODO(Subv): Implement
 | ||||||
|  |     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); | ||||||
|  |     return ResultCode(-1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -26,7 +26,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     std::string GetName() const override { return "RomFS"; } |     std::string GetName() const override { return "RomFS"; } | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::shared_ptr<FileUtil::IOFile> romfs_file; |     std::shared_ptr<FileUtil::IOFile> romfs_file; | ||||||
|  |  | ||||||
|  | @ -26,11 +26,17 @@ static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) { | static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) { | ||||||
|     u32 high = program_id >> 32; |     u32 high = (u32)(program_id >> 32); | ||||||
|     u32 low = program_id & 0xFFFFFFFF; |     u32 low = (u32)(program_id & 0xFFFFFFFF); | ||||||
|     return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); |     return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) { | ||||||
|  |     u32 high = (u32)(program_id >> 32); | ||||||
|  |     u32 low = (u32)(program_id & 0xFFFFFFFF); | ||||||
|  |     return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(), high, low); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory) | ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory) | ||||||
|         : mount_point(GetSaveDataContainerPath(sdmc_directory)) { |         : mount_point(GetSaveDataContainerPath(sdmc_directory)) { | ||||||
|     LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); |     LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | ||||||
|  | @ -51,11 +57,35 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_SaveData::Format(const Path& path) { | ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); |     std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id); | ||||||
|     FileUtil::DeleteDirRecursively(concrete_mount_point); |     FileUtil::DeleteDirRecursively(concrete_mount_point); | ||||||
|     FileUtil::CreateFullPath(concrete_mount_point); |     FileUtil::CreateFullPath(concrete_mount_point); | ||||||
|  | 
 | ||||||
|  |     // Write the format metadata
 | ||||||
|  |     std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id); | ||||||
|  |     FileUtil::IOFile file(metadata_path, "wb"); | ||||||
|  | 
 | ||||||
|  |     if (file.IsOpen()) { | ||||||
|  |         file.WriteBytes(&format_info, sizeof(format_info)); | ||||||
|  |         return RESULT_SUCCESS; | ||||||
|  |     } | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const { | ||||||
|  |     std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id); | ||||||
|  |     FileUtil::IOFile file(metadata_path, "rb"); | ||||||
|  | 
 | ||||||
|  |     if (!file.IsOpen()) { | ||||||
|  |         LOG_ERROR(Service_FS, "Could not open metadata information for archive"); | ||||||
|  |         // TODO(Subv): Verify error code
 | ||||||
|  |         return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ArchiveFormatInfo info = {}; | ||||||
|  |     file.ReadBytes(&info, sizeof(info)); | ||||||
|  |     return MakeResult<ArchiveFormatInfo>(info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,9 @@ public: | ||||||
|     std::string GetName() const override { return "SaveData"; } |     std::string GetName() const override { return "SaveData"; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  | 
 | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::string mount_point; |     std::string mount_point; | ||||||
|  |  | ||||||
|  | @ -48,11 +48,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path) { | ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); |     LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); | ||||||
|     // TODO: Verify error code
 |     // TODO: Verify error code
 | ||||||
|     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, |     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||||||
|         ErrorSummary::NotSupported, ErrorLevel::Permanent); |         ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const { | ||||||
|  |     // TODO(Subv): Implement
 | ||||||
|  |     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); | ||||||
|  |     return ResultCode(-1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,8 @@ public: | ||||||
|     std::string GetName() const override { return "SaveDataCheck"; } |     std::string GetName() const override { return "SaveDataCheck"; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::string mount_point; |     std::string mount_point; | ||||||
|  |  | ||||||
|  | @ -40,9 +40,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_SDMC::Format(const Path& path) { | ResultCode ArchiveFactory_SDMC::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     // This is kind of an undesirable operation, so let's just ignore it. :)
 |     // This is kind of an undesirable operation, so let's just ignore it. :)
 | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const { | ||||||
|  |     // TODO(Subv): Implement
 | ||||||
|  |     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); | ||||||
|  |     return ResultCode(-1); | ||||||
|  | } | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -29,7 +29,8 @@ public: | ||||||
|     std::string GetName() const override { return "SDMC"; } |     std::string GetName() const override { return "SDMC"; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::string sdmc_directory; |     std::string sdmc_directory; | ||||||
|  |  | ||||||
|  | @ -63,11 +63,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c | ||||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) { | ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     std::string fullpath = GetSystemSaveDataPath(base_path, path); |     std::string fullpath = GetSystemSaveDataPath(base_path, path); | ||||||
|     FileUtil::DeleteDirRecursively(fullpath); |     FileUtil::DeleteDirRecursively(fullpath); | ||||||
|     FileUtil::CreateFullPath(fullpath); |     FileUtil::CreateFullPath(fullpath); | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const { | ||||||
|  |     // TODO(Subv): Implement
 | ||||||
|  |     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); | ||||||
|  |     return ResultCode(-1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,8 @@ public: | ||||||
|     ArchiveFactory_SystemSaveData(const std::string& mount_point); |     ArchiveFactory_SystemSaveData(const std::string& mount_point); | ||||||
| 
 | 
 | ||||||
|     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|     ResultCode Format(const Path& path) override; |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
| 
 | 
 | ||||||
|     std::string GetName() const override { return "SystemSaveData"; } |     std::string GetName() const override { return "SystemSaveData"; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,16 +17,28 @@ | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | ResultVal<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); |     LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | ||||||
|     auto file = Common::make_unique<DiskFile>(*this, path, mode); |     auto file = Common::make_unique<DiskFile>(*this, path, mode); | ||||||
|     if (!file->Open()) |     ResultCode result = file->Open(); | ||||||
|         return nullptr; |     if (result.IsError()) | ||||||
|     return std::move(file); |         return result; | ||||||
|  |     return MakeResult<std::unique_ptr<FileBackend>>(std::move(file)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool DiskArchive::DeleteFile(const Path& path) const { | ResultCode DiskArchive::DeleteFile(const Path& path) const { | ||||||
|     return FileUtil::Delete(mount_point + path.AsString()); |     std::string file_path = mount_point + path.AsString(); | ||||||
|  | 
 | ||||||
|  |     if (FileUtil::IsDirectory(file_path)) | ||||||
|  |         return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|  |     if (!FileUtil::Exists(file_path)) | ||||||
|  |         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|  |     if (FileUtil::Delete(file_path)) | ||||||
|  |         return RESULT_SUCCESS; | ||||||
|  | 
 | ||||||
|  |     return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ||||||
|  | @ -37,11 +49,14 @@ bool DiskArchive::DeleteDirectory(const Path& path) const { | ||||||
|     return FileUtil::DeleteDir(mount_point + path.AsString()); |     return FileUtil::DeleteDir(mount_point + path.AsString()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { | ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const { | ||||||
|     std::string full_path = mount_point + path.AsString(); |     std::string full_path = mount_point + path.AsString(); | ||||||
| 
 | 
 | ||||||
|  |     if (FileUtil::IsDirectory(full_path)) | ||||||
|  |         return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|     if (FileUtil::Exists(full_path)) |     if (FileUtil::Exists(full_path)) | ||||||
|         return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); |         return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Status); | ||||||
| 
 | 
 | ||||||
|     if (size == 0) { |     if (size == 0) { | ||||||
|         FileUtil::CreateEmptyFile(full_path); |         FileUtil::CreateEmptyFile(full_path); | ||||||
|  | @ -89,38 +104,57 @@ DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode | ||||||
|     this->mode.hex = mode.hex; |     this->mode.hex = mode.hex; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool DiskFile::Open() { | ResultCode DiskFile::Open() { | ||||||
|     if (!mode.create_flag && !FileUtil::Exists(path)) { |     if (FileUtil::IsDirectory(path)) | ||||||
|         LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str()); |         return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|         return false; | 
 | ||||||
|  |     // Specifying only the Create flag is invalid
 | ||||||
|  |     if (mode.create_flag && !mode.read_flag && !mode.write_flag) { | ||||||
|  |         return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string mode_string; |     if (!FileUtil::Exists(path)) { | ||||||
|     if (mode.create_flag) |         if (!mode.create_flag) { | ||||||
|         mode_string = "w+"; |             LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str()); | ||||||
|     else if (mode.write_flag) |             return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); | ||||||
|         mode_string = "r+"; // Files opened with Write access can be read from
 |         } else { | ||||||
|  |             // Create the file
 | ||||||
|  |             FileUtil::CreateEmptyFile(path); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::string mode_string = ""; | ||||||
|  |     if (mode.write_flag) | ||||||
|  |         mode_string += "r+"; // Files opened with Write access can be read from
 | ||||||
|     else if (mode.read_flag) |     else if (mode.read_flag) | ||||||
|         mode_string = "r"; |         mode_string += "r"; | ||||||
| 
 | 
 | ||||||
|     // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 |     // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 | ||||||
|     mode_string += "b"; |     mode_string += "b"; | ||||||
| 
 | 
 | ||||||
|     file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); |     file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); | ||||||
|     return file->IsOpen(); |     if (file->IsOpen()) | ||||||
|  |         return RESULT_SUCCESS; | ||||||
|  |     return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { | ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { | ||||||
|  |     if (!mode.read_flag && !mode.write_flag) | ||||||
|  |         return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|     file->Seek(offset, SEEK_SET); |     file->Seek(offset, SEEK_SET); | ||||||
|     return file->ReadBytes(buffer, length); |     return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { | ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { | ||||||
|  |     if (!mode.write_flag) | ||||||
|  |         return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
|  | 
 | ||||||
|     file->Seek(offset, SEEK_SET); |     file->Seek(offset, SEEK_SET); | ||||||
|     size_t written = file->WriteBytes(buffer, length); |     size_t written = file->WriteBytes(buffer, length); | ||||||
|     if (flush) |     if (flush) | ||||||
|         file->Flush(); |         file->Flush(); | ||||||
|     return written; |     return MakeResult<size_t>(written); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 DiskFile::GetSize() const { | u64 DiskFile::GetSize() const { | ||||||
|  |  | ||||||
|  | @ -33,11 +33,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual std::string GetName() const override { return "DiskArchive: " + mount_point; } |     virtual std::string GetName() const override { return "DiskArchive: " + mount_point; } | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; |     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override; | ||||||
|     bool DeleteFile(const Path& path) const override; |     ResultCode DeleteFile(const Path& path) const override; | ||||||
|     bool RenameFile(const Path& src_path, const Path& dest_path) const override; |     bool RenameFile(const Path& src_path, const Path& dest_path) const override; | ||||||
|     bool DeleteDirectory(const Path& path) const override; |     bool DeleteDirectory(const Path& path) const override; | ||||||
|     ResultCode CreateFile(const Path& path, u32 size) const override; |     ResultCode CreateFile(const Path& path, u64 size) const override; | ||||||
|     bool CreateDirectory(const Path& path) const override; |     bool CreateDirectory(const Path& path) const override; | ||||||
|     bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; |     bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||||||
|     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||||||
|  | @ -54,9 +54,9 @@ class DiskFile : public FileBackend { | ||||||
| public: | public: | ||||||
|     DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); |     DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); | ||||||
| 
 | 
 | ||||||
|     bool Open() override; |     ResultCode Open() override; | ||||||
|     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; | ||||||
|     size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |     ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||||||
|     u64 GetSize() const override; |     u64 GetSize() const override; | ||||||
|     bool SetSize(u64 size) const override; |     bool SetSize(u64 size) const override; | ||||||
|     bool Close() const override; |     bool Close() const override; | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // FileSys namespace
 | // FileSys namespace
 | ||||||
|  | @ -20,18 +21,18 @@ public: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Open the file |      * Open the file | ||||||
|      * @return true if the file opened correctly |      * @return Result of the file operation | ||||||
|      */ |      */ | ||||||
|     virtual bool Open() = 0; |     virtual ResultCode Open() = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Read data from the file |      * Read data from the file | ||||||
|      * @param offset Offset in bytes to start reading data from |      * @param offset Offset in bytes to start reading data from | ||||||
|      * @param length Length in bytes of data to read from file |      * @param length Length in bytes of data to read from file | ||||||
|      * @param buffer Buffer to read data into |      * @param buffer Buffer to read data into | ||||||
|      * @return Number of bytes read |      * @return Number of bytes read, or error code | ||||||
|      */ |      */ | ||||||
|     virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0; |     virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Write data to the file |      * Write data to the file | ||||||
|  | @ -39,9 +40,9 @@ public: | ||||||
|      * @param length Length in bytes of data to write to file |      * @param length Length in bytes of data to write to file | ||||||
|      * @param flush The flush parameters (0 == do not flush) |      * @param flush The flush parameters (0 == do not flush) | ||||||
|      * @param buffer Buffer to read data from |      * @param buffer Buffer to read data from | ||||||
|      * @return Number of bytes written |      * @return Number of bytes written, or error code | ||||||
|      */ |      */ | ||||||
|     virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; |     virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Get the size of the file in bytes |      * Get the size of the file in bytes | ||||||
|  |  | ||||||
|  | @ -20,13 +20,15 @@ std::string IVFCArchive::GetName() const { | ||||||
|     return "IVFC"; |     return "IVFC"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | ||||||
|     return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); |     return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IVFCArchive::DeleteFile(const Path& path) const { | ResultCode IVFCArchive::DeleteFile(const Path& path) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str()); |     LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str()); | ||||||
|     return false; |     // TODO(Subv): Verify error code
 | ||||||
|  |     return ResultCode(ErrorDescription::NoData, ErrorModule::FS, | ||||||
|  |                       ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ||||||
|  | @ -39,7 +41,7 @@ bool IVFCArchive::DeleteDirectory(const Path& path) const { | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode IVFCArchive::CreateFile(const Path& path, u32 size) const { | ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | ||||||
|     LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str()); |     LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str()); | ||||||
|     // TODO: Verify error code
 |     // TODO: Verify error code
 | ||||||
|     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); |     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); | ||||||
|  | @ -66,17 +68,18 @@ u64 IVFCArchive::GetFreeBytes() const { | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| size_t IVFCFile::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); | ||||||
| 
 | 
 | ||||||
|     return romfs_file->ReadBytes(buffer, read_length); |     return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { | ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { | ||||||
|     LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); |     LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); | ||||||
|     return 0; |     // TODO(Subv): Find error code
 | ||||||
|  |     return MakeResult<size_t>(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u64 IVFCFile::GetSize() const { | u64 IVFCFile::GetSize() const { | ||||||
|  |  | ||||||
|  | @ -34,11 +34,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     std::string GetName() const override; |     std::string GetName() const override; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; |     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override; | ||||||
|     bool DeleteFile(const Path& path) const override; |     ResultCode DeleteFile(const Path& path) const override; | ||||||
|     bool RenameFile(const Path& src_path, const Path& dest_path) const override; |     bool RenameFile(const Path& src_path, const Path& dest_path) const override; | ||||||
|     bool DeleteDirectory(const Path& path) const override; |     bool DeleteDirectory(const Path& path) const override; | ||||||
|     ResultCode CreateFile(const Path& path, u32 size) const override; |     ResultCode CreateFile(const Path& path, u64 size) const override; | ||||||
|     bool CreateDirectory(const Path& path) const override; |     bool CreateDirectory(const Path& path) const override; | ||||||
|     bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; |     bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||||||
|     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; |     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||||||
|  | @ -55,9 +55,9 @@ public: | ||||||
|     IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |     IVFCFile(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) {} | ||||||
| 
 | 
 | ||||||
|     bool Open() override { return true; } |     ResultCode Open() override { return RESULT_SUCCESS; } | ||||||
|     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; | ||||||
|     size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |     ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||||||
|     u64 GetSize() const override; |     u64 GetSize() const override; | ||||||
|     bool SetSize(u64 size) const override; |     bool SetSize(u64 size) const override; | ||||||
|     bool Close() const override { return false; } |     bool Close() const override { return false; } | ||||||
|  |  | ||||||
|  | @ -19,8 +19,12 @@ | ||||||
| enum class ErrorDescription : u32 { | enum class ErrorDescription : u32 { | ||||||
|     Success = 0, |     Success = 0, | ||||||
|     WrongAddress = 53, |     WrongAddress = 53, | ||||||
|     FS_NotFound = 100, |     FS_NotFound = 120, | ||||||
|  |     FS_AlreadyExists = 190, | ||||||
|  |     FS_InvalidOpenFlags = 230, | ||||||
|  |     FS_NotAFile = 250, | ||||||
|     FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
 |     FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
 | ||||||
|  |     FS_InvalidPath = 702, | ||||||
|     InvalidSection = 1000, |     InvalidSection = 1000, | ||||||
|     TooLarge = 1001, |     TooLarge = 1001, | ||||||
|     NotAuthorized = 1002, |     NotAuthorized = 1002, | ||||||
|  |  | ||||||
|  | @ -310,7 +310,8 @@ ResultCode UpdateConfigNANDSavegame() { | ||||||
| 
 | 
 | ||||||
| ResultCode FormatConfig() { | ResultCode FormatConfig() { | ||||||
|     ResultCode res = DeleteConfigNANDSaveFile(); |     ResultCode res = DeleteConfigNANDSaveFile(); | ||||||
|     if (!res.IsSuccess()) |     // The delete command fails if the file doesn't exist, so we have to check that too
 | ||||||
|  |     if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound) | ||||||
|         return res; |         return res; | ||||||
|     // Delete the old data
 |     // Delete the old data
 | ||||||
|     cfg_config_file_buffer.fill(0); |     cfg_config_file_buffer.fill(0); | ||||||
|  | @ -407,7 +408,7 @@ void Init() { | ||||||
|     // If the archive didn't exist, create the files inside
 |     // If the archive didn't exist, create the files inside
 | ||||||
|     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { |     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { | ||||||
|         // Format the archive to create the directories
 |         // Format the archive to create the directories
 | ||||||
|         Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); |         Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, FileSys::ArchiveFormatInfo(), archive_path); | ||||||
| 
 | 
 | ||||||
|         // Open it again to get a valid archive now that the folder exists
 |         // Open it again to get a valid archive now that the folder exists
 | ||||||
|         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); |         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); | ||||||
|  |  | ||||||
|  | @ -103,7 +103,18 @@ ResultVal<bool> File::SyncRequest() { | ||||||
|             u32 address = cmd_buff[5]; |             u32 address = cmd_buff[5]; | ||||||
|             LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", |             LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||||||
|                       GetTypeName().c_str(), GetName().c_str(), offset, length, address); |                       GetTypeName().c_str(), GetName().c_str(), offset, length, address); | ||||||
|             cmd_buff[2] = static_cast<u32>(backend->Read(offset, length, Memory::GetPointer(address))); | 
 | ||||||
|  |             if (offset + length > backend->GetSize()) { | ||||||
|  |                 LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX", | ||||||
|  |                           offset, length, backend->GetSize()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address)); | ||||||
|  |             if (read.Failed()) { | ||||||
|  |                 cmd_buff[1] = read.Code().raw; | ||||||
|  |                 return read.Code(); | ||||||
|  |             } | ||||||
|  |             cmd_buff[2] = static_cast<u32>(*read); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -116,7 +127,13 @@ ResultVal<bool> File::SyncRequest() { | ||||||
|             u32 address = cmd_buff[6]; |             u32 address = cmd_buff[6]; | ||||||
|             LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", |             LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", | ||||||
|                       GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); |                       GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); | ||||||
|             cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address))); | 
 | ||||||
|  |             ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address)); | ||||||
|  |             if (written.Failed()) { | ||||||
|  |                 cmd_buff[1] = written.Code().raw; | ||||||
|  |                 return written.Code(); | ||||||
|  |             } | ||||||
|  |             cmd_buff[2] = static_cast<u32>(*written); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -294,13 +311,11 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han | ||||||
|     if (archive == nullptr) |     if (archive == nullptr) | ||||||
|         return ERR_INVALID_HANDLE; |         return ERR_INVALID_HANDLE; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode); |     auto backend = archive->OpenFile(path, mode); | ||||||
|     if (backend == nullptr) { |     if (backend.Failed()) | ||||||
|         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, |         return backend.Code(); | ||||||
|                           ErrorSummary::NotFound, ErrorLevel::Status); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path)); |     auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path)); | ||||||
|     return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); |     return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -309,10 +324,7 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa | ||||||
|     if (archive == nullptr) |     if (archive == nullptr) | ||||||
|         return ERR_INVALID_HANDLE; |         return ERR_INVALID_HANDLE; | ||||||
| 
 | 
 | ||||||
|     if (archive->DeleteFile(path)) |     return archive->DeleteFile(path); | ||||||
|         return RESULT_SUCCESS; |  | ||||||
|     return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
 |  | ||||||
|                       ErrorSummary::Canceled, ErrorLevel::Status); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, | ||||||
|  | @ -347,7 +359,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy | ||||||
|                       ErrorSummary::Canceled, ErrorLevel::Status); |                       ErrorSummary::Canceled, ErrorLevel::Status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { | ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) { | ||||||
|     ArchiveBackend* archive = GetArchive(archive_handle); |     ArchiveBackend* archive = GetArchive(archive_handle); | ||||||
|     if (archive == nullptr) |     if (archive == nullptr) | ||||||
|         return ERR_INVALID_HANDLE; |         return ERR_INVALID_HANDLE; | ||||||
|  | @ -395,7 +407,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); |     std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); | ||||||
|     if (backend == nullptr) { |     if (backend == nullptr) { | ||||||
|         return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, |         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||||||
|                           ErrorSummary::NotFound, ErrorLevel::Permanent); |                           ErrorSummary::NotFound, ErrorLevel::Permanent); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -410,49 +422,45 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { | ||||||
|     return MakeResult<u64>(archive->GetFreeBytes()); |     return MakeResult<u64>(archive->GetFreeBytes()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) { | ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path) { | ||||||
|     auto archive_itr = id_code_map.find(id_code); |     auto archive_itr = id_code_map.find(id_code); | ||||||
|     if (archive_itr == id_code_map.end()) { |     if (archive_itr == id_code_map.end()) { | ||||||
|         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 |         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return archive_itr->second->Format(path); |     return archive_itr->second->Format(path, format_info); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size) { | ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path) { | ||||||
|  |     auto archive = id_code_map.find(id_code); | ||||||
|  |     if (archive == id_code_map.end()) { | ||||||
|  |         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return archive->second->GetFormatInfo(archive_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info) { | ||||||
|     // Construct the binary path to the archive first
 |     // Construct the binary path to the archive first
 | ||||||
|     FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low); |     FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low); | ||||||
| 
 | 
 | ||||||
|     std::string media_type_directory; |     auto archive = id_code_map.find(media_type == MediaType::NAND ? ArchiveIdCode::SharedExtSaveData : ArchiveIdCode::ExtSaveData); | ||||||
|     if (media_type == MediaType::NAND) { | 
 | ||||||
|         media_type_directory = FileUtil::GetUserPath(D_NAND_IDX); |     if (archive == id_code_map.end()) { | ||||||
|     } else if (media_type == MediaType::SDMC) { |         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | ||||||
|         media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX); |  | ||||||
|     } else { |  | ||||||
|         LOG_ERROR(Service_FS, "Unsupported media type %u", media_type); |  | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); |     auto ext_savedata = static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get()); | ||||||
|     std::string game_path = FileSys::GetExtSaveDataPath(base_path, path); | 
 | ||||||
|     // These two folders are always created with the ExtSaveData
 |     ResultCode result = ext_savedata->Format(path, format_info); | ||||||
|     std::string user_path = game_path + "user/"; |     if (result.IsError()) | ||||||
|     std::string boss_path = game_path + "boss/"; |         return result; | ||||||
|     if (!FileUtil::CreateFullPath(user_path)) |  | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |  | ||||||
|     if (!FileUtil::CreateFullPath(boss_path)) |  | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |  | ||||||
| 
 | 
 | ||||||
|     u8* smdh_icon = Memory::GetPointer(icon_buffer); |     u8* smdh_icon = Memory::GetPointer(icon_buffer); | ||||||
|     if (!smdh_icon) |     if (!smdh_icon) | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |         return ResultCode(-1); // TODO(Subv): Find the right error code
 | ||||||
| 
 | 
 | ||||||
|     // Create the icon
 |     ext_savedata->WriteIcon(path, smdh_icon, icon_size); | ||||||
|     FileUtil::IOFile icon_file(game_path + "icon", "wb+"); |  | ||||||
|     if (!icon_file.IsGood()) |  | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |  | ||||||
| 
 |  | ||||||
|     icon_file.WriteBytes(smdh_icon, icon_size); |  | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -473,7 +481,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) { | ||||||
|     // Delete all directories (/user, /boss) and the icon file.
 |     // Delete all directories (/user, /boss) and the icon file.
 | ||||||
|     std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); |     std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); | ||||||
|     std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path); |     std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path); | ||||||
|     if (!FileUtil::DeleteDirRecursively(extsavedata_path)) |     if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path)) | ||||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 |         return ResultCode(-1); // TODO(Subv): Find the right error code
 | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -136,7 +136,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy | ||||||
|  * @param file_size The size of the new file, filled with zeroes |  * @param file_size The size of the new file, filled with zeroes | ||||||
|  * @return File creation result code |  * @return File creation result code | ||||||
|  */ |  */ | ||||||
| ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size); | ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Create a Directory from an Archive |  * Create a Directory from an Archive | ||||||
|  | @ -177,10 +177,20 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle); | ||||||
|  * Erases the contents of the physical folder that contains the archive |  * Erases the contents of the physical folder that contains the archive | ||||||
|  * identified by the specified id code and path |  * identified by the specified id code and path | ||||||
|  * @param id_code The id of the archive to format |  * @param id_code The id of the archive to format | ||||||
|  |  * @param format_info Format information about the new archive | ||||||
|  * @param path The path to the archive, if relevant. |  * @param path The path to the archive, if relevant. | ||||||
|  * @return ResultCode 0 on success or the corresponding code on error |  * @return ResultCode 0 on success or the corresponding code on error | ||||||
|  */ |  */ | ||||||
| ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = FileSys::Path()); | ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path = FileSys::Path()); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Retrieves the format info about the archive of the specified type and path. | ||||||
|  |  * The format info is supplied by the client code when creating archives. | ||||||
|  |  * @param id_code The id of the archive | ||||||
|  |  * @param archive_path The path of the archive, if relevant | ||||||
|  |  * @return The format info of the archive, or the corresponding error code if failed. | ||||||
|  |  */ | ||||||
|  | ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Creates a blank SharedExtSaveData archive for the specified extdata ID |  * Creates a blank SharedExtSaveData archive for the specified extdata ID | ||||||
|  | @ -189,9 +199,10 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = File | ||||||
|  * @param low The low word of the extdata id to create |  * @param low The low word of the extdata id to create | ||||||
|  * @param icon_buffer VAddr of the SMDH icon for this ExtSaveData |  * @param icon_buffer VAddr of the SMDH icon for this ExtSaveData | ||||||
|  * @param icon_size Size of the SMDH icon |  * @param icon_size Size of the SMDH icon | ||||||
|  |  * @param format_info Format information about the new archive | ||||||
|  * @return ResultCode 0 on success or the corresponding code on error |  * @return ResultCode 0 on success or the corresponding code on error | ||||||
|  */ |  */ | ||||||
| ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size); | ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deletes the SharedExtSaveData archive for the specified extdata ID |  * Deletes the SharedExtSaveData archive for the specified extdata ID | ||||||
|  |  | ||||||
|  | @ -234,7 +234,7 @@ static void DeleteDirectory(Service::Interface* self) { | ||||||
|  *      3 : Archive handle upper word |  *      3 : Archive handle upper word | ||||||
|  *      4 : File path string type |  *      4 : File path string type | ||||||
|  *      5 : File path string size |  *      5 : File path string size | ||||||
|  *      7 : File size (filled with zeroes) |  *      7-8 : File size | ||||||
|  *      10: File path string data |  *      10: File path string data | ||||||
|  *  Outputs: |  *  Outputs: | ||||||
|  *      1 : Result of function, 0 on success, otherwise error code |  *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  | @ -245,12 +245,12 @@ static void CreateFile(Service::Interface* self) { | ||||||
|     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |     ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); | ||||||
|     auto filename_type    = static_cast<FileSys::LowPathType>(cmd_buff[4]); |     auto filename_type    = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||||
|     u32 filename_size     = cmd_buff[5]; |     u32 filename_size     = cmd_buff[5]; | ||||||
|     u32 file_size         = cmd_buff[7]; |     u64 file_size         = ((u64)cmd_buff[8] << 32) | cmd_buff[7]; | ||||||
|     u32 filename_ptr      = cmd_buff[10]; |     u32 filename_ptr      = cmd_buff[10]; | ||||||
| 
 | 
 | ||||||
|     FileSys::Path file_path(filename_type, filename_size, filename_ptr); |     FileSys::Path file_path(filename_type, filename_size, filename_ptr); | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); |     LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; |     cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; | ||||||
| } | } | ||||||
|  | @ -443,17 +443,22 @@ static void IsSdmcWriteable(Service::Interface* self) { | ||||||
|  *  Inputs: |  *  Inputs: | ||||||
|  *      0  : 0x084C0242 |  *      0  : 0x084C0242 | ||||||
|  *      1  : Archive ID |  *      1  : Archive ID | ||||||
|  *      2  : Archive low path type |  *      2  : Archive path type | ||||||
|  *      3  : Archive low path size |  *      3  : Archive path size | ||||||
|  *      10 : (LowPathSize << 14) | 2 |  *      4  : Size in Blocks (1 block = 512 bytes) | ||||||
|  |  *      5  : Number of directories | ||||||
|  |  *      6  : Number of files | ||||||
|  |  *      7  : Directory bucket count | ||||||
|  |  *      8  : File bucket count | ||||||
|  |  *      9  : Duplicate data | ||||||
|  |  *      10 : (PathSize << 14) | 2 | ||||||
|  *      11 : Archive low path |  *      11 : Archive low path | ||||||
|  *  Outputs: |  *  Outputs: | ||||||
|  *      1 : Result of function, 0 on success, otherwise error code |  *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  */ |  */ | ||||||
| static void FormatSaveData(Service::Interface* self) { | static void FormatSaveData(Service::Interface* self) { | ||||||
|     // TODO(Subv): Find out what the other inputs and outputs of this function are
 |  | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|     LOG_DEBUG(Service_FS, "(STUBBED)"); |     LOG_WARNING(Service_FS, "(STUBBED)"); | ||||||
| 
 | 
 | ||||||
|     auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); |     auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||||||
|     auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); |     auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||||||
|  | @ -464,9 +469,9 @@ static void FormatSaveData(Service::Interface* self) { | ||||||
|     LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); |     LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||||||
| 
 | 
 | ||||||
|     if (archive_id != FS::ArchiveIdCode::SaveData) { |     if (archive_id != FS::ArchiveIdCode::SaveData) { | ||||||
|         // TODO(Subv): What should happen if somebody attempts to format a different archive?
 |         LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", archive_id); | ||||||
|         LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]); |         cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS, | ||||||
|         cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; |                                  ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -477,23 +482,40 @@ static void FormatSaveData(Service::Interface* self) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; |     FileSys::ArchiveFormatInfo format_info; | ||||||
|  |     format_info.duplicate_data = cmd_buff[9] & 0xFF; | ||||||
|  |     format_info.number_directories = cmd_buff[5]; | ||||||
|  |     format_info.number_files = cmd_buff[6]; | ||||||
|  |     format_info.total_size = cmd_buff[4] * 512; | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * FS_User::FormatThisUserSaveData service function |  * FS_User::FormatThisUserSaveData service function | ||||||
|  *  Inputs: |  *  Inputs: | ||||||
|  *      0: 0x080F0180 |  *      0: 0x080F0180 | ||||||
|  |  *      1  : Size in Blocks (1 block = 512 bytes) | ||||||
|  |  *      2  : Number of directories | ||||||
|  |  *      3  : Number of files | ||||||
|  |  *      4  : Directory bucket count | ||||||
|  |  *      5  : File bucket count | ||||||
|  |  *      6  : Duplicate data | ||||||
|  *  Outputs: |  *  Outputs: | ||||||
|  *      1 : Result of function, 0 on success, otherwise error code |  *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  */ |  */ | ||||||
| static void FormatThisUserSaveData(Service::Interface* self) { | static void FormatThisUserSaveData(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|     LOG_DEBUG(Service_FS, "(STUBBED)"); |  | ||||||
| 
 | 
 | ||||||
|     // TODO(Subv): Find out what the inputs and outputs of this function are
 |     FileSys::ArchiveFormatInfo format_info; | ||||||
|  |     format_info.duplicate_data = cmd_buff[6] & 0xFF; | ||||||
|  |     format_info.number_directories = cmd_buff[2]; | ||||||
|  |     format_info.number_files = cmd_buff[3]; | ||||||
|  |     format_info.total_size = cmd_buff[1] * 512; | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw; |     cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw; | ||||||
|  | 
 | ||||||
|  |     LOG_TRACE(Service_FS, "called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -531,10 +553,9 @@ static void GetFreeBytes(Service::Interface* self) { | ||||||
|  *      2 : Low word of the saveid to create |  *      2 : Low word of the saveid to create | ||||||
|  *      3 : High word of the saveid to create |  *      3 : High word of the saveid to create | ||||||
|  *      4 : Unknown |  *      4 : Unknown | ||||||
|  *      5 : Unknown |  *      5 : Number of directories | ||||||
|  *      6 : Unknown |  *      6 : Number of files | ||||||
|  *      7 : Unknown |  *      7-8 : Size limit | ||||||
|  *      8 : Unknown |  | ||||||
|  *      9 : Size of the SMDH icon |  *      9 : Size of the SMDH icon | ||||||
|  *      10: (SMDH Size << 4) | 0x0000000A |  *      10: (SMDH Size << 4) | 0x0000000A | ||||||
|  *      11: Pointer to the SMDH icon for the new ExtSaveData |  *      11: Pointer to the SMDH icon for the new ExtSaveData | ||||||
|  | @ -556,7 +577,12 @@ static void CreateExtSaveData(Service::Interface* self) { | ||||||
|             cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size, |             cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size, | ||||||
|             cmd_buff[10], icon_buffer); |             cmd_buff[10], icon_buffer); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size).raw; |     FileSys::ArchiveFormatInfo format_info; | ||||||
|  |     format_info.number_directories = cmd_buff[5]; | ||||||
|  |     format_info.number_files = cmd_buff[6]; | ||||||
|  |     format_info.duplicate_data = false; | ||||||
|  |     format_info.total_size = 0; | ||||||
|  |     cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size, format_info).raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -731,6 +757,51 @@ static void GetArchiveResource(Service::Interface* self) { | ||||||
|     cmd_buff[5] = 0x80000; // 8GiB free
 |     cmd_buff[5] = 0x80000; // 8GiB free
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * FS_User::GetFormatInfo service function. | ||||||
|  |  *  Inputs: | ||||||
|  |  *      0 : 0x084500C2 | ||||||
|  |  *      1 : Archive ID | ||||||
|  |  *      2 : Archive path type | ||||||
|  |  *      3 : Archive path size | ||||||
|  |  *      4 : (PathSize << 14) | 2 | ||||||
|  |  *      5 : Archive low path | ||||||
|  |  *  Outputs: | ||||||
|  |  *      0 : 0x08450140 | ||||||
|  |  *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |  *      2 : Total size | ||||||
|  |  *      3 : Number of directories | ||||||
|  |  *      4 : Number of files | ||||||
|  |  *      5 : Duplicate data | ||||||
|  |  */ | ||||||
|  | static void GetFormatInfo(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  | 
 | ||||||
|  |     auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||||||
|  |     auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||||||
|  |     u32 archivename_size = cmd_buff[3]; | ||||||
|  |     u32 archivename_ptr = cmd_buff[5]; | ||||||
|  |     FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(0x0845, 5, 0); | ||||||
|  | 
 | ||||||
|  |     auto format_info = GetArchiveFormatInfo(archive_id, archive_path); | ||||||
|  | 
 | ||||||
|  |     if (format_info.Failed()) { | ||||||
|  |         LOG_ERROR(Service_FS, "Failed to retrieve the format info"); | ||||||
|  |         cmd_buff[1] = format_info.Code().raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
|  |     cmd_buff[2] = format_info->total_size; | ||||||
|  |     cmd_buff[3] = format_info->number_directories; | ||||||
|  |     cmd_buff[4] = format_info->number_files; | ||||||
|  |     cmd_buff[5] = format_info->duplicate_data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const Interface::FunctionInfo FunctionTable[] = { | const Interface::FunctionInfo FunctionTable[] = { | ||||||
|     {0x000100C6, nullptr,                  "Dummy1"}, |     {0x000100C6, nullptr,                  "Dummy1"}, | ||||||
|     {0x040100C4, nullptr,                  "Control"}, |     {0x040100C4, nullptr,                  "Control"}, | ||||||
|  | @ -802,7 +873,7 @@ const Interface::FunctionInfo FunctionTable[] = { | ||||||
|     {0x08420040, nullptr,                  "DeleteAllExtSaveDataOnNand"}, |     {0x08420040, nullptr,                  "DeleteAllExtSaveDataOnNand"}, | ||||||
|     {0x08430000, nullptr,                  "InitializeCtrFileSystem"}, |     {0x08430000, nullptr,                  "InitializeCtrFileSystem"}, | ||||||
|     {0x08440000, nullptr,                  "CreateSeed"}, |     {0x08440000, nullptr,                  "CreateSeed"}, | ||||||
|     {0x084500C2, nullptr,                  "GetFormatInfo"}, |     {0x084500C2, GetFormatInfo,            "GetFormatInfo"}, | ||||||
|     {0x08460102, nullptr,                  "GetLegacyRomHeader2"}, |     {0x08460102, nullptr,                  "GetLegacyRomHeader2"}, | ||||||
|     {0x08470180, nullptr,                  "FormatCtrCardUserSaveData"}, |     {0x08470180, nullptr,                  "FormatCtrCardUserSaveData"}, | ||||||
|     {0x08480042, nullptr,                  "GetSdmcCtrRootPath"}, |     {0x08480042, nullptr,                  "GetSdmcCtrRootPath"}, | ||||||
|  |  | ||||||
|  | @ -103,7 +103,7 @@ void Init() { | ||||||
|     // If the archive didn't exist, create the files inside
 |     // If the archive didn't exist, create the files inside
 | ||||||
|     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { |     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { | ||||||
|         // Format the archive to create the directories
 |         // Format the archive to create the directories
 | ||||||
|         Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); |         Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, FileSys::ArchiveFormatInfo(), archive_path); | ||||||
|         // Open it again to get a valid archive now that the folder exists
 |         // Open it again to get a valid archive now that the folder exists
 | ||||||
|         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); |         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); | ||||||
|         ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); |         ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue