From 7795b662a9b6054343195db9fc221a920509c1be Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Mon, 29 Jan 2024 16:32:34 +0100
Subject: [PATCH] Mod: Do LayeredFs loading Parallel to improve speed (#6180)

* Mod: Do LayeredFs loading Parallel to improve speed

This fixes and superseed #5672 due to inactivity, nothing more.
(See original PR for description)

Testing are welcome.

Close #5661

* Addresses gdkchan's feedback

* commit to test mako change

* Revert "commit to test mako change"

This reverts commit 8b0caa8a21db298db3dfcbe5b7e9029c4f066c46.
---
 src/Ryujinx.HLE/HOS/ModLoader.cs              | 13 ++--
 .../Services/Fs/FileSystemProxy/LazyFile.cs   | 65 +++++++++++++++++++
 2 files changed, 72 insertions(+), 6 deletions(-)
 create mode 100644 src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/LazyFile.cs

diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs
index 322c8afdc0..8ae529655c 100644
--- a/src/Ryujinx.HLE/HOS/ModLoader.cs
+++ b/src/Ryujinx.HLE/HOS/ModLoader.cs
@@ -18,6 +18,7 @@ using System.Collections.Specialized;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using LazyFile = Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy.LazyFile;
 using Path = System.IO.Path;
 
 namespace Ryujinx.HLE.HOS
@@ -512,7 +513,7 @@ namespace Ryujinx.HLE.HOS
 
                 using (IFileSystem fs = new LocalFileSystem(mod.Path.FullName))
                 {
-                    AddFiles(fs, mod.Name, fileSet, builder);
+                    AddFiles(fs, mod.Name, mod.Path.FullName, fileSet, builder);
                 }
                 count++;
             }
@@ -528,7 +529,7 @@ namespace Ryujinx.HLE.HOS
                 Logger.Info?.Print(LogClass.ModLoader, $"Found 'romfs.bin' for Application {applicationId:X16}");
                 using (IFileSystem fs = new RomFsFileSystem(mod.Path.OpenRead().AsStorage()))
                 {
-                    AddFiles(fs, mod.Name, fileSet, builder);
+                    AddFiles(fs, mod.Name, mod.Path.FullName, fileSet, builder);
                 }
                 count++;
             }
@@ -561,18 +562,18 @@ namespace Ryujinx.HLE.HOS
             return newStorage;
         }
 
-        private static void AddFiles(IFileSystem fs, string modName, ISet<string> fileSet, RomFsBuilder builder)
+        private static void AddFiles(IFileSystem fs, string modName, string rootPath, ISet<string> fileSet, RomFsBuilder builder)
         {
             foreach (var entry in fs.EnumerateEntries()
+                                    .AsParallel()
                                     .Where(f => f.Type == DirectoryEntryType.File)
                                     .OrderBy(f => f.FullPath, StringComparer.Ordinal))
             {
-                using var file = new UniqueRef<IFile>();
+                var file = new LazyFile(entry.FullPath, rootPath, fs);
 
-                fs.OpenFile(ref file.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
                 if (fileSet.Add(entry.FullPath))
                 {
-                    builder.AddFile(entry.FullPath, file.Release());
+                    builder.AddFile(entry.FullPath, file);
                 }
                 else
                 {
diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/LazyFile.cs b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/LazyFile.cs
new file mode 100644
index 0000000000..a179e8e381
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/LazyFile.cs
@@ -0,0 +1,65 @@
+using LibHac;
+using LibHac.Common;
+using LibHac.Fs;
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
+{
+    class LazyFile : LibHac.Fs.Fsa.IFile
+    {
+        private readonly LibHac.Fs.Fsa.IFileSystem _fs;
+        private readonly string _filePath;
+        private readonly UniqueRef<LibHac.Fs.Fsa.IFile> _fileReference = new();
+        private readonly FileInfo _fileInfo;
+
+        public LazyFile(string filePath, string prefix, LibHac.Fs.Fsa.IFileSystem fs)
+        {
+            _fs = fs;
+            _filePath = filePath;
+            _fileInfo = new FileInfo(prefix + "/" + filePath);
+        }
+
+        private void PrepareFile()
+        {
+            if (_fileReference.Get == null)
+            {
+                _fs.OpenFile(ref _fileReference.Ref, _filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+            }
+        }
+
+        protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
+        {
+            PrepareFile();
+
+            return _fileReference.Get!.Read(out bytesRead, offset, destination);
+        }
+
+        protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
+        {
+            throw new NotSupportedException();
+        }
+
+        protected override Result DoFlush()
+        {
+            throw new NotSupportedException();
+        }
+
+        protected override Result DoSetSize(long size)
+        {
+            throw new NotSupportedException();
+        }
+
+        protected override Result DoGetSize(out long size)
+        {
+            size = _fileInfo.Length;
+
+            return Result.Success;
+        }
+
+        protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
+        {
+            throw new NotSupportedException();
+        }
+    }
+}