From d26c6b3212ed36970410814593ee5ec082b1d95a Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Mon, 28 Dec 2015 13:51:44 -0500
Subject: [PATCH] HLE/FS: Implemented GetFormatInfo

Format information is currently only implemented for the ExtSaveData, SharedExtSaveData and SaveData archives, the information is stored in a file alongside the root folder of the archive.
---
 src/core/file_sys/archive_backend.h          |  18 +++-
 src/core/file_sys/archive_extsavedata.cpp    |  36 ++++++-
 src/core/file_sys/archive_extsavedata.h      |  11 +-
 src/core/file_sys/archive_romfs.cpp          |   8 +-
 src/core/file_sys/archive_romfs.h            |   3 +-
 src/core/file_sys/archive_savedata.cpp       |  32 +++++-
 src/core/file_sys/archive_savedata.h         |   4 +-
 src/core/file_sys/archive_savedatacheck.cpp  |   8 +-
 src/core/file_sys/archive_savedatacheck.h    |   3 +-
 src/core/file_sys/archive_sdmc.cpp           |   7 +-
 src/core/file_sys/archive_sdmc.h             |   3 +-
 src/core/file_sys/archive_systemsavedata.cpp |   8 +-
 src/core/file_sys/archive_systemsavedata.h   |   3 +-
 src/core/hle/result.h                        |   1 +
 src/core/hle/service/cfg/cfg.cpp             |   2 +-
 src/core/hle/service/fs/archive.cpp          |  48 ++++-----
 src/core/hle/service/fs/archive.h            |  15 ++-
 src/core/hle/service/fs/fs_user.cpp          | 107 +++++++++++++++----
 src/core/hle/service/ptm/ptm.cpp             |   2 +-
 19 files changed, 257 insertions(+), 62 deletions(-)

diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index 60108b4b0..800ac1541 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -62,6 +62,14 @@ private:
     std::u16string u16str;
 };
 
+struct ArchiveFormatInfo {
+    u32 total_size; ///< The pre-defined size of the archive, as specified in the Create or Format call
+    u32 number_directories; ///< The pre-defined number of directories in the archive, as specified in the Create or Format call
+    u32 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() {
@@ -159,9 +167,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
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index 92dad8e6f..e83a6153d 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -82,13 +82,45 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
     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);
-    return RESULT_SUCCESS;
+
+    // Write the format metadata
+    std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
+    FileUtil::IOFile file(metadata_path, "wb");
+
+    if (file.IsOpen()) {
+        file.WriteBytes(&format_info, sizeof(format_info));
+        return RESULT_SUCCESS;
+    }
+
+    // TODO(Subv): Find the correct error code
+    return ResultCode(-1);
+}
+
+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()) {
+        ArchiveFormatInfo info;
+        file.ReadBytes(&info, sizeof(info));
+        return MakeResult<ArchiveFormatInfo>(info);
+    }
+
+    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);
+}
+
+void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, u8* icon_data, u32 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
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index ec8d770fc..48e092ee7 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -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, u8* icon_data, u32 icon_size);
+
 private:
     /**
      * This holds the full directory path for this archive, it is only set after a successful call
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
index 696b51a94..a9a29ebde 100644
--- a/src/core/file_sys/archive_romfs.cpp
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -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
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index 2bedfa9c6..c5a329122 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -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;
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 12876899f..82f49af5d 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -31,6 +31,12 @@ static std::string GetSaveDataPath(const std::string& mount_location, u64 progra
     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 = program_id >> 32;
+    u32 low = 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()) {
+        ArchiveFormatInfo info;
+        file.ReadBytes(&info, sizeof(info));
+        return MakeResult<ArchiveFormatInfo>(info);
+    }
+
+    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);
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
index 1f65297dd..7a5a24089 100644
--- a/src/core/file_sys/archive_savedata.h
+++ b/src/core/file_sys/archive_savedata.h
@@ -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;
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp
index ea1dfe2c7..3db11c500 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_savedatacheck.cpp
@@ -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
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_savedatacheck.h
index b14aefe8b..ea2624d64 100644
--- a/src/core/file_sys/archive_savedatacheck.h
+++ b/src/core/file_sys/archive_savedatacheck.h
@@ -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;
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 5c825f429..657221cbf 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -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
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 10b273bdb..35c0f3725 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -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;
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 896f89529..e1780de2f 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -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
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index afc689848..2bc13d4ee 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -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"; }
 
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index b68c0ff0d..0cb76ba1c 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -24,6 +24,7 @@ enum class ErrorDescription : u32 {
     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,
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 4c82a58e4..7bcedc0ae 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -407,7 +407,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);
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index b034de8f1..63381250a 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -421,49 +421,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;
 }
 
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 430dc2ef9..b17d7c902 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -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
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 12ed609e9..ff7a9975e 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -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"},
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 6bdee4d9e..94f494690 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -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!");