private void AddUpdate(string path, bool showErrorDialog = true) { if (File.Exists(path)) { using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); } } foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); try { Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" == _titleId)
private static IFileSystem GetControlFs(PartitionFileSystem Pfs) { Nca controlNca = null; // Add keys to keyset if needed foreach (DirectoryEntryEx ticketEntry in Pfs.EnumerateEntries("/", "*.tik")) { Result result = Pfs.OpenFile(out IFile ticketFile, ticketEntry.FullPath, OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } // Find the Control NCA and store it in variable called controlNca foreach (DirectoryEntryEx fileEntry in Pfs.EnumerateEntries("/", "*.nca")) { Pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } // Return the ControlFS return(controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None)); }
private static void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId) { Nca controlNca = null; // Add keys to key set if needed foreach (DirectoryEntryEx ticketEntry in pfs.EnumerateEntries("/", "*.tik")) { Result result = pfs.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); } } // Find the Control NCA and store it in variable called controlNca foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } // Return the ControlFS controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); titleId = controlNca?.Header.TitleId.ToString("x16"); }
private static IFileSystem GetControlFs(PartitionFileSystem Pfs) { Nca controlNca = null; // Add keys to keyset if needed foreach (DirectoryEntry ticketEntry in Pfs.EnumerateEntries("*.tik")) { Ticket ticket = new Ticket(Pfs.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(KeySet)); } } // Find the Control NCA and store it in variable called controlNca foreach (DirectoryEntry fileEntry in Pfs.EnumerateEntries("*.nca")) { Nca nca = new Nca(KeySet, Pfs.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage()); if (nca.Header.ContentType == ContentType.Control) { controlNca = nca; } } // Return the ControlFS return(controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None)); }
public void LoadNsp(string nspFile) { FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read); PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath, OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } else { mainNca = nca; } } else if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } if (mainNca != null) { LoadNca(mainNca, patchNca, controlNca); return; } // This is not a normal NSP, it's actually a ExeFS as a NSP LoadExeFs(nsp, out _); }
public void LoadNsp(string nspFile) { FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read); PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntry ticketEntry in nsp.EnumerateEntries("*.tik")) { Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(KeySet)); } } Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; foreach (DirectoryEntry fileEntry in nsp.EnumerateEntries("*.nca")) { IStorage ncaStorage = nsp.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage(); Nca nca = new Nca(KeySet, ncaStorage); if (nca.Header.ContentType == ContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, ContentType.Program); if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } else { mainNca = nca; } } else if (nca.Header.ContentType == ContentType.Control) { controlNca = nca; } } if (mainNca != null) { LoadNca(mainNca, patchNca, controlNca); return; } // This is not a normal NSP, it's actually a ExeFS as a NSP LoadExeFs(nsp, out _); }
private (Nca main, Nca patch, Nca control) GetGameData(PartitionFileSystem pfs) { Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; _fileSystem.ImportTickets(pfs); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_fileSystem.KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } else { mainNca = nca; } } else if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } return(mainNca, patchNca, controlNca); }
private static bool IsUpdateApplied(string titleId, out string version) { string jsonPath = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "updates.json"); if (File.Exists(jsonPath)) { string updatePath = JsonHelper.DeserializeFromFile <TitleUpdateMetadata>(jsonPath).Selected; if (!File.Exists(updatePath)) { version = ""; return(false); } using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); _virtualFileSystem.ImportTickets(nsp); foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); try { Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) { if (mainNca.Header.ContentType != NcaContentType.Program) { Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); return; } IStorage dataStorage = null; IFileSystem codeFs = null; // Load Update string titleUpdateMetadataPath = Path.Combine(_fileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"); if (File.Exists(titleUpdateMetadataPath)) { string updatePath = JsonHelper.DeserializeFromFile <TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; if (File.Exists(updatePath)) { FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); _fileSystem.ImportTickets(nsp); foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_fileSystem.KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) { if (mainNca.Header.ContentType != NcaContentType.Program) { Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); return; } IStorage dataStorage = null; IFileSystem codeFs = null; if (File.Exists(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"))) { using (Stream stream = File.OpenRead(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"))) { IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); string updatePath = JsonSerializer.Deserialize <TitleUpdateMetadata>(stream, resolver).Selected; if (File.Exists(updatePath)) { FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
public static IStorage ProcessPartitionFileSystem(PartitionFileSystem pfs, PartitionFileSystemType pfsType, bool compress) { PartitionFileSystemBuilder pfsBuilder = new PartitionFileSystemBuilder(); foreach (DirectoryEntryEx ticketEntry in pfs.EnumerateEntries("/", "*.tik")) { Result result = pfs.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries()) { pfs.OpenFile(out IFile file, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); if (compress && Path.GetExtension(fileEntry.Name).ToLower() == ".nca") { (IStorage processedNca, byte[] metaBuffer) = ProcessNca(file.AsStorage()); IFile zcaFile = new ZraCompressionStorageHack(processedNca, CompressionLevel, FrameSize, metaBuffer, TempPath).AsFile(OpenMode.Read); pfsBuilder.AddFile($"{Path.GetFileNameWithoutExtension(fileEntry.Name)}.zca", zcaFile); } else if (!compress && Path.GetExtension(fileEntry.Name).ToLower() == ".zca") { IStorage ncaStorage = new ZraDecompressionStream(file.AsStream()).AsStorage(); IFile ncaFile = new Nca(KeySet, ncaStorage).OpenEncryptedNca().AsFile(OpenMode.Read); pfsBuilder.AddFile($"{Path.GetFileNameWithoutExtension(fileEntry.Name)}.nca", ncaFile); } else { pfsBuilder.AddFile(fileEntry.Name, file); } } return(pfsBuilder.Build(pfsType)); }
private static bool IsUpdateApplied(string titleId, out string version) { string jsonPath = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "updates.json"); if (File.Exists(jsonPath)) { using (Stream stream = File.OpenRead(jsonPath)) { IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); string updatePath = JsonSerializer.Deserialize <TitleUpdateMetadata>(stream, resolver).Selected; if (!File.Exists(updatePath)) { version = ""; return(false); } using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); } } foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
private async Task AddDownloadableContent(string path) { if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) { return; } using (FileStream containerFile = File.OpenRead(path)) { PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); bool containsDownloadableContent = false; VirtualFileSystem.ImportTickets(pfs); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path); if (nca == null) { continue; } if (nca.Header.ContentType == NcaContentType.PublicData) { if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId) { break; } DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); containsDownloadableContent = true; } } if (!containsDownloadableContent) { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]); } } }
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) { Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; fileSystem.ImportTickets(pfs); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); if (ncaProgramIndex != programIndex) { continue; } if (nca.Header.ContentType == NcaContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } else { mainNca = nca; } } else if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } return(mainNca, patchNca, controlNca); }
private void AddUpdate(string path, bool showErrorDialog = true) { if (File.Exists(path)) { using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); _virtualFileSystem.ImportTickets(nsp); foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); try { Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" == _titleId)
public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) { Nca patchNca = null; Nca controlNca = null; fileSystem.ImportTickets(pfs); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(fileSystem.KeySet, ncaFile.AsStorage()); int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); if (ncaProgramIndex != programIndex) { continue; } if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
private static void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId) { Nca controlNca = null; // Add keys to key set if needed _virtualFileSystem.ImportTickets(pfs); // Find the Control NCA and store it in variable called controlNca foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Control) { controlNca = nca; } } // Return the ControlFS controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); titleId = controlNca?.Header.TitleId.ToString("x16"); }
public static void LoadApplications(List <string> appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _loadingError = false; _virtualFileSystem = virtualFileSystem; _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.PrintWarning(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"; string saveDataPath = null; 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") { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType == NcaContentType.Program && !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; Result result = pfs.OpenFile(out IFile npdmFile, "/main.npdm".ToU8Span(), OpenMode.Read); if (ResultFs.PathNotFound.Includes(result)) { Npdm npdm = new Npdm(npdmFile.AsStream()); titleName = npdm.TitleName; titleId = npdm.Aci0.TitleId.ToString("x16"); } } else { // Store the ControlFS in variable called controlFs GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); ReadControlData(controlFs, controlHolder.ByteSpan); // Creates NACP class from the NACP file controlFs.OpenFile(out IFile controlNacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); // Get the title name, title ID, developer name and version number from the NACP version = IsUpdateApplied(titleId, out string updateVersion) ? updateVersion : controlHolder.Value.DisplayVersion.ToString(); GetNameIdDeveloper(ref controlHolder.Value, out titleName, out _, out developer); // Read the icon from the ControlFS and store it as a byte array try { controlFs.OpenFile(out IFile icon, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } controlFs.OpenFile(out IFile icon, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.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.PrintWarning(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.PrintWarning(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.PrintWarning(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); Logger.PrintDebug(LogClass.Application, exception.ToString()); numApplicationsFound--; _loadingError = true; 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); // Get the title name, title ID, developer name and version number from the NACP version = controlHolder.Value.DisplayVersion.ToString(); GetNameIdDeveloper(ref controlHolder.Value, out titleName, out titleId, out developer); } else { applicationIcon = _nroIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } } catch { Logger.PrintWarning(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.Header.GetFsHeader(dataIndex).IsPatchSection()) { numApplicationsFound--; continue; } } catch (InvalidDataException) { Logger.PrintWarning(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.PrintWarning(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); numApplicationsFound--; _loadingError = true; 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.PrintWarning(LogClass.Application, exception.Message); numApplicationsFound--; _loadingError = true; continue; } ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNum)) { SaveDataFilter filter = new SaveDataFilter(); filter.SetUserId(new UserId(1, 0)); filter.SetProgramId(new TitleId(titleIdNum)); Result result = virtualFileSystem.FsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); if (result.IsSuccess()) { saveDataPath = Path.Combine(virtualFileSystem.GetNandPath(), "user", "save", saveDataInfo.SaveDataId.ToString("x16")); } } 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, SaveDataPath = saveDataPath, ControlHolder = controlHolder }; numApplicationsLoaded++; OnApplicationAdded(new ApplicationAddedEventArgs() { AppData = data }); OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); } OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); if (_loadingError) { Gtk.Application.Invoke(delegate { GtkDialog.CreateErrorDialog("One or more files encountered could not be loaded, check logs for more info."); }); } }
private void ImportGameDataClicked(object sender, RoutedEventArgs arg) { string filter = @" Any game data (*.xci,*.nca,*.nsp)|*.xci;*.nca;*.nsp| NX Card Image (*.xci)|*.xci| Nintendo Content Archive (*.nca)|*.nca| Nintendo Installable Package (*.nsp)|*.nsp ".FilterMultilineString(); FileInfo[] files = RequestOpenFilesFromUser(".*", filter, "Select game data..."); if (files == null) { return; } TaskManagerPage.Current.Queue.Submit(new RunTask("Processing imported game data...", new Task(() => { IEnumerable <FileInfo> ncas = files.Where((f) => { try { new Nca(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> xcis = files.Except(ncas).Where((f) => { try { new Xci(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> nsp = files.Except(ncas).Except(xcis).Where((f) => { try { new PartitionFileSystem(new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); List <SwitchFs> switchFilesystems = new List <SwitchFs>(); if (ncas.Any()) { switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, ncas.MakeFs())); } foreach (FileInfo file in xcis) { Xci xci = new Xci(HACGUIKeyset.Keyset, new LocalFile(file.FullName, OpenMode.Read).AsStorage()); switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, xci.OpenPartition(XciPartitionType.Secure))); } bool foundTicket = true; foreach (FileInfo file in nsp) { PartitionFileSystem fs = new PartitionFileSystem(new LocalFile(file.FullName, OpenMode.Read).AsStorage()); foreach (DirectoryEntry d in fs.EnumerateEntries().Where(e => e.Type == DirectoryEntryType.File && e.Name.EndsWith(".tik"))) { using (IFile tikFile = fs.OpenFile(d.FullPath, OpenMode.Read)) { Ticket t = new Ticket(new BinaryReader(tikFile.AsStream())); try { HACGUIKeyset.Keyset.TitleKeys[t.RightsId] = t.GetTitleKey(HACGUIKeyset.Keyset); foundTicket = true; } catch (Exception e) { MessageBox.Show("Failed to import .tik file included in NSP."); } } } switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, fs)); } if (foundTicket) { TaskManagerPage.Current.Queue.Submit(new SaveKeysetTask(Preferences.Current.DefaultConsoleName)); } foreach (SwitchFs fs in switchFilesystems) { DeviceService.FsView.LoadFileSystemAsync("Opening imported data...", () => fs, FSView.TitleSource.Imported, false); } }))); }
public static void LoadApplications(List <string> appDirs, Keyset keySet, TitleLanguage desiredTitleLanguage, FileSystemClient fsClient = null, VirtualFileSystem vfs = null) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _keySet = keySet; _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) == false) { Logger.PrintWarning(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\""); continue; } foreach (string app in Directory.GetFiles(appDir, "*.*", SearchOption.AllDirectories)) { if ((Path.GetExtension(app) == ".xci") || (Path.GetExtension(app) == ".nro") || (Path.GetExtension(app) == ".nso") || (Path.GetFileName(app) == "hbl.nsp")) { applications.Add(app); numApplicationsFound++; } else if ((Path.GetExtension(app) == ".nsp") || (Path.GetExtension(app) == ".pfs0")) { try { bool hasMainNca = false; PartitionFileSystem nsp = new PartitionFileSystem(new FileStream(app, FileMode.Open, FileAccess.Read).AsStorage()); foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_keySet, ncaFile.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType == NcaContentType.Program && !nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { hasMainNca = true; } } if (!hasMainNca) { continue; } } catch (InvalidDataException) { Logger.PrintWarning(LogClass.Application, $"{app}: The header key is incorrect or missing and therefore the NCA header content type check has failed."); } applications.Add(app); numApplicationsFound++; } else if (Path.GetExtension(app) == ".nca") { try { Nca nca = new Nca(_keySet, new FileStream(app, FileMode.Open, FileAccess.Read).AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType != NcaContentType.Program || nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { continue; } } catch (InvalidDataException) { Logger.PrintWarning(LogClass.Application, $"{app}: The header key is incorrect or missing and therefore the NCA header content type check has failed."); } 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"; string saveDataPath = null; byte[] applicationIcon = null; using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if ((Path.GetExtension(applicationPath) == ".nsp") || (Path.GetExtension(applicationPath) == ".pfs0") || (Path.GetExtension(applicationPath) == ".xci")) { try { PartitionFileSystem pfs; if (Path.GetExtension(applicationPath) == ".xci") { Xci xci = new Xci(_keySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); } // Store the ControlFS in variable called controlFs IFileSystem controlFs = GetControlFs(pfs); // If this is null then this is probably not a normal NSP, it's probably an ExeFS as an NSP if (controlFs == null) { applicationIcon = _nspIcon; Result result = pfs.OpenFile(out IFile npdmFile, "/main.npdm", OpenMode.Read); if (result != ResultFs.PathNotFound) { Npdm npdm = new Npdm(npdmFile.AsStream()); titleName = npdm.TitleName; titleId = npdm.Aci0.TitleId.ToString("x16"); } } else { // Creates NACP class from the NACP file controlFs.OpenFile(out IFile controlNacpFile, "/control.nacp", OpenMode.Read).ThrowIfFailure(); Nacp controlData = new Nacp(controlNacpFile.AsStream()); // Get the title name, title ID, developer name and version number from the NACP version = controlData.DisplayVersion; titleName = controlData.Descriptions[(int)_desiredTitleLanguage].Title; if (string.IsNullOrWhiteSpace(titleName)) { titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; } titleId = controlData.PresenceGroupId.ToString("x16"); if (string.IsNullOrWhiteSpace(titleId)) { titleId = controlData.SaveDataOwnerId.ToString("x16"); } if (string.IsNullOrWhiteSpace(titleId)) { titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16"); } developer = controlData.Descriptions[(int)_desiredTitleLanguage].Developer; if (string.IsNullOrWhiteSpace(developer)) { developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer; } // Read the icon from the ControlFS and store it as a byte array try { controlFs.OpenFile(out IFile icon, $"/icon_{_desiredTitleLanguage}.dat", OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } controlFs.OpenFile(out IFile icon, entry.FullPath, OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } if (applicationIcon != null) { break; } } if (applicationIcon == null) { applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException exception) { applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon; Logger.PrintWarning(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } catch (InvalidDataException) { applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon; Logger.PrintWarning(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); } } else if (Path.GetExtension(applicationPath) == ".nro") { BinaryReader reader = new BinaryReader(file); byte[] Read(long position, int size) { file.Seek(position, SeekOrigin.Begin); return(reader.ReadBytes(size)); } 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); // Creates memory stream out of byte array which is the NACP using (MemoryStream stream = new MemoryStream(Read(assetOffset + (int)nacpOffset, (int)nacpSize))) { // Creates NACP class from the memory stream Nacp controlData = new Nacp(stream); // Get the title name, title ID, developer name and version number from the NACP version = controlData.DisplayVersion; titleName = controlData.Descriptions[(int)_desiredTitleLanguage].Title; if (string.IsNullOrWhiteSpace(titleName)) { titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; } titleId = controlData.PresenceGroupId.ToString("x16"); if (string.IsNullOrWhiteSpace(titleId)) { titleId = controlData.SaveDataOwnerId.ToString("x16"); } if (string.IsNullOrWhiteSpace(titleId)) { titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16"); } developer = controlData.Descriptions[(int)_desiredTitleLanguage].Developer; if (string.IsNullOrWhiteSpace(developer)) { developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer; } } } else { applicationIcon = _nroIcon; } } // If its an NCA or NSO we just set defaults else if ((Path.GetExtension(applicationPath) == ".nca") || (Path.GetExtension(applicationPath) == ".nso")) { applicationIcon = Path.GetExtension(applicationPath) == ".nca" ? _ncaIcon : _nsoIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } } ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNum)) { SaveDataFilter filter = new SaveDataFilter(); filter.SetUserId(new UserId(1, 0)); filter.SetTitleId(new TitleId(titleIdNum)); Result result = fsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); if (result.IsSuccess()) { saveDataPath = Path.Combine(vfs.GetNandPath(), $"user/save/{saveDataInfo.SaveDataId:x16}"); } } 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, SaveDataPath = saveDataPath }; numApplicationsLoaded++; OnApplicationAdded(new ApplicationAddedEventArgs() { AppData = data, NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); } }
public byte[] GetApplicationIcon(string applicationPath) { byte[] applicationIcon = null; try { // Look for icon only if applicationPath is not a directory if (!Directory.Exists(applicationPath)) { string extension = Path.GetExtension(applicationPath).ToLower(); using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { try { PartitionFileSystem pfs; bool isExeFs = false; if (extension == ".xci") { Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { isExeFs = true; } } } if (isExeFs) { applicationIcon = _nspIcon; } else { // Store the ControlFS in variable called controlFs GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); // Read the icon from the ControlFS and store it as a byte array try { 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 = extension == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } catch (InvalidDataException) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } catch (Exception exception) { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); } } else if (extension == ".nro") { BinaryReader reader = new(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); // Reads and stores game icon as byte array applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); } else { applicationIcon = _nroIcon; } } catch { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); } } else if (extension == ".nca") { applicationIcon = _ncaIcon; } // If its an NSO we just set defaults else if (extension == ".nso") { applicationIcon = _nsoIcon; } } } } catch (Exception) { Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); } return(applicationIcon ?? _ncaIcon); }
private void AddButton_Clicked(object sender, EventArgs args) { FileChooserNative fileChooser = new FileChooserNative("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") { SelectMultiple = true }; FileFilter filter = new FileFilter() { Name = "Switch Game DLCs" }; filter.AddPattern("*.nsp"); fileChooser.AddFilter(filter); if (fileChooser.Run() == (int)ResponseType.Accept) { foreach (string containerPath in fileChooser.Filenames) { if (!File.Exists(containerPath)) { return; } using (FileStream containerFile = File.OpenRead(containerPath)) { PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); bool containsDlc = false; _virtualFileSystem.ImportTickets(pfs); TreeIter?parentIter = null; foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.AsStorage(), containerPath); if (nca == null) { continue; } if (nca.Header.ContentType == NcaContentType.PublicData) { if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) { break; } parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath); ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); containsDlc = true; } } if (!containsDlc) { GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); } } } } fileChooser.Dispose(); }
public void ShowNcaInfo() { Window.RemoveAll(); string NcaPath = (string)Utils.FirmwareUtils.OpenNcaStorageByTitleName(Path, FwTui.NcaNames[FwTui.FirmwareListView.SelectedItem], true); NcaInfo ncaInfo = new NcaInfo(new LocalStorage(NcaPath, FileAccess.Read)); List <string> NcaInfoLines = new List <string>(); string FormattedTid = $"{ncaInfo.Nca.Header.TitleId:X16}"; string FormattedName = $"{ncaInfo.TitleName}"; NcaInfoLines.Add($"Title ID: {FormattedTid}"); if (FormattedName != FormattedTid) { NcaInfoLines.Add($"Title Name: {FormattedName}"); } NcaInfoLines.Add($"Content Type: {ncaInfo.Nca.Header.ContentType}"); string ncaID = NcaPath.Split("/").Last(); if (ncaID == "00.nca") { ncaID = NcaPath.Split("/")[NcaPath.Split("/").Length - 2]; } else { ncaID = ncaID.Replace(".nca", ""); } NcaInfoLines.Add($"Nca ID: {ncaID}"); for (NcaSectionType section = NcaSectionType.Code; section <= NcaSectionType.Logo; section++) { if (ncaInfo.Nca.SectionExists(section)) { NcaInfoLines.Add($"\n{section}"); try { using (PartitionFileSystem pfs = new PartitionFileSystem(ncaInfo.TryOpenStorageSection(section))) { foreach (DirectoryEntryEx dirEnt in pfs.EnumerateEntries()) { NcaInfoLines.Add($"{dirEnt.Name} - {dirEnt.Size} bytes"); } } } catch (LibHac.HorizonResultException) { } } } //Actually draw them int Y = 1; foreach (string content in NcaInfoLines) { int lines = System.Text.RegularExpressions.Regex.Matches(content, "\n").Count + 1; Window.Add(new Label(1, Y, content)); Y += lines; } }
public void LoadApplications(List <string> appDirs, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _desiredTitleLanguage = desiredTitleLanguage; _cancellationToken = new CancellationTokenSource(); // Builds the applications list with paths to found applications List <string> applications = new List <string>(); try { foreach (string appDir in appDirs) { if (_cancellationToken.Token.IsCancellationRequested) { return; } 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 (_cancellationToken.Token.IsCancellationRequested) { return; } string extension = Path.GetExtension(app).ToLower(); if ((extension == ".nsp") || (extension == ".pfs0") || (extension == ".xci") || (extension == ".nca") || (extension == ".nro") || (extension == ".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) { if (_cancellationToken.Token.IsCancellationRequested) { return; } 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 { string extension = Path.GetExtension(applicationPath).ToLower(); using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { try { PartitionFileSystem pfs; bool isExeFs = false; if (extension == ".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 = extension == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException exception) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } catch (InvalidDataException) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"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 (extension == ".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 (extension == ".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 (extension == ".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 }); } finally { _cancellationToken.Dispose(); _cancellationToken = null; } }