diff options
author | Emmanuel Hansen <emmausssss@gmail.com> | 2022-05-15 11:30:15 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-15 13:30:15 +0200 |
commit | deb99d2cae3e80bdf70cb52c6c160094dc7c9292 (patch) | |
tree | e60f44d1b4bd45bbf36fcfa750fb99787febfdbe /Ryujinx/Ui/App/ApplicationLibrary.cs | |
parent | 9ba73ffbe5f78c0403cf102b95768f388da05122 (diff) |
Avalonia UI - Part 1 (#3270)1.1.122
* avalonia part 1
* remove vulkan ui backend
* move ui common files to ui common project
* get name for oading screen from device
* rebase.
* review 1
* review 1.1
* review
* cleanup
* addressed review
* use cancellation token
* review
* review
* rebased
* cancel library loading when closing window
* remove star image, use fonticon instead
* delete render control frame buffer when game ends. change position of fav star
* addressed @Thog review
* ensure the right ui is downloaded in updates
* fix crash when showing not supported dialog during controller request
* add prefix to artifact names
* Auto-format Avalonia project
* Fix input
* Fix build, simplify app disposal
* remove nv stutter thread
* addressed review
* add missing change
* maintain window size if new size is zero length
* add game, handheld, docked to local
* reverse scale main window
* Update de_DE.json
* Update de_DE.json
* Update de_DE.json
* Update italian json
* Update it_IT.json
* let render timer poll with no wait
* remove unused code
* more unused code
* enabled tiered compilation and trimming
* check if window event is not closed before signaling
* fix atmospher case
* locale fix
* locale fix
* remove explicit tiered compilation declarations
* Remove ) it_IT.json
* Remove ) de_DE.json
* Update it_IT.json
* Update pt_BR locale with latest strings
* Remove ')'
* add more strings to locale
* update locale
* remove extra slash
* remove extra slash
* set firmware version to 0 if key's not found
* fix
* revert timer changes
* lock on object instead
* Update it_IT.json
* remove unused method
* add load screen text to locale
* drop swap event
* Update de_DE.json
* Update de_DE.json
* do null check when stopping emulator
* Update de_DE.json
* Create tr_TR.json
* Add tr_TR
* Add tr_TR + Turkish
* Update it_IT.json
* Update Ryujinx.Ava/Input/AvaloniaMappingHelper.cs
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Apply suggestions from code review
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Apply suggestions from code review
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* addressed review
* Update Ryujinx.Ava/Ui/Backend/OpenGl/OpenGlRenderTarget.cs
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
* use avalonia's inbuilt renderer on linux
* removed whitespace
* workaround for queue render crash with vsync off
* drop custom backend
* format files
* fix not closing issue
* remove warnings
* rebase
* update avalonia library
* Reposition the Text and Button on About Page
* Assign build version
* Remove appveyor text
Co-authored-by: gdk <gab.dark.100@gmail.com>
Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com>
Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>
Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: MostlyWhat <78652091+MostlyWhat@users.noreply.github.com>
Diffstat (limited to 'Ryujinx/Ui/App/ApplicationLibrary.cs')
-rw-r--r-- | Ryujinx/Ui/App/ApplicationLibrary.cs | 648 |
1 files changed, 0 insertions, 648 deletions
diff --git a/Ryujinx/Ui/App/ApplicationLibrary.cs b/Ryujinx/Ui/App/ApplicationLibrary.cs deleted file mode 100644 index 3658dfe2..00000000 --- a/Ryujinx/Ui/App/ApplicationLibrary.cs +++ /dev/null @@ -1,648 +0,0 @@ -using LibHac; -using LibHac.Common; -using LibHac.Common.Keys; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Configuration.System; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Loaders.Npdm; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Text.Json; - -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; -using Path = System.IO.Path; - -namespace Ryujinx.Ui.App -{ - public class ApplicationLibrary - { - public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded; - public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated; - - private readonly byte[] _nspIcon; - private readonly byte[] _xciIcon; - private readonly byte[] _ncaIcon; - private readonly byte[] _nroIcon; - private readonly byte[] _nsoIcon; - - private VirtualFileSystem _virtualFileSystem; - private Language _desiredTitleLanguage; - - public ApplicationLibrary(VirtualFileSystem virtualFileSystem) - { - _virtualFileSystem = virtualFileSystem; - - _nspIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NSP.png"); - _xciIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_XCI.png"); - _ncaIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NCA.png"); - _nroIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NRO.png"); - _nsoIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NSO.png"); - } - - private byte[] GetResourceBytes(string resourceName) - { - Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); - byte[] resourceByteArray = new byte[resourceStream.Length]; - - resourceStream.Read(resourceByteArray); - - return resourceByteArray; - } - - public IEnumerable<string> GetFilesInDirectory(string directory) - { - Stack<string> stack = new Stack<string>(); - - stack.Push(directory); - - while (stack.Count > 0) - { - string dir = stack.Pop(); - string[] content = Array.Empty<string>(); - - try - { - content = Directory.GetFiles(dir, "*"); - } - catch (UnauthorizedAccessException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\""); - } - - if (content.Length > 0) - { - foreach (string file in content) - { - yield return file; - } - } - - try - { - content = Directory.GetDirectories(dir); - } - catch (UnauthorizedAccessException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{dir}\""); - } - - if (content.Length > 0) - { - foreach (string subdir in content) - { - stack.Push(subdir); - } - } - } - } - - public void ReadControlData(IFileSystem controlFs, Span<byte> outProperty) - { - using var controlFile = new UniqueRef<IFile>(); - - controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); - } - - public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage) - { - int numApplicationsFound = 0; - int numApplicationsLoaded = 0; - - _desiredTitleLanguage = desiredTitleLanguage; - - // Builds the applications list with paths to found applications - List<string> applications = new List<string>(); - - foreach (string appDir in appDirs) - { - - if (!Directory.Exists(appDir)) - { - Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\""); - - continue; - } - - foreach (string app in GetFilesInDirectory(appDir)) - { - if ((Path.GetExtension(app).ToLower() == ".nsp") || - (Path.GetExtension(app).ToLower() == ".pfs0") || - (Path.GetExtension(app).ToLower() == ".xci") || - (Path.GetExtension(app).ToLower() == ".nca") || - (Path.GetExtension(app).ToLower() == ".nro") || - (Path.GetExtension(app).ToLower() == ".nso")) - { - applications.Add(app); - numApplicationsFound++; - } - } - } - - // Loops through applications list, creating a struct and then firing an event containing the struct for each application - foreach (string applicationPath in applications) - { - double fileSize = new FileInfo(applicationPath).Length * 0.000000000931; - string titleName = "Unknown"; - string titleId = "0000000000000000"; - string developer = "Unknown"; - string version = "0"; - byte[] applicationIcon = null; - - BlitStruct<ApplicationControlProperty> controlHolder = new BlitStruct<ApplicationControlProperty>(1); - - try - { - using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) - { - if ((Path.GetExtension(applicationPath).ToLower() == ".nsp") || - (Path.GetExtension(applicationPath).ToLower() == ".pfs0") || - (Path.GetExtension(applicationPath).ToLower() == ".xci")) - { - try - { - PartitionFileSystem pfs; - - bool isExeFs = false; - - if (Path.GetExtension(applicationPath).ToLower() == ".xci") - { - Xci xci = new Xci(_virtualFileSystem.KeySet, 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; - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) - { - if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") - { - using var ncaFile = new UniqueRef<IFile>(); - - 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); - - // 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") - { - isExeFs = true; - } - } - - if (!hasMainNca && !isExeFs) - { - numApplicationsFound--; - - continue; - } - } - - if (isExeFs) - { - applicationIcon = _nspIcon; - - using var npdmFile = new UniqueRef<IFile>(); - - Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); - - if (ResultFs.PathNotFound.Includes(result)) - { - Npdm npdm = new Npdm(npdmFile.Get.AsStream()); - - titleName = npdm.TitleName; - titleId = npdm.Aci0.TitleId.ToString("x16"); - } - } - else - { - GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); - - // 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); - - 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 var icon = new UniqueRef<IFile>(); - - controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using (MemoryStream stream = new MemoryStream()) - { - icon.Get.AsStream().CopyTo(stream); - 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) - { - applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; - } - } - } - } - catch (MissingKeyException exception) - { - applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; - - Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); - } - catch (InvalidDataException) - { - applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".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}"); - - numApplicationsFound--; - - continue; - } - } - else if (Path.GetExtension(applicationPath).ToLower() == ".nro") - { - BinaryReader reader = new BinaryReader(file); - - byte[] Read(long position, int size) - { - file.Seek(position, SeekOrigin.Begin); - - return reader.ReadBytes(size); - } - - try - { - 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); - - ulong nacpOffset = reader.ReadUInt64(); - ulong nacpSize = reader.ReadUInt64(); - - // 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); - - GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version); - } - else - { - applicationIcon = _nroIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - - numApplicationsFound--; - - continue; - } - } - else if (Path.GetExtension(applicationPath).ToLower() == ".nca") - { - try - { - Nca nca = new Nca(_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--; - - 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}"); - - numApplicationsFound--; - - continue; - } - - applicationIcon = _ncaIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - // If its an NSO we just set defaults - else if (Path.GetExtension(applicationPath).ToLower() == ".nso") - { - applicationIcon = _nsoIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - } - } - catch (IOException exception) - { - Logger.Warning?.Print(LogClass.Application, exception.Message); - - numApplicationsFound--; - - continue; - } - - ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); - - 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?)"); - - appMetadata.LastPlayed = "Never"; - } - - ApplicationData data = new ApplicationData - { - Favorite = appMetadata.Favorite, - Icon = applicationIcon, - TitleName = titleName, - TitleId = titleId, - Developer = developer, - Version = version, - TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed), - LastPlayed = appMetadata.LastPlayed, - FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), - FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MB" : fileSize.ToString("0.##") + "GB", - Path = applicationPath, - ControlHolder = controlHolder - }; - - numApplicationsLoaded++; - - OnApplicationAdded(new ApplicationAddedEventArgs() - { - AppData = data - }); - - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() - { - NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded - }); - } - - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() - { - NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded - }); - } - - protected void OnApplicationAdded(ApplicationAddedEventArgs e) - { - ApplicationAdded?.Invoke(null, e); - } - - protected void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e) - { - ApplicationCountUpdated?.Invoke(null, e); - } - - private void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId) - { - (_, _, Nca controlNca) = ApplicationLoader.GetGameData(_virtualFileSystem, pfs, 0); - - // Return the ControlFS - controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - titleId = controlNca?.Header.TitleId.ToString("x16"); - } - - internal ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null) - { - string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui"); - string metadataFile = Path.Combine(metadataFolder, "metadata.json"); - - ApplicationMetadata appMetadata; - - if (!File.Exists(metadataFile)) - { - Directory.CreateDirectory(metadataFolder); - - appMetadata = new ApplicationMetadata(); - - using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) - { - JsonHelper.Serialize(stream, appMetadata, true); - } - } - - try - { - appMetadata = JsonHelper.DeserializeFromFile<ApplicationMetadata>(metadataFile); - } - catch (JsonException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults."); - - appMetadata = new ApplicationMetadata(); - } - - if (modifyFunction != null) - { - modifyFunction(appMetadata); - - using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) - { - JsonHelper.Serialize(stream, appMetadata, true); - } - } - - return appMetadata; - } - - private string ConvertSecondsToReadableString(double seconds) - { - const int secondsPerMinute = 60; - const int secondsPerHour = secondsPerMinute * 60; - const int secondsPerDay = secondsPerHour * 24; - - string readableString; - - if (seconds < secondsPerMinute) - { - readableString = $"{seconds}s"; - } - else if (seconds < secondsPerHour) - { - readableString = $"{Math.Round(seconds / secondsPerMinute, 2, MidpointRounding.AwayFromZero)} mins"; - } - else if (seconds < secondsPerDay) - { - readableString = $"{Math.Round(seconds / secondsPerHour, 2, MidpointRounding.AwayFromZero)} hrs"; - } - else - { - readableString = $"{Math.Round(seconds / secondsPerDay, 2, MidpointRounding.AwayFromZero)} days"; - } - - return readableString; - } - - private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version) - { - _ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage); - - if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage) - { - titleName = controlData.Title[(int)desiredTitleLanguage].NameString.ToString(); - publisher = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString(); - } - else - { - titleName = null; - publisher = null; - } - - if (string.IsNullOrWhiteSpace(titleName)) - { - foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) - { - if (!controlTitle.NameString.IsEmpty()) - { - titleName = controlTitle.NameString.ToString(); - - break; - } - } - } - - if (string.IsNullOrWhiteSpace(publisher)) - { - foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) - { - if (!controlTitle.PublisherString.IsEmpty()) - { - publisher = controlTitle.PublisherString.ToString(); - - break; - } - } - } - - if (controlData.PresenceGroupId != 0) - { - titleId = controlData.PresenceGroupId.ToString("x16"); - } - else if (controlData.SaveDataOwnerId != 0) - { - titleId = controlData.SaveDataOwnerId.ToString(); - } - else if (controlData.AddOnContentBaseId != 0) - { - titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16"); - } - else - { - titleId = "0000000000000000"; - } - - version = controlData.DisplayVersionString.ToString(); - } - - private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs) - { - updatedControlFs = null; - - string updatePath = "(unknown)"; - - try - { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); - - if (patchNca != null && controlNca != null) - { - updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - - return true; - } - } - 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}"); - } - catch (MissingKeyException exception) - { - Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {updatePath}"); - } - - return false; - } - } -}
\ No newline at end of file |