forked from Mirror/Ryujinx
Keep the GUI alive when closing a game (#888)
* Keep the GUI alive when closing a game Make HLE.Switch init when starting a game and dispose it when closing the GlScreen. This also make HLE in charge of disposing the audio and gpu backend. * Address Ac_k's comments * Make sure to dispose the Discord module and use GTK quit method Also update Discord Precense when closing a game. * Make sure to dispose MainWindow * Address gdk's comments
This commit is contained in:
parent
b4b2b8b162
commit
d6b9babe1d
16 changed files with 284 additions and 215 deletions
|
@ -116,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
Methods.ShaderCache.Dispose();
|
Methods.ShaderCache.Dispose();
|
||||||
Methods.BufferManager.Dispose();
|
Methods.BufferManager.Dispose();
|
||||||
Methods.TextureManager.Dispose();
|
Methods.TextureManager.Dispose();
|
||||||
|
Renderer.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
internal class ContentManager
|
public class ContentManager
|
||||||
{
|
{
|
||||||
private const ulong SystemVersionTitleId = 0x0100000000000809;
|
private const ulong SystemVersionTitleId = 0x0100000000000809;
|
||||||
private const ulong SystemUpdateTitleId = 0x0100000000000816;
|
private const ulong SystemUpdateTitleId = 0x0100000000000816;
|
||||||
|
@ -26,11 +26,11 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary;
|
private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary;
|
||||||
|
|
||||||
private Switch _device;
|
private VirtualFileSystem _virtualFileSystem;
|
||||||
|
|
||||||
private readonly object _lock = new object();
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
public ContentManager(Switch device)
|
public ContentManager(VirtualFileSystem virtualFileSystem)
|
||||||
{
|
{
|
||||||
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
|
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
|
||||||
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
|
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
|
||||||
|
@ -55,10 +55,10 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{ "FontNintendoExtended", "nintendo_ext_003.bfttf" }
|
{ "FontNintendoExtended", "nintendo_ext_003.bfttf" }
|
||||||
};
|
};
|
||||||
|
|
||||||
_device = device;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadEntries(bool ignoreMissingFonts = false)
|
public void LoadEntries(Switch device = null)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
contentPathString = LocationHelper.GetContentRoot(storageId);
|
contentPathString = LocationHelper.GetContentRoot(storageId);
|
||||||
contentDirectory = LocationHelper.GetRealPath(_device.FileSystem, contentPathString);
|
contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString);
|
||||||
registeredDirectory = Path.Combine(contentDirectory, "registered");
|
registeredDirectory = Path.Combine(contentDirectory, "registered");
|
||||||
}
|
}
|
||||||
catch (NotSupportedException)
|
catch (NotSupportedException)
|
||||||
|
@ -99,7 +99,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
using (FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0]))
|
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);
|
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))
|
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);
|
string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
|
@ -156,9 +156,11 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeManager.Instance.InitializeTimeZone(_device);
|
if (device != null)
|
||||||
|
{
|
||||||
_device.System.Font.Initialize(this, ignoreMissingFonts);
|
TimeManager.Instance.InitializeTimeZone(device);
|
||||||
|
device.System.Font.Initialize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +273,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string installedPath = _device.FileSystem.SwitchPathToSystemPath(locationEntry.ContentPath);
|
string installedPath = _virtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(installedPath))
|
if (!string.IsNullOrWhiteSpace(installedPath))
|
||||||
{
|
{
|
||||||
|
@ -279,7 +281,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read))
|
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;
|
bool contentCheck = nca.Header.ContentType == contentType;
|
||||||
|
|
||||||
return contentCheck;
|
return contentCheck;
|
||||||
|
@ -351,7 +353,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
public void InstallFirmware(string firmwareSource)
|
public void InstallFirmware(string firmwareSource)
|
||||||
{
|
{
|
||||||
string contentPathString = LocationHelper.GetContentRoot(StorageId.NandSystem);
|
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 registeredDirectory = Path.Combine(contentDirectory, "registered");
|
||||||
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
|
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
|
||||||
|
|
||||||
|
@ -386,7 +388,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ".xci":
|
case ".xci":
|
||||||
Xci xci = new Xci(_device.System.KeySet, file.AsStorage());
|
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
|
||||||
InstallFromCart(xci, temporaryDirectory);
|
InstallFromCart(xci, temporaryDirectory);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -418,7 +420,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
foreach (var entry in filesystem.EnumerateEntries("/", "*.nca"))
|
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);
|
SaveNca(nca, entry.Name.Remove(entry.Name.IndexOf('.')), temporaryDirectory);
|
||||||
}
|
}
|
||||||
|
@ -537,7 +539,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
return VerifyAndGetVersionZip(archive);
|
return VerifyAndGetVersionZip(archive);
|
||||||
}
|
}
|
||||||
case ".xci":
|
case ".xci":
|
||||||
Xci xci = new Xci(_device.System.KeySet, file.AsStorage());
|
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
|
||||||
|
|
||||||
if (xci.HasPartition(XciPartitionType.Update))
|
if (xci.HasPartition(XciPartitionType.Update))
|
||||||
{
|
{
|
||||||
|
@ -561,6 +563,8 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
||||||
{
|
{
|
||||||
|
IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel();
|
||||||
|
|
||||||
SystemVersion systemVersion = null;
|
SystemVersion systemVersion = null;
|
||||||
|
|
||||||
foreach (var entry in archive.Entries)
|
foreach (var entry in archive.Entries)
|
||||||
|
@ -571,7 +575,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
IStorage storage = ncaStream.AsStorage();
|
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))
|
if (updateNcas.ContainsKey(nca.Header.TitleId))
|
||||||
{
|
{
|
||||||
|
@ -598,9 +602,9 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
using (Stream ncaStream = GetZipStream(fileEntry))
|
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;
|
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||||
|
|
||||||
|
@ -628,9 +632,9 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
using (Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)))
|
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())
|
if (romfs.OpenFile(out IFile systemVersionFile, "/file", OpenMode.Read).IsSuccess())
|
||||||
{
|
{
|
||||||
|
@ -663,9 +667,9 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
using (Stream contentNcaStream = GetZipStream(contentZipEntry))
|
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;
|
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||||
|
|
||||||
|
@ -722,6 +726,8 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
|
SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
|
||||||
{
|
{
|
||||||
|
IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel();
|
||||||
|
|
||||||
SystemVersion systemVersion = null;
|
SystemVersion systemVersion = null;
|
||||||
|
|
||||||
CnmtContentMetaEntry[] metaEntries = null;
|
CnmtContentMetaEntry[] metaEntries = null;
|
||||||
|
@ -730,11 +736,11 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage();
|
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)
|
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;
|
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)
|
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())
|
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 metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaEntry.path, OpenMode.Read).AsStorage();
|
||||||
IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, 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;
|
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||||
|
|
||||||
|
@ -851,7 +857,9 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
|
|
||||||
public SystemVersion GetCurrentFirmwareVersion()
|
public SystemVersion GetCurrentFirmwareVersion()
|
||||||
{
|
{
|
||||||
LoadEntries(true);
|
IntegrityCheckLevel integrityCheckLevel = Switch.GetIntigrityCheckLevel();
|
||||||
|
|
||||||
|
LoadEntries();
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
@ -861,15 +869,15 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
if (entry.ContentType == NcaContentType.Data)
|
if (entry.ContentType == NcaContentType.Data)
|
||||||
{
|
{
|
||||||
var path = _device.FileSystem.SwitchPathToSystemPath(entry.ContentPath);
|
var path = _virtualFileSystem.SwitchPathToSystemPath(entry.ContentPath);
|
||||||
|
|
||||||
using (FileStream fileStream = File.OpenRead(path))
|
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)
|
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())
|
if (romfs.OpenFile(out IFile systemVersionFile, "/file", OpenMode.Read).IsSuccess())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.FileSystem
|
namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
internal enum StorageId
|
public enum StorageId
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Host,
|
Host,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsService;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using System;
|
using System;
|
||||||
|
@ -16,6 +20,16 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
public static string SystemNandPath = Path.Combine(NandPath, "system");
|
public static string SystemNandPath = Path.Combine(NandPath, "system");
|
||||||
public static string UserNandPath = Path.Combine(NandPath, "user");
|
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 Stream RomFs { get; private set; }
|
||||||
|
|
||||||
public void LoadRomFs(string fileName)
|
public void LoadRomFs(string fileName)
|
||||||
|
@ -183,6 +197,69 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
return Path.Combine(appDataPath, BasePath);
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
@ -192,7 +269,7 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
RomFs?.Dispose();
|
Unload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,15 +44,15 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
_fontsPath = Path.Combine(device.FileSystem.GetSystemPath(), "fonts");
|
_fontsPath = Path.Combine(device.FileSystem.GetSystemPath(), "fonts");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(ContentManager contentManager, bool ignoreMissingFonts)
|
public void Initialize(ContentManager contentManager)
|
||||||
{
|
{
|
||||||
_fontData?.Clear();
|
_fontData?.Clear();
|
||||||
_fontData = null;
|
_fontData = null;
|
||||||
|
|
||||||
EnsureInitialized(contentManager, ignoreMissingFonts);
|
EnsureInitialized(contentManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureInitialized(ContentManager contentManager, bool ignoreMissingFonts)
|
public void EnsureInitialized(ContentManager contentManager)
|
||||||
{
|
{
|
||||||
if (_fontData == null)
|
if (_fontData == null)
|
||||||
{
|
{
|
||||||
|
@ -120,12 +120,10 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
else if (!ignoreMissingFonts)
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\".");
|
throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\".");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FontInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fontData = new Dictionary<SharedFontType, FontInfo>
|
_fontData = new Dictionary<SharedFontType, FontInfo>
|
||||||
|
@ -138,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fontOffset > Horizon.FontSize && !ignoreMissingFonts)
|
if (fontOffset > Horizon.FontSize)
|
||||||
{
|
{
|
||||||
throw new InvalidSystemResourceException(
|
throw new InvalidSystemResourceException(
|
||||||
$"The sum of all fonts size exceed the shared memory size. " +
|
$"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)
|
public int GetFontSize(SharedFontType fontType)
|
||||||
{
|
{
|
||||||
EnsureInitialized(_device.System.ContentManager, false);
|
EnsureInitialized(_device.System.ContentManager);
|
||||||
|
|
||||||
return _fontData[fontType].Size;
|
return _fontData[fontType].Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetSharedMemoryAddressOffset(SharedFontType fontType)
|
public int GetSharedMemoryAddressOffset(SharedFontType fontType)
|
||||||
{
|
{
|
||||||
EnsureInitialized(_device.System.ContentManager, false);
|
EnsureInitialized(_device.System.ContentManager);
|
||||||
|
|
||||||
return _fontData[fontType].Offset + 8;
|
return _fontData[fontType].Offset + 8;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ using LibHac;
|
||||||
using LibHac.Account;
|
using LibHac.Account;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsService;
|
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.NcaUtils;
|
using LibHac.FsSystem.NcaUtils;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
@ -105,7 +104,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal KEvent VsyncEvent { get; private set; }
|
internal KEvent VsyncEvent { get; private set; }
|
||||||
|
|
||||||
public Keyset KeySet { get; private set; }
|
public Keyset KeySet => Device.FileSystem.KeySet;
|
||||||
|
|
||||||
private bool _hasStarted;
|
private bool _hasStarted;
|
||||||
|
|
||||||
|
@ -122,12 +121,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal long HidBaseAddress { get; private set; }
|
internal long HidBaseAddress { get; private set; }
|
||||||
|
|
||||||
internal FileSystemServer FsServer { get; private set; }
|
public Horizon(Switch device, ContentManager contentManager)
|
||||||
public FileSystemClient FsClient { get; private set; }
|
|
||||||
|
|
||||||
internal EmulatedGameCard GameCard { get; private set; }
|
|
||||||
|
|
||||||
public Horizon(Switch device)
|
|
||||||
{
|
{
|
||||||
ControlData = new BlitStruct<ApplicationControlProperty>(1);
|
ControlData = new BlitStruct<ApplicationControlProperty>(1);
|
||||||
|
|
||||||
|
@ -211,9 +205,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
VsyncEvent = new KEvent(this);
|
VsyncEvent = new KEvent(this);
|
||||||
|
|
||||||
LoadKeySet();
|
ContentManager = contentManager;
|
||||||
|
|
||||||
ContentManager = new ContentManager(device);
|
|
||||||
|
|
||||||
// TODO: use set:sys (and get external clock source id from settings)
|
// 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.
|
// 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
|
// FIXME: TimeZone shoud be init here but it's actually done in ContentManager
|
||||||
|
|
||||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
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)
|
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||||
|
@ -284,7 +260,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries(Device);
|
||||||
|
|
||||||
LoadNca(mainNca, patchNca, controlNca);
|
LoadNca(mainNca, patchNca, controlNca);
|
||||||
}
|
}
|
||||||
|
@ -578,7 +554,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
LoadNso("subsdk");
|
LoadNso("subsdk");
|
||||||
LoadNso("sdk");
|
LoadNso("sdk");
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries(Device);
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
||||||
}
|
}
|
||||||
|
@ -671,7 +647,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
staticObject = new NxStaticObject(input);
|
staticObject = new NxStaticObject(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries(Device);
|
||||||
|
|
||||||
TitleName = metaData.TitleName;
|
TitleName = metaData.TitleName;
|
||||||
TitleId = metaData.Aci0.TitleId;
|
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.");
|
"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())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
|
@ -722,57 +698,6 @@ namespace Ryujinx.HLE.HOS
|
||||||
return rc;
|
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()
|
public void SignalVsync()
|
||||||
{
|
{
|
||||||
VsyncEvent.ReadableEvent.Signal();
|
VsyncEvent.ReadableEvent.Signal();
|
||||||
|
|
|
@ -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.");
|
"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);
|
ref context.Device.System.ControlData.Value, ref userId);
|
||||||
|
|
||||||
context.ResponseData.Write(requiredSize);
|
context.ResponseData.Write(requiredSize);
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
||||||
|
|
||||||
public IFileSystemProxy(ServiceCtx context)
|
public IFileSystemProxy(ServiceCtx context)
|
||||||
{
|
{
|
||||||
_baseFileSystemProxy = context.Device.System.FsServer.CreateFileSystemProxyService();
|
_baseFileSystemProxy = context.Device.FileSystem.FsServer.CreateFileSystemProxyService();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(1)]
|
[Command(1)]
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
|
||||||
// GetSharedMemoryNativeHandle() -> handle<copy>
|
// GetSharedMemoryNativeHandle() -> handle<copy>
|
||||||
public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context)
|
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)
|
if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out int handle) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Configuration;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services;
|
using Ryujinx.HLE.HOS.Services;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
@ -15,11 +16,11 @@ namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
public class Switch : IDisposable
|
public class Switch : IDisposable
|
||||||
{
|
{
|
||||||
internal IAalOutput AudioOut { get; private set; }
|
public IAalOutput AudioOut { get; private set; }
|
||||||
|
|
||||||
internal DeviceMemory Memory { 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; }
|
public VirtualFileSystem FileSystem { get; private set; }
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public event EventHandler Finish;
|
public event EventHandler Finish;
|
||||||
|
|
||||||
public Switch(IRenderer renderer, IAalOutput audioOut)
|
public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, IRenderer renderer, IAalOutput audioOut)
|
||||||
{
|
{
|
||||||
if (renderer == null)
|
if (renderer == null)
|
||||||
{
|
{
|
||||||
|
@ -53,9 +54,9 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
Gpu = new GpuContext(renderer);
|
Gpu = new GpuContext(renderer);
|
||||||
|
|
||||||
FileSystem = new VirtualFileSystem();
|
FileSystem = fileSystem;
|
||||||
|
|
||||||
System = new Horizon(this);
|
System = new Horizon(this, contentManager);
|
||||||
|
|
||||||
Statistics = new PerformanceStatistics();
|
Statistics = new PerformanceStatistics();
|
||||||
|
|
||||||
|
@ -78,15 +79,20 @@ namespace Ryujinx.HLE
|
||||||
System.EnableMultiCoreScheduling();
|
System.EnableMultiCoreScheduling();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
System.FsIntegrityCheckLevel = GetIntigrityCheckLevel();
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
|
||||||
: IntegrityCheckLevel.None;
|
|
||||||
|
|
||||||
System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
||||||
|
|
||||||
ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
|
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)
|
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||||
{
|
{
|
||||||
System.LoadCart(exeFsDir, romFsFile);
|
System.LoadCart(exeFsDir, romFsFile);
|
||||||
|
@ -129,7 +135,7 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
internal void Unload()
|
internal void Unload()
|
||||||
{
|
{
|
||||||
FileSystem.Dispose();
|
FileSystem.Unload();
|
||||||
|
|
||||||
Memory.Dispose();
|
Memory.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -150,6 +156,7 @@ namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
System.Dispose();
|
System.Dispose();
|
||||||
VsyncEvent.Dispose();
|
VsyncEvent.Dispose();
|
||||||
|
AudioOut.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,5 +88,23 @@ namespace Ryujinx.Configuration
|
||||||
|
|
||||||
DiscordClient?.SetPresence(DiscordPresence);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ namespace Ryujinx
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Profile.Initialize();
|
Profile.Initialize();
|
||||||
|
|
||||||
Application.Init();
|
Application.Init();
|
||||||
|
|
|
@ -7,6 +7,7 @@ using LibHac.FsSystem.NcaUtils;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Spl;
|
using LibHac.Spl;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Configuration.System;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
using System;
|
using System;
|
||||||
|
@ -20,7 +21,6 @@ using Utf8Json;
|
||||||
using Utf8Json.Resolvers;
|
using Utf8Json.Resolvers;
|
||||||
|
|
||||||
using RightsId = LibHac.Fs.RightsId;
|
using RightsId = LibHac.Fs.RightsId;
|
||||||
using TitleLanguage = Ryujinx.HLE.HOS.SystemState.TitleLanguage;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
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[] _nroIcon = GetResourceBytes("Ryujinx.Ui.assets.NROIcon.png");
|
||||||
private static readonly byte[] _nsoIcon = GetResourceBytes("Ryujinx.Ui.assets.NSOIcon.png");
|
private static readonly byte[] _nsoIcon = GetResourceBytes("Ryujinx.Ui.assets.NSOIcon.png");
|
||||||
|
|
||||||
private static Keyset _keySet;
|
private static Keyset _keySet;
|
||||||
private static TitleLanguage _desiredTitleLanguage;
|
private static Language _desiredTitleLanguage;
|
||||||
|
|
||||||
public static void LoadApplications(List<string> appDirs, Keyset keySet, TitleLanguage desiredTitleLanguage, FileSystemClient fsClient = null, VirtualFileSystem vfs = null)
|
public static void LoadApplications(List<string> appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage)
|
||||||
{
|
{
|
||||||
int numApplicationsFound = 0;
|
int numApplicationsFound = 0;
|
||||||
int numApplicationsLoaded = 0;
|
int numApplicationsLoaded = 0;
|
||||||
|
|
||||||
_keySet = keySet;
|
_keySet = virtualFileSystem.KeySet;
|
||||||
_desiredTitleLanguage = desiredTitleLanguage;
|
_desiredTitleLanguage = desiredTitleLanguage;
|
||||||
|
|
||||||
// Builds the applications list with paths to found applications
|
// Builds the applications list with paths to found applications
|
||||||
|
@ -346,11 +346,11 @@ namespace Ryujinx.Ui
|
||||||
filter.SetUserId(new UserId(1, 0));
|
filter.SetUserId(new UserId(1, 0));
|
||||||
filter.SetTitleId(new TitleId(titleIdNum));
|
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())
|
if (result.IsSuccess())
|
||||||
{
|
{
|
||||||
saveDataPath = Path.Combine(vfs.GetNandPath(), $"user/save/{saveDataInfo.SaveDataId:x16}");
|
saveDataPath = Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{saveDataInfo.SaveDataId:x16}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,14 +45,20 @@ namespace Ryujinx.Ui
|
||||||
private ProfileWindowManager _profileWindow;
|
private ProfileWindowManager _profileWindow;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public GlScreen(Switch device, Renderer renderer)
|
public GlScreen(Switch device)
|
||||||
: base(1280, 720,
|
: base(1280, 720,
|
||||||
new GraphicsMode(), "Ryujinx", 0,
|
new GraphicsMode(), "Ryujinx", 0,
|
||||||
DisplayDevice.Default, 3, 3,
|
DisplayDevice.Default, 3, 3,
|
||||||
GraphicsContextFlags.ForwardCompatible)
|
GraphicsContextFlags.ForwardCompatible)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
_renderer = renderer;
|
|
||||||
|
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);
|
_primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
|
||||||
|
|
||||||
|
@ -108,7 +114,6 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.DisposeGpu();
|
_device.DisposeGpu();
|
||||||
_renderer.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MainLoop()
|
public void MainLoop()
|
||||||
|
|
|
@ -3,8 +3,10 @@ using JsonPrettyPrinterPlus;
|
||||||
using Ryujinx.Audio;
|
using Ryujinx.Audio;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Configuration;
|
using Ryujinx.Configuration;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.OpenGL;
|
using Ryujinx.Graphics.OpenGL;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.Profiler;
|
using Ryujinx.Profiler;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -22,11 +24,10 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
public class MainWindow : Window
|
public class MainWindow : Window
|
||||||
{
|
{
|
||||||
private static HLE.Switch _device;
|
private static VirtualFileSystem _virtualFileSystem;
|
||||||
|
private static ContentManager _contentManager;
|
||||||
|
|
||||||
private static Renderer _renderer;
|
private static HLE.Switch _emulationContext;
|
||||||
|
|
||||||
private static IAalOutput _audioOut;
|
|
||||||
|
|
||||||
private static GlScreen _screen;
|
private static GlScreen _screen;
|
||||||
|
|
||||||
|
@ -75,29 +76,29 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
_gameTable.ButtonReleaseEvent += Row_Clicked;
|
_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);
|
bool continueWithStartup = Migration.PromptIfMigrationNeededForStartup(this, out bool migrationNeeded);
|
||||||
if (!continueWithStartup)
|
if (!continueWithStartup)
|
||||||
{
|
{
|
||||||
End();
|
End(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderer = new Renderer();
|
_virtualFileSystem = new VirtualFileSystem();
|
||||||
|
_contentManager = new ContentManager(_virtualFileSystem);
|
||||||
_audioOut = InitializeAudioEngine();
|
|
||||||
|
|
||||||
// TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation.
|
|
||||||
_device = InitializeSwitchInstance();
|
|
||||||
|
|
||||||
if (migrationNeeded)
|
if (migrationNeeded)
|
||||||
{
|
{
|
||||||
bool migrationSuccessful = Migration.DoMigrationForStartup(this, _device);
|
bool migrationSuccessful = Migration.DoMigrationForStartup(this, _virtualFileSystem);
|
||||||
|
|
||||||
if (!migrationSuccessful)
|
if (!migrationSuccessful)
|
||||||
{
|
{
|
||||||
End();
|
End(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure that everything is loaded.
|
||||||
|
_virtualFileSystem.Reload();
|
||||||
|
|
||||||
_treeView = _gameTable;
|
_treeView = _gameTable;
|
||||||
|
|
||||||
ApplyTheme();
|
ApplyTheme();
|
||||||
|
@ -199,7 +200,9 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private HLE.Switch InitializeSwitchInstance()
|
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();
|
instance.Initialize();
|
||||||
|
|
||||||
|
@ -218,8 +221,7 @@ namespace Ryujinx.Ui
|
||||||
_tableStore.Clear();
|
_tableStore.Clear();
|
||||||
|
|
||||||
await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs,
|
await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs,
|
||||||
_device.System.KeySet, _device.System.State.DesiredTitleLanguage, _device.System.FsClient,
|
_virtualFileSystem, ConfigurationState.Instance.System.Language));
|
||||||
_device.FileSystem));
|
|
||||||
|
|
||||||
_updatingGameTable = false;
|
_updatingGameTable = false;
|
||||||
}
|
}
|
||||||
|
@ -234,8 +236,10 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Logger.RestartTime();
|
Logger.RestartTime();
|
||||||
|
|
||||||
|
HLE.Switch device = InitializeSwitchInstance();
|
||||||
|
|
||||||
// TODO: Move this somewhere else + reloadable?
|
// 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))
|
if (Directory.Exists(path))
|
||||||
{
|
{
|
||||||
|
@ -249,12 +253,12 @@ namespace Ryujinx.Ui
|
||||||
if (romFsFiles.Length > 0)
|
if (romFsFiles.Length > 0)
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS.");
|
Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS.");
|
||||||
_device.LoadCart(path, romFsFiles[0]);
|
device.LoadCart(path, romFsFiles[0]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
||||||
_device.LoadCart(path);
|
device.LoadCart(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (File.Exists(path))
|
else if (File.Exists(path))
|
||||||
|
@ -263,22 +267,22 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
case ".xci":
|
case ".xci":
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as XCI.");
|
Logger.PrintInfo(LogClass.Application, "Loading as XCI.");
|
||||||
_device.LoadXci(path);
|
device.LoadXci(path);
|
||||||
break;
|
break;
|
||||||
case ".nca":
|
case ".nca":
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as NCA.");
|
Logger.PrintInfo(LogClass.Application, "Loading as NCA.");
|
||||||
_device.LoadNca(path);
|
device.LoadNca(path);
|
||||||
break;
|
break;
|
||||||
case ".nsp":
|
case ".nsp":
|
||||||
case ".pfs0":
|
case ".pfs0":
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as NSP.");
|
Logger.PrintInfo(LogClass.Application, "Loading as NSP.");
|
||||||
_device.LoadNsp(path);
|
device.LoadNsp(path);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logger.PrintInfo(LogClass.Application, "Loading as homebrew.");
|
Logger.PrintInfo(LogClass.Application, "Loading as homebrew.");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_device.LoadProgram(path);
|
device.LoadProgram(path);
|
||||||
}
|
}
|
||||||
catch (ArgumentOutOfRangeException)
|
catch (ArgumentOutOfRangeException)
|
||||||
{
|
{
|
||||||
|
@ -290,13 +294,15 @@ namespace Ryujinx.Ui
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||||
End();
|
End(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_emulationContext = device;
|
||||||
|
|
||||||
#if MACOS_BUILD
|
#if MACOS_BUILD
|
||||||
CreateGameWindow();
|
CreateGameWindow(device);
|
||||||
#else
|
#else
|
||||||
new Thread(CreateGameWindow).Start();
|
new Thread(() => CreateGameWindow(device)).Start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_gameLoaded = true;
|
_gameLoaded = true;
|
||||||
|
@ -305,28 +311,55 @@ namespace Ryujinx.Ui
|
||||||
_firmwareInstallFile.Sensitive = false;
|
_firmwareInstallFile.Sensitive = false;
|
||||||
_firmwareInstallDirectory.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();
|
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();
|
_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)
|
if (_ending)
|
||||||
{
|
{
|
||||||
|
@ -335,22 +368,23 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
_ending = true;
|
_ending = true;
|
||||||
|
|
||||||
if (_gameLoaded)
|
if (device != null)
|
||||||
{
|
{
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(_device.System.TitleIdText, appMetadata =>
|
UpdateGameMetadata(device.System.TitleIdText);
|
||||||
{
|
|
||||||
DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
|
|
||||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
|
|
||||||
|
|
||||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dispose();
|
||||||
|
|
||||||
Profile.FinishProfiling();
|
Profile.FinishProfiling();
|
||||||
_device?.Dispose();
|
device?.Dispose();
|
||||||
_audioOut?.Dispose();
|
DiscordIntegrationModule.Exit();
|
||||||
Logger.Shutdown();
|
Logger.Shutdown();
|
||||||
Environment.Exit(0);
|
Application.Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IRenderer InitializeRenderer()
|
||||||
|
{
|
||||||
|
return new Renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -427,7 +461,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
if (treeIter.UserData == IntPtr.Zero) return;
|
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.ShowAll();
|
||||||
contextMenu.PopupAtPointer(null);
|
contextMenu.PopupAtPointer(null);
|
||||||
}
|
}
|
||||||
|
@ -477,20 +511,18 @@ namespace Ryujinx.Ui
|
||||||
private void Exit_Pressed(object sender, EventArgs args)
|
private void Exit_Pressed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
_screen?.Exit();
|
_screen?.Exit();
|
||||||
End();
|
End(_emulationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Close(object sender, DeleteEventArgs args)
|
private void Window_Close(object sender, DeleteEventArgs args)
|
||||||
{
|
{
|
||||||
_screen?.Exit();
|
_screen?.Exit();
|
||||||
End();
|
End(_emulationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopEmulation_Pressed(object sender, EventArgs args)
|
private void StopEmulation_Pressed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
// TODO: Write logic to kill running game
|
_screen?.Exit();
|
||||||
|
|
||||||
_gameLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Installer_File_Pressed(object o, EventArgs args)
|
private void Installer_File_Pressed(object o, EventArgs args)
|
||||||
|
@ -525,7 +557,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void RefreshFirmwareLabel()
|
private void RefreshFirmwareLabel()
|
||||||
{
|
{
|
||||||
var currentFirmware = _device.System.GetCurrentFirmwareVersion();
|
var currentFirmware = _contentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
GLib.Idle.Add(new GLib.IdleHandler(() =>
|
GLib.Idle.Add(new GLib.IdleHandler(() =>
|
||||||
{
|
{
|
||||||
|
@ -547,7 +579,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
fileChooser.Dispose();
|
fileChooser.Dispose();
|
||||||
|
|
||||||
var firmwareVersion = _device.System.VerifyFirmwarePackage(filename);
|
var firmwareVersion = _contentManager.VerifyFirmwarePackage(filename);
|
||||||
|
|
||||||
if (firmwareVersion == null)
|
if (firmwareVersion == null)
|
||||||
{
|
{
|
||||||
|
@ -566,7 +598,7 @@ namespace Ryujinx.Ui
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentVersion = _device.System.GetCurrentFirmwareVersion();
|
var currentVersion = _contentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed.";
|
string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed.";
|
||||||
|
|
||||||
|
@ -606,7 +638,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_device.System.InstallFirmware(filename);
|
_contentManager.InstallFirmware(filename);
|
||||||
|
|
||||||
GLib.Idle.Add(new GLib.IdleHandler(() =>
|
GLib.Idle.Add(new GLib.IdleHandler(() =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
|
using Ryujinx.HLE.FileSystem;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using Switch = Ryujinx.HLE.Switch;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
internal class Migration
|
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)
|
public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded)
|
||||||
|
@ -48,11 +47,11 @@ namespace Ryujinx.Ui
|
||||||
return dialogResponse == (int)ResponseType.Yes;
|
return dialogResponse == (int)ResponseType.Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DoMigrationForStartup(Window parentWindow, Switch device)
|
public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Migration migration = new Migration(device);
|
Migration migration = new Migration(virtualFileSystem);
|
||||||
int saveCount = migration.Migrate();
|
int saveCount = migration.Migrate();
|
||||||
|
|
||||||
using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null)
|
using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null)
|
||||||
|
@ -64,9 +63,6 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
dialogSuccess.Run();
|
dialogSuccess.Run();
|
||||||
|
|
||||||
// Reload key set after migration to be sure to catch the keys in the system directory.
|
|
||||||
device.System.LoadKeySet();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (HorizonResultException ex)
|
catch (HorizonResultException ex)
|
||||||
|
@ -80,6 +76,9 @@ namespace Ryujinx.Ui
|
||||||
// Returns the number of saves migrated
|
// Returns the number of saves migrated
|
||||||
public int Migrate()
|
public int Migrate()
|
||||||
{
|
{
|
||||||
|
// Make sure FsClient is initialized
|
||||||
|
_virtualFileSystem.Reload();
|
||||||
|
|
||||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
|
|
||||||
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
|
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
|
||||||
|
@ -89,7 +88,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
CopyRyuFs(oldBasePath, newBasePath);
|
CopyRyuFs(oldBasePath, newBasePath);
|
||||||
|
|
||||||
SaveImporter importer = new SaveImporter(oldSaveDir, Device.System.FsClient);
|
SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient);
|
||||||
|
|
||||||
return importer.Import();
|
return importer.Import();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue