|
@@ -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))
|
|
|
|
|
- {
|
|
|
|
|
- Npdm npdm = new(npdmFile.Get.AsStream());
|
|
|
|
|
|
|
+ Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
|
|
|
|
|
|
|
- data.Name = npdm.TitleName;
|
|
|
|
|
- data.Id = npdm.Aci0.TitleId;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return data;
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception exception)
|
|
|
|
|
|
|
+ if (ResultFs.PathNotFound.Includes(result))
|
|
|
{
|
|
{
|
|
|
- Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
|
|
|
|
|
|
|
+ Npdm npdm = new(npdmFile.Get.AsStream());
|
|
|
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ data.Name = npdm.TitleName;
|
|
|
|
|
+ data.Id = npdm.Aci0.TitleId;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ 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,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ Id = titleId,
|
|
|
|
|
+ Path = filePath,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
|
|
|
|
- Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
|
|
|
|
|
|
+ Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
|
|
|
|
|
+ Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
|
|
|
|
|
|
|
|
- BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
|
|
|
|
|
|
+ BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
|
|
|
|
|
|
|
- IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
|
|
|
|
|
|
|
+ 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;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 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;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (controlFs == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ReadControlData(controlFs, controlHolder.ByteSpan);
|
|
|
|
|
|
|
+ ReadControlData(controlFs, controlHolder.ByteSpan);
|
|
|
|
|
|
|
|
- GetApplicationInformation(ref controlHolder.Value, ref applicationData);
|
|
|
|
|
|
|
+ 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();
|
|
|
|
|
|
|
+ // 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();
|
|
|
|
|
|
|
+ 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);
|
|
|
|
|
- applicationData.Icon = stream.ToArray();
|
|
|
|
|
- }
|
|
|
|
|
- catch (HorizonResultException)
|
|
|
|
|
|
|
+ icon.Get.AsStream().CopyTo(stream);
|
|
|
|
|
+ applicationData.Icon = 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;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- using var icon = new UniqueRef<IFile>();
|
|
|
|
|
|
|
+ using var icon = new UniqueRef<IFile>();
|
|
|
|
|
|
|
|
- controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
|
|
|
|
|
+ controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
|
|
|
|
|
|
- using MemoryStream stream = new();
|
|
|
|
|
|
|
+ using MemoryStream stream = new();
|
|
|
|
|
|
|
|
- icon.Get.AsStream().CopyTo(stream);
|
|
|
|
|
- applicationData.Icon = stream.ToArray();
|
|
|
|
|
|
|
+ icon.Get.AsStream().CopyTo(stream);
|
|
|
|
|
+ applicationData.Icon = stream.ToArray();
|
|
|
|
|
|
|
|
- if (applicationData.Icon != null)
|
|
|
|
|
- {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (applicationData.Icon != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- applicationData.ControlHolder = controlHolder;
|
|
|
|
|
-
|
|
|
|
|
- applications.Add(applicationData);
|
|
|
|
|
|
|
+ applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- 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}");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ applicationData.ControlHolder = controlHolder;
|
|
|
|
|
+
|
|
|
|
|
+ applications.Add(applicationData);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return applications;
|
|
return applications;
|
|
@@ -319,53 +314,44 @@ 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")
|
|
|
|
|
- {
|
|
|
|
|
- byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
|
|
|
|
|
|
+ file.Seek(24, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
- long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
|
|
|
|
- long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
|
|
|
|
|
|
+ int assetOffset = reader.ReadInt32();
|
|
|
|
|
|
|
|
- ulong nacpOffset = reader.ReadUInt64();
|
|
|
|
|
- ulong nacpSize = reader.ReadUInt64();
|
|
|
|
|
|
|
+ if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
|
|
|
|
|
+ {
|
|
|
|
|
+ byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
|
|
|
|
|
|
|
|
- // Reads and stores game icon as byte array
|
|
|
|
|
- if (iconSize > 0)
|
|
|
|
|
- {
|
|
|
|
|
- application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- application.Icon = _nroIcon;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
|
|
|
|
+ long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
|
|
|
|
|
|
|
- // Read the NACP data
|
|
|
|
|
- Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
|
|
|
|
|
|
+ ulong nacpOffset = reader.ReadUInt64();
|
|
|
|
|
+ ulong nacpSize = reader.ReadUInt64();
|
|
|
|
|
|
|
|
- GetApplicationInformation(ref controlHolder.Value, ref application);
|
|
|
|
|
|
|
+ // Reads and stores game icon as byte array
|
|
|
|
|
+ if (iconSize > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
application.Icon = _nroIcon;
|
|
application.Icon = _nroIcon;
|
|
|
- application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- application.ControlHolder = controlHolder;
|
|
|
|
|
- applications.Add(application);
|
|
|
|
|
|
|
+ // Read the NACP data
|
|
|
|
|
+ Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
|
|
|
|
+
|
|
|
|
|
+ GetApplicationInformation(ref controlHolder.Value, ref application);
|
|
|
}
|
|
}
|
|
|
- catch
|
|
|
|
|
|
|
+ else
|
|
|
{
|
|
{
|
|
|
- Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
|
|
|
|
|
-
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ application.Icon = _nroIcon;
|
|
|
|
|
+ application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ application.ControlHolder = controlHolder;
|
|
|
|
|
+ applications.Add(application);
|
|
|
|
|
+
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
byte[] Read(long position, int size)
|
|
byte[] Read(long position, int size)
|
|
@@ -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())
|
|
|
|
|
- {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ApplicationData application = new();
|
|
|
|
|
|
|
|
- application.Icon = _ncaIcon;
|
|
|
|
|
- application.Name = Path.GetFileNameWithoutExtension(applicationPath);
|
|
|
|
|
- application.ControlHolder = controlHolder;
|
|
|
|
|
|
|
+ Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
|
|
|
|
|
|
|
|
- applications.Add(application);
|
|
|
|
|
- }
|
|
|
|
|
- catch (InvalidDataException)
|
|
|
|
|
|
|
+ if (!nca.IsProgram() || nca.IsPatch())
|
|
|
{
|
|
{
|
|
|
- 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)
|
|
|
{
|
|
{
|