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 = 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: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."); }); } }
public static Title processXci(string filename) { Title title = new Title(); using (var filestream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { Xci xci; string biggestNca = null, controlNca = null; try { xci = new Xci(keyset, filestream.AsStorage()); title.distribution = Title.Distribution.Cartridge; log?.WriteLine("Processing XCI {0}", filename); if (xci.RootPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.RootPartition); } if (xci.UpdatePartition?.Files.Length > 0) { PfsFileEntry[] fileEntries = xci.UpdatePartition.Files; List <string> cnmtNca = fileEntries.Select(x => x.Name).Where(x => x.EndsWith(".cnmt.nca")).Intersect(Title.SystemUpdate.Keys).ToList(); if (cnmtNca.Any()) { uint systemUpdate = unchecked ((uint)-1); Title.SystemUpdate.TryGetValue(cnmtNca.First(), out systemUpdate); title.systemUpdate = systemUpdate; } title.structure.Add(Title.Structure.UpdatePartition); } if (xci.NormalPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.NormalPartition); } if (xci.SecurePartition?.Files.Length > 0) { PfsFileEntry[] fileEntries = xci.SecurePartition.Files; foreach (PfsFileEntry entry in fileEntries) { if (entry.Name.EndsWith(".cnmt.nca")) { try { using (var cnmtNca = xci.SecurePartition.OpenFile(entry)) { var nca = processCnmtNca(cnmtNca, ref title); if (nca.Item1 != null && (nca.Item2 != null || title.type == TitleType.AddOnContent)) { (biggestNca, controlNca) = nca; } } } catch (FileNotFoundException) { if (xci.SecurePartition.FileExists(entry.Name.Replace(".nca", ".ncz"))) { title.error = "Unsupported Format: Compressed NCA"; } } title.structure.Add(Title.Structure.CnmtNca); } else if (entry.Name.EndsWith(".cert")) { title.structure.Add(Title.Structure.Cert); } else if (entry.Name.EndsWith(".tik")) { try { using (var tik = xci.SecurePartition.OpenFile(entry)) { if (entry.Name.Split('.')[0].TryToBytes(out byte[] rightsId)) { processTik(tik, rightsId, ref keyset, out byte[] titleKey); title.titleKey = BitConverter.ToString(titleKey).Replace("-", "").ToUpper(); } } } catch (FileNotFoundException) { if (xci.SecurePartition.FileExists(entry.Name.Replace(".nca", ".ncz"))) { title.error = "Unsupported Format: Compressed NCA"; } } title.structure.Add(Title.Structure.Tik); } } if (!String.IsNullOrEmpty(biggestNca)) { try { using (var biggest = xci.SecurePartition.OpenFile(biggestNca)) { processBiggestNca(biggest, ref title); } } catch (FileNotFoundException) { if (xci.SecurePartition.FileExists(biggestNca.Replace(".nca", ".ncz"))) { title.error = "Unsupported Format: Compressed NCA"; } } } if (!String.IsNullOrEmpty(controlNca)) { try { using (var control = xci.SecurePartition.OpenFile(controlNca)) { processControlNca(control, ref title); } } catch (FileNotFoundException) { if (xci.SecurePartition.FileExists(controlNca.Replace(".nca", ".ncz"))) { title.error = "Unsupported Format: Compressed NCA"; } } } title.structure.Add(Title.Structure.SecurePartition); } if (xci.LogoPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.LogoPartition); } } catch (InvalidDataException) { return(null); } } if (title.type == TitleType.Application || title.type == TitleType.Patch) { if (versionList.TryGetValue(title.baseTitleID, out uint version)) { title.latestVersion = version; } } log?.WriteLine("XCI information for {0}: [{1}] {2}", filename, title.titleID, title.titleName); return(title); }
static void Main(string[] args) { Console.Title = "XCIRepacker v0.1 by Ac_K"; Console.WriteLine(Environment.NewLine + " XCIRepacker v0.1 by Ac_K" + Environment.NewLine + Environment.NewLine + " Provide with the courtesy of the mob." + Environment.NewLine + Environment.NewLine + "---------------------------------------" + Environment.NewLine); if (args.Length == 1) { Keyset keySet = OpenKeyset(); if (keySet != null) { if (File.Exists(args[0])) { try { Xci xciFile = new Xci(keySet, new LocalStorage(args[0], FileAccess.Read)); if (xciFile.HasPartition(XciPartitionType.Secure)) { XciPartition xciPartition = xciFile.OpenPartition(XciPartitionType.Root); IFile ncaStorage = xciPartition.OpenFile("secure", OpenMode.Read); string outputPath = Path.GetDirectoryName(args[0]) + Path.GetFileNameWithoutExtension(args[0]) + ".nsp"; Console.WriteLine($" Input File Path: {args[0]}"); Console.WriteLine($" Output File Path: {outputPath}" + Environment.NewLine); using (Stream dataInput = ncaStorage.AsStream()) using (FileStream fileOutput = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite)) using (BinaryReader reader = new BinaryReader(fileOutput)) using (BinaryWriter writer = new BinaryWriter(fileOutput)) { int bytesRead = -1; long totalReads = 0; long totalBytes = dataInput.Length; byte[] bytes = new byte[_bufferSize]; Console.WriteLine(" Extracting Secure partition..." + Environment.NewLine); while ((bytesRead = dataInput.Read(bytes, 0, _bufferSize)) > 0) { fileOutput.Write(bytes, 0, bytesRead); totalReads += bytesRead; DrawTextProgressBar(totalReads, totalBytes); } Console.WriteLine(Environment.NewLine + Environment.NewLine + " Secure partition extracted!" + Environment.NewLine + Environment.NewLine + "---------------------------------------" + Environment.NewLine); Console.Write(" Patching HFS0 Header to PFS0..."); fileOutput.Position = 0; string Magic = Encoding.ASCII.GetString(reader.ReadBytes(0x04)); if (Magic == "HFS0") { fileOutput.Seek(0x00, SeekOrigin.Begin); writer.Write(new byte[] { 0x50, 0x46, 0x53, 0x30 }); // PFS0 int filesNumber = reader.ReadInt32(); // Skip write files number because is at same offset int filesNamesSize = reader.ReadInt32(); reader.ReadInt32(); // Skip reserved long HFS0HeaderSize = filesNamesSize + 0x10; for (int i = 0; i < filesNumber; i++) { fileOutput.Seek((i * 0x40) + 0x10, SeekOrigin.Begin); long fileOffset = reader.ReadInt64(); long fileSize = reader.ReadInt64(); int fileNameOffset = reader.ReadInt32(); int hashedSize = reader.ReadInt32(); reader.ReadInt64(); // reserved reader.ReadBytes(0x20); HFS0HeaderSize += 0x40; fileOutput.Seek((i * 0x18) + 0x10, SeekOrigin.Begin); writer.Write(fileOffset); writer.Write(fileSize); writer.Write(fileNameOffset); writer.Write(0x00); // reserved } long endPatchedHeader = fileOutput.Position; fileOutput.Seek((4 * 0x40) + 0x10, SeekOrigin.Begin); byte[] filesNamesTable = reader.ReadBytes(filesNamesSize); fileOutput.Seek(endPatchedHeader, SeekOrigin.Begin); writer.Write(filesNamesTable); int shiftSize = (int)(HFS0HeaderSize - fileOutput.Position); writer.Write(new byte[shiftSize]); fileOutput.Seek(0x08, SeekOrigin.Begin); writer.Write(filesNamesSize + shiftSize); Console.WriteLine(" Done!"); } else { Console.WriteLine(" ERROR: Extracted file isn't HFS0!"); } } } } catch (Exception ex) { Console.WriteLine($" ERROR: {ex.Message}"); } } else { Console.WriteLine(" ERROR: XCI file not found!"); } } else { Console.WriteLine(" ERROR: Keys file not found!"); } } else { Console.WriteLine(" USAGE: XCIRepacker.exe \"PathOfFile.xci\""); } Console.ReadKey(); }
public static void Process(Context ctx) { using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { var xci = new Xci(ctx.Keyset, file); ctx.Logger.LogMessage(xci.Print()); if (ctx.Options.RootDir != null) { xci.OpenPartition(XciPartitionType.Root).Extract(ctx.Options.RootDir, ctx.Logger); } if (ctx.Options.UpdateDir != null && xci.HasPartition(XciPartitionType.Update)) { xci.OpenPartition(XciPartitionType.Update).Extract(ctx.Options.UpdateDir, ctx.Logger); } if (ctx.Options.NormalDir != null && xci.HasPartition(XciPartitionType.Normal)) { xci.OpenPartition(XciPartitionType.Normal).Extract(ctx.Options.NormalDir, ctx.Logger); } if (ctx.Options.SecureDir != null && xci.HasPartition(XciPartitionType.Secure)) { xci.OpenPartition(XciPartitionType.Secure).Extract(ctx.Options.SecureDir, ctx.Logger); } if (ctx.Options.LogoDir != null && xci.HasPartition(XciPartitionType.Logo)) { xci.OpenPartition(XciPartitionType.Logo).Extract(ctx.Options.LogoDir, ctx.Logger); } if (ctx.Options.OutDir != null) { XciPartition root = xci.OpenPartition(XciPartitionType.Root); if (root == null) { ctx.Logger.LogMessage("Could not find root partition"); return; } foreach (PartitionFileEntry sub in root.Files) { var subPfs = new PartitionFileSystem(root.OpenFile(sub, OpenMode.Read).AsStorage()); string subDir = Path.Combine(ctx.Options.OutDir, sub.Name); subPfs.Extract(subDir, ctx.Logger); } } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } if (!mainNca.SectionExists(NcaSectionType.Code)) { ctx.Logger.LogMessage("NCA has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { mainNca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { mainNca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ListRomFs) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } if (!mainNca.SectionExists(NcaSectionType.Data)) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } ProcessRomfs.Process(ctx, mainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel, false)); } } }
public static void Process(Context ctx) { using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read)) { var xci = new Xci(ctx.Keyset, file.AsStorage()); ctx.Logger.LogMessage(xci.Print()); if (ctx.Options.RootDir != null) { xci.RootPartition?.Extract(ctx.Options.RootDir, ctx.Logger); } if (ctx.Options.UpdateDir != null) { xci.UpdatePartition?.Extract(ctx.Options.UpdateDir, ctx.Logger); } if (ctx.Options.NormalDir != null) { xci.NormalPartition?.Extract(ctx.Options.NormalDir, ctx.Logger); } if (ctx.Options.SecureDir != null) { xci.SecurePartition?.Extract(ctx.Options.SecureDir, ctx.Logger); } if (ctx.Options.LogoDir != null) { xci.LogoPartition?.Extract(ctx.Options.LogoDir, ctx.Logger); } if (ctx.Options.OutDir != null && xci.RootPartition != null) { XciPartition root = xci.RootPartition; if (root == null) { ctx.Logger.LogMessage("Could not find root partition"); return; } foreach (PfsFileEntry sub in root.Files) { var subPfs = new Pfs(root.OpenFile(sub)); string subDir = Path.Combine(ctx.Options.OutDir, sub.Name); subPfs.Extract(subDir, ctx.Logger); } } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } NcaSection exefsSection = mainNca.Sections[(int)ProgramPartitionType.Code]; if (exefsSection == null) { ctx.Logger.LogMessage("NCA has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { Nca mainNca = GetXciMainNca(xci, ctx); if (mainNca == null) { ctx.Logger.LogMessage("Could not find Program NCA"); return; } NcaSection romfsSection = mainNca.Sections.FirstOrDefault(x => x.Type == SectionType.Romfs); if (romfsSection == null) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.IntegrityLevel, true)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } } }
public static void Init(List <string> AppDirs, Keyset keySet, SystemState.TitleLanguage desiredTitleLanguage) { KeySet = keySet; DesiredTitleLanguage = desiredTitleLanguage; // Loads the default application Icons RyujinxNspIcon = GetResourceBytes("Ryujinx.Ui.assets.ryujinxNSPIcon.png"); RyujinxXciIcon = GetResourceBytes("Ryujinx.Ui.assets.ryujinxXCIIcon.png"); RyujinxNcaIcon = GetResourceBytes("Ryujinx.Ui.assets.ryujinxNCAIcon.png"); RyujinxNroIcon = GetResourceBytes("Ryujinx.Ui.assets.ryujinxNROIcon.png"); RyujinxNsoIcon = GetResourceBytes("Ryujinx.Ui.assets.ryujinxNSOIcon.png"); // 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; } DirectoryInfo AppDirInfo = new DirectoryInfo(appDir); foreach (FileInfo App in AppDirInfo.GetFiles()) { if ((Path.GetExtension(App.ToString()) == ".xci") || (Path.GetExtension(App.ToString()) == ".nca") || (Path.GetExtension(App.ToString()) == ".nsp") || (Path.GetExtension(App.ToString()) == ".pfs0") || (Path.GetExtension(App.ToString()) == ".nro") || (Path.GetExtension(App.ToString()) == ".nso")) { applications.Add(App.ToString()); } } } // Loops through applications list, creating a struct for each application and then adding the struct to a list of structs ApplicationLibraryData = new List <ApplicationData>(); foreach (string applicationPath in applications) { double filesize = new FileInfo(applicationPath).Length * 0.000000000931; string titleName = null; string titleId = null; string developer = null; string version = 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 { IFileSystem controlFs = null; // Store the ControlFS in variable called controlFs if (Path.GetExtension(applicationPath) == ".xci") { Xci xci = new Xci(KeySet, file.AsStorage()); controlFs = GetControlFs(xci.OpenPartition(XciPartitionType.Secure)); } else { controlFs = GetControlFs(new PartitionFileSystem(file.AsStorage())); } // 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 = NspOrXciIcon(applicationPath); } } } catch (MissingKeyException exception) { titleName = "Unknown"; titleId = "Unknown"; developer = "Unknown"; version = "?"; applicationIcon = NspOrXciIcon(applicationPath); Logger.PrintWarning(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } catch (InvalidDataException) { titleName = "Unknown"; titleId = "Unknown"; developer = "Unknown"; version = "?"; applicationIcon = NspOrXciIcon(applicationPath); Logger.PrintWarning(LogClass.Application, $"The file is not an NCA file or the header key is incorrect. Errored File: {applicationPath}"); } catch (Exception exception) { Logger.PrintWarning(LogClass.Application, $"This warning usualy means that you have a DLC in one of you game directories\n{exception}"); continue; } } 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 = RyujinxNroIcon; titleName = "Application"; titleId = "0000000000000000"; developer = "Unknown"; version = "?"; } } // If its an NCA or NSO we just set defaults else if ((Path.GetExtension(applicationPath) == ".nca") || (Path.GetExtension(applicationPath) == ".nso")) { if (Path.GetExtension(applicationPath) == ".nca") { applicationIcon = RyujinxNcaIcon; } else if (Path.GetExtension(applicationPath) == ".nso") { applicationIcon = RyujinxNsoIcon; } string fileName = Path.GetFileName(applicationPath); string fileExt = Path.GetExtension(applicationPath); StringBuilder titlename = new StringBuilder(); titlename.Append(fileName); titlename.Remove(fileName.Length - fileExt.Length, fileExt.Length); titleName = titlename.ToString(); titleId = "0000000000000000"; version = "?"; developer = "Unknown"; } } string[] playedData = GetPlayedData(titleId, "00000000000000000000000000000001"); ApplicationData data = new ApplicationData() { Icon = applicationIcon, TitleName = titleName, TitleId = titleId, Developer = developer, Version = version, TimePlayed = playedData[0], LastPlayed = playedData[1], FileExt = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), FileSize = (filesize < 1) ? (filesize * 1024).ToString("0.##") + "MB" : filesize.ToString("0.##") + "GB", Path = applicationPath, }; ApplicationLibraryData.Add(data); } }
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(); } }
public static Title processXci(string filename) { Title title = new Title(); using (var filestream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { Xci xci; string biggestNca = null, controlNca = null; try { xci = new Xci(keyset, filestream.AsStorage()); title.distribution = Title.Distribution.Cartridge; } catch (InvalidDataException) { return(null); } log?.WriteLine("Processing XCI {0}", filename); if (xci.RootPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.RootPartition); } if (xci.UpdatePartition?.Files.Length > 0) { title.structure.Add(Title.Structure.UpdatePartition); } if (xci.NormalPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.NormalPartition); } if (xci.SecurePartition?.Files.Length > 0) { PfsFileEntry[] fileEntries = xci.SecurePartition.Files; foreach (PfsFileEntry entry in fileEntries) { if (entry.Name.EndsWith(".cnmt.nca")) { using (var cnmtNca = xci.SecurePartition.OpenFile(entry)) { var nca = processCnmtNca(cnmtNca, ref title); if (nca.Item1 != null && (nca.Item2 != null || title.type == TitleType.AddOnContent)) { (biggestNca, controlNca) = nca; } } title.structure.Add(Title.Structure.CnmtNca); } } if (!String.IsNullOrEmpty(biggestNca)) { using (var biggest = xci.SecurePartition.OpenFile(biggestNca)) { processBiggestNca(biggest, ref title); } } if (!String.IsNullOrEmpty(controlNca)) { using (var control = xci.SecurePartition.OpenFile(controlNca)) { processControlNca(control, ref title); } } title.structure.Add(Title.Structure.SecurePartition); } if (xci.LogoPartition?.Files.Length > 0) { title.structure.Add(Title.Structure.LogoPartition); } } if (title.type == TitleType.Application || title.type == TitleType.Patch) { if (versionList.TryGetValue(title.titleIDApplication, out uint version)) { title.latestVersion = version; } } log?.WriteLine("XCI information for {0}: [{1}] {2}", filename, title.titleID, title.titleName); return(title); }
private void ImportGameDataClicked(object sender, RoutedEventArgs e) { 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>(); PseudoFileSystem ncaFs = new PseudoFileSystem(); foreach (FileInfo file in ncas) { LocalFileSystem fs = new LocalFileSystem(file.Directory.FullName); // clean up filename so it only ends with .nca, then map to actual name string s = file.Name; while (s.EndsWith(".nca")) { s = s.Substring(0, s.IndexOf(".nca")); } ncaFs.Add($"/{s}.nca", $"/{file.Name}", fs); } if (ncas.Any()) { switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, ncaFs)); } 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))); } foreach (FileInfo file in nsp) { PartitionFileSystem fs = new PartitionFileSystem(new LocalFile(file.FullName, OpenMode.Read).AsStorage()); switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, fs)); } foreach (SwitchFs fs in switchFilesystems) { DeviceService.FsView.LoadFileSystemAsync("Opening imported data...", () => fs, FSView.TitleSource.Imported, false); } }))); }
private void ExtractSection(NcaSectionType ncaSectionType) { FileChooserDialog fileChooser = new FileChooserDialog("Choose the folder to extract into", null, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Extract", ResponseType.Accept); fileChooser.SetPosition(WindowPosition.Center); int response = fileChooser.Run(); string destination = fileChooser.Filename; fileChooser.Dispose(); if (response == (int)ResponseType.Accept) { Thread extractorThread = new Thread(() => { string sourceFile = _gameTableStore.GetValue(_rowIter, 9).ToString(); Gtk.Application.Invoke(delegate { _dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null) { Title = "Ryujinx - NCA Section Extractor", Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"), SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(sourceFile)}...", WindowPosition = WindowPosition.Center }; int dialogResponse = _dialog.Run(); if (dialogResponse == (int)ResponseType.Cancel || dialogResponse == (int)ResponseType.DeleteEvent) { _cancel = true; _dialog.Dispose(); } }); using (FileStream file = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) { Nca mainNca = null; Nca patchNca = null; if ((System.IO.Path.GetExtension(sourceFile).ToLower() == ".nsp") || (System.IO.Path.GetExtension(sourceFile).ToLower() == ".pfs0") || (System.IO.Path.GetExtension(sourceFile).ToLower() == ".xci")) { PartitionFileSystem pfs; if (System.IO.Path.GetExtension(sourceFile) == ".xci") { Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); } foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.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 (System.IO.Path.GetExtension(sourceFile).ToLower() == ".nca") { mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); } if (mainNca == null) { Logger.PrintError(LogClass.Application, "Extraction failed. The main NCA was not present in the selected file."); Gtk.Application.Invoke(delegate { GtkDialog.CreateErrorDialog("Extraction failed. The main NCA was not present in the selected file."); }); return; } int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); IFileSystem ncaFileSystem = patchNca != null ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); FileSystemClient fsClient = _virtualFileSystem.FsClient; string source = DateTime.Now.ToFileTime().ToString().Substring(10); string output = DateTime.Now.ToFileTime().ToString().Substring(10); fsClient.Register(source.ToU8Span(), ncaFileSystem); fsClient.Register(output.ToU8Span(), new LocalFileSystem(destination)); (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); if (!canceled) { if (resultCode.Value.IsFailure()) { Logger.PrintError(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); Gtk.Application.Invoke(delegate { _dialog?.Dispose(); GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); }); } else if (resultCode.Value.IsSuccess()) { Gtk.Application.Invoke(delegate { _dialog?.Dispose(); MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) { Title = "Ryujinx - NCA Section Extractor", Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"), SecondaryText = "Extraction has completed successfully.", WindowPosition = WindowPosition.Center }; dialog.Run(); dialog.Dispose(); }); } } fsClient.Unmount(source); fsClient.Unmount(output); } }); extractorThread.Name = "GUI.NcaSectionExtractorThread"; extractorThread.IsBackground = true; extractorThread.Start(); } }
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 }); } }
private void Open() { treeView1.Nodes.Clear(); string FileToOpen = null; if (Program.FileArg != null) { FileToOpen = Program.FileArg; } else { FileToOpen = openFileDialog1.FileName; } Program.FileArg = null; Stream Input = null; try { string ExpEnv(string In) => Environment.ExpandEnvironmentVariables(In); var ProdKeys = ExpEnv(@"%USERPROFILE%\.switch\prod.keys"); var TitleKeys = ExpEnv(@"%USERPROFILE%\.switch\title.keys"); var Keys = ExternalKeys.ReadKeyFile(ProdKeys, TitleKeys); var Ext = (new FileInfo(FileToOpen).Extension); if (Ext == ".nsp") { var InputPFS = File.OpenRead(FileToOpen); var Pfs = new Pfs(InputPFS.AsStorage()); var CnmtNca = new Nca(Keys, Pfs.OpenFile(Pfs.Files.FirstOrDefault(s => s.Name.Contains(".cnmt.nca"))), false); var CnmtPfs = new Pfs(CnmtNca.OpenSection(0, false, IntegrityCheckLevel.None, true)); var Cnmt = new Cnmt(CnmtPfs.OpenFile(CnmtPfs.Files[0]).AsStream()); var Program = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Program); var CtrlEntry = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Control); if (CtrlEntry != null) { Control = new Nca(Keys, Pfs.OpenFile($"{CtrlEntry.NcaId.ToHexString().ToLower()}.nca"), false); } Input = Pfs.OpenFile($"{Program.NcaId.ToHexString().ToLower()}.nca").AsStream(); } else if (Ext == ".xci") { var InputPFS = File.OpenRead(FileToOpen); var Xci = new Xci(Keys, InputPFS.AsStorage()); var CnmtNca = new Nca(Keys, Xci.SecurePartition.OpenFile(Xci.SecurePartition.Files.FirstOrDefault(s => s.Name.Contains(".cnmt.nca"))), false); var CnmtPfs = new Pfs(CnmtNca.OpenSection(0, false, IntegrityCheckLevel.None, true)); var Cnmt = new Cnmt(CnmtPfs.OpenFile(CnmtPfs.Files[0]).AsStream()); var Program = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Program); var CtrlEntry = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Control); if (CtrlEntry != null) { Control = new Nca(Keys, Xci.SecurePartition.OpenFile($"{CtrlEntry.NcaId.ToHexString().ToLower()}.nca"), false); } Input = Xci.SecurePartition.OpenFile($"{Program.NcaId.ToHexString().ToLower()}.nca").AsStream(); } else if (FileToOpen.Split('.')[1] == "cnmt" && Ext == ".nca") { var TargetFile = File.OpenRead(FileToOpen); var CnmtNca = new Nca(Keys, TargetFile.AsStorage(), false); var CnmtPfs = new Pfs(CnmtNca.OpenSection(0, false, IntegrityCheckLevel.None, true)); var Cnmt = new Cnmt(CnmtPfs.OpenFile(CnmtPfs.Files[0]).AsStream()); var Program = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Program); var CtrlEntry = Cnmt.ContentEntries.FirstOrDefault(c => c.Type == CnmtContentType.Control); if (CtrlEntry != null) { Control = new Nca(Keys, File.OpenRead($"{CtrlEntry.NcaId.ToHexString().ToLower()}.nca").AsStorage(), false); } Input = File.OpenRead($"{Program.NcaId.ToHexString().ToLower()}.nca"); } else { Input = File.OpenRead(FileToOpen); } try { Nca = new Nca(Keys, Input.AsStorage(), true); if (Nca.HasRightsId && !Keys.TitleKeys.Keys.Any(k => k.SequenceEqual(Nca.Header.RightsId))) { MessageBox.Show($"Error: the titlekey for {Nca.Header.RightsId.ToHexString().ToLower()} is not present in your key file."); } else { var isUpdateNca = false; if (Nca.Sections.Any(s => s?.Type == SectionType.Bktr)) { isUpdateNca = true; } if (isUpdateNca) { openFileDialog1.Title = "Select base Nca"; openFileDialog1.ShowDialog(); var Input2 = File.OpenRead(openFileDialog1.FileName); Patch = new Nca(Keys, Input2.AsStorage(), true); Nca.SetBaseNca(Patch); } new Thread (() => { Thread.CurrentThread.IsBackground = true; var Info = GetTitleMeta($"{Nca.Header.TitleId:x16}"); label2.Invoke(new Action(() => { label2.Text = Info[0]; label3.Text = Info[1]; })); }).Start(); Rom = new Romfs( Nca.OpenSection(Nca.Sections.FirstOrDefault (s => s?.Type == SectionType.Romfs || s?.Type == SectionType.Bktr) .SectionNum, false, IntegrityCheckLevel.None, true) ); IO.PopulateTreeView(treeView1.Nodes, Rom.RootDir); } } catch { MessageBox.Show("There was an error reading the NCA. Are you sure the correct keys are present in your keyfiles?"); } } catch (ArgumentNullException) { MessageBox.Show("Error: key files are missing!"); } }
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; } }
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); } }))); }
private (Nca Main, Nca patch, Nca Control) GetXciGameData(Xci xci) { if (!xci.HasPartition(XciPartitionType.Secure)) { throw new InvalidDataException("Could not find XCI secure partition"); } Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; XciPartition securePartition = xci.OpenPartition(XciPartitionType.Secure); foreach (DirectoryEntryEx ticketEntry in securePartition.EnumerateEntries("/", "*.tik")) { Result result = securePartition.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 securePartition.EnumerateEntries("/", "*.nca")) { Result result = securePartition.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsFailure()) { continue; } 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) { Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } if (controlNca != null) { ReadControlData(controlNca); } else { ControlData.ByteSpan.Clear(); } return(mainNca, patchNca, controlNca); }
public static IEnumerable <(PartitionFileSystem subPfs, PartitionFileEntry subPfsFile)> FileIterator(Xci xci, Keyset keyset, Output Out) { var root = xci.OpenPartition(XciPartitionType.Root); if (root == null) { throw new InvalidDataException("Could not find root partition"); } foreach (var sub in root.Files) { var subPfs = new PartitionFileSystem(new FileStorage(root.OpenFile(sub, OpenMode.Read))); foreach (var subPfsFile in subPfs.Files) { yield return(subPfs, subPfsFile); } } }
private (Nca Main, Nca Control) GetXciGameData(Xci xci) { if (xci.SecurePartition == null) { throw new InvalidDataException("Could not find XCI secure partition"); } Nca mainNca = null; Nca patchNca = null; Nca controlNca = null; foreach (PfsFileEntry ticketEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik"))) { Ticket ticket = new Ticket(xci.SecurePartition.OpenFile(ticketEntry)); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(KeySet)); } } foreach (PfsFileEntry fileEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca"))) { Stream ncaStream = xci.SecurePartition.OpenFile(fileEntry); Nca nca = new Nca(KeySet, ncaStream, true); if (nca.Header.ContentType == ContentType.Program) { if (nca.Sections.Any(x => x?.Type == SectionType.Romfs)) { mainNca = nca; } else if (nca.Sections.Any(x => x?.Type == SectionType.Bktr)) { patchNca = nca; } } else if (nca.Header.ContentType == ContentType.Control) { controlNca = nca; } } if (mainNca == null) { Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } mainNca.SetBaseNca(patchNca); if (controlNca != null) { ReadControlData(controlNca); } if (patchNca != null) { patchNca.SetBaseNca(mainNca); return(patchNca, controlNca); } return(mainNca, controlNca); }
private void Open() { treeView1.Nodes.Clear(); string FileToOpen = null; if (Program.FileArg != null) { FileToOpen = Program.FileArg; } else { FileToOpen = openFileDialog1.FileName; } Program.FileArg = null; Stream Input = null; try { string ExpEnv(string In) { return(Environment.ExpandEnvironmentVariables(In)); } var ProdKeys = ExpEnv(@"%USERPROFILE%\.switch\prod.keys"); var TitleKeys = ExpEnv(@"%USERPROFILE%\.switch\title.keys"); var Keys = ExternalKeys.ReadKeyFile(ProdKeys, TitleKeys); var Ext = (new FileInfo(FileToOpen).Extension); if (Ext == ".nsp") { var InputPFS = File.OpenRead(FileToOpen); var Pfs = new Pfs(InputPFS); Input = Pfs.OpenFile ( Pfs.Files.OrderByDescending(s => s.Size) .FirstOrDefault() ); } else if (Ext == ".xci") { var InputPFS = File.OpenRead(FileToOpen); var Xci = new Xci(Keys, InputPFS); Input = Xci.SecurePartition.OpenFile ( Xci.SecurePartition.Files .OrderByDescending(s => s.Size) .FirstOrDefault() ); } else { Input = File.OpenRead(FileToOpen); } try { Nca = new Nca(Keys, Input, true); if (Nca.HasRightsId) { if (!Keys.TitleKeys.Keys.Contains(Nca.Header.RightsId)) { MessageBox.Show($"Error: the titlekey for {Nca.Header.RightsId.ToHexString()} is not present in your key file."); } } bool IsUpdateNca = false; foreach (var Section in Nca.Sections) { if (Section?.Type == SectionType.Bktr) { IsUpdateNca = true; } } if (IsUpdateNca) { openFileDialog1.Title = "Select base Nca"; openFileDialog1.ShowDialog(); var Input2 = File.OpenRead(openFileDialog1.FileName); Patch = new Nca(Keys, Input2, true); Nca.SetBaseNca(Patch); } new Thread ( () => { Thread.CurrentThread.IsBackground = true; var Info = GetTitleMeta($"{Nca.Header.TitleId:x16}"); label2.Invoke ( new Action ( () => { label2.Text = Info[0]; } ) ); label3.Invoke ( new Action ( () => { label3.Text = Info[1]; } ) ); } ) .Start(); Rom = new Romfs ( Nca.OpenSection ( Nca.Sections.FirstOrDefault (s => s?.Type == SectionType.Romfs || s?.Type == SectionType.Bktr) .SectionNum, false, false ) ); IO.PopulateTreeView(treeView1.Nodes, Rom.RootDir); } catch (Exception) { MessageBox.Show("There was an error reading the NCA. Are you sure the correct keys are present in your keyfiles?"); } } catch (Exception) { MessageBox.Show("Error: key files are missing!"); } }