private static void PrintPartition(StringBuilder sb, int colLen, XciPartition partition, XciPartitionType type) { const int fileNameLen = 57; sb.AppendLine($"{type.ToString()} Partition:{partition.HashValidity.GetValidityString()}"); PrintItem(sb, colLen, " Magic:", partition.Header.Magic); PrintItem(sb, colLen, " Offset:", $"{partition.Offset:x12}"); PrintItem(sb, colLen, " Number of files:", partition.Files.Length); string name = type.GetFileName(); if (partition.Files.Length > 0 && partition.Files.Length < 100) { for (int i = 0; i < partition.Files.Length; i++) { PartitionFileEntry file = partition.Files[i]; string label = i == 0 ? " Files:" : ""; string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}{file.HashValidity.GetValidityString()}"; string data = $"{name}:/{file.Name}".PadRight(fileNameLen) + offsets; PrintItem(sb, colLen, label, data); } } }
public static IStorage ProcessXci(IStorage xciStorage, bool compress) { Xci xci = new Xci(KeySet, xciStorage); IStorage xciHeaderStorage = new MemoryStorage(new byte[0x200]); xciStorage.CopyTo(xciHeaderStorage); XciPartition securePartition = xci.OpenPartition(XciPartitionType.Secure); IStorage processedSecurePartition = ProcessPartitionFileSystem(securePartition, PartitionFileSystemType.Hashed, compress); PartitionFileSystemBuilder rootPartitionBuilder = new PartitionFileSystemBuilder(); rootPartitionBuilder.AddFile("secure", processedSecurePartition.AsFile(OpenMode.Read)); IStorage rootPartitionStorage = rootPartitionBuilder.Build(PartitionFileSystemType.Hashed); using (BinaryReader reader = new BinaryReader(rootPartitionStorage.AsStream())) { PartitionFileSystemHeader pfsHeader = new PartitionFileSystemHeader(reader); Span <byte> pfsHeaderBytes = stackalloc byte[pfsHeader.HeaderSize]; Span <byte> pfsHeaderHash = stackalloc byte[0x20]; rootPartitionStorage.Read(0, pfsHeaderBytes); Sha256.GenerateSha256Hash(pfsHeaderBytes, pfsHeaderHash); xciHeaderStorage.Write(0x138, BitConverter.GetBytes(pfsHeader.HeaderSize)); xciHeaderStorage.Write(0x140, pfsHeaderHash); } return(new ConcatenationStorage(new[] { xciHeaderStorage, new MemoryStorage(new byte[xci.Header.RootPartitionOffset - 0x200]), // Cert and padding rootPartitionStorage }, 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 (DirectoryEntry ticketEntry in securePartition.EnumerateEntries("*.tik")) { Ticket ticket = new Ticket(securePartition.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(KeySet)); } } foreach (DirectoryEntry fileEntry in securePartition.EnumerateEntries("*.nca")) { IStorage ncaStorage = securePartition.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) { Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } if (controlNca != null) { ReadControlData(controlNca); } return(mainNca, patchNca, controlNca); }
private static string Print(this Xci xci) { const int colLen = 36; var sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine("XCI:"); PrintItem(sb, colLen, "Magic:", xci.Header.Magic); PrintItem(sb, colLen, $"Header Signature{xci.Header.SignatureValidity.GetValidityString()}:", xci.Header.Signature); PrintItem(sb, colLen, $"Header Hash{xci.Header.PartitionFsHeaderValidity.GetValidityString()}:", xci.Header.RootPartitionHeaderHash); PrintItem(sb, colLen, "Cartridge Type:", GetCartridgeType(xci.Header.GameCardSize)); PrintItem(sb, colLen, "Cartridge Size:", $"0x{Utilities.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}"); PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv); PrintPartition(sb, colLen, xci.OpenPartition(XciPartitionType.Root), XciPartitionType.Root); foreach (XciPartitionType type in Enum.GetValues(typeof(XciPartitionType))) { if (type == XciPartitionType.Root || !xci.HasPartition(type)) { continue; } XciPartition partition = xci.OpenPartition(type); PrintPartition(sb, colLen, partition, type); } return(sb.ToString()); }
private static Nca GetXciMainNca(Xci xci, Context ctx) { XciPartition partition = xci.OpenPartition(XciPartitionType.Secure); if (partition == null) { ctx.Logger.LogMessage("Could not find secure partition"); return(null); } Nca mainNca = null; foreach (PartitionFileEntry fileEntry in partition.Files.Where(x => x.Name.EndsWith(".nca"))) { IStorage ncaStorage = partition.OpenFile(fileEntry, OpenMode.Read).AsStorage(); var nca = new Nca(ctx.Keyset, ncaStorage); if (nca.Header.ContentType == NcaContentType.Program) { mainNca = nca; } } return(mainNca); }
public XciPartitionItem(XciPartition xciPartition, XciPartitionType xciPartitionType, XciItem parentItem) : base(xciPartition) { XciPartition = xciPartition ?? throw new ArgumentNullException(nameof(xciPartition)); XciPartitionType = xciPartitionType; ParentItem = parentItem ?? throw new ArgumentNullException(nameof(parentItem)); }
public Result Create(out ReferenceCountedDisposable <IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionType) { // Use the old xci code temporarily UnsafeHelpers.SkipParamInit(out fileSystem); Result rc = GameCard.GetXci(out Xci xci, handle); if (rc.IsFailure()) { return(rc); } if (!xci.HasPartition((XciPartitionType)partitionType)) { return(ResultFs.PartitionNotFound.Log()); } XciPartition fs = xci.OpenPartition((XciPartitionType)partitionType); fileSystem = new ReferenceCountedDisposable <IFileSystem>(fs); return(Result.Success); }
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, 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, 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); }
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 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 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)); } } }