From e0af248e6f96efe7009915935407fc809eb774a9 Mon Sep 17 00:00:00 2001
From: Alex Barney <thealexbarney@gmail.com>
Date: Fri, 20 Aug 2021 13:36:14 -0700
Subject: [PATCH] Clean the SD card save directory when opening the emulator
 (#2564)

Cleans "sdcard:/Nintendo/save" and deletes "sdcard:/save" when opening the emulator.

Works around invalid encryption when keys or the SD card encryption seed are changed.
---
 .../FileSystem/EncryptedFileSystemCreator.cs  | 35 +------------------
 Ryujinx.HLE/HOS/LibHacHorizonManager.cs       | 26 ++++++++++++++
 2 files changed, 27 insertions(+), 34 deletions(-)

diff --git a/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs b/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs
index aaa8004467..f8e59fb59d 100644
--- a/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs
+++ b/Ryujinx.HLE/FileSystem/EncryptedFileSystemCreator.cs
@@ -9,8 +9,6 @@ namespace Ryujinx.HLE.FileSystem
 {
     public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
     {
-        public EncryptedFileSystemCreator() { }
-
         public Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
             EncryptedFsKeyId keyId, in EncryptionSeed encryptionSeed)
         {
@@ -23,40 +21,9 @@ namespace Ryujinx.HLE.FileSystem
 
             // Force all-zero keys for now since people can open the emulator with different keys or sd seeds sometimes
             var fs = new AesXtsFileSystem(baseFileSystem, new byte[0x32], 0x4000);
-            var aesFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
-
-            // This wrapper will handle deleting files that were created with different keys
-            var wrappedFs = new ChangedEncryptionHandlingFileSystem(aesFileSystem);
-            encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(wrappedFs);
+            encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
 
             return Result.Success;
         }
     }
-
-    public class ChangedEncryptionHandlingFileSystem : ForwardingFileSystem
-    {
-        public ChangedEncryptionHandlingFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(baseFileSystem) { }
-
-        protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
-        {
-            UnsafeHelpers.SkipParamInit(out file);
-
-            try
-            {
-                return base.DoOpenFile(out file, path, mode);
-            }
-            catch (HorizonResultException ex)
-            {
-                if (ResultFs.AesXtsFileHeaderInvalidKeys.Includes(ex.ResultValue))
-                {
-                    Result rc = DeleteFile(path);
-                    if (rc.IsFailure()) return rc;
-
-                    return base.DoOpenFile(out file, path, mode);
-                }
-
-                throw;
-            }
-        }
-    }
 }
diff --git a/Ryujinx.HLE/HOS/LibHacHorizonManager.cs b/Ryujinx.HLE/HOS/LibHacHorizonManager.cs
index 48077aa8a0..61e0227f3b 100644
--- a/Ryujinx.HLE/HOS/LibHacHorizonManager.cs
+++ b/Ryujinx.HLE/HOS/LibHacHorizonManager.cs
@@ -1,5 +1,8 @@
 using LibHac;
 using LibHac.Bcat;
+using LibHac.Common;
+using LibHac.Fs.Fsa;
+using LibHac.Fs.Shim;
 using LibHac.FsSrv.Impl;
 using LibHac.Loader;
 using LibHac.Ncm;
@@ -57,6 +60,8 @@ namespace Ryujinx.HLE.HOS
             virtualFileSystem.InitializeFsServer(Server, out var fsClient);
 
             FsClient = fsClient;
+
+            CleanSdCardDirectory();
         }
 
         public void InitializeSystemClients()
@@ -80,6 +85,27 @@ namespace Ryujinx.HLE.HOS
                 npdm.FsAccessControlData, npdm.FsAccessControlDescriptor);
         }
 
+        // This function was added to avoid errors that come from a user's keys or SD encryption seed changing.
+        // Catching these errors and recreating the file ended up not working because of the different ways
+        // applications respond to a file suddenly containing all zeros or having a length of zero.
+        // Clearing the SD card save directory was determined to be the best option for the moment since
+        // the saves on the SD card are meant as caches that can be deleted at any time.
+        private void CleanSdCardDirectory()
+        {
+            Result rc = RyujinxClient.Fs.MountSdCard("sdcard".ToU8Span());
+            if (rc.IsFailure()) return;
+
+            try
+            {
+                RyujinxClient.Fs.CleanDirectoryRecursively("sdcard:/Nintendo/save".ToU8Span()).IgnoreResult();
+                RyujinxClient.Fs.DeleteDirectoryRecursively("sdcard:/save".ToU8Span()).IgnoreResult();
+            }
+            finally
+            {
+                RyujinxClient.Fs.Unmount("sdcard".ToU8Span());
+            }
+        }
+
         private static AccessControlBits.Bits AccountFsPermissions => AccessControlBits.Bits.SystemSaveData |
                                                                       AccessControlBits.Bits.GameCard |
                                                                       AccessControlBits.Bits.SaveDataMeta |