Przeglądaj źródła

UI: Fix applications times (#4294)

* Fix applications times

* Add spaces

* Fix TimeString formatting
Ac_K 3 lat temu
rodzic
commit
64263c5218
1 zmienionych plików z 297 dodań i 313 usunięć
  1. 297 313
      Ryujinx.Ui.Common/App/ApplicationLibrary.cs

+ 297 - 313
Ryujinx.Ui.Common/App/ApplicationLibrary.cs

@@ -38,9 +38,9 @@ namespace Ryujinx.Ui.App.Common
         private readonly byte[] _nroIcon;
         private readonly byte[] _nroIcon;
         private readonly byte[] _nsoIcon;
         private readonly byte[] _nsoIcon;
 
 
-        private VirtualFileSystem       _virtualFileSystem;
-        private Language                _desiredTitleLanguage;
-        private CancellationTokenSource _cancellationToken;
+        private readonly VirtualFileSystem _virtualFileSystem;
+        private Language                   _desiredTitleLanguage;
+        private CancellationTokenSource    _cancellationToken;
 
 
         public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
         public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
         {
         {
@@ -53,7 +53,7 @@ namespace Ryujinx.Ui.App.Common
             _nsoIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSO.png");
             _nsoIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSO.png");
         }
         }
 
 
-        private byte[] GetResourceBytes(string resourceName)
+        private static byte[] GetResourceBytes(string resourceName)
         {
         {
             Stream resourceStream    = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
             Stream resourceStream    = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
             byte[] resourceByteArray = new byte[resourceStream.Length];
             byte[] resourceByteArray = new byte[resourceStream.Length];
@@ -68,9 +68,9 @@ namespace Ryujinx.Ui.App.Common
             _cancellationToken?.Cancel();
             _cancellationToken?.Cancel();
         }
         }
 
 
-        public void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
+        public static void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
         {
         {
-            using var controlFile = new UniqueRef<IFile>();
+            using UniqueRef<IFile> controlFile = new();
 
 
             controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
             controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
             controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
             controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
@@ -86,7 +86,7 @@ namespace Ryujinx.Ui.App.Common
             _cancellationToken = new CancellationTokenSource();
             _cancellationToken = new CancellationTokenSource();
 
 
             // Builds the applications list with paths to found applications
             // Builds the applications list with paths to found applications
-            List<string> applications = new List<string>();
+            List<string> applications = new();
 
 
             try
             try
             {
             {
@@ -143,251 +143,246 @@ namespace Ryujinx.Ui.App.Common
                     string version = "0";
                     string version = "0";
                     byte[] applicationIcon = null;
                     byte[] applicationIcon = null;
 
 
-                    BlitStruct<ApplicationControlProperty> controlHolder = new BlitStruct<ApplicationControlProperty>(1);
+                    BlitStruct<ApplicationControlProperty> controlHolder = new(1);
 
 
                     try
                     try
                     {
                     {
                         string extension = Path.GetExtension(applicationPath).ToLower();
                         string extension = Path.GetExtension(applicationPath).ToLower();
 
 
-                        using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read))
+                        using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read);
+
+                        if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
                         {
                         {
-                            if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
+                            try
                             {
                             {
-                                try
-                                {
-                                    PartitionFileSystem pfs;
+                                PartitionFileSystem pfs;
 
 
-                                    bool isExeFs = false;
+                                bool isExeFs = false;
 
 
-                                    if (extension == ".xci")
-                                    {
-                                        Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
+                                if (extension == ".xci")
+                                {
+                                    Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
 
 
-                                        pfs = xci.OpenPartition(XciPartitionType.Secure);
-                                    }
-                                    else
-                                    {
-                                        pfs = new PartitionFileSystem(file.AsStorage());
+                                    pfs = xci.OpenPartition(XciPartitionType.Secure);
+                                }
+                                else
+                                {
+                                    pfs = new PartitionFileSystem(file.AsStorage());
 
 
-                                        // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application.
-                                        bool hasMainNca = false;
+                                    // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application.
+                                    bool hasMainNca = false;
 
 
-                                        foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
+                                    foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
+                                    {
+                                        if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca")
                                         {
                                         {
-                                            if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca")
-                                            {
-                                                using var ncaFile = new UniqueRef<IFile>();
+                                            using UniqueRef<IFile> ncaFile = new();
 
 
-                                                pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                                            pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
 
-                                                Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
-                                                int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+                                            Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
+                                            int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
 
 
-                                                // Some main NCAs don't have a data partition, so check if the partition exists before opening it
-                                                if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
-                                                {
-                                                    hasMainNca = true;
-
-                                                    break;
-                                                }
-                                            }
-                                            else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
+                                            // Some main NCAs don't have a data partition, so check if the partition exists before opening it
+                                            if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
                                             {
                                             {
-                                                isExeFs = true;
+                                                hasMainNca = true;
+
+                                                break;
                                             }
                                             }
                                         }
                                         }
-
-                                        if (!hasMainNca && !isExeFs)
+                                        else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
                                         {
                                         {
-                                            numApplicationsFound--;
-
-                                            continue;
+                                            isExeFs = true;
                                         }
                                         }
                                     }
                                     }
 
 
-                                    if (isExeFs)
+                                    if (!hasMainNca && !isExeFs)
                                     {
                                     {
-                                        applicationIcon = _nspIcon;
+                                        numApplicationsFound--;
+
+                                        continue;
+                                    }
+                                }
+
+                                if (isExeFs)
+                                {
+                                    applicationIcon = _nspIcon;
 
 
-                                        using var npdmFile = new UniqueRef<IFile>();
+                                    using UniqueRef<IFile> npdmFile = new();
 
 
-                                        Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
+                                    Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
 
 
-                                        if (ResultFs.PathNotFound.Includes(result))
-                                        {
-                                            Npdm npdm = new Npdm(npdmFile.Get.AsStream());
+                                    if (ResultFs.PathNotFound.Includes(result))
+                                    {
+                                        Npdm npdm = new(npdmFile.Get.AsStream());
 
 
-                                            titleName = npdm.TitleName;
-                                            titleId = npdm.Aci0.TitleId.ToString("x16");
-                                        }
+                                        titleName = npdm.TitleName;
+                                        titleId = npdm.Aci0.TitleId.ToString("x16");
                                     }
                                     }
-                                    else
+                                }
+                                else
+                                {
+                                    GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId);
+
+                                    // Check if there is an update available.
+                                    if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs))
                                     {
                                     {
-                                        GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId);
+                                        // Replace the original ControlFs by the updated one.
+                                        controlFs = updatedControlFs;
+                                    }
 
 
-                                        // Check if there is an update available.
-                                        if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs))
-                                        {
-                                            // Replace the original ControlFs by the updated one.
-                                            controlFs = updatedControlFs;
-                                        }
+                                    ReadControlData(controlFs, controlHolder.ByteSpan);
 
 
-                                        ReadControlData(controlFs, controlHolder.ByteSpan);
+                                    GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version);
 
 
-                                        GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version);
+                                    // Read the icon from the ControlFS and store it as a byte array
+                                    try
+                                    {
+                                        using UniqueRef<IFile> icon = new();
 
 
-                                        // Read the icon from the ControlFS and store it as a byte array
-                                        try
-                                        {
-                                            using var icon = new UniqueRef<IFile>();
+                                        controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
 
-                                            controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                                        using MemoryStream stream = new();
 
 
-                                            using (MemoryStream stream = new MemoryStream())
-                                            {
-                                                icon.Get.AsStream().CopyTo(stream);
-                                                applicationIcon = stream.ToArray();
-                                            }
-                                        }
-                                        catch (HorizonResultException)
+                                        icon.Get.AsStream().CopyTo(stream);
+                                        applicationIcon = stream.ToArray();
+                                    }
+                                    catch (HorizonResultException)
+                                    {
+                                        foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
                                         {
                                         {
-                                            foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
+                                            if (entry.Name == "control.nacp")
                                             {
                                             {
-                                                if (entry.Name == "control.nacp")
-                                                {
-                                                    continue;
-                                                }
-
-                                                using var icon = new UniqueRef<IFile>();
+                                                continue;
+                                            }
 
 
-                                                controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                                            using var icon = new UniqueRef<IFile>();
 
 
-                                                using (MemoryStream stream = new MemoryStream())
-                                                {
-                                                    icon.Get.AsStream().CopyTo(stream);
-                                                    applicationIcon = stream.ToArray();
-                                                }
+                                            controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
 
-                                                if (applicationIcon != null)
-                                                {
-                                                    break;
-                                                }
-                                            }
+                                            using MemoryStream stream = new();
+                                            
+                                            icon.Get.AsStream().CopyTo(stream);
+                                            applicationIcon = stream.ToArray();
+                                            
 
 
-                                            if (applicationIcon == null)
+                                            if (applicationIcon != null)
                                             {
                                             {
-                                                applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                                                break;
                                             }
                                             }
                                         }
                                         }
+
+                                        applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon;
                                     }
                                     }
                                 }
                                 }
-                                catch (MissingKeyException exception)
-                                {
-                                    applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                            }
+                            catch (MissingKeyException exception)
+                            {
+                                applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
 
 
-                                    Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
-                                }
-                                catch (InvalidDataException)
-                                {
-                                    applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                                Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
+                            }
+                            catch (InvalidDataException)
+                            {
+                                applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
 
 
-                                    Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
-                                }
-                                catch (Exception exception)
-                                {
-                                    Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
+                                Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
+                            }
+                            catch (Exception exception)
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
 
 
-                                    numApplicationsFound--;
+                                numApplicationsFound--;
 
 
-                                    continue;
-                                }
+                                continue;
                             }
                             }
-                            else if (extension == ".nro")
-                            {
-                                BinaryReader reader = new BinaryReader(file);
+                        }
+                        else if (extension == ".nro")
+                        {
+                            BinaryReader reader = new(file);
 
 
-                                byte[] Read(long position, int size)
-                                {
-                                    file.Seek(position, SeekOrigin.Begin);
+                            byte[] Read(long position, int size)
+                            {
+                                file.Seek(position, SeekOrigin.Begin);
 
 
-                                    return reader.ReadBytes(size);
-                                }
+                                return reader.ReadBytes(size);
+                            }
 
 
-                                try
-                                {
-                                    file.Seek(24, SeekOrigin.Begin);
+                            try
+                            {
+                                file.Seek(24, SeekOrigin.Begin);
 
 
-                                    int assetOffset = reader.ReadInt32();
+                                int assetOffset = reader.ReadInt32();
 
 
-                                    if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
-                                    {
-                                        byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
+                                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);
 
 
-                                        ulong nacpOffset = reader.ReadUInt64();
-                                        ulong nacpSize = reader.ReadUInt64();
+                                    ulong nacpOffset = reader.ReadUInt64();
+                                    ulong nacpSize = reader.ReadUInt64();
 
 
-                                        // Reads and stores game icon as byte array
-                                        applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
+                                    // Reads and stores game icon as byte array
+                                    applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
 
 
-                                        // Read the NACP data
-                                        Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
+                                    // Read the NACP data
+                                    Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
 
 
-                                        GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version);
-                                    }
-                                    else
-                                    {
-                                        applicationIcon = _nroIcon;
-                                        titleName = Path.GetFileNameWithoutExtension(applicationPath);
-                                    }
+                                    GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version);
                                 }
                                 }
-                                catch
+                                else
                                 {
                                 {
-                                    Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
-
-                                    numApplicationsFound--;
-
-                                    continue;
+                                    applicationIcon = _nroIcon;
+                                    titleName = Path.GetFileNameWithoutExtension(applicationPath);
                                 }
                                 }
                             }
                             }
-                            else if (extension == ".nca")
+                            catch
                             {
                             {
-                                try
-                                {
-                                    Nca nca = new Nca(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
-                                    int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
 
 
-                                    if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
-                                    {
-                                        numApplicationsFound--;
+                                numApplicationsFound--;
 
 
-                                        continue;
-                                    }
-                                }
-                                catch (InvalidDataException)
-                                {
-                                    Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
-                                }
-                                catch
-                                {
-                                    Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+                                continue;
+                            }
+                        }
+                        else if (extension == ".nca")
+                        {
+                            try
+                            {
+                                Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
+                                int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
 
 
+                                if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
+                                {
                                     numApplicationsFound--;
                                     numApplicationsFound--;
 
 
                                     continue;
                                     continue;
                                 }
                                 }
-
-                                applicationIcon = _ncaIcon;
-                                titleName = Path.GetFileNameWithoutExtension(applicationPath);
                             }
                             }
-                            // If its an NSO we just set defaults
-                            else if (extension == ".nso")
+                            catch (InvalidDataException)
                             {
                             {
-                                applicationIcon = _nsoIcon;
-                                titleName = Path.GetFileNameWithoutExtension(applicationPath);
+                                Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
                             }
                             }
+                            catch
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+
+                                numApplicationsFound--;
+
+                                continue;
+                            }
+
+                            applicationIcon = _ncaIcon;
+                            titleName = Path.GetFileNameWithoutExtension(applicationPath);
+                        }
+                        // If its an NSO we just set defaults
+                        else if (extension == ".nso")
+                        {
+                            applicationIcon = _nsoIcon;
+                            titleName = Path.GetFileNameWithoutExtension(applicationPath);
                         }
                         }
                     }
                     }
                     catch (IOException exception)
                     catch (IOException exception)
@@ -404,14 +399,21 @@ namespace Ryujinx.Ui.App.Common
                         appMetadata.Title = titleName;
                         appMetadata.Title = titleName;
                     });
                     });
 
 
-                    if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _))
-                    {
-                        Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)");
+                    if (appMetadata.LastPlayed != "Never")
+                    { 
+                        if (!DateTime.TryParse(appMetadata.LastPlayed, out _))
+                        {
+                            Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)");
 
 
-                        appMetadata.LastPlayed = "Never";
+                            appMetadata.LastPlayed = "Never";
+                        }
+                        else
+                        {
+                            appMetadata.LastPlayed = appMetadata.LastPlayed[..^3];
+                        }
                     }
                     }
 
 
-                    ApplicationData data = new ApplicationData
+                    ApplicationData data = new()
                     {
                     {
                         Favorite = appMetadata.Favorite,
                         Favorite = appMetadata.Favorite,
                         Icon = applicationIcon,
                         Icon = applicationIcon,
@@ -419,7 +421,7 @@ namespace Ryujinx.Ui.App.Common
                         TitleId = titleId,
                         TitleId = titleId,
                         Developer = developer,
                         Developer = developer,
                         Version = version,
                         Version = version,
-                        TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed),
+                        TimePlayed = ConvertSecondsToFormattedString(appMetadata.TimePlayed),
                         TimePlayedNum = appMetadata.TimePlayed,
                         TimePlayedNum = appMetadata.TimePlayed,
                         LastPlayed = appMetadata.LastPlayed,
                         LastPlayed = appMetadata.LastPlayed,
                         FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1),
                         FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1),
@@ -488,10 +490,9 @@ namespace Ryujinx.Ui.App.Common
 
 
                 appMetadata = new ApplicationMetadata();
                 appMetadata = new ApplicationMetadata();
 
 
-                using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
-                {
-                    JsonHelper.Serialize(stream, appMetadata, true);
-                }
+                using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
+
+                JsonHelper.Serialize(stream, appMetadata, true);
             }
             }
 
 
             try
             try
@@ -509,10 +510,9 @@ namespace Ryujinx.Ui.App.Common
             {
             {
                 modifyFunction(appMetadata);
                 modifyFunction(appMetadata);
 
 
-                using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
-                {
-                    JsonHelper.Serialize(stream, appMetadata, true);
-                }
+                using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough);
+
+                JsonHelper.Serialize(stream, appMetadata, true);
             }
             }
 
 
             return appMetadata;
             return appMetadata;
@@ -529,192 +529,177 @@ namespace Ryujinx.Ui.App.Common
                 {
                 {
                     string extension = Path.GetExtension(applicationPath).ToLower();
                     string extension = Path.GetExtension(applicationPath).ToLower();
 
 
-                    using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read))
+                    using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read);
+
+                    if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
                     {
                     {
-                        if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
+                        try
                         {
                         {
-                            try
-                            {
-                                PartitionFileSystem pfs;
+                            PartitionFileSystem pfs;
 
 
-                                bool isExeFs = false;
+                            bool isExeFs = false;
 
 
-                                if (extension == ".xci")
-                                {
-                                    Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
+                            if (extension == ".xci")
+                            {
+                                Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
 
 
-                                    pfs = xci.OpenPartition(XciPartitionType.Secure);
-                                }
-                                else
-                                {
-                                    pfs = new PartitionFileSystem(file.AsStorage());
+                                pfs = xci.OpenPartition(XciPartitionType.Secure);
+                            }
+                            else
+                            {
+                                pfs = new PartitionFileSystem(file.AsStorage());
 
 
-                                    foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
+                                foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
+                                {
+                                    if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
                                     {
                                     {
-                                        if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
-                                        {
-                                            isExeFs = true;
-                                        }
+                                        isExeFs = true;
                                     }
                                     }
                                 }
                                 }
+                            }
 
 
-                                if (isExeFs)
+                            if (isExeFs)
+                            {
+                                applicationIcon = _nspIcon;
+                            }
+                            else
+                            {
+                                // Store the ControlFS in variable called controlFs
+                                GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _);
+
+                                // Read the icon from the ControlFS and store it as a byte array
+                                try
                                 {
                                 {
-                                    applicationIcon = _nspIcon;
+                                    using var icon = new UniqueRef<IFile>();
+
+                                    controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                                    using MemoryStream stream = new();
+
+                                    icon.Get.AsStream().CopyTo(stream);
+                                    applicationIcon = stream.ToArray();
                                 }
                                 }
-                                else
+                                catch (HorizonResultException)
                                 {
                                 {
-                                    // Store the ControlFS in variable called controlFs
-                                    GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _);
-
-                                    // Read the icon from the ControlFS and store it as a byte array
-                                    try
+                                    foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
                                     {
                                     {
+                                        if (entry.Name == "control.nacp")
+                                        {
+                                            continue;
+                                        }
+
                                         using var icon = new UniqueRef<IFile>();
                                         using var icon = new UniqueRef<IFile>();
 
 
-                                        controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                                        controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
 
-                                        using (MemoryStream stream = new MemoryStream())
+                                        using (MemoryStream stream = new())
                                         {
                                         {
                                             icon.Get.AsStream().CopyTo(stream);
                                             icon.Get.AsStream().CopyTo(stream);
                                             applicationIcon = stream.ToArray();
                                             applicationIcon = stream.ToArray();
                                         }
                                         }
-                                    }
-                                    catch (HorizonResultException)
-                                    {
-                                        foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
-                                        {
-                                            if (entry.Name == "control.nacp")
-                                            {
-                                                continue;
-                                            }
-
-                                            using var icon = new UniqueRef<IFile>();
-
-                                            controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                                            using (MemoryStream stream = new MemoryStream())
-                                            {
-                                                icon.Get.AsStream().CopyTo(stream);
-                                                applicationIcon = stream.ToArray();
-                                            }
 
 
-                                            if (applicationIcon != null)
-                                            {
-                                                break;
-                                            }
-                                        }
-
-                                        if (applicationIcon == null)
+                                        if (applicationIcon != null)
                                         {
                                         {
-                                            applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                                            break;
                                         }
                                         }
                                     }
                                     }
+
+                                    applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon;
                                 }
                                 }
                             }
                             }
-                            catch (MissingKeyException)
-                            {
-                                applicationIcon = extension == ".xci"
-                                    ? _xciIcon
-                                    : _nspIcon;
-                            }
-                            catch (InvalidDataException)
-                            {
-                                applicationIcon = extension == ".xci"
-                                    ? _xciIcon
-                                    : _nspIcon;
-                            }
-                            catch (Exception exception)
-                            {
-                                Logger.Warning?.Print(LogClass.Application,
-                                    $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
-                            }
                         }
                         }
-                        else if (extension == ".nro")
+                        catch (MissingKeyException)
                         {
                         {
-                            BinaryReader reader = new(file);
+                            applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                        }
+                        catch (InvalidDataException)
+                        {
+                            applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+                        }
+                        catch (Exception exception)
+                        {
+                            Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
+                        }
+                    }
+                    else if (extension == ".nro")
+                    {
+                        BinaryReader reader = new(file);
 
 
-                            byte[] Read(long position, int size)
-                            {
-                                file.Seek(position, SeekOrigin.Begin);
+                        byte[] Read(long position, int size)
+                        {
+                            file.Seek(position, SeekOrigin.Begin);
 
 
-                                return reader.ReadBytes(size);
-                            }
+                            return reader.ReadBytes(size);
+                        }
 
 
-                            try
-                            {
-                                file.Seek(24, SeekOrigin.Begin);
+                        try
+                        {
+                            file.Seek(24, SeekOrigin.Begin);
 
 
-                                int assetOffset = reader.ReadInt32();
+                            int assetOffset = reader.ReadInt32();
 
 
-                                if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
-                                {
-                                    byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
+                            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);
 
 
-                                    // Reads and stores game icon as byte array
-                                    applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
-                                }
-                                else
-                                {
-                                    applicationIcon = _nroIcon;
-                                }
+                                // Reads and stores game icon as byte array
+                                applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
                             }
                             }
-                            catch
+                            else
                             {
                             {
-                                Logger.Warning?.Print(LogClass.Application,
-                                    $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+                                applicationIcon = _nroIcon;
                             }
                             }
                         }
                         }
-                        else if (extension == ".nca")
+                        catch
                         {
                         {
-                            applicationIcon = _ncaIcon;
-                        }
-                        // If its an NSO we just set defaults
-                        else if (extension == ".nso")
-                        {
-                            applicationIcon = _nsoIcon;
+                            Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
                         }
                         }
                     }
                     }
+                    else if (extension == ".nca")
+                    {
+                        applicationIcon = _ncaIcon;
+                    }
+                    // If its an NSO we just set defaults
+                    else if (extension == ".nso")
+                    {
+                        applicationIcon = _nsoIcon;
+                    }
                 }
                 }
             }
             }
             catch(Exception)
             catch(Exception)
             {
             {
-                Logger.Warning?.Print(LogClass.Application,
-                    $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}");
+                Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}");
             }
             }
 
 
             return applicationIcon ?? _ncaIcon;
             return applicationIcon ?? _ncaIcon;
         }
         }
 
 
-        private string ConvertSecondsToReadableString(double seconds)
+        private static string ConvertSecondsToFormattedString(double seconds)
         {
         {
-            const int secondsPerMinute = 60;
-            const int secondsPerHour   = secondsPerMinute * 60;
-            const int secondsPerDay    = secondsPerHour   * 24;
-
-            string readableString;
+            System.TimeSpan time = System.TimeSpan.FromSeconds(seconds);
 
 
-            if (seconds < secondsPerMinute)
+            string timeString;
+            if (time.Days != 0)
             {
             {
-                readableString = $"{seconds} seconds";
+                timeString = $"{time.Days}d {time.Hours:D2}h {time.Minutes:D2}m";
             }
             }
-            else if (seconds < secondsPerHour)
+            else if (time.Hours != 0)
             {
             {
-                readableString = $"{Math.Round(seconds / secondsPerMinute, 0, MidpointRounding.AwayFromZero)} minutes";
+                timeString = $"{time.Hours:D2}h {time.Minutes:D2}m";
             }
             }
-            else if (seconds < secondsPerDay)
+            else if (time.Minutes != 0)
             {
             {
-                readableString = $"{Math.Round(seconds / secondsPerHour, 1, MidpointRounding.AwayFromZero)} hours";
+                timeString = $"{time.Minutes:D2}m";
             }
             }
             else
             else
             {
             {
-                readableString = $"{Math.Round(seconds / secondsPerDay, 1, MidpointRounding.AwayFromZero)} days";
+                timeString = "Never";
             }
             }
 
 
-            return readableString;
+            return timeString;
         }
         }
 
 
         private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version)
         private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version)
@@ -797,8 +782,7 @@ namespace Ryujinx.Ui.App.Common
             }
             }
             catch (InvalidDataException)
             catch (InvalidDataException)
             {
             {
-                Logger.Warning?.Print(LogClass.Application,
-                    $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}");
+                Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}");
             }
             }
             catch (MissingKeyException exception)
             catch (MissingKeyException exception)
             {
             {