diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index b644c54dd..a6ccc7352 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -116,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu Methods.ShaderCache.Dispose(); Methods.BufferManager.Dispose(); Methods.TextureManager.Dispose(); + Renderer.Dispose(); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs index 680ebd522..1c6364c7d 100644 --- a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs @@ -14,7 +14,7 @@ using System.Linq; namespace Ryujinx.HLE.FileSystem.Content { - internal class ContentManager + public class ContentManager { private const ulong SystemVersionTitleId = 0x0100000000000809; private const ulong SystemUpdateTitleId = 0x0100000000000816; @@ -26,11 +26,11 @@ namespace Ryujinx.HLE.FileSystem.Content private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary; - private Switch _device; + private VirtualFileSystem _virtualFileSystem; private readonly object _lock = new object(); - public ContentManager(Switch device) + public ContentManager(VirtualFileSystem virtualFileSystem) { _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); _locationEntries = new Dictionary>(); @@ -55,10 +55,10 @@ namespace Ryujinx.HLE.FileSystem.Content { "FontNintendoExtended", "nintendo_ext_003.bfttf" } }; - _device = device; + _virtualFileSystem = virtualFileSystem; } - public void LoadEntries(bool ignoreMissingFonts = false) + public void LoadEntries(Switch device = null) { lock (_lock) { @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.FileSystem.Content try { contentPathString = LocationHelper.GetContentRoot(storageId); - contentDirectory = LocationHelper.GetRealPath(_device.FileSystem, contentPathString); + contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString); registeredDirectory = Path.Combine(contentDirectory, "registered"); } catch (NotSupportedException) @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.FileSystem.Content using (FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0])) { - Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); string switchPath = contentPathString + ":/" + ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); @@ -126,7 +126,7 @@ namespace Ryujinx.HLE.FileSystem.Content using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { - Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar); @@ -156,9 +156,11 @@ namespace Ryujinx.HLE.FileSystem.Content } } - TimeManager.Instance.InitializeTimeZone(_device); - - _device.System.Font.Initialize(this, ignoreMissingFonts); + if (device != null) + { + TimeManager.Instance.InitializeTimeZone(device); + device.System.Font.Initialize(this); + } } } @@ -271,7 +273,7 @@ namespace Ryujinx.HLE.FileSystem.Content return false; } - string installedPath = _device.FileSystem.SwitchPathToSystemPath(locationEntry.ContentPath); + string installedPath = _virtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath); if (!string.IsNullOrWhiteSpace(installedPath)) { @@ -279,7 +281,7 @@ namespace Ryujinx.HLE.FileSystem.Content { using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read)) { - Nca nca = new Nca(_device.System.KeySet, file.AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); bool contentCheck = nca.Header.ContentType == contentType; return contentCheck; @@ -351,7 +353,7 @@ namespace Ryujinx.HLE.FileSystem.Content public void InstallFirmware(string firmwareSource) { string contentPathString = LocationHelper.GetContentRoot(StorageId.NandSystem); - string contentDirectory = LocationHelper.GetRealPath(_device.FileSystem, contentPathString); + string contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString); string registeredDirectory = Path.Combine(contentDirectory, "registered"); string temporaryDirectory = Path.Combine(contentDirectory, "temp"); @@ -386,7 +388,7 @@ namespace Ryujinx.HLE.FileSystem.Content } break; case ".xci": - Xci xci = new Xci(_device.System.KeySet, file.AsStorage()); + Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); InstallFromCart(xci, temporaryDirectory); break; default: @@ -418,7 +420,7 @@ namespace Ryujinx.HLE.FileSystem.Content { foreach (var entry in filesystem.EnumerateEntries("/", "*.nca")) { - Nca nca = new Nca(_device.System.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage()); SaveNca(nca, entry.Name.Remove(entry.Name.IndexOf('.')), temporaryDirectory); } @@ -537,7 +539,7 @@ namespace Ryujinx.HLE.FileSystem.Content return VerifyAndGetVersionZip(archive); } case ".xci": - Xci xci = new Xci(_device.System.KeySet, file.AsStorage()); + Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); if (xci.HasPartition(XciPartitionType.Update)) { @@ -561,6 +563,8 @@ namespace Ryujinx.HLE.FileSystem.Content SystemVersion VerifyAndGetVersionZip(ZipArchive archive) { + IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel(); + SystemVersion systemVersion = null; foreach (var entry in archive.Entries) @@ -571,7 +575,7 @@ namespace Ryujinx.HLE.FileSystem.Content { IStorage storage = ncaStream.AsStorage(); - Nca nca = new Nca(_device.System.KeySet, storage); + Nca nca = new Nca(_virtualFileSystem.KeySet, storage); if (updateNcas.ContainsKey(nca.Header.TitleId)) { @@ -598,9 +602,9 @@ namespace Ryujinx.HLE.FileSystem.Content using (Stream ncaStream = GetZipStream(fileEntry)) { - Nca metaNca = new Nca(_device.System.KeySet, ncaStream.AsStorage()); + Nca metaNca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -628,9 +632,9 @@ namespace Ryujinx.HLE.FileSystem.Content using (Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry))) { - Nca nca = new Nca(_device.System.KeySet, ncaStream.AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - var romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); if (romfs.OpenFile(out IFile systemVersionFile, "/file", OpenMode.Read).IsSuccess()) { @@ -663,9 +667,9 @@ namespace Ryujinx.HLE.FileSystem.Content { using (Stream contentNcaStream = GetZipStream(contentZipEntry)) { - Nca metaNca = new Nca(_device.System.KeySet, metaNcaStream.AsStorage()); + Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -722,6 +726,8 @@ namespace Ryujinx.HLE.FileSystem.Content SystemVersion VerifyAndGetVersion(IFileSystem filesystem) { + IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel(); + SystemVersion systemVersion = null; CnmtContentMetaEntry[] metaEntries = null; @@ -730,11 +736,11 @@ namespace Ryujinx.HLE.FileSystem.Content { IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage(); - Nca nca = new Nca(_device.System.KeySet, ncaStorage); + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStorage); if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta) { - IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -752,7 +758,7 @@ namespace Ryujinx.HLE.FileSystem.Content } else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); if (romfs.OpenFile(out IFile systemVersionFile, "/file", OpenMode.Read).IsSuccess()) { @@ -797,9 +803,9 @@ namespace Ryujinx.HLE.FileSystem.Content IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaEntry.path, OpenMode.Read).AsStorage(); IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage(); - Nca metaNca = new Nca(_device.System.KeySet, metaStorage); + Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaStorage); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -851,7 +857,9 @@ namespace Ryujinx.HLE.FileSystem.Content public SystemVersion GetCurrentFirmwareVersion() { - LoadEntries(true); + IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel(); + + LoadEntries(); lock (_lock) { @@ -861,15 +869,15 @@ namespace Ryujinx.HLE.FileSystem.Content { if (entry.ContentType == NcaContentType.Data) { - var path = _device.FileSystem.SwitchPathToSystemPath(entry.ContentPath); + var path = _virtualFileSystem.SwitchPathToSystemPath(entry.ContentPath); using (FileStream fileStream = File.OpenRead(path)) { - Nca nca = new Nca(_device.System.KeySet, fileStream.AsStorage()); + Nca nca = new Nca(_virtualFileSystem.KeySet, fileStream.AsStorage()); if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); if (romfs.OpenFile(out IFile systemVersionFile, "/file", OpenMode.Read).IsSuccess()) { diff --git a/Ryujinx.HLE/FileSystem/StorageId.cs b/Ryujinx.HLE/FileSystem/StorageId.cs index 1ef38e016..d4043e2c7 100644 --- a/Ryujinx.HLE/FileSystem/StorageId.cs +++ b/Ryujinx.HLE/FileSystem/StorageId.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.FileSystem { - internal enum StorageId + public enum StorageId { None, Host, diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 257a55a22..070ec3bc0 100644 --- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -1,3 +1,7 @@ +using LibHac; +using LibHac.Fs; +using LibHac.FsService; +using LibHac.FsSystem; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS; using System; @@ -16,6 +20,16 @@ namespace Ryujinx.HLE.FileSystem public static string SystemNandPath = Path.Combine(NandPath, "system"); public static string UserNandPath = Path.Combine(NandPath, "user"); + public Keyset KeySet { get; private set; } + public FileSystemServer FsServer { get; private set; } + public FileSystemClient FsClient { get; private set; } + public EmulatedGameCard GameCard { get; private set; } + + public VirtualFileSystem() + { + Reload(); + } + public Stream RomFs { get; private set; } public void LoadRomFs(string fileName) @@ -183,6 +197,69 @@ namespace Ryujinx.HLE.FileSystem return Path.Combine(appDataPath, BasePath); } + public void Reload() + { + ReloadKeySet(); + + LocalFileSystem serverBaseFs = new LocalFileSystem(GetBasePath()); + + DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet); + + GameCard = fsServerObjects.GameCard; + + FileSystemServerConfig fsServerConfig = new FileSystemServerConfig + { + FsCreators = fsServerObjects.FsCreators, + DeviceOperator = fsServerObjects.DeviceOperator, + ExternalKeySet = KeySet.ExternalKeySet + }; + + FsServer = new FileSystemServer(fsServerConfig); + FsClient = FsServer.CreateFileSystemClient(); + } + + + private void ReloadKeySet() + { + string keyFile = null; + string titleKeyFile = null; + string consoleKeyFile = null; + + string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + + LoadSetAtPath(Path.Combine(home, ".switch")); + LoadSetAtPath(GetSystemPath()); + + void LoadSetAtPath(string basePath) + { + string localKeyFile = Path.Combine(basePath, "prod.keys"); + string localTitleKeyFile = Path.Combine(basePath, "title.keys"); + string localConsoleKeyFile = Path.Combine(basePath, "console.keys"); + + if (File.Exists(localKeyFile)) + { + keyFile = localKeyFile; + } + + if (File.Exists(localTitleKeyFile)) + { + titleKeyFile = localTitleKeyFile; + } + + if (File.Exists(localConsoleKeyFile)) + { + consoleKeyFile = localConsoleKeyFile; + } + } + + KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile); + } + + public void Unload() + { + RomFs?.Dispose(); + } + public void Dispose() { Dispose(true); @@ -192,7 +269,7 @@ namespace Ryujinx.HLE.FileSystem { if (disposing) { - RomFs?.Dispose(); + Unload(); } } } diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs index e126cd579..c54c8194b 100644 --- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs @@ -44,15 +44,15 @@ namespace Ryujinx.HLE.HOS.Font _fontsPath = Path.Combine(device.FileSystem.GetSystemPath(), "fonts"); } - public void Initialize(ContentManager contentManager, bool ignoreMissingFonts) + public void Initialize(ContentManager contentManager) { _fontData?.Clear(); _fontData = null; - EnsureInitialized(contentManager, ignoreMissingFonts); + EnsureInitialized(contentManager); } - public void EnsureInitialized(ContentManager contentManager, bool ignoreMissingFonts) + public void EnsureInitialized(ContentManager contentManager) { if (_fontData == null) { @@ -120,12 +120,10 @@ namespace Ryujinx.HLE.HOS.Font return info; } - else if (!ignoreMissingFonts) + else { throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\"."); } - - return new FontInfo(); } _fontData = new Dictionary @@ -138,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Font { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } }; - if (fontOffset > Horizon.FontSize && !ignoreMissingFonts) + if (fontOffset > Horizon.FontSize) { throw new InvalidSystemResourceException( $"The sum of all fonts size exceed the shared memory size. " + @@ -161,14 +159,14 @@ namespace Ryujinx.HLE.HOS.Font public int GetFontSize(SharedFontType fontType) { - EnsureInitialized(_device.System.ContentManager, false); + EnsureInitialized(_device.System.ContentManager); return _fontData[fontType].Size; } public int GetSharedMemoryAddressOffset(SharedFontType fontType) { - EnsureInitialized(_device.System.ContentManager, false); + EnsureInitialized(_device.System.ContentManager); return _fontData[fontType].Offset + 8; } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 428e19227..e4e089434 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -2,7 +2,6 @@ using LibHac; using LibHac.Account; using LibHac.Common; using LibHac.Fs; -using LibHac.FsService; using LibHac.FsSystem; using LibHac.FsSystem.NcaUtils; using LibHac.Ncm; @@ -105,7 +104,7 @@ namespace Ryujinx.HLE.HOS internal KEvent VsyncEvent { get; private set; } - public Keyset KeySet { get; private set; } + public Keyset KeySet => Device.FileSystem.KeySet; private bool _hasStarted; @@ -122,12 +121,7 @@ namespace Ryujinx.HLE.HOS internal long HidBaseAddress { get; private set; } - internal FileSystemServer FsServer { get; private set; } - public FileSystemClient FsClient { get; private set; } - - internal EmulatedGameCard GameCard { get; private set; } - - public Horizon(Switch device) + public Horizon(Switch device, ContentManager contentManager) { ControlData = new BlitStruct(1); @@ -211,9 +205,7 @@ namespace Ryujinx.HLE.HOS VsyncEvent = new KEvent(this); - LoadKeySet(); - - ContentManager = new ContentManager(device); + ContentManager = contentManager; // TODO: use set:sys (and get external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. @@ -239,22 +231,6 @@ namespace Ryujinx.HLE.HOS // FIXME: TimeZone shoud be init here but it's actually done in ContentManager TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock(); - - LocalFileSystem serverBaseFs = new LocalFileSystem(device.FileSystem.GetBasePath()); - - DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet); - - GameCard = fsServerObjects.GameCard; - - FileSystemServerConfig fsServerConfig = new FileSystemServerConfig - { - FsCreators = fsServerObjects.FsCreators, - DeviceOperator = fsServerObjects.DeviceOperator, - ExternalKeySet = KeySet.ExternalKeySet - }; - - FsServer = new FileSystemServer(fsServerConfig); - FsClient = FsServer.CreateFileSystemClient(); } public void LoadCart(string exeFsDir, string romFsFile = null) @@ -284,7 +260,7 @@ namespace Ryujinx.HLE.HOS return; } - ContentManager.LoadEntries(); + ContentManager.LoadEntries(Device); LoadNca(mainNca, patchNca, controlNca); } @@ -578,7 +554,7 @@ namespace Ryujinx.HLE.HOS LoadNso("subsdk"); LoadNso("sdk"); - ContentManager.LoadEntries(); + ContentManager.LoadEntries(Device); ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray()); } @@ -671,7 +647,7 @@ namespace Ryujinx.HLE.HOS staticObject = new NxStaticObject(input); } - ContentManager.LoadEntries(); + ContentManager.LoadEntries(Device); TitleName = metaData.TitleName; TitleId = metaData.Aci0.TitleId; @@ -712,7 +688,7 @@ namespace Ryujinx.HLE.HOS "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - Result rc = EnsureApplicationSaveData(FsClient, out _, titleId, ref ControlData.Value, ref user); + Result rc = EnsureApplicationSaveData(Device.FileSystem.FsClient, out _, titleId, ref ControlData.Value, ref user); if (rc.IsFailure()) { @@ -722,57 +698,6 @@ namespace Ryujinx.HLE.HOS return rc; } - public void LoadKeySet() - { - string keyFile = null; - string titleKeyFile = null; - string consoleKeyFile = null; - - string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - - LoadSetAtPath(Path.Combine(home, ".switch")); - LoadSetAtPath(Device.FileSystem.GetSystemPath()); - - KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile); - - void LoadSetAtPath(string basePath) - { - string localKeyFile = Path.Combine(basePath, "prod.keys"); - string localTitleKeyFile = Path.Combine(basePath, "title.keys"); - string localConsoleKeyFile = Path.Combine(basePath, "console.keys"); - - if (File.Exists(localKeyFile)) - { - keyFile = localKeyFile; - } - - if (File.Exists(localTitleKeyFile)) - { - titleKeyFile = localTitleKeyFile; - } - - if (File.Exists(localConsoleKeyFile)) - { - consoleKeyFile = localConsoleKeyFile; - } - } - } - - public SystemVersion VerifyFirmwarePackage(string firmwarePackage) - { - return ContentManager.VerifyFirmwarePackage(firmwarePackage); - } - - public SystemVersion GetCurrentFirmwareVersion() - { - return ContentManager.GetCurrentFirmwareVersion(); - } - - public void InstallFirmware(string firmwarePackage) - { - ContentManager.InstallFirmware(firmwarePackage); - } - public void SignalVsync() { VsyncEvent.ReadableEvent.Signal(); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 904264aa8..0840a9137 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - Result result = EnsureApplicationSaveData(context.Device.System.FsClient, out long requiredSize, titleId, + Result result = EnsureApplicationSaveData(context.Device.FileSystem.FsClient, out long requiredSize, titleId, ref context.Device.System.ControlData.Value, ref userId); context.ResponseData.Write(requiredSize); diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 02743a47b..7c31814f3 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs public IFileSystemProxy(ServiceCtx context) { - _baseFileSystemProxy = context.Device.System.FsServer.CreateFileSystemProxyService(); + _baseFileSystemProxy = context.Device.FileSystem.FsServer.CreateFileSystemProxyService(); } [Command(1)] diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index 418c15f2c..4560d9545 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl // GetSharedMemoryNativeHandle() -> handle public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) { - context.Device.System.Font.EnsureInitialized(context.Device.System.ContentManager, false); + context.Device.System.Font.EnsureInitialized(context.Device.System.ContentManager); if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out int handle) != KernelResult.Success) { diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 90723c4ca..b7db5c625 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -4,6 +4,7 @@ using Ryujinx.Configuration; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services; using Ryujinx.HLE.HOS.SystemState; @@ -15,11 +16,11 @@ namespace Ryujinx.HLE { public class Switch : IDisposable { - internal IAalOutput AudioOut { get; private set; } + public IAalOutput AudioOut { get; private set; } internal DeviceMemory Memory { get; private set; } - internal GpuContext Gpu { get; private set; } + public GpuContext Gpu { get; private set; } public VirtualFileSystem FileSystem { get; private set; } @@ -35,7 +36,7 @@ namespace Ryujinx.HLE public event EventHandler Finish; - public Switch(IRenderer renderer, IAalOutput audioOut) + public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, IRenderer renderer, IAalOutput audioOut) { if (renderer == null) { @@ -53,9 +54,9 @@ namespace Ryujinx.HLE Gpu = new GpuContext(renderer); - FileSystem = new VirtualFileSystem(); + FileSystem = fileSystem; - System = new Horizon(this); + System = new Horizon(this, contentManager); Statistics = new PerformanceStatistics(); @@ -78,15 +79,20 @@ namespace Ryujinx.HLE System.EnableMultiCoreScheduling(); } - System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; + System.FsIntegrityCheckLevel = GetIntigrityCheckLevel(); System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode; ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices; } + public static IntegrityCheckLevel GetIntigrityCheckLevel() + { + return ConfigurationState.Instance.System.EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + } + public void LoadCart(string exeFsDir, string romFsFile = null) { System.LoadCart(exeFsDir, romFsFile); @@ -129,7 +135,7 @@ namespace Ryujinx.HLE internal void Unload() { - FileSystem.Dispose(); + FileSystem.Unload(); Memory.Dispose(); } @@ -150,6 +156,7 @@ namespace Ryujinx.HLE { System.Dispose(); VsyncEvent.Dispose(); + AudioOut.Dispose(); } } } diff --git a/Ryujinx/Configuration/DiscordIntegrationModule.cs b/Ryujinx/Configuration/DiscordIntegrationModule.cs index 15540a1c8..87cff0080 100644 --- a/Ryujinx/Configuration/DiscordIntegrationModule.cs +++ b/Ryujinx/Configuration/DiscordIntegrationModule.cs @@ -88,5 +88,23 @@ namespace Ryujinx.Configuration DiscordClient?.SetPresence(DiscordPresence); } + + public static void SwitchToMainMenu() + { + DiscordPresence.Details = "Main Menu"; + DiscordPresence.State = "Idling"; + DiscordPresence.Assets.LargeImageKey = "ryujinx"; + DiscordPresence.Assets.LargeImageText = LargeDescription; + DiscordPresence.Assets.SmallImageKey = null; + DiscordPresence.Assets.SmallImageText = null; + DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow); + + DiscordClient?.SetPresence(DiscordPresence); + } + + public static void Exit() + { + DiscordClient?.Dispose(); + } } } diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index fbf1196d0..9bab74a26 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -43,7 +43,6 @@ namespace Ryujinx ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath); } - Profile.Initialize(); Application.Init(); diff --git a/Ryujinx/Ui/ApplicationLibrary.cs b/Ryujinx/Ui/ApplicationLibrary.cs index 2ab610777..3984b78dc 100644 --- a/Ryujinx/Ui/ApplicationLibrary.cs +++ b/Ryujinx/Ui/ApplicationLibrary.cs @@ -7,6 +7,7 @@ using LibHac.FsSystem.NcaUtils; using LibHac.Ncm; using LibHac.Spl; using Ryujinx.Common.Logging; +using Ryujinx.Configuration.System; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Npdm; using System; @@ -20,7 +21,6 @@ using Utf8Json; using Utf8Json.Resolvers; using RightsId = LibHac.Fs.RightsId; -using TitleLanguage = Ryujinx.HLE.HOS.SystemState.TitleLanguage; namespace Ryujinx.Ui { @@ -34,15 +34,15 @@ namespace Ryujinx.Ui private static readonly byte[] _nroIcon = GetResourceBytes("Ryujinx.Ui.assets.NROIcon.png"); private static readonly byte[] _nsoIcon = GetResourceBytes("Ryujinx.Ui.assets.NSOIcon.png"); - private static Keyset _keySet; - private static TitleLanguage _desiredTitleLanguage; + private static Keyset _keySet; + private static Language _desiredTitleLanguage; - public static void LoadApplications(List appDirs, Keyset keySet, TitleLanguage desiredTitleLanguage, FileSystemClient fsClient = null, VirtualFileSystem vfs = null) + public static void LoadApplications(List appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; - _keySet = keySet; + _keySet = virtualFileSystem.KeySet; _desiredTitleLanguage = desiredTitleLanguage; // Builds the applications list with paths to found applications @@ -346,11 +346,11 @@ namespace Ryujinx.Ui filter.SetUserId(new UserId(1, 0)); filter.SetTitleId(new TitleId(titleIdNum)); - Result result = fsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); + Result result = virtualFileSystem.FsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); if (result.IsSuccess()) { - saveDataPath = Path.Combine(vfs.GetNandPath(), $"user/save/{saveDataInfo.SaveDataId:x16}"); + saveDataPath = Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{saveDataInfo.SaveDataId:x16}"); } } diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index d32ddb5ca..295cf22f4 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -45,14 +45,20 @@ namespace Ryujinx.Ui private ProfileWindowManager _profileWindow; #endif - public GlScreen(Switch device, Renderer renderer) + public GlScreen(Switch device) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, DisplayDevice.Default, 3, 3, GraphicsContextFlags.ForwardCompatible) { - _device = device; - _renderer = renderer; + _device = device; + + if (!(device.Gpu.Renderer is Renderer)) + { + throw new NotSupportedException($"GPU renderer must be an OpenGL renderer when using GlScreen!"); + } + + _renderer = (Renderer)device.Gpu.Renderer; _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls); @@ -108,7 +114,6 @@ namespace Ryujinx.Ui } _device.DisposeGpu(); - _renderer.Dispose(); } public void MainLoop() diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 451df2fd9..f9f365760 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -3,8 +3,10 @@ using JsonPrettyPrinterPlus; using Ryujinx.Audio; using Ryujinx.Common.Logging; using Ryujinx.Configuration; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL; using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; using Ryujinx.Profiler; using System; using System.Diagnostics; @@ -22,11 +24,10 @@ namespace Ryujinx.Ui { public class MainWindow : Window { - private static HLE.Switch _device; + private static VirtualFileSystem _virtualFileSystem; + private static ContentManager _contentManager; - private static Renderer _renderer; - - private static IAalOutput _audioOut; + private static HLE.Switch _emulationContext; private static GlScreen _screen; @@ -75,29 +76,29 @@ namespace Ryujinx.Ui _gameTable.ButtonReleaseEvent += Row_Clicked; + // First we check that a migration isn't needed. (because VirtualFileSystem will create the new directory otherwise) bool continueWithStartup = Migration.PromptIfMigrationNeededForStartup(this, out bool migrationNeeded); if (!continueWithStartup) { - End(); + End(null); } - _renderer = new Renderer(); - - _audioOut = InitializeAudioEngine(); - - // TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation. - _device = InitializeSwitchInstance(); + _virtualFileSystem = new VirtualFileSystem(); + _contentManager = new ContentManager(_virtualFileSystem); if (migrationNeeded) { - bool migrationSuccessful = Migration.DoMigrationForStartup(this, _device); + bool migrationSuccessful = Migration.DoMigrationForStartup(this, _virtualFileSystem); if (!migrationSuccessful) { - End(); + End(null); } } + // Make sure that everything is loaded. + _virtualFileSystem.Reload(); + _treeView = _gameTable; ApplyTheme(); @@ -199,7 +200,9 @@ namespace Ryujinx.Ui private HLE.Switch InitializeSwitchInstance() { - HLE.Switch instance = new HLE.Switch(_renderer, _audioOut); + _virtualFileSystem.Reload(); + + HLE.Switch instance = new HLE.Switch(_virtualFileSystem, _contentManager, InitializeRenderer(), InitializeAudioEngine()); instance.Initialize(); @@ -218,8 +221,7 @@ namespace Ryujinx.Ui _tableStore.Clear(); await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, - _device.System.KeySet, _device.System.State.DesiredTitleLanguage, _device.System.FsClient, - _device.FileSystem)); + _virtualFileSystem, ConfigurationState.Instance.System.Language)); _updatingGameTable = false; } @@ -234,8 +236,10 @@ namespace Ryujinx.Ui { Logger.RestartTime(); + HLE.Switch device = InitializeSwitchInstance(); + // TODO: Move this somewhere else + reloadable? - Ryujinx.Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; + Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; if (Directory.Exists(path)) { @@ -249,12 +253,12 @@ namespace Ryujinx.Ui if (romFsFiles.Length > 0) { Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS."); - _device.LoadCart(path, romFsFiles[0]); + device.LoadCart(path, romFsFiles[0]); } else { Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS."); - _device.LoadCart(path); + device.LoadCart(path); } } else if (File.Exists(path)) @@ -263,22 +267,22 @@ namespace Ryujinx.Ui { case ".xci": Logger.PrintInfo(LogClass.Application, "Loading as XCI."); - _device.LoadXci(path); + device.LoadXci(path); break; case ".nca": Logger.PrintInfo(LogClass.Application, "Loading as NCA."); - _device.LoadNca(path); + device.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.PrintInfo(LogClass.Application, "Loading as NSP."); - _device.LoadNsp(path); + device.LoadNsp(path); break; default: Logger.PrintInfo(LogClass.Application, "Loading as homebrew."); try { - _device.LoadProgram(path); + device.LoadProgram(path); } catch (ArgumentOutOfRangeException) { @@ -290,13 +294,15 @@ namespace Ryujinx.Ui else { Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - End(); + End(device); } + _emulationContext = device; + #if MACOS_BUILD - CreateGameWindow(); + CreateGameWindow(device); #else - new Thread(CreateGameWindow).Start(); + new Thread(() => CreateGameWindow(device)).Start(); #endif _gameLoaded = true; @@ -305,28 +311,55 @@ namespace Ryujinx.Ui _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; - DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleIdText, _device.System.TitleName); + DiscordIntegrationModule.SwitchToPlayingState(device.System.TitleIdText, device.System.TitleName); - ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleIdText, appMetadata => + ApplicationLibrary.LoadAndSaveMetaData(device.System.TitleIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); } } - private static void CreateGameWindow() + private void CreateGameWindow(HLE.Switch device) { - _device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType); + device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType); - using (_screen = new GlScreen(_device, _renderer)) + using (_screen = new GlScreen(device)) { _screen.MainLoop(); + } - End(); + device.Dispose(); + + _emulationContext = null; + _screen = null; + _gameLoaded = false; + + DiscordIntegrationModule.SwitchToMainMenu(); + + Application.Invoke(delegate + { + _stopEmulation.Sensitive = false; + _firmwareInstallFile.Sensitive = true; + _firmwareInstallDirectory.Sensitive = true; + }); + } + + private static void UpdateGameMetadata(string titleId) + { + if (_gameLoaded) + { + ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => + { + DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); + double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; + + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + }); } } - private static void End() + private void End(HLE.Switch device) { if (_ending) { @@ -335,22 +368,23 @@ namespace Ryujinx.Ui _ending = true; - if (_gameLoaded) + if (device != null) { - ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleIdText, appMetadata => - { - DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); - }); + UpdateGameMetadata(device.System.TitleIdText); } + Dispose(); + Profile.FinishProfiling(); - _device?.Dispose(); - _audioOut?.Dispose(); + device?.Dispose(); + DiscordIntegrationModule.Exit(); Logger.Shutdown(); - Environment.Exit(0); + Application.Quit(); + } + + private static IRenderer InitializeRenderer() + { + return new Renderer(); } /// @@ -427,7 +461,7 @@ namespace Ryujinx.Ui if (treeIter.UserData == IntPtr.Zero) return; - GameTableContextMenu contextMenu = new GameTableContextMenu(_tableStore, treeIter, _device.System.FsClient); + GameTableContextMenu contextMenu = new GameTableContextMenu(_tableStore, treeIter, _virtualFileSystem.FsClient); contextMenu.ShowAll(); contextMenu.PopupAtPointer(null); } @@ -477,20 +511,18 @@ namespace Ryujinx.Ui private void Exit_Pressed(object sender, EventArgs args) { _screen?.Exit(); - End(); + End(_emulationContext); } private void Window_Close(object sender, DeleteEventArgs args) { _screen?.Exit(); - End(); + End(_emulationContext); } private void StopEmulation_Pressed(object sender, EventArgs args) { - // TODO: Write logic to kill running game - - _gameLoaded = false; + _screen?.Exit(); } private void Installer_File_Pressed(object o, EventArgs args) @@ -525,7 +557,7 @@ namespace Ryujinx.Ui private void RefreshFirmwareLabel() { - var currentFirmware = _device.System.GetCurrentFirmwareVersion(); + var currentFirmware = _contentManager.GetCurrentFirmwareVersion(); GLib.Idle.Add(new GLib.IdleHandler(() => { @@ -547,7 +579,7 @@ namespace Ryujinx.Ui fileChooser.Dispose(); - var firmwareVersion = _device.System.VerifyFirmwarePackage(filename); + var firmwareVersion = _contentManager.VerifyFirmwarePackage(filename); if (firmwareVersion == null) { @@ -566,7 +598,7 @@ namespace Ryujinx.Ui return; } - var currentVersion = _device.System.GetCurrentFirmwareVersion(); + var currentVersion = _contentManager.GetCurrentFirmwareVersion(); string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed."; @@ -606,7 +638,7 @@ namespace Ryujinx.Ui try { - _device.System.InstallFirmware(filename); + _contentManager.InstallFirmware(filename); GLib.Idle.Add(new GLib.IdleHandler(() => { diff --git a/Ryujinx/Ui/Migration.cs b/Ryujinx/Ui/Migration.cs index 9e9b80001..20d810647 100644 --- a/Ryujinx/Ui/Migration.cs +++ b/Ryujinx/Ui/Migration.cs @@ -1,21 +1,20 @@ using Gtk; using LibHac; +using Ryujinx.HLE.FileSystem; using System; using System.IO; using System.Linq; using System.Reflection; -using Switch = Ryujinx.HLE.Switch; - namespace Ryujinx.Ui { internal class Migration { - private Switch Device { get; } + private VirtualFileSystem _virtualFileSystem; - public Migration(Switch device) + public Migration(VirtualFileSystem virtualFileSystem) { - Device = device; + _virtualFileSystem = virtualFileSystem; } public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded) @@ -48,11 +47,11 @@ namespace Ryujinx.Ui return dialogResponse == (int)ResponseType.Yes; } - public static bool DoMigrationForStartup(Window parentWindow, Switch device) + public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem) { try { - Migration migration = new Migration(device); + Migration migration = new Migration(virtualFileSystem); int saveCount = migration.Migrate(); using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null) @@ -64,9 +63,6 @@ namespace Ryujinx.Ui dialogSuccess.Run(); - // Reload key set after migration to be sure to catch the keys in the system directory. - device.System.LoadKeySet(); - return true; } catch (HorizonResultException ex) @@ -80,6 +76,9 @@ namespace Ryujinx.Ui // Returns the number of saves migrated public int Migrate() { + // Make sure FsClient is initialized + _virtualFileSystem.Reload(); + string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string oldBasePath = Path.Combine(appDataPath, "RyuFs"); @@ -89,7 +88,7 @@ namespace Ryujinx.Ui CopyRyuFs(oldBasePath, newBasePath); - SaveImporter importer = new SaveImporter(oldSaveDir, Device.System.FsClient); + SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient); return importer.Import(); }