Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
c6dc00815a | |||
99f04ac1a6 | |||
ce09450743 | |||
2cb80f37d4 | |||
827069e784 |
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -26,7 +26,11 @@ namespace Ryujinx.Horizon.Ngc.Ipc
|
|||||||
}
|
}
|
||||||
|
|
||||||
[CmifCommand(1)]
|
[CmifCommand(1)]
|
||||||
public Result Check(out uint checkMask, ReadOnlySpan<byte> text, uint regionMask, ProfanityFilterOption option)
|
public Result Check(
|
||||||
|
out uint checkMask,
|
||||||
|
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> text,
|
||||||
|
uint regionMask,
|
||||||
|
ProfanityFilterOption option)
|
||||||
{
|
{
|
||||||
lock (_profanityFilter)
|
lock (_profanityFilter)
|
||||||
{
|
{
|
||||||
|
@ -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>();
|
||||||
|
@ -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();
|
||||||
|
@ -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()
|
||||||
|
@ -72,37 +72,43 @@ 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);
|
||||||
|
|
||||||
|
if (ResultFs.PathNotFound.Includes(result))
|
||||||
{
|
{
|
||||||
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
Npdm npdm = new(npdmFile.Get.AsStream());
|
||||||
|
|
||||||
if (ResultFs.PathNotFound.Includes(result))
|
data.Name = npdm.TitleName;
|
||||||
{
|
data.Id = npdm.Aci0.TitleId;
|
||||||
Npdm npdm = new(npdmFile.Get.AsStream());
|
|
||||||
|
|
||||||
data.Name = npdm.TitleName;
|
|
||||||
data.Id = npdm.Aci0.TitleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return data;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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,99 +176,88 @@ 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()
|
Id = titleId,
|
||||||
|
Path = filePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
||||||
|
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
||||||
|
|
||||||
|
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
||||||
|
|
||||||
|
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
|
||||||
|
|
||||||
|
// Check if there is an update available.
|
||||||
|
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
|
||||||
|
{
|
||||||
|
// Replace the original ControlFs by the updated one.
|
||||||
|
controlFs = updatedControlFs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controlFs == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadControlData(controlFs, controlHolder.ByteSpan);
|
||||||
|
|
||||||
|
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
|
||||||
|
|
||||||
|
// Read the icon from the ControlFS and store it as a byte array
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using UniqueRef<IFile> icon = new();
|
||||||
|
|
||||||
|
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
|
using MemoryStream stream = new();
|
||||||
|
|
||||||
|
icon.Get.AsStream().CopyTo(stream);
|
||||||
|
applicationData.Icon = stream.ToArray();
|
||||||
|
}
|
||||||
|
catch (HorizonResultException)
|
||||||
|
{
|
||||||
|
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
|
||||||
{
|
{
|
||||||
Id = titleId,
|
if (entry.Name == "control.nacp")
|
||||||
Path = filePath,
|
{
|
||||||
};
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
using var icon = new UniqueRef<IFile>();
|
||||||
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
|
||||||
|
|
||||||
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
|
|
||||||
|
|
||||||
// Check if there is an update available.
|
|
||||||
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
|
|
||||||
{
|
|
||||||
// Replace the original ControlFs by the updated one.
|
|
||||||
controlFs = updatedControlFs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controlFs == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadControlData(controlFs, controlHolder.ByteSpan);
|
|
||||||
|
|
||||||
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
|
|
||||||
|
|
||||||
// Read the icon from the ControlFS and store it as a byte array
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using UniqueRef<IFile> icon = new();
|
|
||||||
|
|
||||||
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
using MemoryStream stream = new();
|
using MemoryStream stream = new();
|
||||||
|
|
||||||
icon.Get.AsStream().CopyTo(stream);
|
icon.Get.AsStream().CopyTo(stream);
|
||||||
applicationData.Icon = stream.ToArray();
|
applicationData.Icon = stream.ToArray();
|
||||||
}
|
|
||||||
catch (HorizonResultException)
|
if (applicationData.Icon != null)
|
||||||
{
|
|
||||||
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
|
|
||||||
{
|
{
|
||||||
if (entry.Name == "control.nacp")
|
break;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var icon = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
using MemoryStream stream = new();
|
|
||||||
|
|
||||||
icon.Get.AsStream().CopyTo(stream);
|
|
||||||
applicationData.Icon = stream.ToArray();
|
|
||||||
|
|
||||||
if (applicationData.Icon != null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationData.ControlHolder = controlHolder;
|
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
||||||
|
|
||||||
applications.Add(applicationData);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (MissingKeyException exception)
|
applicationData.ControlHolder = controlHolder;
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
|
applications.Add(applicationData);
|
||||||
}
|
|
||||||
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,52 +314,43 @@ 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);
|
||||||
|
|
||||||
|
int assetOffset = reader.ReadInt32();
|
||||||
|
|
||||||
|
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
|
||||||
{
|
{
|
||||||
file.Seek(24, SeekOrigin.Begin);
|
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
||||||
|
|
||||||
int assetOffset = reader.ReadInt32();
|
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
||||||
|
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
||||||
|
|
||||||
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
|
ulong nacpOffset = reader.ReadUInt64();
|
||||||
|
ulong nacpSize = reader.ReadUInt64();
|
||||||
|
|
||||||
|
// Reads and stores game icon as byte array
|
||||||
|
if (iconSize > 0)
|
||||||
{
|
{
|
||||||
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||||
|
|
||||||
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
|
|
||||||
if (iconSize > 0)
|
|
||||||
{
|
|
||||||
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
application.Icon = _nroIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the NACP data
|
|
||||||
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
|
||||||
|
|
||||||
GetApplicationInformation(ref controlHolder.Value, ref application);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
application.Icon = _nroIcon;
|
application.Icon = _nroIcon;
|
||||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
application.ControlHolder = controlHolder;
|
// Read the NACP data
|
||||||
applications.Add(application);
|
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
|
|
||||||
|
|
||||||
return false;
|
GetApplicationInformation(ref controlHolder.Value, ref application);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
application.Icon = _nroIcon;
|
||||||
|
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
application.ControlHolder = controlHolder;
|
||||||
|
applications.Add(application);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -377,34 +363,21 @@ namespace Ryujinx.UI.App.Common
|
|||||||
}
|
}
|
||||||
case ".nca":
|
case ".nca":
|
||||||
{
|
{
|
||||||
try
|
ApplicationData application = new();
|
||||||
|
|
||||||
|
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
|
||||||
|
|
||||||
|
if (!nca.IsProgram() || nca.IsPatch())
|
||||||
{
|
{
|
||||||
ApplicationData application = new();
|
|
||||||
|
|
||||||
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
|
|
||||||
|
|
||||||
if (!nca.IsProgram() || nca.IsPatch())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
application.Icon = _ncaIcon;
|
|
||||||
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
|
||||||
application.ControlHolder = controlHolder;
|
|
||||||
|
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
application.Icon = _ncaIcon;
|
||||||
|
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
||||||
|
application.ControlHolder = controlHolder;
|
||||||
|
|
||||||
|
applications.Add(application);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// If its an NSO we just set defaults
|
// If its an NSO we just set defaults
|
||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -367,32 +367,24 @@ 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);
|
float scaleX = e.FlipX ? -1 : 1;
|
||||||
|
float scaleY = e.FlipY ? -1 : 1;
|
||||||
|
|
||||||
canvas.Clear(SKColors.Transparent);
|
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
|
||||||
|
|
||||||
float scaleX = e.FlipX ? -1 : 1;
|
canvas.SetMatrix(matrix);
|
||||||
float scaleY = e.FlipY ? -1 : 1;
|
canvas.DrawBitmap(bitmap, SKPoint.Empty);
|
||||||
|
|
||||||
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
|
SaveBitmapAsPng(bitmapToSave, path);
|
||||||
|
|
||||||
canvas.SetMatrix(matrix);
|
|
||||||
|
|
||||||
canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
Reference in New Issue
Block a user