diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 5873223ef6..d6922ceb51 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS internal KEvent VsyncEvent { get; private set; } - internal Keyset KeySet { get; private set; } + public Keyset KeySet { get; private set; } private bool _hasStarted; @@ -455,8 +455,6 @@ namespace Ryujinx.HLE.HOS TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; TitleID = metaData.Aci0.TitleId.ToString("x16"); - CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; - if (string.IsNullOrWhiteSpace(CurrentTitle)) { TitleName = CurrentTitle = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; diff --git a/Ryujinx/ApplicationLibrary.cs b/Ryujinx/ApplicationLibrary.cs index 7e494c1e23..b2da9fb16e 100644 --- a/Ryujinx/ApplicationLibrary.cs +++ b/Ryujinx/ApplicationLibrary.cs @@ -1,7 +1,11 @@ -using Ryujinx.Common.Logging; +using LibHac; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Text; @@ -22,8 +26,6 @@ namespace Ryujinx { public Gdk.Pixbuf Icon; public string Game; - public string Version; - public string DLC; public string TP; public string LP; public string FileSize; @@ -37,7 +39,6 @@ namespace Ryujinx RyujinxNCAIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxNCAIcon.png", 75, 75); RyujinxNROIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxNROIcon.png", 75, 75); RyujinxNSOIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxNSOIcon.png", 75, 75); - RyujinxROMIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxROMIcon.png", 75, 75); List Games = new List(); @@ -58,62 +59,129 @@ namespace Ryujinx ApplicationLibraryData = new List(); foreach (string GamePath in Games) { - double filesize = new FileInfo(GamePath).Length * 0.000000000931; - ApplicationData data = new ApplicationData() + double filesize = new FileInfo(GamePath).Length * 0.000000000931; + + using (FileStream file = new FileStream(GamePath, FileMode.Open, FileAccess.Read)) { - Icon = GetGameIcon(GamePath), - Game = (Path.GetExtension(GamePath) == ".nro") ? "Application" : "", - Version = "", - DLC = (Path.GetExtension(GamePath) == ".nro") ? "N/A" : "", - TP = "", - LP = "", - FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB", - Path = GamePath, - }; - ApplicationLibraryData.Add(data); - } - } + Nca controlNca = null; + PartitionFileSystem PFS = null; + IFileSystem ControlFs = null; + string TitleName = null; + Gdk.Pixbuf GameIcon = null; - internal static Gdk.Pixbuf GetGameIcon(string filePath) - { - using (FileStream Input = File.Open(filePath, FileMode.Open, FileAccess.Read)) - { - BinaryReader Reader = new BinaryReader(Input); - - if ((Path.GetExtension(filePath) == ".nsp") || (Path.GetExtension(filePath) == ".pfs0")) { return RyujinxNSPIcon; } - - else if (Path.GetExtension(filePath) == ".xci") { return RyujinxXCIIcon; } - - else if (Path.GetExtension(filePath) == ".nca") { return RyujinxNCAIcon; } - - else if (Path.GetExtension(filePath) == ".nso") { return RyujinxNSOIcon; } - - else if (Path.GetExtension(filePath) == ".nro") - { - Input.Seek(24, SeekOrigin.Begin); - int AssetOffset = Reader.ReadInt32(); - - byte[] Read(long Position, int Size) + if ((Path.GetExtension(GamePath) == ".nsp") || (Path.GetExtension(GamePath) == ".pfs0")) { - Input.Seek(Position, SeekOrigin.Begin); - return Reader.ReadBytes(Size); + PFS = new PartitionFileSystem(file.AsStorage()); } - if (Encoding.ASCII.GetString(Read(AssetOffset, 4)) == "ASET") + else if (Path.GetExtension(GamePath) == ".xci") { - byte[] IconSectionInfo = Read(AssetOffset + 8, 0x10); - - long IconOffset = BitConverter.ToInt64(IconSectionInfo, 0); - long IconSize = BitConverter.ToInt64(IconSectionInfo, 8); - - byte[] IconData = Read(AssetOffset + IconOffset, (int)IconSize); - - return new Gdk.Pixbuf(IconData, 75, 75); + Xci xci = new Xci(MainMenu.device.System.KeySet, file.AsStorage()); + PFS = xci.OpenPartition(XciPartitionType.Secure); } - else { return RyujinxNROIcon; } + + if (PFS != null) + { + foreach (DirectoryEntry ticketEntry in PFS.EnumerateEntries("*.tik")) + { + Ticket ticket = new Ticket(PFS.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); + + if (!MainMenu.device.System.KeySet.TitleKeys.ContainsKey(ticket.RightsId)) + { + MainMenu.device.System.KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(MainMenu.device.System.KeySet)); + } + } + + foreach (DirectoryEntry fileEntry in PFS.EnumerateEntries("*.nca")) + { + Nca nca = new Nca(MainMenu.device.System.KeySet, PFS.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage()); + if (nca.Header.ContentType == ContentType.Control) + { + controlNca = nca; + } + } + + ControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, MainMenu.device.System.FsIntegrityCheckLevel); + } + + if ((Path.GetExtension(GamePath) == ".nca") || (Path.GetExtension(GamePath) == ".nro") || (Path.GetExtension(GamePath) == ".nso")) { TitleName = Path.GetFileName(GamePath); } + else + { + IFile controlFile = ControlFs.OpenFile("/control.nacp", OpenMode.Read); + Nacp ControlData = new Nacp(controlFile.AsStream()); + + TitleName = ControlData.Descriptions[(int)MainMenu.device.System.State.DesiredTitleLanguage].Title; + if (string.IsNullOrWhiteSpace(TitleName)) + { + TitleName = ControlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; + } + } + + if (Path.GetExtension(GamePath) == ".nca") { GameIcon = RyujinxNCAIcon; } + + else if ((Path.GetExtension(GamePath) == ".xci") || (Path.GetExtension(GamePath) == ".nsp") || (Path.GetExtension(GamePath) == ".pfs0")) + { + try + { + IFile logo = ControlFs.OpenFile($"/icon_{MainMenu.device.System.State.DesiredTitleLanguage}.dat", OpenMode.Read); + GameIcon = new Gdk.Pixbuf(logo.AsStream(), 75, 75); + } + catch(FileNotFoundException) + { + try + { + IFile logo = ControlFs.OpenFile($"/icon_AmericanEnglish.dat", OpenMode.Read); + GameIcon = new Gdk.Pixbuf(logo.AsStream(), 75, 75); + } + catch (FileNotFoundException) + { + if (Path.GetExtension(GamePath) == ".xci") { GameIcon = RyujinxXCIIcon; } + else { GameIcon = RyujinxNSPIcon; } + } + } + } + + else if (Path.GetExtension(GamePath) == ".nso") { GameIcon = RyujinxNSOIcon; } + + else if (Path.GetExtension(GamePath) == ".nro") + { + BinaryReader Reader = new BinaryReader(file); + + file.Seek(24, SeekOrigin.Begin); + int AssetOffset = Reader.ReadInt32(); + + byte[] Read(long Position, int Size) + { + file.Seek(Position, SeekOrigin.Begin); + return Reader.ReadBytes(Size); + } + + if (Encoding.ASCII.GetString(Read(AssetOffset, 4)) == "ASET") + { + byte[] IconSectionInfo = Read(AssetOffset + 8, 0x10); + + long IconOffset = BitConverter.ToInt64(IconSectionInfo, 0); + long IconSize = BitConverter.ToInt64(IconSectionInfo, 8); + + byte[] IconData = Read(AssetOffset + IconOffset, (int)IconSize); + + GameIcon = new Gdk.Pixbuf(IconData, 75, 75); + } + else { GameIcon = RyujinxNROIcon; } + } + + ApplicationData data = new ApplicationData() + { + Icon = GameIcon, + Game = TitleName, + TP = "", + LP = "", + FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB", + Path = GamePath, + }; + + ApplicationLibraryData.Add(data); } - - else { return RyujinxROMIcon; } } } } diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json index be81c3db45..f7ce6039c3 100644 --- a/Ryujinx/Config.json +++ b/Ryujinx/Config.json @@ -9,7 +9,7 @@ "logging_enable_fs_access_log": false, "logging_filtered_classes": [ ], "enable_file_log": true, - "system_language": "BritishEnglish", + "system_language": "AmericanEnglish", "docked_mode": false, "enable_discord_integration": true, "enable_vsync": true, diff --git a/Ryujinx/GUI/MainMenu.cs b/Ryujinx/GUI/MainMenu.cs index 420a36397f..01290ec766 100644 --- a/Ryujinx/GUI/MainMenu.cs +++ b/Ryujinx/GUI/MainMenu.cs @@ -25,7 +25,7 @@ namespace Ryujinx private static IAalOutput audioOut; - private static HLE.Switch device { get; set; } + internal static HLE.Switch device { get; set; } private static Application gtkapp { get; set; } @@ -61,16 +61,16 @@ namespace Ryujinx if (DiscordIntegrationEnabled) { - DiscordClient = new DiscordRpcClient("568815339807309834"); + DiscordClient = new DiscordRpcClient("568815339807309834"); DiscordPresence = new RichPresence { Assets = new Assets { - LargeImageKey = "ryujinx", + LargeImageKey = "ryujinx", LargeImageText = "Ryujinx is an emulator for the Nintendo Switch" }, - Details = "Main Menu", - State = "Idling", + Details = "Main Menu", + State = "Idling", Timestamps = new Timestamps(DateTime.UtcNow) }; @@ -105,15 +105,13 @@ namespace Ryujinx NFC.Sensitive = false; GameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 0); - //GameTable.AppendColumn("Game", new CellRendererText(), "text", 1); - //GameTable.AppendColumn("Version", new CellRendererText(), "text", 2); - //GameTable.AppendColumn("DLC", new CellRendererText(), "text", 3); - //GameTable.AppendColumn("Time Played", new CellRendererText(), "text", 4); - //GameTable.AppendColumn("Last Played", new CellRendererText(), "text", 5); - GameTable.AppendColumn("File Size", new CellRendererText(), "text", 6); - GameTable.AppendColumn("Path", new CellRendererText(), "text", 7); + GameTable.AppendColumn("Game", new CellRendererText(), "text", 1); + //GameTable.AppendColumn("Time Played", new CellRendererText(), "text", 2); + //GameTable.AppendColumn("Last Played", new CellRendererText(), "text", 3); + GameTable.AppendColumn("File Size", new CellRendererText(), "text", 4); + GameTable.AppendColumn("Path", new CellRendererText(), "text", 5); - TableStore = new ListStore(typeof(Gdk.Pixbuf), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string)); + TableStore = new ListStore(typeof(Gdk.Pixbuf), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string)); GameTable.Model = TableStore; UpdateGameTable(); @@ -127,13 +125,13 @@ namespace Ryujinx foreach (ApplicationLibrary.ApplicationData AppData in ApplicationLibrary.ApplicationLibraryData) { - TableStore.AppendValues(AppData.Icon, AppData.Game, AppData.Version, AppData.DLC, AppData.TP, AppData.LP, AppData.FileSize, AppData.Path); + TableStore.AppendValues(AppData.Icon, AppData.Game, AppData.TP, AppData.LP, AppData.FileSize, AppData.Path); } } public static void ApplyTheme() { - var settings = Settings.Default; + Settings settings = Settings.Default; settings.XftRgba = "rgb"; settings.XftDpi = 96; settings.XftHinting = 1; @@ -232,7 +230,7 @@ namespace Ryujinx private void Row_Activated(object obj, RowActivatedArgs args) { TableStore.GetIter(out TreeIter treeiter, new TreePath(args.Path.ToString())); - string path = (string)TableStore.GetValue(treeiter, 7); + string path = (string)TableStore.GetValue(treeiter, 5); LoadApplication(path); @@ -349,7 +347,7 @@ namespace Ryujinx private void Settings_Pressed(object o, EventArgs args) { - var SettingsWin = new SwitchSettings(device); + SwitchSettings SettingsWin = new SwitchSettings(device); gtkapp.Register(GLib.Cancellable.Current); gtkapp.AddWindow(SettingsWin); SettingsWin.Show(); @@ -375,7 +373,7 @@ namespace Ryujinx private void About_Pressed(object o, EventArgs args) { - var AboutWin = new AboutWindow(); + AboutWindow AboutWin = new AboutWindow(); gtkapp.Register(GLib.Cancellable.Current); gtkapp.AddWindow(AboutWin); AboutWin.Show(); diff --git a/Ryujinx/GUI/SwitchSettings.cs b/Ryujinx/GUI/SwitchSettings.cs index 437ca35c09..ce2131390f 100644 --- a/Ryujinx/GUI/SwitchSettings.cs +++ b/Ryujinx/GUI/SwitchSettings.cs @@ -13,9 +13,9 @@ namespace Ryujinx { public class SwitchSettings : Window { - internal static Configuration SwitchConfig { get; private set; } + internal static Configuration SwitchConfig { get; set; } - private HLE.Switch device { get; set; } + internal HLE.Switch device { get; set; } private static ListStore GameDirsBoxStore { get; set; } diff --git a/Ryujinx/GUI/assets/ryujinxROMIcon.png b/Ryujinx/GUI/assets/ryujinxROMIcon.png deleted file mode 100644 index b2f8abf532..0000000000 Binary files a/Ryujinx/GUI/assets/ryujinxROMIcon.png and /dev/null differ diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 82370961ec..9ce38b3d41 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -1,4 +1,5 @@ -using Ryujinx.Common.Logging; +using Gtk; +using Ryujinx.Common.Logging; using Ryujinx.Profiler; using System; using System.IO; @@ -11,9 +12,8 @@ namespace Ryujinx { Console.Title = "Ryujinx Console"; - string parentDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).ToString(); string systemPATH = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); - Environment.SetEnvironmentVariable("Path", $"{Path.Combine(parentDir, "bin")};{systemPATH}"); + Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPATH}"); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; @@ -22,16 +22,16 @@ namespace Ryujinx ApplicationLibrary.Init(); - Gtk.Application.Init(); + Application.Init(); - var gtkapp = new Gtk.Application("Ryujinx.Ryujinx", GLib.ApplicationFlags.None); - var win = new MainMenu(args, gtkapp); + Application gtkapp = new Application("Ryujinx.Ryujinx", GLib.ApplicationFlags.None); + MainMenu win = new MainMenu(args, gtkapp); gtkapp.Register(GLib.Cancellable.Current); gtkapp.AddWindow(win); win.Show(); - Gtk.Application.Run(); + Application.Run(); } private static void CurrentDomain_ProcessExit(object sender, EventArgs e) diff --git a/Ryujinx/RPsupported.dat b/Ryujinx/RPsupported.dat index ad2d715df0..5d2bada965 100644 --- a/Ryujinx/RPsupported.dat +++ b/Ryujinx/RPsupported.dat @@ -35,4 +35,5 @@ 0100e9f00b882000 0100eab00605c000 0100efd00a4fa000 -0100f6a00a684000 \ No newline at end of file +0100f6a00a684000 +0100f9f00c696000 \ No newline at end of file diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index eb76dcd586..7ed6bd76dc 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -18,13 +18,6 @@ false - - - - - - - @@ -36,7 +29,6 @@ -