From f449895e6d8af90f727de6590fd6120038c73986 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 18 Jan 2023 14:50:42 +0100 Subject: [PATCH] HOS: Load RomFs by pid (#4301) We currently loading only one RomFs at a time, which could be wrong if one day we want to load more than one guest at time. This PR fixes that by loading romfs by pid. --- Ryujinx.HLE/FileSystem/VirtualFileSystem.cs | 38 +++++++++++++++---- Ryujinx.HLE/HOS/ApplicationLoader.cs | 30 +++++++++------ Ryujinx.HLE/HOS/ProgramLoader.cs | 8 ++-- .../HOS/Services/Fs/IFileSystemProxy.cs | 9 +++-- 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index c958c6e8..95a2fcda 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -16,6 +16,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS; using System; using System.Buffers.Text; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; @@ -35,7 +36,8 @@ namespace Ryujinx.HLE.FileSystem public EmulatedGameCard GameCard { get; private set; } public EmulatedSdCard SdCard { get; private set; } public ModLoader ModLoader { get; private set; } - public Stream RomFs { get; private set; } + + private readonly ConcurrentDictionary _romFsByPid; private static bool _isInitialized = false; @@ -55,17 +57,34 @@ namespace Ryujinx.HLE.FileSystem { ReloadKeySet(); ModLoader = new ModLoader(); // Should only be created once + _romFsByPid = new ConcurrentDictionary(); } - public void LoadRomFs(string fileName) + public void LoadRomFs(ulong pid, string fileName) { - RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + var romfsStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + _romFsByPid.AddOrUpdate(pid, romfsStream, (pid, oldStream) => + { + oldStream.Close(); + + return romfsStream; + }); } - public void SetRomFs(Stream romfsStream) + public void SetRomFs(ulong pid, Stream romfsStream) { - RomFs?.Close(); - RomFs = romfsStream; + _romFsByPid.AddOrUpdate(pid, romfsStream, (pid, oldStream) => + { + oldStream.Close(); + + return romfsStream; + }); + } + + public Stream GetRomFs(ulong pid) + { + return _romFsByPid[pid]; } public string GetFullPath(string basePath, string fileName) @@ -583,7 +602,12 @@ namespace Ryujinx.HLE.FileSystem { if (disposing) { - RomFs?.Dispose(); + foreach (var stream in _romFsByPid.Values) + { + stream.Close(); + } + + _romFsByPid.Clear(); } } } diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 06281b49..67e0a9c7 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -76,11 +76,6 @@ namespace Ryujinx.HLE.HOS public void LoadCart(string exeFsDir, string romFsFile = null) { - if (romFsFile != null) - { - _device.Configuration.VirtualFileSystem.LoadRomFs(romFsFile); - } - LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); MetaLoader metaData = ReadNpdm(codeFs); @@ -95,7 +90,12 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId)); } - LoadExeFs(codeFs, string.Empty, metaData); + ulong pid = LoadExeFs(codeFs, string.Empty, metaData); + + if (romFsFile != null) + { + _device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile); + } } public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) @@ -491,6 +491,8 @@ namespace Ryujinx.HLE.HOS _displayVersion = displayVersion; + ulong pid = LoadExeFs(codeFs, displayVersion, metaData); + if (dataStorage == null) { Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); @@ -499,7 +501,7 @@ namespace Ryujinx.HLE.HOS { IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); - _device.Configuration.VirtualFileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read)); + _device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read)); } // Don't create save data for system programs. @@ -510,8 +512,6 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); } - LoadExeFs(codeFs, displayVersion, metaData); - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); } @@ -579,7 +579,7 @@ namespace Ryujinx.HLE.HOS } } - private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) + private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) { if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) { @@ -654,6 +654,8 @@ namespace Ryujinx.HLE.HOS DiskCacheLoadState = result.DiskCacheLoadState; _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); + + return result.ProcessId; } public void LoadProgram(string filePath) @@ -665,6 +667,7 @@ namespace Ryujinx.HLE.HOS bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; IExecutable executable; + Stream romfsStream = null; if (isNro) { @@ -697,7 +700,7 @@ namespace Ryujinx.HLE.HOS if (romfsSize != 0) { - _device.Configuration.VirtualFileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset)); + romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset); } if (nacpSize != 0) @@ -758,6 +761,11 @@ namespace Ryujinx.HLE.HOS ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); + if (romfsStream != null) + { + _device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream); + } + DiskCacheLoadState = result.DiskCacheLoadState; _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index b6a39a20..695f4672 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -41,17 +41,19 @@ namespace Ryujinx.HLE.HOS struct ProgramLoadResult { - public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null); + public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null, 0); public readonly bool Success; public readonly ProcessTamperInfo TamperInfo; public readonly IDiskCacheLoadState DiskCacheLoadState; + public readonly ulong ProcessId; - public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState) + public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState, ulong pid) { Success = success; TamperInfo = tamperInfo; DiskCacheLoadState = diskCacheLoadState; + ProcessId = pid; } } @@ -366,7 +368,7 @@ namespace Ryujinx.HLE.HOS process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart); - return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState); + return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState, process.Pid); } private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index d812e374..a4bc6254 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -27,6 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs class IFileSystemProxy : DisposableIpcService { private SharedRef _baseFileSystemProxy; + private ulong _pid; public IFileSystemProxy(ServiceCtx context) : base(context.Device.System.FsServer) { @@ -38,6 +39,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs // SetCurrentProcess(u64, pid) public ResultCode SetCurrentProcess(ServiceCtx context) { + _pid = context.Request.HandleDesc.PId; + return ResultCode.Success; } @@ -702,7 +705,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenDataStorageByCurrentProcess() -> object dataStorage public ResultCode OpenDataStorageByCurrentProcess(ServiceCtx context) { - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); @@ -791,7 +794,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs // OpenPatchDataStorageByCurrentProcess() -> object public ResultCode OpenPatchDataStorageByCurrentProcess(ServiceCtx context) { - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref())); @@ -811,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); } - var storage = context.Device.FileSystem.RomFs.AsStorage(true); + var storage = context.Device.FileSystem.GetRomFs(_pid).AsStorage(true); using var sharedStorage = new SharedRef(storage); using var sfStorage = new SharedRef(new StorageInterfaceAdapter(ref sharedStorage.Ref()));