Added LastPlayed and TimePlayed columns to the game list

This commit is contained in:
Xpl0itR 2019-07-01 13:07:35 +01:00
parent e1ba904108
commit 708a518e10
No known key found for this signature in database
GPG key ID: 91798184109676AD
9 changed files with 149 additions and 36 deletions

View file

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

View file

@ -3,7 +3,7 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Npdm
{
class Acid
public 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
{
class FsAccessControl
public 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
{
class FsAccessHeader
public 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
{
class KernelAccessControl
public class KernelAccessControl
{
public int[] Capabilities { get; private set; }

View file

@ -4,10 +4,10 @@ using System.Text;
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
class 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
{
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;

View file

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

View file

@ -2,6 +2,7 @@
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;
@ -25,6 +26,7 @@ namespace Ryujinx
{
public Gdk.Pixbuf Icon;
public string GameName;
public string GameId;
public string TimePlayed;
public string LastPlayed;
public string FileSize;
@ -72,10 +74,14 @@ namespace Ryujinx
using (FileStream file = new FileStream(GamePath, 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"))
@ -104,13 +110,45 @@ namespace Ryujinx
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)
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")) { TitleName = Path.GetFileName(GamePath); }
@ -183,8 +221,9 @@ namespace Ryujinx
{
Icon = GameIcon,
GameName = TitleName,
TimePlayed = "",
LastPlayed = "",
GameId = TitleId,
TimePlayed = GetPlayedData(TitleId)[0],
LastPlayed = GetPlayedData(TitleId)[1],
FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB",
Path = GamePath,
};
@ -193,5 +232,45 @@ namespace Ryujinx
}
}
}
private static string[] GetPlayedData(string TitleId)
{
string[] playedData = new string[2];
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string savePath = Path.Combine(appdataPath, "RyuFs", "nand", "user", "save", "0000000000000000", "savecommon", TitleId);
if (File.Exists(Path.Combine(savePath, "TimePlayed.dat")) == false)
{
Directory.CreateDirectory(savePath);
using (FileStream file = File.OpenWrite(Path.Combine(savePath, "TimePlayed.dat"))) { file.Write(Encoding.ASCII.GetBytes("0")); }
}
using (FileStream fs = File.OpenRead(Path.Combine(savePath, "TimePlayed.dat")))
{
using (StreamReader sr = new StreamReader(fs))
{
float timePlayed = float.Parse(sr.ReadLine());
if (timePlayed <= 60.0) { playedData[0] = $"{timePlayed}s"; }
else if(timePlayed <= 3600.0) { playedData[0] = $"{Math.Round(timePlayed / 60 , 2, MidpointRounding.AwayFromZero)}mins"; }
else if(timePlayed <= 86400.0) { playedData[0] = $"{Math.Round(timePlayed / 3600 , 2, MidpointRounding.AwayFromZero)}hrs"; }
else { playedData[0] = $"{Math.Round(timePlayed / 86400, 2, MidpointRounding.AwayFromZero)}days"; }
}
}
if (File.Exists(Path.Combine(savePath, "LastPlayed.dat")) == false)
{
Directory.CreateDirectory(savePath);
using (FileStream file = File.OpenWrite(Path.Combine(savePath, "LastPlayed.dat"))) { file.Write(Encoding.ASCII.GetBytes("Never")); }
}
using (FileStream fs = File.OpenRead(Path.Combine(savePath, "LastPlayed.dat")))
{
using (StreamReader sr = new StreamReader(fs))
{
playedData[1] = sr.ReadLine();
}
}
return playedData;
}
}
}

View file

@ -99,12 +99,12 @@ namespace Ryujinx
Nfc.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("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.Model = _TableStore;
@ -120,7 +120,7 @@ namespace Ryujinx
foreach (ApplicationLibrary.ApplicationData AppData in ApplicationLibrary.ApplicationLibraryData)
{
_TableStore.AppendValues(AppData.Icon, AppData.GameName, AppData.TimePlayed, AppData.LastPlayed, AppData.FileSize, AppData.Path);
_TableStore.AppendValues(AppData.Icon, $"{AppData.GameName}\n{AppData.GameId.ToUpper()}", AppData.TimePlayed, AppData.LastPlayed, AppData.FileSize, AppData.Path);
}
}
@ -157,7 +157,7 @@ namespace Ryujinx
{
if (_GameLoaded)
{
MessageDialog eRrOr = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, "A game has already been loaded, please close the game and try again");
MessageDialog eRrOr = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, "A game has already been loaded, please unload the game and try again");
eRrOr.SetSizeRequest(100, 20);
eRrOr.Title = "Ryujinx - Error";
eRrOr.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.GUI.assets.ryujinxIcon.png");
@ -235,10 +235,20 @@ namespace Ryujinx
DiscordPresence.Assets.LargeImageText = _device.System.TitleName;
DiscordPresence.Assets.SmallImageKey = "ryujinx";
DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch";
DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
DiscordPresence.Timestamps = new Timestamps(DateTime.UnixEpoch);
DiscordClient.SetPresence(DiscordPresence);
}
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")))
{
using (StreamWriter sr = new StreamWriter(fs))
{
sr.WriteLine(DateTime.UtcNow);
}
}
}
private static void CreateGameWindow()
@ -254,6 +264,42 @@ namespace Ryujinx
_audioOut.Dispose();
}
}
private static void End()
{
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string savePath = System.IO.Path.Combine(appdataPath, "RyuFs", "nand", "user", "save", "0000000000000000", "savecommon", _device.System.TitleID);
double currentPlayTime = 0;
using (FileStream fs = File.OpenRead(System.IO.Path.Combine(savePath, "LastPlayed.dat")))
{
using (StreamReader sr = new StreamReader(fs))
{
DateTime startTime = DateTime.Parse(sr.ReadLine());
using (FileStream lpfs = File.OpenRead(System.IO.Path.Combine(savePath, "TimePlayed.dat")))
{
using (StreamReader lpsr = new StreamReader(lpfs))
{
currentPlayTime = double.Parse(lpsr.ReadLine());
}
}
using (FileStream tpfs = File.OpenWrite(System.IO.Path.Combine(savePath, "TimePlayed.dat")))
{
using (StreamWriter tpsr = new StreamWriter(tpfs))
{
tpsr.WriteLine(currentPlayTime + Math.Round(DateTime.UtcNow.Subtract(startTime).TotalSeconds, MidpointRounding.AwayFromZero));
}
}
}
}
_audioOut.Dispose();
DiscordClient.Dispose();
Logger.Shutdown();
Environment.Exit(0);
}
//Events
private void Row_Activated(object obj, RowActivatedArgs args)
@ -303,21 +349,9 @@ namespace Ryujinx
fc.Destroy();
}
private void Exit_Pressed(object o, EventArgs args)
{
_audioOut.Dispose();
DiscordClient.Dispose();
Logger.Shutdown();
Environment.Exit(0);
}
private void Exit_Pressed(object o, EventArgs args) { End(); }
private void Window_Close(object obj, DeleteEventArgs args)
{
_audioOut.Dispose();
DiscordClient.Dispose();
Logger.Shutdown();
Environment.Exit(0);
}
private void Window_Close(object obj, DeleteEventArgs args) { End(); }
private void ReturnMain_Pressed(object o, EventArgs args)
{