From ae453ab6a829942a0db25b59e2cf97b5f7f1ecf9 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Sun, 16 Oct 2022 22:58:44 -0400
Subject: [PATCH] savedata_factory: Detect future save data paths

Enable compatibility for new account/device save paths planned on a future implementation.
---
 src/core/file_sys/savedata_factory.cpp | 58 ++++++++++++++++++++++----
 src/core/file_sys/savedata_factory.h   |  4 +-
 src/yuzu/main.cpp                      | 10 +++--
 3 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 8c1b2523cb..1567da2310 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -5,6 +5,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "common/logging/log.h"
+#include "common/uuid.h"
 #include "core/core.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/file_sys/vfs.h"
@@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA
             attr.title_id == 0 && attr.save_id == 0);
 }
 
+std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
+                                  u128 user_id) {
+    // Only detect nand user saves.
+    const auto space_id_path = [space_id]() -> std::string_view {
+        switch (space_id) {
+        case SaveDataSpaceId::NandUser:
+            return "/user/save";
+        default:
+            return "";
+        }
+    }();
+
+    if (space_id_path.empty()) {
+        return "";
+    }
+
+    Common::UUID uuid;
+    std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
+
+    // Only detect account/device saves from the future location.
+    switch (type) {
+    case SaveDataType::SaveData:
+        return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
+    case SaveDataType::DeviceSaveData:
+        return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
+    default:
+        return "";
+    }
+}
+
 } // Anonymous namespace
 
 std::string SaveDataAttribute::DebugInfo() const {
@@ -82,7 +113,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
     PrintSaveDataAttributeWarnings(meta);
 
     const auto save_directory =
-        GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
+        GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
 
     auto out = dir->CreateDirectoryRelative(save_directory);
 
@@ -99,7 +130,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
                                             const SaveDataAttribute& meta) const {
 
     const auto save_directory =
-        GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
+        GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
 
     auto out = dir->GetDirectoryRelative(save_directory);
 
@@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
     }
 }
 
-std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space,
-                                         SaveDataType type, u64 title_id, u128 user_id,
-                                         u64 save_id) {
+std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
+                                         SaveDataSpaceId space, SaveDataType type, u64 title_id,
+                                         u128 user_id, u64 save_id) {
     // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
     // be interpreted as the title id of the current process.
     if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
@@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
         }
     }
 
+    // For compat with a future impl.
+    if (std::string future_path =
+            GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id);
+        !future_path.empty()) {
+        // Check if this location exists, and prefer it over the old.
+        if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) {
+            LOG_INFO(Service_FS, "Using save at new location: {}", future_path);
+            return future_path;
+        }
+    }
+
     std::string out = GetSaveDataSpaceIdPath(space);
 
     switch (type) {
@@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
 
 SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
                                                u128 user_id) const {
-    const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
+    const auto path =
+        GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
     const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
 
     const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
@@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
 
 void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
                                         SaveDataSize new_value) const {
-    const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
+    const auto path =
+        GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
     const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
 
     const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index a763b94c88..d3633ef039 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -95,8 +95,8 @@ public:
     VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
 
     static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
-    static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type,
-                                   u64 title_id, u128 user_id, u64 save_id);
+    static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
+                                   SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
 
     SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
     void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a94624be63..d05c7ece80 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1895,6 +1895,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
     case GameListOpenTarget::SaveData: {
         open_target = tr("Save Data");
         const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
+        auto vfs_nand_dir =
+            vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
 
         if (has_user_save) {
             // User save data
@@ -1921,15 +1923,15 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
             ASSERT(user_id);
 
             const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
-                *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
-                program_id, user_id->AsU128(), 0);
+                *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
+                FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0);
 
             path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
         } else {
             // Device save data
             const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
-                *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
-                program_id, {}, 0);
+                *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
+                FileSys::SaveDataType::SaveData, program_id, {}, 0);
 
             path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
         }