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/common_types.h" | ||||
| #include "common/swap.h" | ||||
| 
 | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
|  | @ -62,6 +63,14 @@ private: | |||
|     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 { | ||||
| public: | ||||
|     virtual ~ArchiveBackend() { | ||||
|  | @ -76,16 +85,16 @@ public: | |||
|      * 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 | ||||
|      * @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 | ||||
|      * @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 | ||||
|  | @ -108,7 +117,7 @@ public: | |||
|      * @param size The size of the new file, filled with zeroes | ||||
|      * @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 | ||||
|  | @ -159,9 +168,17 @@ public: | |||
|     /**
 | ||||
|      * Deletes the archive contents and then re-creates the base folder | ||||
|      * @param path Path to the archive | ||||
|      * @param format_info Format information for the new archive | ||||
|      * @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
 | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) { | |||
| } | ||||
| 
 | ||||
| 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()); | ||||
| } | ||||
| 
 | ||||
|  | @ -74,21 +74,59 @@ bool ArchiveFactory_ExtSaveData::Initialize() { | |||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) { | ||||
|     std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/"; | ||||
|     if (!FileUtil::Exists(fullpath)) { | ||||
|         // TODO(Subv): Check error code, this one is probably wrong
 | ||||
|         return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||||
|             ErrorSummary::InvalidState, ErrorLevel::Status); | ||||
|         // 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, | ||||
|                               ErrorSummary::InvalidState, ErrorLevel::Status); | ||||
|         } | ||||
|     } | ||||
|     auto archive = Common::make_unique<DiskArchive>(fullpath); | ||||
|     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
 | ||||
|     std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/"; | ||||
|     std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/"; | ||||
|     FileUtil::CreateFullPath(user_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; | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|  |  | |||
|  | @ -31,10 +31,19 @@ public: | |||
|     std::string GetName() const override { return "ExtSaveData"; } | ||||
| 
 | ||||
|     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; } | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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: | ||||
|     /**
 | ||||
|      * 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. | ||||
|      */ | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| 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."); | ||||
|     // TODO: Verify error code
 | ||||
|     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||||
|             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
 | ||||
|  |  | |||
|  | @ -26,7 +26,8 @@ public: | |||
| 
 | ||||
|     std::string GetName() const override { return "RomFS"; } | ||||
|     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: | ||||
|     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) { | ||||
|     u32 high = program_id >> 32; | ||||
|     u32 low = program_id & 0xFFFFFFFF; | ||||
|     u32 high = (u32)(program_id >> 32); | ||||
|     u32 low = (u32)(program_id & 0xFFFFFFFF); | ||||
|     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) | ||||
|         : mount_point(GetSaveDataContainerPath(sdmc_directory)) { | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     FileUtil::DeleteDirRecursively(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; | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|  |  | |||
|  | @ -23,7 +23,9 @@ public: | |||
|     std::string GetName() const override { return "SaveData"; } | ||||
| 
 | ||||
|     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: | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| 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."); | ||||
|     // TODO: Verify error code
 | ||||
|     return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, | ||||
|         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
 | ||||
|  |  | |||
|  | @ -23,7 +23,8 @@ public: | |||
|     std::string GetName() const override { return "SaveDataCheck"; } | ||||
| 
 | ||||
|     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: | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| 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. :)
 | ||||
|     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
 | ||||
|  |  | |||
|  | @ -29,7 +29,8 @@ public: | |||
|     std::string GetName() const override { return "SDMC"; } | ||||
| 
 | ||||
|     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: | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     FileUtil::DeleteDirRecursively(fullpath); | ||||
|     FileUtil::CreateFullPath(fullpath); | ||||
|     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
 | ||||
|  |  | |||
|  | @ -23,7 +23,8 @@ public: | |||
|     ArchiveFactory_SystemSaveData(const std::string& mount_point); | ||||
| 
 | ||||
|     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"; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,16 +17,28 @@ | |||
| 
 | ||||
| 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); | ||||
|     auto file = Common::make_unique<DiskFile>(*this, path, mode); | ||||
|     if (!file->Open()) | ||||
|         return nullptr; | ||||
|     return std::move(file); | ||||
|     ResultCode result = file->Open(); | ||||
|     if (result.IsError()) | ||||
|         return result; | ||||
|     return MakeResult<std::unique_ptr<FileBackend>>(std::move(file)); | ||||
| } | ||||
| 
 | ||||
| bool DiskArchive::DeleteFile(const Path& path) const { | ||||
|     return FileUtil::Delete(mount_point + path.AsString()); | ||||
| ResultCode DiskArchive::DeleteFile(const Path& path) const { | ||||
|     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 { | ||||
|  | @ -37,11 +49,14 @@ bool DiskArchive::DeleteDirectory(const Path& path) const { | |||
|     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(); | ||||
| 
 | ||||
|     if (FileUtil::IsDirectory(full_path)) | ||||
|         return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||
| 
 | ||||
|     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) { | ||||
|         FileUtil::CreateEmptyFile(full_path); | ||||
|  | @ -89,38 +104,57 @@ DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode | |||
|     this->mode.hex = mode.hex; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| ResultCode DiskFile::Open() { | ||||
|     if (FileUtil::IsDirectory(path)) | ||||
|         return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); | ||||
| 
 | ||||
|     // 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 (mode.create_flag) | ||||
|         mode_string = "w+"; | ||||
|     else if (mode.write_flag) | ||||
|         mode_string = "r+"; // Files opened with Write access can be read from
 | ||||
|     if (!FileUtil::Exists(path)) { | ||||
|         if (!mode.create_flag) { | ||||
|             LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str()); | ||||
|             return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); | ||||
|         } 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) | ||||
|         mode_string = "r"; | ||||
|         mode_string += "r"; | ||||
| 
 | ||||
|     // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 | ||||
|     mode_string += "b"; | ||||
| 
 | ||||
|     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); | ||||
|     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); | ||||
|     size_t written = file->WriteBytes(buffer, length); | ||||
|     if (flush) | ||||
|         file->Flush(); | ||||
|     return written; | ||||
|     return MakeResult<size_t>(written); | ||||
| } | ||||
| 
 | ||||
| u64 DiskFile::GetSize() const { | ||||
|  |  | |||
|  | @ -33,11 +33,11 @@ public: | |||
| 
 | ||||
|     virtual std::string GetName() const override { return "DiskArchive: " + mount_point; } | ||||
| 
 | ||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||||
|     bool DeleteFile(const Path& path) const override; | ||||
|     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override; | ||||
|     ResultCode DeleteFile(const Path& path) const override; | ||||
|     bool RenameFile(const Path& src_path, const Path& dest_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 RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||||
|     std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||||
|  | @ -54,9 +54,9 @@ class DiskFile : public FileBackend { | |||
| public: | ||||
|     DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); | ||||
| 
 | ||||
|     bool Open() override; | ||||
|     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; | ||||
|     ResultCode Open() override; | ||||
|     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||||
|     ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||||
|     u64 GetSize() const override; | ||||
|     bool SetSize(u64 size) const override; | ||||
|     bool Close() const override; | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <cstddef> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // FileSys namespace
 | ||||
|  | @ -20,18 +21,18 @@ public: | |||
| 
 | ||||
|     /**
 | ||||
|      * 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 | ||||
|      * @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 | ||||
|      * @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 | ||||
|  | @ -39,9 +40,9 @@ public: | |||
|      * @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 | ||||
|      * @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 | ||||
|  |  | |||
|  | @ -20,13 +20,15 @@ std::string IVFCArchive::GetName() const { | |||
|     return "IVFC"; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | ||||
|     return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size); | ||||
| ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const { | ||||
|     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()); | ||||
|     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 { | ||||
|  | @ -39,7 +41,7 @@ bool IVFCArchive::DeleteDirectory(const Path& path) const { | |||
|     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()); | ||||
|     // TODO: Verify error code
 | ||||
|     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); | ||||
|     romfs_file->Seek(data_offset + offset, SEEK_SET); | ||||
|     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"); | ||||
|     return 0; | ||||
|     // TODO(Subv): Find error code
 | ||||
|     return MakeResult<size_t>(0); | ||||
| } | ||||
| 
 | ||||
| u64 IVFCFile::GetSize() const { | ||||
|  |  | |||
|  | @ -34,11 +34,11 @@ public: | |||
| 
 | ||||
|     std::string GetName() const override; | ||||
| 
 | ||||
|     std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||||
|     bool DeleteFile(const Path& path) const override; | ||||
|     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override; | ||||
|     ResultCode DeleteFile(const Path& path) const override; | ||||
|     bool RenameFile(const Path& src_path, const Path& dest_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 RenameDirectory(const Path& src_path, const Path& dest_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) | ||||
|         : romfs_file(file), data_offset(offset), data_size(size) {} | ||||
| 
 | ||||
|     bool Open() override { return true; } | ||||
|     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; | ||||
|     ResultCode Open() override { return RESULT_SUCCESS; } | ||||
|     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||||
|     ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||||
|     u64 GetSize() const override; | ||||
|     bool SetSize(u64 size) const override; | ||||
|     bool Close() const override { return false; } | ||||
|  |  | |||
|  | @ -19,8 +19,12 @@ | |||
| enum class ErrorDescription : u32 { | ||||
|     Success = 0, | ||||
|     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_InvalidPath = 702, | ||||
|     InvalidSection = 1000, | ||||
|     TooLarge = 1001, | ||||
|     NotAuthorized = 1002, | ||||
|  |  | |||
|  | @ -310,7 +310,8 @@ ResultCode UpdateConfigNANDSavegame() { | |||
| 
 | ||||
| ResultCode FormatConfig() { | ||||
|     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; | ||||
|     // Delete the old data
 | ||||
|     cfg_config_file_buffer.fill(0); | ||||
|  | @ -407,7 +408,7 @@ void Init() { | |||
|     // If the archive didn't exist, create the files inside
 | ||||
|     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { | ||||
|         // 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
 | ||||
|         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); | ||||
|  |  | |||
|  | @ -103,7 +103,18 @@ ResultVal<bool> File::SyncRequest() { | |||
|             u32 address = cmd_buff[5]; | ||||
|             LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", | ||||
|                       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; | ||||
|         } | ||||
| 
 | ||||
|  | @ -116,7 +127,13 @@ ResultVal<bool> File::SyncRequest() { | |||
|             u32 address = cmd_buff[6]; | ||||
|             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); | ||||
|             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; | ||||
|         } | ||||
| 
 | ||||
|  | @ -294,13 +311,11 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han | |||
|     if (archive == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
| 
 | ||||
|     std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode); | ||||
|     if (backend == nullptr) { | ||||
|         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||||
|                           ErrorSummary::NotFound, ErrorLevel::Status); | ||||
|     } | ||||
|     auto backend = archive->OpenFile(path, mode); | ||||
|     if (backend.Failed()) | ||||
|         return backend.Code(); | ||||
| 
 | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
|  | @ -309,10 +324,7 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa | |||
|     if (archive == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
| 
 | ||||
|     if (archive->DeleteFile(path)) | ||||
|         return RESULT_SUCCESS; | ||||
|     return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
 | ||||
|                       ErrorSummary::Canceled, ErrorLevel::Status); | ||||
|     return archive->DeleteFile(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); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|     if (archive == nullptr) | ||||
|         return ERR_INVALID_HANDLE; | ||||
|  | @ -395,7 +407,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a | |||
| 
 | ||||
|     std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); | ||||
|     if (backend == nullptr) { | ||||
|         return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | ||||
|         return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||||
|                           ErrorSummary::NotFound, ErrorLevel::Permanent); | ||||
|     } | ||||
| 
 | ||||
|  | @ -410,49 +422,45 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { | |||
|     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); | ||||
|     if (archive_itr == id_code_map.end()) { | ||||
|         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
 | ||||
|     FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low); | ||||
| 
 | ||||
|     std::string media_type_directory; | ||||
|     if (media_type == MediaType::NAND) { | ||||
|         media_type_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||||
|     } else if (media_type == MediaType::SDMC) { | ||||
|         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
 | ||||
|     auto archive = id_code_map.find(media_type == MediaType::NAND ? ArchiveIdCode::SharedExtSaveData : ArchiveIdCode::ExtSaveData); | ||||
| 
 | ||||
|     if (archive == id_code_map.end()) { | ||||
|         return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | ||||
|     } | ||||
| 
 | ||||
|     std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); | ||||
|     std::string game_path = FileSys::GetExtSaveDataPath(base_path, path); | ||||
|     // These two folders are always created with the ExtSaveData
 | ||||
|     std::string user_path = game_path + "user/"; | ||||
|     std::string boss_path = game_path + "boss/"; | ||||
|     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
 | ||||
|     auto ext_savedata = static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get()); | ||||
| 
 | ||||
|     ResultCode result = ext_savedata->Format(path, format_info); | ||||
|     if (result.IsError()) | ||||
|         return result; | ||||
| 
 | ||||
|     u8* smdh_icon = Memory::GetPointer(icon_buffer); | ||||
|     if (!smdh_icon) | ||||
|         return ResultCode(-1); // TODO(Subv): Find the right error code
 | ||||
| 
 | ||||
|     // Create the icon
 | ||||
|     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); | ||||
|     ext_savedata->WriteIcon(path, smdh_icon, icon_size); | ||||
|     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.
 | ||||
|     std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND); | ||||
|     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 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 | ||||
|  * @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 | ||||
|  | @ -177,10 +177,20 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle); | |||
|  * Erases the contents of the physical folder that contains the archive | ||||
|  * identified by the specified id code and path | ||||
|  * @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. | ||||
|  * @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 | ||||
|  | @ -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 icon_buffer VAddr of the SMDH icon for this ExtSaveData | ||||
|  * @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 | ||||
|  */ | ||||
| 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 | ||||
|  |  | |||
|  | @ -234,7 +234,7 @@ static void DeleteDirectory(Service::Interface* self) { | |||
|  *      3 : Archive handle upper word | ||||
|  *      4 : File path string type | ||||
|  *      5 : File path string size | ||||
|  *      7 : File size (filled with zeroes) | ||||
|  *      7-8 : File size | ||||
|  *      10: File path string data | ||||
|  *  Outputs: | ||||
|  *      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]); | ||||
|     auto filename_type    = static_cast<FileSys::LowPathType>(cmd_buff[4]); | ||||
|     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]; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
|  | @ -443,17 +443,22 @@ static void IsSdmcWriteable(Service::Interface* self) { | |||
|  *  Inputs: | ||||
|  *      0  : 0x084C0242 | ||||
|  *      1  : Archive ID | ||||
|  *      2  : Archive low path type | ||||
|  *      3  : Archive low path size | ||||
|  *      10 : (LowPathSize << 14) | 2 | ||||
|  *      2  : Archive path type | ||||
|  *      3  : Archive path size | ||||
|  *      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 | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| 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(); | ||||
|     LOG_DEBUG(Service_FS, "(STUBBED)"); | ||||
|     LOG_WARNING(Service_FS, "(STUBBED)"); | ||||
| 
 | ||||
|     auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||||
|     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()); | ||||
| 
 | ||||
|     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", cmd_buff[1]); | ||||
|         cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||||
|         LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", archive_id); | ||||
|         cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS, | ||||
|                                  ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -477,23 +482,40 @@ static void FormatSaveData(Service::Interface* self) { | |||
|         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 | ||||
|  *  Inputs: | ||||
|  *      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: | ||||
|  *      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
 | ||||
|     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 | ||||
|  *      3 : High word of the saveid to create | ||||
|  *      4 : Unknown | ||||
|  *      5 : Unknown | ||||
|  *      6 : Unknown | ||||
|  *      7 : Unknown | ||||
|  *      8 : Unknown | ||||
|  *      5 : Number of directories | ||||
|  *      6 : Number of files | ||||
|  *      7-8 : Size limit | ||||
|  *      9 : Size of the SMDH icon | ||||
|  *      10: (SMDH Size << 4) | 0x0000000A | ||||
|  *      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[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
 | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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[] = { | ||||
|     {0x000100C6, nullptr,                  "Dummy1"}, | ||||
|     {0x040100C4, nullptr,                  "Control"}, | ||||
|  | @ -802,7 +873,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
|     {0x08420040, nullptr,                  "DeleteAllExtSaveDataOnNand"}, | ||||
|     {0x08430000, nullptr,                  "InitializeCtrFileSystem"}, | ||||
|     {0x08440000, nullptr,                  "CreateSeed"}, | ||||
|     {0x084500C2, nullptr,                  "GetFormatInfo"}, | ||||
|     {0x084500C2, GetFormatInfo,            "GetFormatInfo"}, | ||||
|     {0x08460102, nullptr,                  "GetLegacyRomHeader2"}, | ||||
|     {0x08470180, nullptr,                  "FormatCtrCardUserSaveData"}, | ||||
|     {0x08480042, nullptr,                  "GetSdmcCtrRootPath"}, | ||||
|  |  | |||
|  | @ -103,7 +103,7 @@ void Init() { | |||
|     // If the archive didn't exist, create the files inside
 | ||||
|     if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) { | ||||
|         // 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
 | ||||
|         archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); | ||||
|         ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue