Compare commits

...

5 Commits

Author SHA1 Message Date
TSRBerry
add681144b Fix checking for the wrong metadata files for applications launched with a different program index (#7055)
* Fix checking for the wrong update metadata file

* Apply the same fix for dlc.json

* Use the base application ids for updates and DLCs in the GUI too

This shouldn't actually change anything, since the program index part of the application id
should always be 0 for all applications currently seen by the GUI.

This was just done for completeness.
2024-07-21 14:42:23 -03:00
TSRBerry
c6dc00815a Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore (#7046)
* Add docstrings for exceptions to methods near TryGetApplicationsFromFile()

* Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore

* Add missing filePath to ApplicationData when loading applications from ExeFS

* Fix typo

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-07-20 16:35:43 -03:00
gdkchan
99f04ac1a6 Fix Skia saving screenshot with transparent background and incorrect origin (#7073)
* Fix Skia saving screenshot with transparent background and incorrect origin

* Remove code that is no longer necessary
2024-07-20 16:27:40 -03:00
gdkchan
ce09450743 Unlink server sessions from multi-wait when service stops processing requests (#7072) 2024-07-20 16:17:40 -03:00
Isaac Marovitz
2cb80f37d4 Ava UI: Auto select newly added updates & DLC (#7026)
* Fix DLC not being selected

* FIx conflicts

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-07-19 19:00:15 +02:00
22 changed files with 233 additions and 186 deletions

View File

@@ -473,7 +473,7 @@ namespace Ryujinx.UI.Widgets
private void ManageDlc_Clicked(object sender, EventArgs args) private void ManageDlc_Clicked(object sender, EventArgs args)
{ {
new DlcWindow(_virtualFileSystem, _applicationData.IdString, _applicationData).Show(); new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show();
} }
private void ManageCheats_Clicked(object sender, EventArgs args) private void ManageCheats_Clicked(object sender, EventArgs args)

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.UI.Windows
public class DlcWindow : Window public class DlcWindow : Window
{ {
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _applicationId; private readonly string _applicationIdBase;
private readonly string _dlcJsonPath; private readonly string _dlcJsonPath;
private readonly List<DownloadableContentContainer> _dlcContainerList; private readonly List<DownloadableContentContainer> _dlcContainerList;
@@ -36,16 +36,16 @@ namespace Ryujinx.UI.Windows
[GUI] TreeSelection _dlcTreeSelection; [GUI] TreeSelection _dlcTreeSelection;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, titleId, applicationData) { } public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow")) private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
_applicationId = applicationId; _applicationIdBase = applicationIdBase;
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json"); _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationId.ToUpper()}]"; _baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]";
try try
{ {
@@ -163,7 +163,7 @@ namespace Ryujinx.UI.Windows
if (nca.Header.ContentType == NcaContentType.PublicData) if (nca.Header.ContentType == NcaContentType.PublicData)
{ {
if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL)) if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber))
{ {
continue; continue;
} }

View File

@@ -51,7 +51,7 @@ namespace Ryujinx.UI.Windows
_applicationData = applicationData; _applicationData = applicationData;
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json"); _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json");
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>(); _radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
try try
@@ -67,7 +67,7 @@ namespace Ryujinx.UI.Windows
}; };
} }
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]"; _baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]";
// Try to get updates from PFS first // Try to get updates from PFS first
AddUpdate(_applicationData.Path, true); AddUpdate(_applicationData.Path, true);

View File

@@ -15,6 +15,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; } public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; } public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACI0 data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
public Aci0(Stream stream, int offset) public Aci0(Stream stream, int offset)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -19,6 +19,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; } public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; } public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACID data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Acid(Stream stream, int offset) public Acid(Stream stream, int offset)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -11,6 +11,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Unknown3 { get; private set; } public int Unknown3 { get; private set; }
public int Unknown4 { get; private set; } public int Unknown4 { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessControl(Stream stream, int offset, int size) public FsAccessControl(Stream stream, int offset, int size)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -9,6 +9,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Version { get; private set; } public int Version { get; private set; }
public ulong PermissionsBitmask { get; private set; } public ulong PermissionsBitmask { get; private set; }
/// <exception cref="InvalidNpdmException">The stream contains invalid data.</exception>
/// <exception cref="NotImplementedException">The ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessHeader(Stream stream, int offset, int size) public FsAccessHeader(Stream stream, int offset, int size)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
{ {
public int[] Capabilities { get; private set; } public int[] Capabilities { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public KernelAccessControl(Stream stream, int offset, int size) public KernelAccessControl(Stream stream, int offset, int size)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -24,6 +24,13 @@ namespace Ryujinx.HLE.Loaders.Npdm
public Aci0 Aci0 { get; private set; } public Aci0 Aci0 { get; private set; }
public Acid Acid { get; private set; } public Acid Acid { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid NPDM data.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Npdm(Stream stream) public Npdm(Stream stream)
{ {
BinaryReader reader = new(stream); BinaryReader reader = new(stream);

View File

@@ -9,6 +9,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
{ {
public IReadOnlyDictionary<string, bool> Services { get; private set; } public IReadOnlyDictionary<string, bool> Services { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public ServiceAccessControl(Stream stream, int offset, int size) public ServiceAccessControl(Stream stream, int offset, int size)
{ {
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
ulong titleIdBase = mainNca.GetProgramIdBase(); ulong titleIdBase = mainNca.GetProgramIdBase();
// Load update information if exists. // Load update information if exists.
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json"); string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath)) if (File.Exists(titleUpdateMetadataPath))
{ {
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected; updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;

View File

@@ -118,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device.Configuration.ContentManager.ClearAocData(); device.Configuration.ContentManager.ClearAocData();
// Load DownloadableContents. // Load DownloadableContents.
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
if (File.Exists(addOnContentMetadataPath)) if (File.Exists(addOnContentMetadataPath))
{ {
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer); List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);

View File

@@ -21,6 +21,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
public long CurrentTime { get; private set; } public long CurrentTime { get; private set; }
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
public MultiWaitImpl() public MultiWaitImpl()
{ {
_multiWaits = new List<MultiWaitHolderBase>(); _multiWaits = new List<MultiWaitHolderBase>();

View File

@@ -1,4 +1,5 @@
using Ryujinx.Horizon.Sdk.OsTypes.Impl; using Ryujinx.Horizon.Sdk.OsTypes.Impl;
using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes namespace Ryujinx.Horizon.Sdk.OsTypes
{ {
@@ -6,6 +7,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
{ {
private readonly MultiWaitImpl _impl; private readonly MultiWaitImpl _impl;
public IEnumerable<MultiWaitHolderBase> MultiWaits => _impl.MultiWaits;
public MultiWait() public MultiWait()
{ {
_impl = new MultiWaitImpl(); _impl = new MultiWaitImpl();

View File

@@ -3,6 +3,7 @@ using Ryujinx.Horizon.Sdk.OsTypes;
using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sf.Cmif;
using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sdk.Sm;
using System; using System;
using System.Linq;
namespace Ryujinx.Horizon.Sdk.Sf.Hipc namespace Ryujinx.Horizon.Sdk.Sf.Hipc
{ {
@@ -116,6 +117,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
while (WaitAndProcessRequestsImpl()) while (WaitAndProcessRequestsImpl())
{ {
} }
// Unlink pending sessions, dispose expects them to be already unlinked.
ServerSession[] serverSessions = Enumerable.OfType<ServerSession>(_multiWait.MultiWaits).ToArray();
foreach (ServerSession serverSession in serverSessions)
{
if (serverSession.IsLinked)
{
serverSession.UnlinkFromMultiWaitHolder();
}
}
} }
public void WaitAndProcessRequests() public void WaitAndProcessRequests()

View File

@@ -42,6 +42,8 @@ namespace Ryujinx.UI.App.Common
[JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL; [JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
[JsonIgnore] public string IdBaseString => IdBase.ToString("x16");
public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath) public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
{ {
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);

View File

@@ -72,17 +72,21 @@ namespace Ryujinx.UI.App.Common
return resourceByteArray; return resourceByteArray;
} }
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath) private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath)
{ {
ApplicationData data = new() ApplicationData data = new()
{ {
Icon = _nspIcon, Icon = _nspIcon,
Path = filePath,
}; };
using UniqueRef<IFile> npdmFile = new(); using UniqueRef<IFile> npdmFile = new();
try
{
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)) if (ResultFs.PathNotFound.Includes(result))
@@ -95,14 +99,16 @@ namespace Ryujinx.UI.App.Common
return data; return data;
} }
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
return null;
}
}
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath) private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath)
{ {
bool isExeFs = false; bool isExeFs = false;
@@ -170,13 +176,15 @@ namespace Ryujinx.UI.App.Common
return null; return null;
} }
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath) private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath)
{ {
var applications = new List<ApplicationData>(); var applications = new List<ApplicationData>();
string extension = Path.GetExtension(filePath).ToLower(); string extension = Path.GetExtension(filePath).ToLower();
try
{
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel)) foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
{ {
ApplicationData applicationData = new() ApplicationData applicationData = new()
@@ -251,19 +259,6 @@ namespace Ryujinx.UI.App.Common
applications.Add(applicationData); applications.Add(applicationData);
} }
}
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
}
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: {filePath}");
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}");
}
return applications; return applications;
} }
@@ -319,8 +314,6 @@ namespace Ryujinx.UI.App.Common
BinaryReader reader = new(file); BinaryReader reader = new(file);
ApplicationData application = new(); ApplicationData application = new();
try
{
file.Seek(24, SeekOrigin.Begin); file.Seek(24, SeekOrigin.Begin);
int assetOffset = reader.ReadInt32(); int assetOffset = reader.ReadInt32();
@@ -358,13 +351,6 @@ namespace Ryujinx.UI.App.Common
application.ControlHolder = controlHolder; application.ControlHolder = controlHolder;
applications.Add(application); applications.Add(application);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
return false;
}
break; break;
@@ -376,8 +362,6 @@ namespace Ryujinx.UI.App.Common
} }
} }
case ".nca": case ".nca":
{
try
{ {
ApplicationData application = new(); ApplicationData application = new();
@@ -393,17 +377,6 @@ namespace Ryujinx.UI.App.Common
application.ControlHolder = controlHolder; application.ControlHolder = controlHolder;
applications.Add(application); applications.Add(application);
}
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}");
return false;
}
break; break;
} }
@@ -417,16 +390,35 @@ namespace Ryujinx.UI.App.Common
}; };
applications.Add(application); applications.Add(application);
break; break;
} }
} }
} }
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
return false;
}
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: {applicationPath}");
return false;
}
catch (IOException exception) catch (IOException exception)
{ {
Logger.Warning?.Print(LogClass.Application, exception.Message); Logger.Warning?.Print(LogClass.Application, exception.Message);
return false; return false;
} }
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
return false;
}
foreach (var data in applications) foreach (var data in applications)
{ {

View File

@@ -367,19 +367,14 @@ namespace Ryujinx.Ava
} }
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
using var bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length); Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length);
SKBitmap bitmapToSave = null; using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
using SKCanvas canvas = new SKCanvas(bitmapToSave);
if (e.FlipX || e.FlipY) canvas.Clear(SKColors.Black);
{
bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
using var canvas = new SKCanvas(bitmapToSave);
canvas.Clear(SKColors.Transparent);
float scaleX = e.FlipX ? -1 : 1; float scaleX = e.FlipX ? -1 : 1;
float scaleY = e.FlipY ? -1 : 1; float scaleY = e.FlipY ? -1 : 1;
@@ -387,12 +382,9 @@ namespace Ryujinx.Ava
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f); var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
canvas.SetMatrix(matrix); canvas.SetMatrix(matrix);
canvas.DrawBitmap(bitmap, SKPoint.Empty);
canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0)); SaveBitmapAsPng(bitmapToSave, path);
}
SaveBitmapAsPng(bitmapToSave ?? bitmap, path);
bitmapToSave?.Dispose();
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
} }

View File

@@ -103,7 +103,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_storageProvider = desktop.MainWindow.StorageProvider; _storageProvider = desktop.MainWindow.StorageProvider;
} }
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json"); _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
if (!File.Exists(_downloadableContentJsonPath)) if (!File.Exists(_downloadableContentJsonPath))
{ {
@@ -263,7 +263,7 @@ namespace Ryujinx.Ava.UI.ViewModels
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true); var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true);
DownloadableContents.Add(content); DownloadableContents.Add(content);
SelectedDownloadableContents.Add(content); Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
success = true; success = true;
} }

View File

@@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StorageProvider = desktop.MainWindow.StorageProvider; StorageProvider = desktop.MainWindow.StorageProvider;
} }
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json"); TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdBaseString, "updates.json");
try try
{ {
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
catch catch
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}"); Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdBaseString} at {TitleUpdateJsonPath}");
TitleUpdateWindowData = new TitleUpdateMetadata TitleUpdateWindowData = new TitleUpdateMetadata
{ {
@@ -169,7 +169,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
private void AddUpdate(string path, bool ignoreNotFound = false) private void AddUpdate(string path, bool ignoreNotFound = false, bool selected = false)
{ {
if (!File.Exists(path) || TitleUpdates.Any(x => x.Path == path)) if (!File.Exists(path) || TitleUpdates.Any(x => x.Path == path))
{ {
@@ -204,7 +204,13 @@ namespace Ryujinx.Ava.UI.ViewModels
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path)); var update = new TitleUpdateModel(controlData, path);
TitleUpdates.Add(update);
if (selected)
{
Dispatcher.UIThread.InvokeAsync(() => SelectedUpdate = update);
}
} }
else else
{ {
@@ -245,7 +251,7 @@ namespace Ryujinx.Ava.UI.ViewModels
foreach (var file in result) foreach (var file in result)
{ {
AddUpdate(file.Path.LocalPath); AddUpdate(file.Path.LocalPath, selected: true);
} }
SortUpdates(); SortUpdates();

View File

@@ -38,7 +38,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData), Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdString), Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString),
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, applicationData), Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdString), Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString),
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());