private static void VerifyFileHash(IProgressReporter progressReporter, IFile file, int bufferSize, IReadOnlyCollection <byte> expectedNcaHash, CancellationToken cancellationToken, out bool hashValid) { if (file.GetSize(out var fileSize) != Result.Success) { fileSize = 0; } var sha256 = SHA256.Create(); var ncaStream = file.AsStream(); var buffer = new byte[bufferSize]; decimal totalRead = 0; int read; while ((read = ncaStream.Read(buffer, 0, buffer.Length)) > 0) { cancellationToken.ThrowIfCancellationRequested(); sha256.TransformBlock(buffer, 0, read, null, 0); totalRead += read; progressReporter.SetPercentage(fileSize == 0 ? 0.0 : (double)(totalRead / fileSize)); } sha256.TransformFinalBlock(Array.Empty <byte>(), 0, 0); var currentNcaHash = sha256.Hash !; hashValid = IsHashEqual(currentNcaHash, expectedNcaHash); }
public static void ExtractRomFS(string inFile, string outDirPath, Keyset keyset, Output Out) { using (var file = new FileStream(inFile, FileMode.Open, FileAccess.Read)) { var pfs = new PartitionFileSystem(file.AsStorage()); var OutDirFs = new LocalFileSystem(outDirPath); IDirectory sourceRoot = pfs.OpenDirectory("/", OpenDirectoryMode.All); IFileSystem sourceFs = sourceRoot.ParentFileSystem; Out.Log(pfs.Print()); foreach (var entry in FileIterator(sourceRoot)) { if (entry.Name.EndsWith(".nca")) { var fullOutDirPath = $"{outDirPath}/{entry.Name}"; Out.Log($"Extracting {entry.Name}...\r\n"); using (IFile srcFile = sourceFs.OpenFile(entry.Name, OpenMode.Read)) { ProcessNca.Extract(srcFile.AsStream(), fullOutDirPath, true, keyset, Out); } } else if (entry.Name.EndsWith(".nca.nsz")) { var fullOutDirPath = $"{outDirPath}/{entry.Name}"; Out.Log($"Extracting {entry.Name}...\r\n"); using (IFile srcFile = sourceFs.OpenFile(entry.Name, OpenMode.Read)) using (var decompressedFile = new DecompressionStorage(srcFile)) { ProcessNca.Extract(decompressedFile.AsStream(), fullOutDirPath, true, keyset, Out, true); // Header can't be patched for now due to OpenSection // and ValidateMasterHash needs to know if AesCtrEx // so Nca.cs was patched and now accepts isDecryptedNca // as constructor argument which disables decryption /* * var DecryptedHeader = new byte[0xC00]; * decompressedFile.AsStream().Read(DecryptedHeader, 0, 0xC00); * DecryptedHeader[1028] = (int)NcaEncryptionType.None; * DecryptedHeader[1540] = (int)NcaEncryptionType.None; * DecryptedHeader[2052] = (int)NcaEncryptionType.None; * DecryptedHeader[2564] = (int)NcaEncryptionType.None; * var HeaderKey1 = new byte[16]; * var HeaderKey2 = new byte[16]; * Buffer.BlockCopy(keyset.HeaderKey, 0, HeaderKey1, 0, 16); * Buffer.BlockCopy(keyset.HeaderKey, 16, HeaderKey2, 0, 16); * var headerEncrypted = CryptoInitialisers.AES_XTS(HeaderKey1, HeaderKey2, 0x200, DecryptedHeader, 0); * var ncaStorageList = new List<IStorage>() { new MemoryStorage(headerEncrypted), decompressedFile.Slice(0xC00) }; * var cleanDecryptedNca = new ConcatenationStorage(ncaStorageList, true); * ProcessNca.Extract(cleanDecryptedNca.AsStream(), fullOutDirPath, true, keyset, Out); */ } } } } }
public void ReadControlData(Nca controlNca) { IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel); IFile controlFile = controlFs.OpenFile("/control.nacp", OpenMode.Read); ControlData = new Nacp(controlFile.AsStream()); TitleName = CurrentTitle = ControlData.Descriptions[(int)State.DesiredTitleLanguage].Title; }
public BdatFile(IFile bdat, string fileName) { using (var reader = new BinaryReader(bdat.AsStream())) { var tableCount = reader.ReadInt32(); var fileLength = reader.ReadInt32(); for (int i = 0; i < tableCount; i++) { reader.BaseStream.Position = 8 + (4 * i); reader.BaseStream.Position = reader.ReadInt32(); Tables.Add(new BdatTable(reader, fileName)); } } }
private void ReadTitles() { foreach (SwitchFsNca nca in Ncas.Values.Where(x => x.Nca.Header.ContentType == ContentType.Meta)) { try { var title = new Title(); IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); string cnmtPath = fs.EnumerateEntries("*.cnmt").Single().FullPath; IFile file = fs.OpenFile(cnmtPath, 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 SwitchFsNca 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; } catch (Exception ex) { Console.WriteLine($"{ex.Message} File: {nca.Filename}"); } } }
private static Ticket[] GetTickets(Keyset keyset, Nand nand, IProgressReport logger = null) { var tickets = new List <Ticket>(); FatFileSystemProvider system = nand.OpenSystemPartition(); IFile saveE1File = system.OpenFile("/save/80000000000000E1", OpenMode.Read); tickets.AddRange(ReadTickets(keyset, saveE1File.AsStream())); IFile saveE2 = system.OpenFile("/save/80000000000000E2", OpenMode.Read); tickets.AddRange(ReadTickets(keyset, saveE2.AsStream())); logger?.LogMessage($"Found {tickets.Count} tickets"); return(tickets.ToArray()); }
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; } }
private void ReadControls() { foreach (Title title in Titles.Values.Where(x => x.ControlNca != null)) { var romfs = new RomFsFileSystem(title.ControlNca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true)); IFile control = romfs.OpenFile("control.nacp", OpenMode.Read); title.Control = new Nacp(control.AsStream()); foreach (NacpDescription desc in title.Control.Descriptions) { if (!string.IsNullOrWhiteSpace(desc.Title)) { title.Name = desc.Title; break; } } } }
private static Validity VerifySignature2(this Nca nca) { if (nca.Header.ContentType != ContentType.Program) { return(Validity.Unchecked); } IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); if (!pfs.FileExists("main.npdm")) { return(Validity.Unchecked); } IFile npdmStorage = pfs.OpenFile("main.npdm", OpenMode.Read); var npdm = new NpdmBinary(npdmStorage.AsStream()); return(nca.Header.VerifySignature2(npdm.AciD.Rsa2048Modulus)); }
public static void Encrypt(IFileSystem sourceFs, IFileSystem destFs, bool verifyEncrypted, Keyset keyset, Output Out) { foreach (var decryptedNcaEntry in sourceFs.EnumerateEntries().Where(item => item.Name.EndsWith(".nca"))) { Out.Log($"Input: {decryptedNcaEntry.Name}\r\n"); using (var decryptedNca = sourceFs.OpenFile(decryptedNcaEntry.FullPath, OpenMode.Read)) { if (destFs != null) { Out.Log("Opened NCA for writing...\r\n"); using (IFile outputFile = FolderTools.CreateAndOpen(decryptedNcaEntry, destFs, decryptedNcaEntry.Name, decryptedNca.GetSize())) { EncryptFunct(decryptedNca.AsStream(), outputFile.AsStream(), decryptedNcaEntry.Name, verifyEncrypted, keyset, Out); } } else { EncryptFunct(decryptedNca.AsStream(), null, decryptedNcaEntry.Name, verifyEncrypted, keyset, Out); } } } }
public static void Process(IFile inFile, IFile outFile, bool verifyBeforeDecrypting, Keyset keyset, Output Out) { using (var file = new StreamStorage(inFile.AsStream(), false)) { var nca = new Nca(keyset, file, false); Out.Log(nca.Print()); if (verifyBeforeDecrypting) { Out.Log($"ValidateMasterHashes...\r\n"); nca.ValidateMasterHashes(); //nca.ParseNpdm(); for (var i = 0; i < 3; ++i) { if (nca.Sections[i] != null) { nca.VerifySection(i, Out); } } } Out.Log($"Decripting...\r\n"); nca.OpenDecryptedNca().CopyToStream(outFile.AsStream()); } }
public DecompressionStorage(IFile inputFile) { var inputFileStream = inputFile.AsStream(); var nsZipMagic = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70 }; var nsZipMagicEncrypted = new byte[5]; inputFileStream.Read(nsZipMagicEncrypted, 0, 5); var nsZipMagicRandomKey = new byte[5]; inputFileStream.Read(nsZipMagicRandomKey, 0, 5); Util.XorArrays(nsZipMagicEncrypted, nsZipMagicRandomKey); if (!Util.ArraysEqual(nsZipMagicEncrypted, nsZipMagic)) { throw new InvalidDataException($"Invalid nsZip magic!\r\n"); } var version = inputFileStream.ReadByte(); var type = inputFileStream.ReadByte(); var bsArray = new byte[5]; inputFileStream.Read(bsArray, 0, 5); long bsReal = (bsArray[0] << 32) + (bsArray[1] << 24) + (bsArray[2] << 16) + (bsArray[3] << 8) + bsArray[4]; if (bsReal > int.MaxValue) { throw new NotImplementedException("Block sizes above 2 GB aren't supported yet!"); } bs = (int)bsReal; var amountOfBlocksArray = new byte[4]; inputFileStream.Read(amountOfBlocksArray, 0, 4); amountOfBlocks = (amountOfBlocksArray[0] << 24) + (amountOfBlocksArray[1] << 16) + (amountOfBlocksArray[2] << 8) + amountOfBlocksArray[3]; var sizeOfSize = (int)Math.Ceiling(Math.Log(bs, 2) / 8); var perBlockHeaderSize = sizeOfSize + 1; decompressBuff = new byte[bs]; compressionAlgorithm = new int[amountOfBlocks]; var compressedBlockSize = new int[amountOfBlocks]; var compressedBlockOffset = new long[amountOfBlocks]; long currentOffset = 0; for (var currentBlockID = 0; currentBlockID < amountOfBlocks; ++currentBlockID) { compressedBlockOffset[currentBlockID] = currentOffset; compressionAlgorithm[currentBlockID] = inputFileStream.ReadByte(); compressedBlockSize[currentBlockID] = 0; for (var j = 0; j < sizeOfSize; ++j) { compressedBlockSize[currentBlockID] += inputFileStream.ReadByte() << ((sizeOfSize - j - 1) * 8); } currentOffset += compressedBlockSize[currentBlockID]; } compressedBlocks = new IStorage[amountOfBlocks]; var compressedData = new FileStorage(inputFile).Slice(inputFileStream.Position); for (int i = 0; i < amountOfBlocks; ++i) { compressedBlocks[i] = compressedData.Slice(compressedBlockOffset[i], compressedBlockSize[i]); } // Cast to long is VERY important or files larger than 2 GB will have a negative size! lastBlockSize = getSizeOfLastBlock(); length = ((long)(amountOfBlocks - 1) * bs) + lastBlockSize; Console.WriteLine($"length={length} lastBlockSize={lastBlockSize}"); }
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 IFile controlNacp = controlFs.OpenFile("/control.nacp", OpenMode.Read); Nacp controlData = new Nacp(controlNacp.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 { IFile icon = controlFs.OpenFile($"/icon_{DesiredTitleLanguage}.dat", OpenMode.Read); using (MemoryStream stream = new MemoryStream()) { icon.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { IDirectory controlDir = controlFs.OpenDirectory("./", OpenDirectoryMode.All); foreach (DirectoryEntry entry in controlDir.Read()) { if (entry.Name == "control.nacp") { continue; } IFile icon = controlFs.OpenFile(entry.FullPath, OpenMode.Read); 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); } }
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 void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) { if (mainNca.Header.ContentType != ContentType.Program) { Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); return; } IStorage dataStorage = null; IFileSystem codeFs = null; if (patchNca == null) { if (mainNca.CanOpenSection(NcaSectionType.Data)) { dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel); } if (mainNca.CanOpenSection(NcaSectionType.Code)) { codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel); } } else { if (patchNca.CanOpenSection(NcaSectionType.Data)) { dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel); } if (patchNca.CanOpenSection(NcaSectionType.Code)) { codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel); } } if (codeFs == null) { Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA"); return; } if (dataStorage == null) { Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); } else { Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read)); } LoadExeFs(codeFs, out Npdm metaData); Nacp ReadControlData() { IFileSystem controlRomfs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel); IFile controlFile = controlRomfs.OpenFile("/control.nacp", OpenMode.Read); Nacp controlData = new Nacp(controlFile.AsStream()); TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; TitleID = metaData.Aci0.TitleId.ToString("x16"); CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; if (string.IsNullOrWhiteSpace(CurrentTitle)) { TitleName = CurrentTitle = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; } return(controlData); } if (controlNca != null) { ReadControlData(); } else { TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); } }
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 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 override Task CreateTask() { return(new Task(() => { FatFileSystemProvider system = NANDService.NAND.OpenSystemPartition(); string accountSaveFileName = "/save/8000000000000010"; if (system.FileExists(accountSaveFileName)) { IFile accountSaveFile = system.OpenFile(accountSaveFileName, OpenMode.Read); SaveDataFileSystem accountSaveFilesystem = new SaveDataFileSystem(HACGUIKeyset.Keyset, accountSaveFile.AsStorage(), IntegrityCheckLevel.ErrorOnInvalid, false); HACGUIKeyset.AccountsFolderInfo.Create(); // make sure folder exists IDirectory avatorsDirectory = accountSaveFilesystem.OpenDirectory("/su/avators/", OpenDirectoryMode.Files); IEnumerable <DirectoryEntry> files = avatorsDirectory.Read(); DirectoryEntry profileEntry = files.FirstOrDefault(e => e.Name == "profiles.dat"); if (profileEntry != null) { if (profileEntry.Size == 0x650) { IFile profileFile = accountSaveFilesystem.OpenFile(profileEntry.FullPath, OpenMode.Read); Stream profileData = profileFile.AsStream(); profileData.Position += 0x10; // skip header for (int i = 0; i < 8; i++) { byte[] data = new byte[0xC8]; profileData.Read(data, 0, data.Length); byte[] uidBytes = new byte[0x10]; byte[] nameBytes = new byte[32]; Array.Copy(data, uidBytes, uidBytes.Length); Array.Copy(data, 0x28, nameBytes, 0, nameBytes.Length); char[] nameChars = Encoding.UTF8.GetChars(nameBytes); int length = Array.IndexOf(nameChars, '\0'); string name = new string(nameChars.Take(length).ToArray()); Guid uid = Guid.Parse(uidBytes.ToHexString()); // ignores endianness, which is what i want if (!string.IsNullOrEmpty(name)) { Preferences.Current.UserIds[uid.ToString()] = name; } } Preferences.Current.Write(); } else { MessageBox.Show("Invalid profiles.dat size! Something seems to be corrupt..."); } } foreach (DirectoryEntry entry in files.Where(e => e.Name != "profiles.dat")) { FileInfo localFile = HACGUIKeyset.AccountsFolderInfo.GetFile(entry.Name); IFile saveFile = accountSaveFilesystem.OpenFile(entry.FullPath, OpenMode.Read); using (Stream localStream = localFile.Open(FileMode.Create)) saveFile.AsStorage().CopyToStream(localStream, saveFile.GetSize()); } } })); }