Large changes

Rewrote ApplicationLibrary.cs (added comments too) so any devs reading it wont get eye cancer, also its probably more efficient now. Added 2 new columns (Developer name and application version) to the game list and wrote the logic for it. Ryujinx now loads NRO's TitleName and TitleID from the NACP file instead of the default NPDM. I also killed a lot of bugs
This commit is contained in:
Xpl0itR 2019-07-07 18:13:28 +01:00
parent fd5853fc24
commit bf82c5f5d5
No known key found for this signature in database
GPG key ID: 91798184109676AD
17 changed files with 269 additions and 193 deletions

View file

@ -10,7 +10,7 @@ namespace Ryujinx.HLE.FileSystem
public static string GetSavePath(SaveInfo saveMetaData, ServiceCtx context)
{
string baseSavePath = NandPath;
long currentTitleId = saveMetaData.TitleId;
ulong currentTitleId = saveMetaData.TitleId;
switch (saveMetaData.SaveSpaceId)
{

View file

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.FileSystem
{
struct SaveInfo
{
public long TitleId { get; private set; }
public ulong TitleId { get; private set; }
public long SaveId { get; private set; }
public UInt128 UserId { get; private set; }
@ -12,7 +12,7 @@ namespace Ryujinx.HLE.FileSystem
public SaveSpaceId SaveSpaceId { get; private set; }
public SaveInfo(
long titleId,
ulong titleId,
long saveId,
SaveDataType saveDataType,
UInt128 userId,

View file

@ -549,18 +549,35 @@ namespace Ryujinx.HLE.HOS
if (asetVersion == 0)
{
ulong iconOffset = reader.ReadUInt64();
ulong iconSize = reader.ReadUInt64();
ulong iconSize = reader.ReadUInt64();
ulong nacpOffset = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
ulong romfsOffset = reader.ReadUInt64();
ulong romfsSize = reader.ReadUInt64();
ulong romfsSize = reader.ReadUInt64();
if (romfsSize != 0)
{
Device.FileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset));
}
if (nacpSize != 0)
{
input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin);
using (MemoryStream stream = new MemoryStream(reader.ReadBytes((int)nacpSize))) { ControlData = new Nacp(stream); }
metaData.TitleName = ControlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
if (string.IsNullOrWhiteSpace(metaData.TitleName))
{
metaData.TitleName = ControlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
}
metaData.Aci0.TitleId = ControlData.PresenceGroupId;
if (metaData.Aci0.TitleId == 0) { metaData.Aci0.TitleId = ControlData.SaveDataOwnerId; }
if (metaData.Aci0.TitleId == 0) { metaData.Aci0.TitleId = ControlData.AddOnContentBaseId - 0x1000; }
if (metaData.Aci0.TitleId.ToString("x16") == "fffffffffffff000") { metaData.Aci0.TitleId = 0000000000000000; }
}
}
else
{
@ -574,10 +591,10 @@ namespace Ryujinx.HLE.HOS
staticObject = new NxStaticObject(input);
}
ContentManager.LoadEntries();
TitleName = CurrentTitle = metaData.TitleName;
TitleID = metaData.Aci0.TitleId.ToString("x16");
TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
TitleName = metaData.TitleName;
ContentManager.LoadEntries();
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
}

View file

@ -60,8 +60,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public KProcessCapabilities Capabilities { get; private set; }
public long TitleId { get; private set; }
public long Pid { get; private set; }
public ulong TitleId { get; private set; }
public long Pid { get; private set; }
private long _creationTimestamp;
private ulong _entrypoint;

View file

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public string Name { get; private set; }
public int Category { get; private set; }
public long TitleId { get; private set; }
public ulong TitleId { get; private set; }
public ulong CodeAddress { get; private set; }
public int CodePagesCount { get; private set; }
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public ProcessCreationInfo(
string name,
int category,
long titleId,
ulong titleId,
ulong codeAddress,
int codePagesCount,
int mmuFlags,

View file

@ -285,7 +285,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
break;
case 18: value = process.TitleId; break;
case 18: value = (long)process.TitleId; break;
case 20: value = (long)process.UserExceptionContextAddress; break;

View file

@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
{
SaveSpaceId saveSpaceId = (SaveSpaceId)context.RequestData.ReadInt64();
long titleId = context.RequestData.ReadInt64();
ulong titleId = context.RequestData.ReadUInt64();
UInt128 userId = context.RequestData.ReadStruct<UInt128>();

View file

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.Loaders.Executables
{
public string Name { get; private set; }
public long TitleId { get; private set; }
public ulong TitleId { get; private set; }
public int ProcessCategory { get; private set; }
@ -65,7 +65,7 @@ namespace Ryujinx.HLE.Loaders.Executables
Name = ReadString(reader, 12);
TitleId = reader.ReadInt64();
TitleId = reader.ReadUInt64();
ProcessCategory = reader.ReadInt32();

View file

@ -3,11 +3,11 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Npdm
{
public class Aci0
class Aci0
{
private const int Aci0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
public long TitleId { get; private set; }
public ulong TitleId { get; set; }
public int FsVersion { get; private set; }
public ulong FsPermissionsBitmask { get; private set; }
@ -28,7 +28,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
stream.Seek(0xc, SeekOrigin.Current);
TitleId = reader.ReadInt64();
TitleId = reader.ReadUInt64();
// Reserved.
stream.Seek(8, SeekOrigin.Current);

View file

@ -3,7 +3,7 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Npdm
{
public class Acid
class Acid
{
private const int AcidMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;

View file

@ -2,7 +2,7 @@
namespace Ryujinx.HLE.Loaders.Npdm
{
public class FsAccessControl
class FsAccessControl
{
public int Version { get; private set; }
public ulong PermissionsBitmask { get; private set; }

View file

@ -4,7 +4,7 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Npdm
{
public class FsAccessHeader
class FsAccessHeader
{
public int Version { get; private set; }
public ulong PermissionsBitmask { get; private set; }

View file

@ -2,7 +2,7 @@
namespace Ryujinx.HLE.Loaders.Npdm
{
public class KernelAccessControl
class KernelAccessControl
{
public int[] Capabilities { get; private set; }

View file

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
//https://github.com/SciresM/hactool/blob/master/npdm.c
//https://github.com/SciresM/hactool/blob/master/npdm.h
//http://switchbrew.org/index.php?title=NPDM
public class Npdm
class Npdm
{
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int PersonalMmHeapSize { get; private set; }
public int ProcessCategory { get; private set; }
public int MainThreadStackSize { get; private set; }
public string TitleName { get; private set; }
public string TitleName { get; set; }
public byte[] ProductCode { get; private set; }
public Aci0 Aci0 { get; private set; }

View file

@ -5,7 +5,7 @@ using System.Text;
namespace Ryujinx.HLE.Loaders.Npdm
{
public class ServiceAccessControl
class ServiceAccessControl
{
public IReadOnlyDictionary<string, bool> Services { get; private set; }

View file

@ -2,7 +2,6 @@
using LibHac.Fs;
using LibHac.Fs.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Loaders.Npdm;
using System;
using System.Collections.Generic;
using System.IO;
@ -14,45 +13,68 @@ namespace Ryujinx
{
public class ApplicationLibrary
{
public static Gdk.Pixbuf RyujinxNspIcon { get; private set; }
public static Gdk.Pixbuf RyujinxXciIcon { get; private set; }
public static Gdk.Pixbuf RyujinxNcaIcon { get; private set; }
public static Gdk.Pixbuf RyujinxNroIcon { get; private set; }
public static Gdk.Pixbuf RyujinxNsoIcon { get; private set; }
private static Keyset KeySet;
public static byte[] RyujinxNspIcon { get; private set; }
public static byte[] RyujinxXciIcon { get; private set; }
public static byte[] RyujinxNcaIcon { get; private set; }
public static byte[] RyujinxNroIcon { get; private set; }
public static byte[] RyujinxNsoIcon { get; private set; }
public static List<ApplicationData> ApplicationLibraryData { get; private set; }
public struct ApplicationData
{
public Gdk.Pixbuf Icon;
public string GameName;
public string GameId;
public string TimePlayed;
public string LastPlayed;
public string FileSize;
public string Path;
public byte[] Icon;
public string TitleName;
public string TitleId;
public string Developer;
public string Version;
public string TimePlayed;
public string LastPlayed;
public string FileSize;
public string Path;
}
public static void Init()
public static void Init(Keyset keySet, HLE.HOS.SystemState.TitleLanguage DesiredTitleLanguage)
{
RyujinxNspIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxNSPIcon.png", 75, 75);
RyujinxXciIcon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxXCIIcon.png", 75, 75);
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);
// Load keyset
KeySet = keySet;
List<string> Games = new List<string>();
foreach (string GameDir in SwitchSettings.SwitchConfig.GameDirs)
// Loads the default application Icons
using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.GUI.assets.ryujinxNSPIcon.png"))
{
if (Directory.Exists(GameDir) == false)
using (MemoryStream ms = new MemoryStream()) { resourceStream.CopyTo(ms); RyujinxNspIcon = ms.ToArray(); }
}
using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.GUI.assets.ryujinxXCIIcon.png"))
{
using (MemoryStream ms = new MemoryStream()) { resourceStream.CopyTo(ms); RyujinxXciIcon = ms.ToArray(); }
}
using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.GUI.assets.ryujinxNCAIcon.png"))
{
using (MemoryStream ms = new MemoryStream()) { resourceStream.CopyTo(ms); RyujinxNcaIcon = ms.ToArray(); }
}
using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.GUI.assets.ryujinxNROIcon.png"))
{
using(MemoryStream ms = new MemoryStream()) { resourceStream.CopyTo(ms); RyujinxNroIcon = ms.ToArray(); }
}
using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.GUI.assets.ryujinxNSOIcon.png"))
{
using (MemoryStream ms = new MemoryStream()) { resourceStream.CopyTo(ms); RyujinxNsoIcon = ms.ToArray(); }
}
// Builds the applications list with paths to found applications
List<string> applications = new List<string>();
foreach (string gameDir in SwitchSettings.SwitchConfig.GameDirs)
{
if (Directory.Exists(gameDir) == false)
{
Logger.PrintError(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{GameDir}\"");
Logger.PrintError(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{gameDir}\"");
continue;
}
DirectoryInfo GameDirInfo = new DirectoryInfo(GameDir);
DirectoryInfo GameDirInfo = new DirectoryInfo(gameDir);
foreach (var Game in GameDirInfo.GetFiles())
{
if ((Path.GetExtension(Game.ToString()) == ".xci") ||
@ -62,182 +84,204 @@ namespace Ryujinx
(Path.GetExtension(Game.ToString()) == ".nro") ||
(Path.GetExtension(Game.ToString()) == ".nso"))
{
Games.Add(Game.ToString());
applications.Add(Game.ToString());
}
}
}
// Loops through applications list, creating a struct for each application and then adding the struct to a list of structs
ApplicationLibraryData = new List<ApplicationData>();
foreach (string GamePath in Games)
foreach (string applicationPath in applications)
{
double filesize = new FileInfo(GamePath).Length * 0.000000000931;
double filesize = new FileInfo(applicationPath).Length * 0.000000000931;
string titleName = null;
string titleId = null;
string developer = null;
string version = null;
byte[] applicationIcon = null;
using (FileStream file = new FileStream(GamePath, FileMode.Open, FileAccess.Read))
using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read))
{
Nca mainNca = null;
Nca patchNca = null;
Nca controlNca = null;
PartitionFileSystem pfs = null;
IFileSystem controlFs = null;
Npdm metaData = null;
string TitleName = null;
string TitleId = "010000000000100D";
Gdk.Pixbuf GameIcon = null;
if ((Path.GetExtension(GamePath) == ".nsp") || (Path.GetExtension(GamePath) == ".pfs0"))
if ((Path.GetExtension(applicationPath) == ".nsp") || (Path.GetExtension(applicationPath) == ".pfs0") || (Path.GetExtension(applicationPath) == ".xci"))
{
pfs = new PartitionFileSystem(file.AsStorage());
}
IFileSystem controlFs = null;
else if (Path.GetExtension(GamePath) == ".xci")
{
Xci xci = new Xci(MainWindow._device.System.KeySet, file.AsStorage());
pfs = xci.OpenPartition(XciPartitionType.Secure);
}
if (pfs != null)
{
foreach (DirectoryEntry ticketEntry in pfs.EnumerateEntries("*.tik"))
// Store the ControlFS in variable called controlFs
if (Path.GetExtension(applicationPath) == ".xci")
{
Ticket ticket = new Ticket(pfs.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream());
if (!MainWindow._device.System.KeySet.TitleKeys.ContainsKey(ticket.RightsId))
{
MainWindow._device.System.KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(MainWindow._device.System.KeySet));
}
Xci xci = new Xci(MainWindow._device.System.KeySet, file.AsStorage());
controlFs = GetControlFs(xci.OpenPartition(XciPartitionType.Secure));
}
else { controlFs = GetControlFs(new PartitionFileSystem(file.AsStorage())); }
foreach (DirectoryEntry fileEntry in pfs.EnumerateEntries("*.nca"))
{
Nca nca = new Nca(MainWindow._device.System.KeySet, pfs.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage());
if (nca.Header.ContentType == ContentType.Program)
{
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, ContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}
else
{
mainNca = nca;
}
}
else if (nca.Header.ContentType == ContentType.Control)
{
controlNca = nca;
}
}
controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, MainWindow._device.System.FsIntegrityCheckLevel);
if (patchNca == null)
{
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
IFileSystem codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, MainWindow._device.System.FsIntegrityCheckLevel);
metaData = new Npdm(codeFs.OpenFile("/main.npdm", OpenMode.Read).AsStream());
TitleId = metaData.Aci0.TitleId.ToString("x16");
}
}
else
{
if (patchNca.CanOpenSection(NcaSectionType.Code))
{
IFileSystem codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, MainWindow._device.System.FsIntegrityCheckLevel);
metaData = new Npdm(codeFs.OpenFile("/main.npdm", OpenMode.Read).AsStream());
TitleId = metaData.Aci0.TitleId.ToString("x16");
}
}
}
if ((Path.GetExtension(GamePath) == ".nca") || (Path.GetExtension(GamePath) == ".nro") || (Path.GetExtension(GamePath) == ".nso"))
{
StringBuilder titleName = new StringBuilder();
titleName.Append(Path.GetFileName(GamePath));
titleName.Remove(Path.GetFileName(GamePath).Length - Path.GetExtension(GamePath).Length, Path.GetExtension(GamePath).Length);
TitleName = titleName.ToString();
}
else
{
// Creates NACP class from the NACP file
IFile controlFile = controlFs.OpenFile("/control.nacp", OpenMode.Read);
Nacp controlData = new Nacp(controlFile.AsStream());
TitleName = controlData.Descriptions[(int)MainWindow._device.System.State.DesiredTitleLanguage].Title;
if (string.IsNullOrWhiteSpace(TitleName))
// Get the title name, title ID, developer name and version number from the NACP
version = controlData.DisplayVersion;
titleName = controlData.Descriptions[(int)MainWindow._device.System.State.DesiredTitleLanguage].Title;
if (string.IsNullOrWhiteSpace(titleName))
{
TitleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
}
}
if (Path.GetExtension(GamePath) == ".nca") { GameIcon = RyujinxNcaIcon; }
titleId = controlData.PresenceGroupId.ToString("X16");
if (string.IsNullOrWhiteSpace(titleId)) { titleId = controlData.SaveDataOwnerId.ToString("X16"); }
if (string.IsNullOrWhiteSpace(titleId)) { titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("X16"); }
else if ((Path.GetExtension(GamePath) == ".xci") || (Path.GetExtension(GamePath) == ".nsp") || (Path.GetExtension(GamePath) == ".pfs0"))
{
developer = controlData.Descriptions[(int)MainWindow._device.System.State.DesiredTitleLanguage].Developer;
if (string.IsNullOrWhiteSpace(developer))
{
developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer;
}
// Read the icon from the ControlFS and store it as a byte array
try
{
IFile logo = controlFs.OpenFile($"/icon_{MainWindow._device.System.State.DesiredTitleLanguage}.dat", OpenMode.Read);
GameIcon = new Gdk.Pixbuf(logo.AsStream(), 75, 75);
IFile logo = controlFs.OpenFile($"/icon_{DesiredTitleLanguage}.dat", OpenMode.Read);
using (MemoryStream ms = new MemoryStream())
{
logo.AsStream().CopyTo(ms);
applicationIcon = ms.ToArray();
}
}
catch(FileNotFoundException)
catch (FileNotFoundException)
{
try
{
IFile logo = controlFs.OpenFile($"/icon_AmericanEnglish.dat", OpenMode.Read);
GameIcon = new Gdk.Pixbuf(logo.AsStream(), 75, 75);
using (MemoryStream ms = new MemoryStream())
{
logo.AsStream().CopyTo(ms);
applicationIcon = ms.ToArray();
}
}
catch (FileNotFoundException)
{
if (Path.GetExtension(GamePath) == ".xci") { GameIcon = RyujinxXciIcon; }
else { GameIcon = RyujinxNspIcon; }
if (Path.GetExtension(applicationPath) == ".xci") { applicationIcon = RyujinxXciIcon; }
else { applicationIcon = RyujinxNspIcon; }
}
}
}
else if (Path.GetExtension(GamePath) == ".nso") { GameIcon = RyujinxNsoIcon; }
else if (Path.GetExtension(GamePath) == ".nro")
else if (Path.GetExtension(applicationPath) == ".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);
}
file.Seek(24, SeekOrigin.Begin);
int AssetOffset = reader.ReadInt32();
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);
long iconOffset = BitConverter.ToInt64(IconSectionInfo, 0);
long iconSize = BitConverter.ToInt64(IconSectionInfo, 8);
byte[] IconData = Read(AssetOffset + IconOffset, (int)IconSize);
ulong nacpOffset = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
GameIcon = new Gdk.Pixbuf(IconData, 75, 75);
// Reads and stores game icon as byte array
applicationIcon = Read(AssetOffset + iconOffset, (int)iconSize);
// Creates memory stream out of byte array which is the NACP
using (MemoryStream stream = new MemoryStream(Read(AssetOffset + (int)nacpOffset, (int)nacpSize)))
{
// Creates NACP class from the memory stream
Nacp controlData = new Nacp(stream);
// Get the title name, title ID, developer name and version number from the NACP
version = controlData.DisplayVersion;
titleName = controlData.Descriptions[(int)DesiredTitleLanguage].Title;
if (string.IsNullOrWhiteSpace(titleName))
{
titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
}
titleId = controlData.PresenceGroupId.ToString("X16");
if (string.IsNullOrWhiteSpace(titleId)) { titleId = controlData.SaveDataOwnerId.ToString("X16"); }
if (string.IsNullOrWhiteSpace(titleId)) { titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("X16"); }
developer = controlData.Descriptions[(int)DesiredTitleLanguage].Developer;
if (string.IsNullOrWhiteSpace(developer))
{
developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer;
}
}
}
else { GameIcon = RyujinxNroIcon; }
else { applicationIcon = RyujinxNroIcon; titleName = "Application"; titleId = "0000000000000000"; developer = "Unknown"; version = "?"; }
}
ApplicationData data = new ApplicationData()
// If its an NCA or NSO we just set defaults
else if ((Path.GetExtension(applicationPath) == ".nca") || (Path.GetExtension(applicationPath) == ".nso"))
{
Icon = GameIcon,
GameName = TitleName,
GameId = TitleId,
TimePlayed = GetPlayedData(TitleId)[0],
LastPlayed = GetPlayedData(TitleId)[1],
FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB",
Path = GamePath,
};
if (Path.GetExtension(applicationPath) == ".nca") { applicationIcon = RyujinxNcaIcon; }
else if (Path.GetExtension(applicationPath) == ".nso") { applicationIcon = RyujinxNsoIcon; }
ApplicationLibraryData.Add(data);
StringBuilder titlename = new StringBuilder();
titlename.Append(Path.GetFileName(applicationPath));
titlename.Remove(Path.GetFileName(applicationPath).Length - Path.GetExtension(applicationPath).Length, Path.GetExtension(applicationPath).Length);
titleName = titlename.ToString();
titleId = "0000000000000000";
version = "?";
developer = "Unknown";
}
}
ApplicationData data = new ApplicationData()
{
Icon = applicationIcon,
TitleName = titleName,
TitleId = titleId,
Developer = developer,
Version = version,
TimePlayed = GetPlayedData(titleId)[0],
LastPlayed = GetPlayedData(titleId)[1],
FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB",
Path = applicationPath,
};
ApplicationLibraryData.Add(data);
}
}
private static IFileSystem GetControlFs(PartitionFileSystem Pfs)
{
Nca controlNca = null;
// Add keys to keyset if needed
foreach (DirectoryEntry ticketEntry in Pfs.EnumerateEntries("*.tik"))
{
Ticket ticket = new Ticket(Pfs.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream());
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
{
KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(MainWindow._device.System.KeySet));
}
}
// Find the Control NCA and store it in variable called controlNca
foreach (DirectoryEntry fileEntry in Pfs.EnumerateEntries("*.nca"))
{
Nca nca = new Nca(MainWindow._device.System.KeySet, Pfs.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage());
if (nca.Header.ContentType == ContentType.Control)
{
controlNca = nca;
}
}
// Return the ControlFS
return controlNca.OpenFileSystem(NcaSectionType.Data, MainWindow._device.System.FsIntegrityCheckLevel);
}
private static string[] GetPlayedData(string TitleId)

View file

@ -10,6 +10,7 @@ using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Ryujinx
@ -58,7 +59,7 @@ namespace Ryujinx
Configuration.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
Configuration.InitialConfigure(_device);
ApplicationLibrary.Init();
ApplicationLibrary.Init(_device.System.KeySet, _device.System.State.DesiredTitleLanguage);
_gtkapp = gtkapp;
@ -97,12 +98,14 @@ namespace Ryujinx
Nfc.Sensitive = false;
ReturnMain.Sensitive = false;
GameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 0);
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));
GameTable.AppendColumn("Application", new CellRendererText(), "text", 1);
GameTable.AppendColumn("Developer", new CellRendererText(), "text", 2);
GameTable.AppendColumn("Version", 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);
_TableStore = new ListStore(typeof(Gdk.Pixbuf), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
GameTable.Model = _TableStore;
UpdateGameTable();
//Temporary code section end
@ -117,13 +120,15 @@ namespace Ryujinx
ReturnMain.Sensitive = false;
GameTable.AppendColumn("Icon" , new CellRendererPixbuf(), "pixbuf", 0);
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);
GameTable.AppendColumn("Application", new CellRendererText() , "text" , 1);
GameTable.AppendColumn("Developer" , new CellRendererText() , "text" , 2);
GameTable.AppendColumn("Version" , 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);
_TableStore = new ListStore(typeof(Gdk.Pixbuf), 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), typeof(string), typeof(string));
GameTable.Model = _TableStore;
UpdateGameTable();
@ -133,11 +138,11 @@ namespace Ryujinx
public static void UpdateGameTable()
{
_TableStore.Clear();
ApplicationLibrary.Init();
ApplicationLibrary.Init(_device.System.KeySet, _device.System.State.DesiredTitleLanguage);
foreach (ApplicationLibrary.ApplicationData AppData in ApplicationLibrary.ApplicationLibraryData)
{
_TableStore.AppendValues(AppData.Icon, $"{AppData.GameName}\n{AppData.GameId.ToUpper()}", AppData.TimePlayed, AppData.LastPlayed, AppData.FileSize, AppData.Path);
_TableStore.AppendValues(new Gdk.Pixbuf(AppData.Icon, 75, 75), $"{AppData.TitleName}\n{AppData.TitleId.ToUpper()}", AppData.Developer, AppData.Version, AppData.TimePlayed, AppData.LastPlayed, AppData.FileSize, AppData.Path);
}
}
@ -259,13 +264,23 @@ namespace Ryujinx
if (_device.System.TitleID != null)
{
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string savePath = System.IO.Path.Combine(appdataPath, "RyuFs", "nand", "user", "save", "0000000000000000", "savecommon", _device.System.TitleID);
using (FileStream fs = File.OpenWrite(System.IO.Path.Combine(savePath, "LastPlayed.dat")))
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string savePath = System.IO.Path.Combine(appdataPath, "RyuFs", "nand", "user", "save", "0000000000000000", "savecommon", _device.System.TitleID);
if (File.Exists(System.IO.Path.Combine(savePath, "TimePlayed.dat")) == false)
{
using (StreamWriter sr = new StreamWriter(fs))
Directory.CreateDirectory(savePath);
using (FileStream file = File.OpenWrite(System.IO.Path.Combine(savePath, "TimePlayed.dat"))) { file.Write(Encoding.ASCII.GetBytes("0")); }
}
if (File.Exists(System.IO.Path.Combine(savePath, "LastPlayed.dat")) == false)
{
Directory.CreateDirectory(savePath);
using (FileStream file = File.OpenWrite(System.IO.Path.Combine(savePath, "LastPlayed.dat")))
{
sr.WriteLine(DateTime.UtcNow);
using (StreamWriter sr = new StreamWriter(file))
{
sr.WriteLine(DateTime.UtcNow);
}
}
}
}
@ -325,7 +340,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, 5);
string path = (string)_TableStore.GetValue(treeiter, 7);
LoadApplication(path);