Added LastPlayed and TimePlayed columns to the game list
This commit is contained in:
parent
e1ba904108
commit
708a518e10
9 changed files with 149 additions and 36 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Ryujinx.HLE.Loaders.Npdm
|
||||
{
|
||||
class KernelAccessControl
|
||||
public class KernelAccessControl
|
||||
{
|
||||
public int[] Capabilities { get; private set; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue