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 (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 void LoadDownloadableContents() { foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) { if (File.Exists(downloadableContentContainer.ContainerPath)) { using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); VirtualFileSystem.ImportTickets(pfs); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) { DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath, downloadableContentNca.Enabled)); } } } } // NOTE: Save the list again to remove leftovers. Save(); }
private long OpenFileSystemFromInternalFile(ServiceCtx context, string fullPath) { DirectoryInfo archivePath = new DirectoryInfo(fullPath).Parent; while (string.IsNullOrWhiteSpace(archivePath.Extension)) { archivePath = archivePath.Parent; } if (archivePath.Extension == ".nsp" && File.Exists(archivePath.FullName)) { FileStream pfsFile = new FileStream( archivePath.FullName.TrimEnd(Path.DirectorySeparatorChar), FileMode.Open, FileAccess.Read); PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage()); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); string filename = fullPath.Replace(archivePath.FullName, string.Empty).TrimStart('\\'); if (nsp.FileExists(filename)) { return(OpenNcaFs(context, fullPath, nsp.OpenFile(filename, OpenMode.Read).AsStorage())); } } return(MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist)); }
private void LoadDlcs() { foreach (DlcContainer dlcContainer in _dlcContainerList) { using FileStream containerFile = File.OpenRead(dlcContainer.Path); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); VirtualFileSystem.ImportTickets(pfs); foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path); if (nca != null) { Dlcs.Add(new DlcModel(nca.Header.TitleId.ToString("X16"), dlcContainer.Path, dlcNca.Path, dlcNca.Enabled)); } } } }
public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel) { aocStorage = null; if (_aocData.TryGetValue(aocTitleId, out AocItem aoc) && aoc.Enabled) { var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read); using var ncaFile = new UniqueRef <IFile>(); PartitionFileSystem pfs; switch (Path.GetExtension(aoc.ContainerPath)) { case ".xci": pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); break; case ".nsp": pfs = new PartitionFileSystem(file.AsStorage()); pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); break; default: return(false); // Print error? } aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()).OpenStorage(NcaSectionType.Data, integrityCheckLevel); return(true); } return(false); }
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); }
public static ResultCode OpenFileSystemFromInternalFile(ServiceCtx context, string fullPath, out IFileSystem openedFileSystem) { openedFileSystem = null; DirectoryInfo archivePath = new DirectoryInfo(fullPath).Parent; while (string.IsNullOrWhiteSpace(archivePath.Extension)) { archivePath = archivePath.Parent; } if (archivePath.Extension == ".nsp" && File.Exists(archivePath.FullName)) { FileStream pfsFile = new FileStream( archivePath.FullName.TrimEnd(Path.DirectorySeparatorChar), FileMode.Open, FileAccess.Read); try { PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage()); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); string filename = fullPath.Replace(archivePath.FullName, string.Empty).TrimStart('\\'); using var ncaFile = new UniqueRef <LibHac.Fs.Fsa.IFile>(); Result result = nsp.OpenFile(ref ncaFile.Ref(), filename.ToU8Span(), OpenMode.Read); if (result.IsFailure()) { return((ResultCode)result.Value); } return(OpenNcaFs(context, fullPath, ncaFile.Release().AsStorage(), out openedFileSystem)); } catch (HorizonResultException ex) { return((ResultCode)ex.ResultValue.Value); } } return(ResultCode.PathDoesNotExist); }
private void ReadTitles() { foreach (Nca nca in Ncas.Values.Where(x => x.Header.ContentType == ContentType.Meta)) { var title = new Title(); // Meta contents always have 1 Partition FS section with 1 file in it IStorage sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true); var pfs0 = new PartitionFileSystem(sect); IFile file = pfs0.OpenFile(pfs0.Files[0], OpenMode.Read); var metadata = new Cnmt(file.AsStream()); title.Id = metadata.TitleId; title.Version = metadata.TitleVersion; title.Metadata = metadata; title.MetaNca = nca; title.Ncas.Add(nca); foreach (CnmtContentEntry content in metadata.ContentEntries) { string ncaId = content.NcaId.ToHexString(); if (Ncas.TryGetValue(ncaId, out Nca contentNca)) { title.Ncas.Add(contentNca); } switch (content.Type) { case CnmtContentType.Program: case CnmtContentType.Data: title.MainNca = contentNca; break; case CnmtContentType.Control: title.ControlNca = contentNca; break; } } Titles[title.Id] = title; } }
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")) { 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.TitleId.ToString("x16")[..^3]}000" != titleId)
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")) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.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(); }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_dlcWindow").Handle) { builder.Autoconnect(this); _titleId = titleId; _virtualFileSystem = virtualFileSystem; _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]"; try { _dlcContainerList = JsonHelper.DeserializeFromFile <List <DlcContainer> >(_dlcJsonPath); } catch { _dlcContainerList = new List <DlcContainer>(); } _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); CellRendererToggle enableToggle = new CellRendererToggle(); enableToggle.Toggled += (sender, args) => { _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); bool newValue = !(bool)_dlcTreeView.Model.GetValue(treeIter, 0); _dlcTreeView.Model.SetValue(treeIter, 0, newValue); if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) { do { _dlcTreeView.Model.SetValue(childIter, 0, newValue); }while (_dlcTreeView.Model.IterNext(ref childIter)); } }; _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); foreach (DlcContainer dlcContainer in _dlcContainerList) { if (File.Exists(dlcContainer.Path)) { // The parent tree item has its own "enabled" check box, but it's the actual // nca entries that store the enabled / disabled state. A bit of a UI inconsistency. // Maybe a tri-state check box would be better, but for now we check the parent // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. bool areAllContentPacksEnabled = dlcContainer.DlcNcaList.TrueForAll((nca) => nca.Enabled); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.Path); using FileStream containerFile = File.OpenRead(dlcContainer.Path); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); _virtualFileSystem.ImportTickets(pfs); foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path); if (nca != null) { ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.Path); } } } else { // DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog. TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.Path}"); } } }
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); } }))); }
static void Main(string[] args) { Console.WriteLine("Nintendo Switch NSP Verifier v1.00"); Console.WriteLine("Copyright 2018 CaitSith2"); Console.WriteLine(""); _path = args.Length >= 1 ? string.Join(" ", args) : Environment.CurrentDirectory; if (new[] { "--help", "-h" }.Any(x => x.Equals(_path, StringComparison.InvariantCultureIgnoreCase))) { Console.WriteLine("Usage: NSPVerify [path to NSP directory]"); Console.WriteLine(""); Console.WriteLine("If the tool is run without specifying a path, it will look for NSPs in the current directory and ALL sub-directories of current directory"); return; } if (!Directory.Exists(_path)) { Console.WriteLine("ERROR: Specified directory does not exist. specify --help for usage information."); return; } IFileSystem fs = new LocalFileSystem(_path); var keys = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch", "prod.keys"); if (File.Exists("keys.txt")) { keys = "keys.txt"; } if (!File.Exists(keys)) { Console.WriteLine($"Cannot verify NSPs without keys.txt. Either put it in the same directory as this tool,"); Console.WriteLine($"or place it in \"{Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch")}\" named as prod.keys"); PressAnyKey(); return; } var keyset = ExternalKeys.ReadKeyFile(keys); var badlist = new List <string>(); var exceptionlist = new List <string>(); var files = new List <DirectoryEntry>(); files.AddRange(fs.EnumerateEntries("*.nsp", SearchOptions.RecurseSubdirectories)); files.AddRange(fs.EnumerateEntries("*.nsx", SearchOptions.RecurseSubdirectories)); if (files.Count == 0) { Console.WriteLine("Error: No NSP/NSX files in specified directory"); PressAnyKey(); return; } foreach (DirectoryEntry dentry in files) { var file = dentry.FullPath; var filename = Path.GetFileName(file); var relativefilename = Util.GetRelativePath(file, "/"); Console.Write($"Checking {filename}: "); try { bool ok = true; using (var nspfile = fs.OpenFile(file, OpenMode.Read)) { var nspdata = new PartitionFileSystem(new FileStorage(nspfile)); var cnmtfile = nspdata.Files.FirstOrDefault(x => x.Name.ToLowerInvariant().EndsWith(".cnmt.nca")); if (cnmtfile == null) { Console.WriteLine($"\rChecking {filename}: No cnmt.nca file present"); badlist.Add(relativefilename); continue; } var cnmtdata = nspdata.OpenFile(cnmtfile, OpenMode.Read); Cnmt cnmt; using (var sr = new BinaryReader(cnmtdata.AsStream())) { var cnmthash = SHA256.Create().ComputeHash(sr.ReadBytes((int)cnmtdata.GetSize())); if (!cnmtfile.Name.ToLowerInvariant().Contains(cnmthash.Take(16).ToArray().ToHexString())) { //Put failure here Console.WriteLine($"\rChecking {filename}: cnmt.nca file is corrupted"); badlist.Add(relativefilename); cnmtdata.Dispose(); continue; } sr.BaseStream.Position = 0; var cnmtnca = new Nca(keyset, new FileStorage(cnmtdata)); var section = cnmtnca.OpenStorage(0, IntegrityCheckLevel.ErrorOnInvalid); var sectionpfs = new PartitionFileSystem(section); cnmt = new Cnmt(sectionpfs.OpenFile(sectionpfs.Files[0], OpenMode.Read).AsStream()); } foreach (var entry in cnmt.ContentEntries) { var entryfile = nspdata.Files.FirstOrDefault(x => x.Name.ToLowerInvariant().EndsWith(entry.NcaId.ToHexString() + ".nca")); if (entryfile == null) { if (entry.Type != CnmtContentType.DeltaFragment) { //Put failure here Console.WriteLine($"\rChecking {filename}: one of the entries required by the cnmt.nca is missing."); badlist.Add(relativefilename); break; } continue; } using (var entrynca = nspdata.OpenFile(entryfile, OpenMode.Read)) { var hash = SHA256.Create(); using (var sr = new BinaryReader(entrynca.AsStream())) { while (entrynca.GetSize() != sr.BaseStream.Position) { var entryncadata = sr.ReadBytes(0x100000); hash.TransformBlock(entryncadata, 0, entryncadata.Length, entryncadata, 0); Console.Write($"\rChecking {filename}: {((sr.BaseStream.Position * 100.0) / entrynca.GetSize()):0.0}%"); } hash.TransformFinalBlock(new byte[0], 0, 0); } if (hash.Hash.ToHexString().Equals(entry.Hash.ToHexString())) { Console.Write($"\rChecking {filename}: {100:0.0}%"); continue; } //Put failure here Console.WriteLine($"\rChecking {filename}: one of the entries required by the cnmt.nca is corrupted"); badlist.Add(relativefilename); ok = false; break; } } if (ok) { Console.WriteLine($"\rChecking {filename}: OK "); } //Put Success here } } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); exceptionlist.Add($"{relativefilename}{Environment.NewLine}Exception: \"{ex.GetType()}\" {ex.Message}{Environment.NewLine}Stack Trace: {ex.StackTrace}{Environment.NewLine}"); } } badlist.Insert(0, badlist.Count == 0 ? "None of the files are corrupted. :)" : "The following NSP/NSX files are corrupted:"); exceptionlist.Insert(0, exceptionlist.Count == 0 ? "No exceptions to log. :)" : "Exceptions caused while parsing the following NSP/NSX files:"); try { File.WriteAllText("Corrupted NSPs.txt", string.Join(Environment.NewLine, badlist)); File.WriteAllText("Exception Log.txt", string.Join(Environment.NewLine, exceptionlist)); } catch (Exception ex) { Console.WriteLine($"Could not write the output files \"Corrupted NSPs.txt\" and \"Exception Log.txt\" due to the following exception."); Console.WriteLine($"Exception: \"{ex.GetType()}\" {ex.Message}"); Console.WriteLine($"Stack Trace: {ex.StackTrace}{Environment.NewLine}"); Console.WriteLine(string.Join(Environment.NewLine, badlist)); Console.WriteLine(); Console.WriteLine(string.Join(Environment.NewLine, exceptionlist)); Console.WriteLine(); } Console.WriteLine("Done."); PressAnyKey(); }
public void LoadApplications(List <string> appDirs, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _desiredTitleLanguage = desiredTitleLanguage; // Builds the applications list with paths to found applications List <string> applications = new List <string>(); foreach (string appDir in appDirs) { if (!Directory.Exists(appDir)) { Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\""); continue; } foreach (string app in GetFilesInDirectory(appDir)) { if ((Path.GetExtension(app).ToLower() == ".nsp") || (Path.GetExtension(app).ToLower() == ".pfs0") || (Path.GetExtension(app).ToLower() == ".xci") || (Path.GetExtension(app).ToLower() == ".nca") || (Path.GetExtension(app).ToLower() == ".nro") || (Path.GetExtension(app).ToLower() == ".nso")) { applications.Add(app); numApplicationsFound++; } } } // Loops through applications list, creating a struct and then firing an event containing the struct for each application foreach (string applicationPath in applications) { double fileSize = new FileInfo(applicationPath).Length * 0.000000000931; string titleName = "Unknown"; string titleId = "0000000000000000"; string developer = "Unknown"; string version = "0"; byte[] applicationIcon = null; BlitStruct <ApplicationControlProperty> controlHolder = new BlitStruct <ApplicationControlProperty>(1); try { using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if ((Path.GetExtension(applicationPath).ToLower() == ".nsp") || (Path.GetExtension(applicationPath).ToLower() == ".pfs0") || (Path.GetExtension(applicationPath).ToLower() == ".xci")) { try { PartitionFileSystem pfs; bool isExeFs = false; if (Path.GetExtension(applicationPath).ToLower() == ".xci") { Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. bool hasMainNca = false; foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); // Some main NCAs don't have a data partition, so check if the partition exists before opening it if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) { hasMainNca = true; break; } } else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { isExeFs = true; } } if (!hasMainNca && !isExeFs) { numApplicationsFound--; continue; } } if (isExeFs) { applicationIcon = _nspIcon; using var npdmFile = new UniqueRef <IFile>(); Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); if (ResultFs.PathNotFound.Includes(result)) { Npdm npdm = new Npdm(npdmFile.Get.AsStream()); titleName = npdm.TitleName; titleId = npdm.Aci0.TitleId.ToString("x16"); } } else { // Store the ControlFS in variable called controlFs GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); ReadControlData(controlFs, controlHolder.ByteSpan); // 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 { using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } if (applicationIcon != null) { break; } } if (applicationIcon == null) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException exception) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } catch (InvalidDataException) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); } catch (Exception exception) { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); numApplicationsFound--; continue; } } else if (Path.GetExtension(applicationPath).ToLower() == ".nro") { BinaryReader reader = new BinaryReader(file); byte[] Read(long position, int size) { file.Seek(position, SeekOrigin.Begin); return(reader.ReadBytes(size)); } try { file.Seek(24, SeekOrigin.Begin); int assetOffset = reader.ReadInt32(); if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") { byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); ulong nacpOffset = reader.ReadUInt64(); ulong nacpSize = reader.ReadUInt64(); // Reads and stores game icon as byte array applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); // Read the NACP data Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); // 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.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); numApplicationsFound--; continue; } } else if (Path.GetExtension(applicationPath).ToLower() == ".nca") { try { Nca nca = new Nca(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) { numApplicationsFound--; continue; } } catch (InvalidDataException) { Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}"); } catch { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); numApplicationsFound--; continue; } applicationIcon = _ncaIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } // If its an NSO we just set defaults else if (Path.GetExtension(applicationPath).ToLower() == ".nso") { applicationIcon = _nsoIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } } } catch (IOException exception) { Logger.Warning?.Print(LogClass.Application, exception.Message); numApplicationsFound--; continue; } ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _)) { Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); appMetadata.LastPlayed = "Never"; } ApplicationData data = new ApplicationData { Favorite = appMetadata.Favorite, Icon = applicationIcon, TitleName = titleName, TitleId = titleId, Developer = developer, Version = version, TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed), LastPlayed = appMetadata.LastPlayed, FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MB" : fileSize.ToString("0.##") + "GB", Path = applicationPath, ControlHolder = controlHolder }; numApplicationsLoaded++; OnApplicationAdded(new ApplicationAddedEventArgs() { AppData = data }); OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); } OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); }
private void OnNandFound() { Nand nand = NANDService.NAND; FileStream pkg2file = HACGUIKeyset.TempPkg2FileInfo.Create(); IStorage pkg2nand = nand.OpenPackage2(0); byte[] pkg2raw = new byte[0x7FC000]; pkg2nand.Read(pkg2raw, 0x4000); MemoryStorage pkg2memory = new MemoryStorage(pkg2raw); pkg2memory.CopyToStream(pkg2file); Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, pkg2memory); HACGUIKeyset.RootTempPkg2FolderInfo.Create(); FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create(); FileStream INI1stream = HACGUIKeyset.TempINI1FileInfo.Create(); pkg2.OpenKernel().CopyToStream(kernelstream); pkg2.OpenIni1().CopyToStream(INI1stream); kernelstream.Close(); INI1stream.Close(); Ini1 INI1 = new Ini1(pkg2.OpenIni1()); List <HashSearchEntry> hashes = new List <HashSearchEntry>(); Dictionary <byte[], byte[]> keys = new Dictionary <byte[], byte[]>(); HACGUIKeyset.RootTempINI1Folder.Create(); foreach (Kip kip in INI1.Kips) { Stream rodatastream, datastream; switch (kip.Header.Name) { case "FS": hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyApplicationSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyOceanSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeySystemSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKeySourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyApplicationSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyApplicationSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyOceanSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyOceanSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeySystemSourceHash], HACGUIKeyset.Keyset.KeyAreaKeySystemSource, 0x10); Array.Copy(keys[NintendoKeys.HeaderKekSourceHash], HACGUIKeyset.Keyset.HeaderKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKekSourceHash], HACGUIKeyset.Keyset.SaveMacKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKeySourceHash], HACGUIKeyset.Keyset.SaveMacKeySource, 0x10); hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); bool sdWarn = false; hashes.Add(new HashSearchEntry(NintendoKeys.SDCardKekSourceHash, 0x10)); try { keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.SDCardKekSourceHash], HACGUIKeyset.Keyset.SdCardKekSource, 0x10); } catch (EndOfStreamException) { MessageBox.Show("Failed to find SD card kek source! The NAND is probably from 1.0.0."); sdWarn = true; } if (!sdWarn) // don't try to find the rest of the keys if the other one couldn't be found { hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardSaveKeySourceHash, 0x20)); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardNcaKeySourceHash, 0x20)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.SDCardSaveKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[0], 0x20); Array.Copy(keys[NintendoKeys.SDCardNcaKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[1], 0x20); } hashes.Clear(); rodatastream.Close(); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKeySourceHash, 0x20)); datastream = new MemoryStream(kip.DecompressSection(2)); keys = datastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.HeaderKeySourceHash], HACGUIKeyset.Keyset.HeaderKeySource, 0x20); datastream.Close(); hashes.Clear(); break; case "spl": hashes.Add(new HashSearchEntry(NintendoKeys.AesKeyGenerationSourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.AesKeyGenerationSourceHash], HACGUIKeyset.Keyset.AesKeyGenerationSource, 0x10); rodatastream.Close(); hashes.Clear(); break; } FileStream kipstream = HACGUIKeyset.RootTempINI1Folder.GetFile(kip.Header.Name + ".kip").Create(); kip.OpenRawFile().CopyToStream(kipstream); kipstream.Close(); } pkg2file.Close(); INI1stream.Close(); HACGUIKeyset.Keyset.DeriveKeys(); SwitchFs fs = SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()); foreach (KeyValuePair <string, Nca> kv in fs.Ncas) { Nca nca = kv.Value; if (nca.CanOpenSection(0)) // mainly a check if the NCA can be decrypted { switch (nca.Header.TitleId) { case 0x0100000000000033: // es switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); IStorage pfsStorage = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false); PartitionFileSystem pfs = new PartitionFileSystem(pfsStorage); Nso nso = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read))); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekekSourceHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] EticketRsaKekSource = new byte[0x10]; byte[] EticketRsaKekekSource = new byte[0x10]; Array.Copy(keys[NintendoKeys.EticketRsaKekSourceHash], EticketRsaKekSource, 0x10); Array.Copy(keys[NintendoKeys.EticketRsaKekekSourceHash], EticketRsaKekekSource, 0x10); byte[] RsaOaepKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[3], out RsaOaepKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaOaepKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, EticketRsaKekekSource, key2, 0x10); Crypto.DecryptEcb(key2, EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek, 0x10); break; } break; case 0x0100000000000024: // ssl switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); IStorage pfsStorage = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false); PartitionFileSystem pfs = new PartitionFileSystem(pfsStorage); Nso nso = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read))); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.SslAesKeyXHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SslRsaKeyYHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] SslAesKeyX = new byte[0x10]; byte[] SslRsaKeyY = new byte[0x10]; Array.Copy(keys[NintendoKeys.SslAesKeyXHash], SslAesKeyX, 0x10); Array.Copy(keys[NintendoKeys.SslRsaKeyYHash], SslRsaKeyY, 0x10); byte[] RsaPrivateKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[1], out RsaPrivateKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaPrivateKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, SslAesKeyX, key2, 0x10); Crypto.DecryptEcb(key2, SslRsaKeyY, HACGUIKeyset.Keyset.SslRsaKek, 0x10); break; } break; } } } // save PRODINFO to file, then derive eticket_ext_key_rsa Stream prodinfo = nand.OpenProdInfo(); Stream prodinfoFile = HACGUIKeyset.TempPRODINFOFileInfo.Create(); prodinfo.CopyTo(prodinfoFile); prodinfo.Close(); prodinfoFile.Seek(0, SeekOrigin.Begin); Calibration cal0 = new Calibration(prodinfoFile); HACGUIKeyset.Keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(cal0.EticketExtKeyRsa, HACGUIKeyset.Keyset.EticketRsaKek); // get client certificate prodinfo.Seek(0x0AD0, SeekOrigin.Begin); byte[] buffer; buffer = new byte[0x4]; prodinfo.Read(buffer, 0, buffer.Length); // read cert length uint certLength = BitConverter.ToUInt32(buffer, 0); buffer = new byte[certLength]; prodinfo.Seek(0x0AE0, SeekOrigin.Begin); // should be redundant? prodinfo.Read(buffer, 0, buffer.Length); // read actual cert byte[] counter = cal0.SslExtKey.Take(0x10).ToArray(); byte[] key = cal0.SslExtKey.Skip(0x10).ToArray(); // bit strange structure but it works new Aes128CtrTransform(HACGUIKeyset.Keyset.SslRsaKek, counter).TransformBlock(key); // decrypt private key X509Certificate2 certificate = new X509Certificate2(); certificate.Import(buffer); certificate.ImportPrivateKey(key); byte[] pfx = certificate.Export(X509ContentType.Pkcs12, "switch"); Stream pfxStream = HACGUIKeyset.GetClientCertificateByName(PickConsolePage.ConsoleName).Create(); pfxStream.Write(pfx, 0, pfx.Length); pfxStream.Close(); prodinfoFile.Close(); // get tickets List <Ticket> tickets = new List <Ticket>(); FatFileSystemProvider system = nand.OpenSystemPartition(); const string e1FileName = "save\\80000000000000E1"; const string e2FileName = "save\\80000000000000E2"; if (system.FileExists(e1FileName)) { IFile e1File = system.OpenFile(e1FileName, OpenMode.Read); IStorage e1Storage = new FileStorage(e1File); tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e1Storage, PickConsolePage.ConsoleName)); } if (system.FileExists(e2FileName)) { IFile e2File = system.OpenFile(e2FileName, OpenMode.Read); IStorage e2Storage = new FileStorage(e2File); tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e2Storage, PickConsolePage.ConsoleName)); } IStorage nsAppmanStorage = new FileStorage(system.OpenFile("save\\8000000000000043", OpenMode.Read)); SaveDataFileSystem save = new SaveDataFileSystem(HACGUIKeyset.Keyset, nsAppmanStorage, IntegrityCheckLevel.ErrorOnInvalid, false); IStorage privateStorage = new FileStorage(save.OpenFile("/private", OpenMode.Read)); byte[] sdSeed = new byte[0x10]; privateStorage.Read(sdSeed, 0x10); HACGUIKeyset.Keyset.SetSdSeed(sdSeed); foreach (Ticket ticket in tickets) { HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId] = new byte[0x10]; Array.Copy(ticket.GetTitleKey(HACGUIKeyset.Keyset), HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId], 0x10); } NANDService.Stop(); DirectoryInfo oldKeysDirectory = HACGUIKeyset.RootFolderInfo.GetDirectory("keys"); if (oldKeysDirectory.Exists) { oldKeysDirectory.Delete(true); // fix old versions after restructure of directory } // write all keys to file new SaveKeysetTask(PickConsolePage.ConsoleName).CreateTask().RunSynchronously(); Preferences.Current.DefaultConsoleName = PickConsolePage.ConsoleName; Preferences.Current.Write(); }
private static void Process(string inputFilePath, string outDirPath, XciTaskType taskType, Keyset keyset, Output Out, bool verifyBeforeDecrypting = true) { using (var inputFile = File.Open(inputFilePath, FileMode.Open, FileAccess.Read).AsStorage()) using (var outputFile = File.Open($"{outDirPath}/xciMeta.dat", FileMode.Create)) { var OutDirFs = new LocalFileSystem(outDirPath); IDirectory destRoot = OutDirFs.OpenDirectory("/", OpenDirectoryMode.All); IFileSystem destFs = destRoot.ParentFileSystem; var header = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x58, 0x43, 0x49, 0x00 }; outputFile.Write(header, 0, header.Length); var xci = new Xci(keyset, inputFile); var xciHeaderData = new byte[0x200]; var xciCertData = new byte[0x200]; inputFile.Read(xciHeaderData, 0); inputFile.Read(xciCertData, 0x7000); outputFile.Write(xciHeaderData, 0, 0x200); outputFile.Write(xciCertData, 0, 0x200); Out.Log(Print.PrintXci(xci)); var root = xci.OpenPartition(XciPartitionType.Root); if (root == null) { throw new InvalidDataException("Could not find root partition"); } ProcessXci.GetTitleKeys(xci, keyset, Out); foreach (var sub in root.Files) { outputFile.WriteByte(0x0A); outputFile.WriteByte(0x0A); var subDirNameChar = Encoding.ASCII.GetBytes(sub.Name); outputFile.Write(subDirNameChar, 0, subDirNameChar.Length); var subPfs = new PartitionFileSystem(new FileStorage(root.OpenFile(sub, OpenMode.Read))); foreach (var subPfsFile in subPfs.Files) { outputFile.WriteByte(0x0A); var subPfsFileNameChar = Encoding.ASCII.GetBytes(subPfsFile.Name); outputFile.Write(subPfsFileNameChar, 0, subPfsFileNameChar.Length); using (IFile srcFile = subPfs.OpenFile(subPfsFile.Name, OpenMode.Read)) { if (taskType == XciTaskType.extractRomFS && subPfsFile.Name.EndsWith(".nca")) { var fullOutDirPath = $"{outDirPath}/{sub.Name}/{subPfsFile.Name}"; Out.Log($"Extracting {subPfsFile.Name}...\r\n"); ProcessNca.Extract(srcFile.AsStream(), fullOutDirPath, verifyBeforeDecrypting, keyset, Out); } else { var destFileName = Path.Combine(sub.Name, subPfsFile.Name); if (!destFs.DirectoryExists(sub.Name)) { destFs.CreateDirectory(sub.Name); } destFs.CreateFile(destFileName, subPfsFile.Size, CreateFileOptions.None); using (IFile dstFile = destFs.OpenFile(destFileName, OpenMode.Write)) { if (taskType == XciTaskType.decrypt && subPfsFile.Name.EndsWith(".nca")) { ProcessNca.Process(srcFile, dstFile, verifyBeforeDecrypting, keyset, Out); } else { srcFile.CopyTo(dstFile); } } } } } } outputFile.WriteByte(0x0A); outputFile.Dispose(); } }