From cb0fe3fc30c3f8a7fcb6591a57407581e5d4a080 Mon Sep 17 00:00:00 2001
From: JosJuice <josjuice@gmail.com>
Date: Fri, 27 Dec 2019 19:14:29 +0100
Subject: [PATCH] Movie: Make checking for existing GC saves more reliable

The old code only handled a raw memory card in slot A, not taking
GCI folders into account at all.
---
 .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp | 57 ++++++++++---------
 .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.h   | 11 ++++
 Source/Core/Core/Movie.cpp                    | 33 ++++++++---
 3 files changed, 65 insertions(+), 36 deletions(-)

diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
index 65c35b12b4..b149b07cf4 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
@@ -9,6 +9,7 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <utility>
 
 #include <fmt/format.h>
 
@@ -158,10 +159,34 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(ind
   SetCardFlashID(header.data(), card_index);
 }
 
+std::pair<std::string /* path */, bool /* migrate */>
+CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder)
+{
+  std::string path_override =
+      Config::Get(card_index == 0 ? Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE :
+                                    Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE);
+
+  if (!path_override.empty())
+    return {std::move(path_override), false};
+
+  std::string path = File::GetUserPath(D_GCUSER_IDX);
+
+  const bool use_movie_folder = allow_movie_folder == AllowMovieFolder::Yes &&
+                                Movie::IsPlayingInput() && Movie::IsConfigSaved() &&
+                                Movie::IsUsingMemcard(card_index) &&
+                                Movie::IsStartingFromClearSave();
+
+  if (use_movie_folder)
+    path += "Movie" DIR_SEP;
+
+  const DiscIO::Region region = SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region);
+  path = path + SConfig::GetDirectoryForRegion(region) + DIR_SEP +
+         fmt::format("Card {}", char('A' + card_index));
+  return {std::move(path), !use_movie_folder};
+}
+
 void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
 {
-  const DiscIO::Region region = SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region);
-
   const std::string& game_id = SConfig::GetInstance().GetGameID();
   u32 CurrentGameId = 0;
   if (game_id.length() >= 4 && game_id != "00000000" &&
@@ -170,32 +195,10 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
     CurrentGameId = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
   }
 
-  const bool shift_jis = region == DiscIO::Region::NTSC_J;
+  const bool shift_jis =
+      SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region) == DiscIO::Region::NTSC_J;
 
-  std::string strDirectoryName = File::GetUserPath(D_GCUSER_IDX);
-
-  bool migrate = true;
-
-  if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(card_index) &&
-      Movie::IsStartingFromClearSave())
-  {
-    strDirectoryName += "Movie" DIR_SEP;
-    migrate = false;
-  }
-
-  const std::string path_override =
-      Config::Get(card_index == 0 ? Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE :
-                                    Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE);
-  if (!path_override.empty())
-  {
-    strDirectoryName = path_override;
-    migrate = false;
-  }
-  else
-  {
-    strDirectoryName = strDirectoryName + SConfig::GetDirectoryForRegion(region) + DIR_SEP +
-                       fmt::format("Card {}", char('A' + card_index));
-  }
+  const auto [strDirectoryName, migrate] = GetGCIFolderPath(card_index, AllowMovieFolder::Yes);
 
   const File::FileInfo file_info(strDirectoryName);
   if (!file_info.Exists())
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h
index 2ac1811515..4075637f25 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h
@@ -6,6 +6,8 @@
 
 #include <functional>
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "Core/HW/EXI/EXI_Device.h"
 
@@ -14,6 +16,12 @@ class PointerWrap;
 
 namespace ExpansionInterface
 {
+enum class AllowMovieFolder
+{
+  Yes,
+  No,
+};
+
 class CEXIMemoryCard : public IEXIDevice
 {
 public:
@@ -34,6 +42,9 @@ public:
   static void Init();
   static void Shutdown();
 
+  static std::pair<std::string /* path */, bool /* migrate */>
+  GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder);
+
 private:
   void SetupGciFolder(u16 sizeMb);
   void SetupRawMemcard(u16 sizeMb);
diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp
index f85c625016..377bfd4c4b 100644
--- a/Source/Core/Core/Movie.cpp
+++ b/Source/Core/Core/Movie.cpp
@@ -44,6 +44,7 @@
 #include "Core/HW/CPU.h"
 #include "Core/HW/DVD/DVDInterface.h"
 #include "Core/HW/EXI/EXI_DeviceIPL.h"
+#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
 #include "Core/HW/ProcessorInterface.h"
 #include "Core/HW/SI/SI.h"
 #include "Core/HW/SI/SI_Device.h"
@@ -1379,6 +1380,15 @@ void SetGraphicsConfig()
 // NOTE: EmuThread / Host Thread
 void GetSettings()
 {
+  const bool slot_a_has_raw_memcard =
+      SConfig::GetInstance().m_EXIDevice[0] == ExpansionInterface::EXIDEVICE_MEMORYCARD;
+  const bool slot_a_has_gci_folder =
+      SConfig::GetInstance().m_EXIDevice[0] == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER;
+  const bool slot_b_has_raw_memcard =
+      SConfig::GetInstance().m_EXIDevice[1] == ExpansionInterface::EXIDEVICE_MEMORYCARD;
+  const bool slot_b_has_gci_folder =
+      SConfig::GetInstance().m_EXIDevice[1] == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER;
+
   s_bSaveConfig = true;
   s_bNetPlay = NetPlay::IsNetPlayRunning();
   if (SConfig::GetInstance().bWii)
@@ -1389,16 +1399,21 @@ void GetSettings()
   }
   else
   {
-    s_bClearSave = !File::Exists(Config::Get(Config::MAIN_MEMCARD_A_PATH));
+    const auto gci_folder_has_saves = [](int card_index) {
+      const auto [path, migrate] = ExpansionInterface::CEXIMemoryCard::GetGCIFolderPath(
+          card_index, ExpansionInterface::AllowMovieFolder::No);
+      const u64 number_of_saves = File::ScanDirectoryTree(path, false).size;
+      return number_of_saves > 0;
+    };
+
+    s_bClearSave =
+        !(slot_a_has_raw_memcard && File::Exists(Config::Get(Config::MAIN_MEMCARD_A_PATH))) &&
+        !(slot_b_has_raw_memcard && File::Exists(Config::Get(Config::MAIN_MEMCARD_B_PATH))) &&
+        !(slot_a_has_gci_folder && gci_folder_has_saves(0)) &&
+        !(slot_b_has_gci_folder && gci_folder_has_saves(1));
   }
-  s_memcards |=
-      (SConfig::GetInstance().m_EXIDevice[0] == ExpansionInterface::EXIDEVICE_MEMORYCARD ||
-       SConfig::GetInstance().m_EXIDevice[0] == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER)
-      << 0;
-  s_memcards |=
-      (SConfig::GetInstance().m_EXIDevice[1] == ExpansionInterface::EXIDEVICE_MEMORYCARD ||
-       SConfig::GetInstance().m_EXIDevice[1] == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER)
-      << 1;
+  s_memcards |= (slot_a_has_raw_memcard || slot_a_has_gci_folder) << 0;
+  s_memcards |= (slot_b_has_raw_memcard || slot_b_has_gci_folder) << 1;
 
   s_revision = ConvertGitRevisionToBytes(Common::scm_rev_git_str);