From 1d61cd446016badb1dd218a2a4692b1e5e3eeb14 Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Sat, 14 Mar 2015 12:00:01 -0500
Subject: [PATCH 1/2] Services/FS: Implemented DeleteExtSaveData,
 CreateSystemSaveData and DeleteSystemSaveData

Also fixed a bug with CreateExtSaveData that made it unable to create ExtSaveData archives in the SDMC directory.
---
 src/core/file_sys/archive_extsavedata.cpp    | 21 +++++
 src/core/file_sys/archive_extsavedata.h      | 10 +++
 src/core/file_sys/archive_systemsavedata.cpp | 22 +++++-
 src/core/file_sys/archive_systemsavedata.h   | 25 ++++++
 src/core/hle/service/fs/archive.cpp          | 74 ++++++++++++++----
 src/core/hle/service/fs/archive.h            | 34 ++++++++-
 src/core/hle/service/fs/fs_user.cpp          | 80 ++++++++++++++++++--
 7 files changed, 240 insertions(+), 26 deletions(-)

diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index 0363c9771..3076fa263 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -34,6 +34,27 @@ std::string GetExtDataContainerPath(const std::string& mount_point, bool shared)
             SYSTEM_ID.c_str(), SDCARD_ID.c_str());
 }
 
+Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
+    std::vector<u8> binary_path;
+    binary_path.reserve(12);
+
+    // Append each word byte by byte
+
+    // The first word is the media type
+    for (unsigned i = 0; i < 4; ++i)
+        binary_path.push_back((media_type >> (8 * i)) & 0xFF);
+
+    // Next is the low word
+    for (unsigned i = 0; i < 4; ++i)
+        binary_path.push_back((low >> (8 * i)) & 0xFF);
+
+    // Next is the high word
+    for (unsigned i = 0; i < 4; ++i)
+        binary_path.push_back((high >> (8 * i)) & 0xFF);
+
+    return { binary_path };
+}
+
 ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared)
         : mount_point(GetExtDataContainerPath(mount_location, shared)) {
     LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index 83c6b0291..c77c04e44 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -58,4 +58,14 @@ std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path)
  */
 std::string GetExtDataContainerPath(const std::string& mount_point, bool shared);
 
+/**
+ * Constructs a FileSys::Path object that refers to the ExtData archive identified by
+ * the specified media type, high save id and low save id.
+ * @param media_type The media type where the archive is located (NAND / SDMC)
+ * @param high The high word of the save id for the archive
+ * @param low The low word of the save id for the archive
+ * @returns A FileSys::Path to the wanted archive
+ */
+Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low);
+
 } // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 25c94cd26..4fe785c97 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -17,7 +17,7 @@
 
 namespace FileSys {
 
-static std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
+std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
     std::vector<u8> vec_data = path.AsBinary();
     const u32* data = reinterpret_cast<const u32*>(vec_data.data());
     u32 save_low = data[1];
@@ -25,10 +25,27 @@ static std::string GetSystemSaveDataPath(const std::string& mount_point, const P
     return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
 }
 
-static std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
+std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
     return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str());
 }
 
+Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
+    std::vector<u8> binary_path;
+    binary_path.reserve(8);
+
+    // Append each word byte by byte
+
+    // First is the high word
+    for (unsigned i = 0; i < 4; ++i)
+        binary_path.push_back((high >> (8 * i)) & 0xFF);
+
+    // Next is the low word
+    for (unsigned i = 0; i < 4; ++i)
+        binary_path.push_back((low >> (8 * i)) & 0xFF);
+
+    return { binary_path };
+}
+
 ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
         : base_path(GetSystemSaveDataContainerPath(nand_path)) {
 }
@@ -46,6 +63,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
 
 ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) {
     std::string fullpath = GetSystemSaveDataPath(base_path, path);
+    FileUtil::DeleteDirRecursively(fullpath);
     FileUtil::CreateFullPath(fullpath);
     return RESULT_SUCCESS;
 }
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index 556a2a488..3431fed88 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -28,4 +28,29 @@ private:
     std::string base_path;
 };
 
+/**
+ * Constructs a path to the concrete SystemSaveData archive in the host filesystem based on the
+ * input Path and base mount point.
+ * @param mount_point The base mount point of the SystemSaveData archives.
+ * @param path The path that identifies the requested concrete SystemSaveData archive.
+ * @returns The complete path to the specified SystemSaveData archive in the host filesystem
+ */
+std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path);
+
+/**
+ * Constructs a path to the base folder to hold concrete SystemSaveData archives in the host file system.
+ * @param mount_point The base folder where this folder resides, ie. SDMC or NAND.
+ * @returns The path to the base SystemSaveData archives' folder in the host file system
+ */
+std::string GetSystemSaveDataContainerPath(const std::string& mount_point);
+
+/**
+ * Constructs a FileSys::Path object that refers to the SystemSaveData archive identified by
+ * the specified high save id and low save id.
+ * @param high The high word of the save id for the archive
+ * @param low The low word of the save id for the archive
+ * @returns A FileSys::Path to the wanted archive
+ */
+Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low);
+
 } // namespace FileSys
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 9da2e7aa2..b0fd834c7 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -395,28 +395,72 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
     return archive_itr->second->Format(path);
 }
 
-ResultCode CreateExtSaveData(u32 high, u32 low) {
+ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low) {
     // Construct the binary path to the archive first
-    std::vector<u8> binary_path;
-    binary_path.reserve(12);
-    // The first word is all zero to specify a NAND archive
-    for (unsigned i = 0; i < 4; ++i)
-        binary_path.push_back(0);
-    // Next is the low word
-    for (unsigned i = 0; i < 4; ++i)
-        binary_path.push_back((low >> (8 * i)) & 0xFF);
-    // Next is the high word
-    for (unsigned i = 0; i < 4; ++i)
-        binary_path.push_back((high >> i) & 0xFF);
-    FileSys::Path path(binary_path);
-    std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
-    std::string base_path = FileSys::GetExtDataContainerPath(nand_directory, true);
+    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
+    }
+
+    std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
     std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
     if (!FileUtil::CreateFullPath(extsavedata_path))
         return ResultCode(-1); // TODO(Subv): Find the right error code
     return RESULT_SUCCESS;
 }
 
+ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
+    // 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
+    }
+
+    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))
+        return ResultCode(-1); // TODO(Subv): Find the right error code
+    return RESULT_SUCCESS;
+}
+
+ResultCode DeleteSystemSaveData(u32 high, u32 low) {
+    // Construct the binary path to the archive first
+    FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
+
+    std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+    std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
+    std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
+    if (!FileUtil::DeleteDirRecursively(systemsavedata_path))
+        return ResultCode(-1); // TODO(Subv): Find the right error code
+    return RESULT_SUCCESS;
+}
+
+ResultCode CreateSystemSaveData(u32 high, u32 low) {
+    // Construct the binary path to the archive first
+    FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
+
+    std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+    std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
+    std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
+    if (!FileUtil::CreateFullPath(systemsavedata_path))
+        return ResultCode(-1); // TODO(Subv): Find the right error code
+    return RESULT_SUCCESS;
+}
+
 /// Initialize archives
 void ArchiveInit() {
     next_handle = 1;
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index c490327d0..b00f0fd60 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -35,6 +35,12 @@ enum class ArchiveIdCode : u32 {
     SaveDataCheck       = 0x2345678A,
 };
 
+/// Media types for the archives
+enum class MediaType : u32 {
+    NAND     = 0,
+    SDMC     = 1
+};
+
 typedef u64 ArchiveHandle;
 
 class File : public Kernel::Session {
@@ -172,11 +178,37 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = File
 
 /**
  * Creates a blank SharedExtSaveData archive for the specified extdata ID
+ * @param media_type The media type of the archive to create (NAND / SDMC)
  * @param high The high word of the extdata id to create
  * @param low The low word of the extdata id to create
  * @return ResultCode 0 on success or the corresponding code on error
  */
-ResultCode CreateExtSaveData(u32 high, u32 low);
+ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low);
+
+/**
+ * Deletes the SharedExtSaveData archive for the specified extdata ID
+ * @param media_type The media type of the archive to delete (NAND / SDMC)
+ * @param high The high word of the extdata id to delete
+ * @param low The low word of the extdata id to delete
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low);
+
+/**
+ * Deletes the SystemSaveData archive folder for the specified save data id
+ * @param high The high word of the SystemSaveData archive to delete
+ * @param low The low word of the SystemSaveData archive to delete
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode DeleteSystemSaveData(u32 high, u32 low);
+
+/**
+ * Creates the SystemSaveData archive folder for the specified save data id
+ * @param high The high word of the SystemSaveData archive to create
+ * @param low The low word of the SystemSaveData archive to create
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode CreateSystemSaveData(u32 high, u32 low);
 
 /// Initialize archives
 void ArchiveInit();
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index eb312496e..02458a00b 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -490,25 +490,45 @@ static void FormatThisUserSaveData(Service::Interface* self) {
 /**
  * FS_User::CreateExtSaveData service function
  *  Inputs:
- *      0: 0x08510242
- *      1: High word of the saveid to create
- *      2: Low word of the saveid to create
+ *      0 : 0x08510242
+ *      1 : Media type (NAND / SDMC)
+ *      2 : Low word of the saveid to create
+ *      3 : High word of the saveid to create
  *  Outputs:
  *      1 : Result of function, 0 on success, otherwise error code
  */
 static void CreateExtSaveData(Service::Interface* self) {
     // TODO(Subv): Figure out the other parameters.
     u32* cmd_buff = Kernel::GetCommandBuffer();
-    u32 save_high = cmd_buff[1];
+    MediaType media_type = static_cast<MediaType>(cmd_buff[1] & 0xFF);
     u32 save_low = cmd_buff[2];
-    // TODO(Subv): For now it is assumed that only SharedExtSaveData can be created like this
-    cmd_buff[1] = CreateExtSaveData(save_high, save_low).raw;
+    u32 save_high = cmd_buff[3];
+    cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low).raw;
+}
+
+/**
+ * FS_User::DeleteExtSaveData service function
+ *  Inputs:
+ *      0 : 0x08520100
+ *      1 : Media type (NAND / SDMC)
+ *      2 : Low word of the saveid to create
+ *      3 : High word of the saveid to create
+ *  Outputs:
+ *      1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteExtSaveData(Service::Interface* self) {
+    // TODO(Subv): Figure out the other parameters.
+    u32* cmd_buff = Kernel::GetCommandBuffer();
+    MediaType media_type = static_cast<MediaType>(cmd_buff[1] & 0xFF);
+    u32 save_low = cmd_buff[2];
+    u32 save_high = cmd_buff[3];
+    cmd_buff[1] = DeleteExtSaveData(media_type, save_high, save_low).raw;
 }
 
 /**
  * FS_User::CardSlotIsInserted service function.
  *  Inputs:
- *      0: 0x08210000
+ *      0 : 0x08210000
  *  Outputs:
  *      1 : Result of function, 0 on success, otherwise error code
  *      2 : Whether there is a game card inserted into the slot or not.
@@ -520,6 +540,48 @@ static void CardSlotIsInserted(Service::Interface* self) {
     LOG_WARNING(Service_FS, "(STUBBED) called");
 }
 
+/**
+ * FS_User::DeleteSystemSaveData service function.
+ *  Inputs:
+ *      0 : 0x08570080
+ *      1 : High word of the SystemSaveData id to delete
+ *      2 : Low word of the SystemSaveData id to delete
+ *  Outputs:
+ *      1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteSystemSaveData(Service::Interface* self) {
+    u32* cmd_buff = Kernel::GetCommandBuffer();
+    u32 savedata_high = cmd_buff[1];
+    u32 savedata_low = cmd_buff[2];
+    
+    cmd_buff[1] = DeleteSystemSaveData(savedata_high, savedata_low).raw;
+}
+
+/**
+ * FS_User::CreateSystemSaveData service function.
+ *  Inputs:
+ *      0 : 0x08560240
+ *      1 : High word of the SystemSaveData id to create
+ *      2 : Low word of the SystemSaveData id to create
+ *      3 : Unknown
+ *      4 : Unknown
+ *      5 : Unknown
+ *      6 : Unknown
+ *      7 : Unknown
+ *      8 : Unknown
+ *      9 : Unknown (Memory address)
+ *  Outputs:
+ *      1 : Result of function, 0 on success, otherwise error code
+ */
+static void CreateSystemSaveData(Service::Interface* self) {
+    // TODO(Subv): Figure out the other parameters.
+    u32* cmd_buff = Kernel::GetCommandBuffer();
+    u32 savedata_high = cmd_buff[1];
+    u32 savedata_low = cmd_buff[2];
+
+    cmd_buff[1] = CreateSystemSaveData(savedata_high, savedata_low).raw;
+}
+
 const Interface::FunctionInfo FunctionTable[] = {
     {0x000100C6, nullptr,               "Dummy1"},
     {0x040100C4, nullptr,               "Control"},
@@ -604,7 +666,9 @@ const Interface::FunctionInfo FunctionTable[] = {
     {0x084F0102, nullptr,               "ReadSpecialFile"},
     {0x08500040, nullptr,               "GetSpecialFileSize"},
     {0x08510242, CreateExtSaveData,     "CreateExtSaveData"},
-    {0x08520100, nullptr,               "DeleteExtSaveData"},
+    {0x08520100, DeleteExtSaveData,     "DeleteExtSaveData"},
+    {0x08560240, CreateSystemSaveData,  "CreateSystemSaveData"},
+    {0x08570080, DeleteSystemSaveData,  "DeleteSystemSaveData"},
     {0x08580000, nullptr,               "GetMovableSedHashedKeyYRandomData"},
     {0x08610042, nullptr,               "InitializeWithSdkVersion"},
     {0x08620040, nullptr,               "SetPriority"},

From b9612fe9195206ab9920d697b6b20e23db8a5be4 Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Thu, 19 Mar 2015 17:39:00 -0500
Subject: [PATCH 2/2] Service/FS: Document and log some unknown values.

In CreateExtSaveData, DeleteExtSaveData and CreateSystemSaveData
---
 src/core/hle/service/fs/fs_user.cpp | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 02458a00b..d8d1d5547 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -494,6 +494,14 @@ static void FormatThisUserSaveData(Service::Interface* self) {
  *      1 : Media type (NAND / SDMC)
  *      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
+ *      9 : Unknown
+ *      10: Unknown
+ *      11: Unknown
  *  Outputs:
  *      1 : Result of function, 0 on success, otherwise error code
  */
@@ -503,6 +511,13 @@ static void CreateExtSaveData(Service::Interface* self) {
     MediaType media_type = static_cast<MediaType>(cmd_buff[1] & 0xFF);
     u32 save_low = cmd_buff[2];
     u32 save_high = cmd_buff[3];
+    
+    LOG_WARNING(Service_FS, "(STUBBED) savedata_high=%08X savedata_low=%08X cmd_buff[3]=%08X "
+            "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X "
+            "cmd_buff[9]=%08X cmd_buff[10]=%08X cmd_buff[11]=%08X", save_high, save_low,
+            cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9], 
+            cmd_buff[10], cmd_buff[11]);
+
     cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low).raw;
 }
 
@@ -513,15 +528,20 @@ static void CreateExtSaveData(Service::Interface* self) {
  *      1 : Media type (NAND / SDMC)
  *      2 : Low word of the saveid to create
  *      3 : High word of the saveid to create
+ *      4 : Unknown
  *  Outputs:
  *      1 : Result of function, 0 on success, otherwise error code
  */
 static void DeleteExtSaveData(Service::Interface* self) {
-    // TODO(Subv): Figure out the other parameters.
     u32* cmd_buff = Kernel::GetCommandBuffer();
     MediaType media_type = static_cast<MediaType>(cmd_buff[1] & 0xFF);
     u32 save_low = cmd_buff[2];
     u32 save_high = cmd_buff[3];
+    u32 unknown = cmd_buff[4]; // TODO(Subv): Figure out what this is
+
+    LOG_WARNING(Service_FS, "(STUBBED) save_low=%08X save_high=%08X media_type=%08X unknown=%08X", 
+            save_low, save_high, cmd_buff[1] & 0xFF, unknown);
+
     cmd_buff[1] = DeleteExtSaveData(media_type, save_high, save_low).raw;
 }
 
@@ -579,6 +599,11 @@ static void CreateSystemSaveData(Service::Interface* self) {
     u32 savedata_high = cmd_buff[1];
     u32 savedata_low = cmd_buff[2];
 
+    LOG_WARNING(Service_FS, "(STUBBED) savedata_high=%08X savedata_low=%08X cmd_buff[3]=%08X "
+            "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X "
+            "cmd_buff[9]=%08X", savedata_high, savedata_low, cmd_buff[3], cmd_buff[4], cmd_buff[5], 
+            cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9]);
+
     cmd_buff[1] = CreateSystemSaveData(savedata_high, savedata_low).raw;
 }