diff --git a/NDecrypt.N3DS/CIATool.cs b/NDecrypt.N3DS/CIATool.cs index 6f91b7f..59dfb31 100644 --- a/NDecrypt.N3DS/CIATool.cs +++ b/NDecrypt.N3DS/CIATool.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Numerics; using NDecrypt.Core; +using SabreTools.IO.Extensions; using SabreTools.Models.N3DS; using static NDecrypt.Core.Helper; using CIADeserializer = SabreTools.Serialization.Deserializers.CIA; @@ -71,8 +72,8 @@ namespace NDecrypt.N3DS try { // Open the read and write on the same file for inplace processing - using var reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); + using var reader = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); // Deserialize the CIA information var cia = ReadCIA(reader); @@ -99,9 +100,9 @@ namespace NDecrypt.N3DS /// Process all partitions in the content file data of a CIA header /// /// CIA representing the 3DS CIA file - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream - private void ProcessAllPartitions(CIA cia, BinaryReader reader, BinaryWriter writer) + /// Stream representing the input + /// Stream representing the output + private void ProcessAllPartitions(CIA cia, Stream reader, Stream writer) { // Iterate over all NCCH partitions for (int p = 0; p < cia.Partitions!.Length; p++) @@ -119,12 +120,12 @@ namespace NDecrypt.N3DS /// /// Index of the partition /// NCCH header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessPartition(int partitionIndex, NCCHHeader ncchHeader, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If we're forcing the operation, tell the user if (decryptArgs.Force) @@ -238,21 +239,21 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private bool ProcessExtendedHeader(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; if (ncchHeader.ExtendedHeaderSizeInBytes > 0) { - reader.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); - writer.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); + reader.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader"); @@ -274,19 +275,19 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFileEntries(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; - reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - var exefsHeader = N3DSDeserializer.ParseExeFSHeader(reader.BaseStream); + reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + var exefsHeader = N3DSDeserializer.ParseExeFSHeader(reader); // If the header failed to read, log and return if (exefsHeader?.FileHeaders == null) @@ -310,8 +311,8 @@ namespace NDecrypt.N3DS var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, decryptArgs.Encrypt); var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !decryptArgs.Encrypt); - reader.BaseStream.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); - writer.BaseStream.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); + reader.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); + writer.Seek((((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin); if (datalenM > 0) { @@ -339,19 +340,19 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFilenameTable(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; - reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table"); @@ -366,13 +367,13 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessExeFS(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // mediaUnitSize; @@ -385,8 +386,8 @@ namespace NDecrypt.N3DS var exeFS = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, decryptArgs.Encrypt); - reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); - writer.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); + reader.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * mediaUnitSize, SeekOrigin.Begin); if (exefsSizeM > 0) { for (int i = 0; i < exefsSizeM; i++) @@ -415,13 +416,13 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void DecryptExeFS(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If the ExeFS size is 0, we log and return if (ncchHeader.ExeFSSizeInMediaUnits == 0) @@ -447,14 +448,14 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Encrypt private void DecryptRomFS(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; @@ -471,8 +472,8 @@ namespace NDecrypt.N3DS var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), decryptArgs.Encrypt); - reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.BaseStream.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); if (romfsSizeM > 0) { for (int i = 0; i < romfsSizeM; i++) @@ -496,21 +497,21 @@ namespace NDecrypt.N3DS /// /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryWriter representing the output stream + /// Stream representing the output private void UpdateDecryptCryptoAndMasks(NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryWriter writer) + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; // Write the new CryptoMethod - writer.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); writer.Write((byte)CryptoMethod.Original); writer.Flush(); // Write the new BitMasks flag - writer.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); BitMasks flag = ncchHeader.Flags!.BitMasks; flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF); flag |= BitMasks.NoCrypto; @@ -528,13 +529,13 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void EncryptExeFS(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If the ExeFS size is 0, we log and return if (ncchHeader.ExeFSSizeInMediaUnits == 0) @@ -561,14 +562,14 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Decrypt private void EncryptRomFS(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; @@ -600,8 +601,8 @@ namespace NDecrypt.N3DS var cipher = CreateAESCipher(NormalKey[partitionIndex], ncchHeader.RomFSIV(), decryptArgs.Encrypt); - reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - writer.BaseStream.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + reader.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); if (romfsSizeM > 0) { for (int i = 0; i < romfsSizeM; i++) @@ -626,17 +627,17 @@ namespace NDecrypt.N3DS /// Index of the partition /// NCCH header representing the partition /// PartitionTableEntry header representing the partition - /// BinaryWriter representing the output stream + /// Stream representing the output private void UpdateEncryptCryptoAndMasks(int partitionIndex, NCCHHeader ncchHeader, PartitionTableEntry tableEntry, - BinaryWriter writer) + Stream writer) { // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value? uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize; // Write the new CryptoMethod - writer.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin); // For partitions 1 and up, set crypto-method to 0x00 if (partitionIndex > 0) @@ -650,7 +651,7 @@ namespace NDecrypt.N3DS writer.Flush(); // Write the new BitMasks flag - writer.BaseStream.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); + writer.Seek((tableEntry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin); BitMasks flag = ncchHeader.Flags!.BitMasks; flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF; @@ -667,13 +668,13 @@ namespace NDecrypt.N3DS /// /// Read from a stream and get a CIA header, if possible /// - /// BinaryReader representing the input stream + /// Stream representing the input /// CIA header object, null on error - private static CIA? ReadCIA(BinaryReader reader) + private static CIA? ReadCIA(Stream reader) { try { - return CIADeserializer.DeserializeStream(reader.BaseStream); + return CIADeserializer.DeserializeStream(reader); } catch { diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs index 14dbe51..57a608e 100644 --- a/NDecrypt.N3DS/ThreeDSTool.cs +++ b/NDecrypt.N3DS/ThreeDSTool.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Numerics; using NDecrypt.Core; +using SabreTools.IO.Extensions; using SabreTools.Models.N3DS; using static NDecrypt.Core.Helper; using N3DSDeserializer = SabreTools.Serialization.Deserializers.N3DS; @@ -69,11 +70,11 @@ namespace NDecrypt.N3DS try { // Open the read and write on the same file for inplace processing - using var reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); + using var reader = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); // Deserialize the cart information - var cart = ReadCart(reader); + var cart = N3DSDeserializer.DeserializeStream(reader); if (cart?.Header == null || cart?.CardInfoHeader?.InitialData?.BackupHeader == null) { Console.WriteLine("Error: Not a 3DS cart image!"); @@ -97,9 +98,9 @@ namespace NDecrypt.N3DS /// Process all partitions in the partition table of an NCSD header /// /// Cart representing the 3DS file - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream - private void ProcessAllPartitions(Cart cart, BinaryReader reader, BinaryWriter writer) + /// Stream representing the input + /// Stream representing the output + private void ProcessAllPartitions(Cart cart, Stream reader, Stream writer) { // Check the partitions table if (cart.Header?.PartitionsTable == null) @@ -127,12 +128,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessPartition(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If we're forcing the operation, tell the user if (decryptArgs.Force) @@ -244,12 +245,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private bool ProcessExtendedHeader(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -258,8 +259,8 @@ namespace NDecrypt.N3DS if (cart.Partitions![partitionIndex]!.ExtendedHeaderSizeInBytes > 0) { // Seek to the extended header - reader.BaseStream.Seek(partitionOffset + 0x200, SeekOrigin.Begin); - writer.BaseStream.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + reader.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + writer.Seek(partitionOffset + 0x200, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader"); @@ -273,7 +274,7 @@ namespace NDecrypt.N3DS #if NET6_0_OR_GREATER // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer - reader.BaseStream.Seek(0, SeekOrigin.Begin); + reader.Seek(0, SeekOrigin.Begin); #endif writer.Flush(); return true; @@ -290,12 +291,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// PartitionTableEntry header representing the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFileEntries(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -303,8 +304,8 @@ namespace NDecrypt.N3DS uint exeFsHeaderOffset = (partitionOffsetMU + exeFsOffsetMU) * cart.MediaUnitSize(); uint exeFsOffset = (partitionOffsetMU + exeFsOffsetMU + 1) * cart.MediaUnitSize(); - reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin); - var exefsHeader = N3DSDeserializer.ParseExeFSHeader(reader.BaseStream); + reader.Seek(exeFsHeaderOffset, SeekOrigin.Begin); + var exefsHeader = N3DSDeserializer.ParseExeFSHeader(reader); // If the header failed to read, log and return if (exefsHeader == null) @@ -330,8 +331,8 @@ namespace NDecrypt.N3DS var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !decryptArgs.Encrypt); // Seek to the file entry - reader.BaseStream.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin); - writer.BaseStream.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin); + reader.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin); + writer.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin); // Process MiB-aligned data if (datalenM > 0) @@ -366,12 +367,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void ProcessExeFSFilenameTable(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -379,8 +380,8 @@ namespace NDecrypt.N3DS uint exeFsHeaderOffset = (partitionOffsetMU + exeFsOffsetMU) * cart.MediaUnitSize(); // Seek to the ExeFS header - reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin); - writer.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin); + reader.Seek(exeFsHeaderOffset, SeekOrigin.Begin); + writer.Seek(exeFsHeaderOffset, SeekOrigin.Begin); Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table"); @@ -394,7 +395,7 @@ namespace NDecrypt.N3DS #if NET6_0_OR_GREATER // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer - reader.BaseStream.Seek(0, SeekOrigin.Begin); + reader.Seek(0, SeekOrigin.Begin); #endif writer.Flush(); } @@ -404,12 +405,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private bool ProcessExeFS(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -434,8 +435,8 @@ namespace NDecrypt.N3DS var cipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, decryptArgs.Encrypt); // Seek to the ExeFS - reader.BaseStream.Seek(exeFsOffset, SeekOrigin.Begin); - writer.BaseStream.Seek(exeFsOffset, SeekOrigin.Begin); + reader.Seek(exeFsOffset, SeekOrigin.Begin); + writer.Seek(exeFsOffset, SeekOrigin.Begin); // Process MiB-aligned data if (exefsSizeM > 0) @@ -472,12 +473,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void DecryptExeFS(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If the ExeFS size is 0, we log and return if (cart.Partitions![partitionIndex]!.ExeFSSizeInMediaUnits == 0) @@ -502,13 +503,13 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Encrypt private bool DecryptRomFS(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -531,8 +532,8 @@ namespace NDecrypt.N3DS var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), decryptArgs.Encrypt); // Seek to the RomFS - reader.BaseStream.Seek(romFsOffset, SeekOrigin.Begin); - writer.BaseStream.Seek(romFsOffset, SeekOrigin.Begin); + reader.Seek(romFsOffset, SeekOrigin.Begin); + writer.Seek(romFsOffset, SeekOrigin.Begin); // Process MiB-aligned data if (romfsSizeM > 0) @@ -565,22 +566,22 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryWriter representing the output stream - private static void UpdateDecryptCryptoAndMasks(Cart cart, int partitionIndex, BinaryWriter writer) + /// Stream representing the output + private static void UpdateDecryptCryptoAndMasks(Cart cart, int partitionIndex, Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; uint partitionOffset = partitionOffsetMU * cart.MediaUnitSize(); // Seek to the CryptoMethod location - writer.BaseStream.Seek(partitionOffset + 0x18B, SeekOrigin.Begin); + writer.Seek(partitionOffset + 0x18B, SeekOrigin.Begin); // Write the new CryptoMethod writer.Write((byte)CryptoMethod.Original); writer.Flush(); // Seek to the BitMasks location - writer.BaseStream.Seek(partitionOffset + 0x18F, SeekOrigin.Begin); + writer.Seek(partitionOffset + 0x18F, SeekOrigin.Begin); // Write the new BitMasks flag BitMasks flag = cart.Partitions![partitionIndex]!.Flags!.BitMasks; @@ -599,12 +600,12 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output private void EncryptExeFS(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // If the ExeFS size is 0, we log and return if (cart.Partitions![partitionIndex]!.ExeFSSizeInMediaUnits == 0) @@ -632,13 +633,13 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream + /// Stream representing the input + /// Stream representing the output /// TODO: See how much can be extracted into a common method with Decrypt private bool EncryptRomFS(Cart cart, int partitionIndex, - BinaryReader reader, - BinaryWriter writer) + Stream reader, + Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -679,8 +680,8 @@ namespace NDecrypt.N3DS var cipher = CreateAESCipher(NormalKey[partitionIndex], cart.RomFSIV(partitionIndex), decryptArgs.Encrypt); // Seek to the RomFS - reader.BaseStream.Seek(romFsOffset, SeekOrigin.Begin); - writer.BaseStream.Seek(romFsOffset, SeekOrigin.Begin); + reader.Seek(romFsOffset, SeekOrigin.Begin); + writer.Seek(romFsOffset, SeekOrigin.Begin); // Process MiB-aligned data if (romfsSizeM > 0) @@ -713,8 +714,8 @@ namespace NDecrypt.N3DS /// /// Cart representing the 3DS file /// Index of the partition - /// BinaryWriter representing the output stream - private static void UpdateEncryptCryptoAndMasks(Cart cart, int partitionIndex, BinaryWriter writer) + /// Stream representing the output + private static void UpdateEncryptCryptoAndMasks(Cart cart, int partitionIndex, Stream writer) { // Get required offsets uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset; @@ -724,7 +725,7 @@ namespace NDecrypt.N3DS var backupHeader = cart.CardInfoHeader!.InitialData!.BackupHeader; // Seek to the CryptoMethod location - writer.BaseStream.Seek(partitionOffset + 0x18B, SeekOrigin.Begin); + writer.Seek(partitionOffset + 0x18B, SeekOrigin.Begin); // Write the new CryptoMethod // - For partitions 1 and up, set crypto-method to 0x00 @@ -734,7 +735,7 @@ namespace NDecrypt.N3DS writer.Flush(); // Seek to the BitMasks location - writer.BaseStream.Seek(partitionOffset + 0x18F, SeekOrigin.Begin); + writer.Seek(partitionOffset + 0x18F, SeekOrigin.Begin); // Write the new BitMasks flag BitMasks flag = cart.Partitions![partitionIndex]!.Flags!.BitMasks; @@ -745,55 +746,5 @@ namespace NDecrypt.N3DS } #endregion - - #region Serialization - - /// - /// Read from a stream and get N3DS cart image, if possible - /// - /// BinaryReader representing the input stream - /// N3DS cart image object, null on error - private static Cart? ReadCart(BinaryReader reader) - { - var cart = new Cart(); - - try - { - cart.Header = N3DSDeserializer.ParseNCSDHeader(reader.BaseStream); - if (cart.Header == null) - return null; - - if (cart.Header.PartitionsFSType == FilesystemType.Normal - || cart.Header.PartitionsFSType == FilesystemType.None) - { - cart.CardInfoHeader = N3DSDeserializer.ParseCardInfoHeader(reader.BaseStream); - if (cart.CardInfoHeader == null) - return null; - } - - cart.Partitions = new NCCHHeader[8]; - for (int i = 0; i < 8; i++) - { - // Check the entry is valid - var tableEntry = cart.Header.PartitionsTable![i]; - if (tableEntry == null || tableEntry.Offset == 0 || tableEntry.Length == 0) - continue; - - // Seek to the beginning of the NCCH partition - long offset = tableEntry.Offset * cart.Header.ImageSizeInMediaUnits; - reader.BaseStream.Seek(offset, SeekOrigin.Begin); - - cart.Partitions[i] = N3DSDeserializer.ParseNCCHHeader(reader.BaseStream, skipSignature: false); - } - - return cart; - } - catch - { - return null; - } - } - - #endregion } } diff --git a/NDecrypt.Nitro/DSTool.cs b/NDecrypt.Nitro/DSTool.cs index 2307b16..f72974e 100644 --- a/NDecrypt.Nitro/DSTool.cs +++ b/NDecrypt.Nitro/DSTool.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using NDecrypt.Core; +using SabreTools.IO.Extensions; using SabreTools.Models.Nitro; using NitroDeserializer = SabreTools.Serialization.Deserializers.Nitro; @@ -40,11 +41,11 @@ namespace NDecrypt.Nitro try { // Open the read and write on the same file for inplace processing - using var reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); + using var reader = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var writer = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); // Deserialize the cart information - Cart? cart = ReadCart(reader); + Cart? cart = NitroDeserializer.DeserializeStream(reader); if (cart == null) { Console.WriteLine("Error: Not a DS or DSi Rom!"); @@ -66,11 +67,11 @@ namespace NDecrypt.Nitro /// /// Process secure area in the DS/DSi file - /// + /// s /// Cart representing the DS file - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream - private void ProcessSecureArea(Cart cart, BinaryReader reader, BinaryWriter writer) + /// Stream representing the input + /// Stream representing the output + private void ProcessSecureArea(Cart cart, Stream reader, Stream writer) { // If we're forcing the operation, tell the user if (decryptArgs.Force) @@ -101,11 +102,11 @@ namespace NDecrypt.Nitro /// /// Determine if the current file is already decrypted or not (or has an empty secure area) /// - /// BinaryReader representing the input stream + /// Stream representing the input /// True if the file has known values for a decrypted file, null if it's empty, false otherwise - private static bool? CheckIfDecrypted(BinaryReader reader) + private static bool? CheckIfDecrypted(Stream reader) { - reader.BaseStream.Seek(0x4000, SeekOrigin.Begin); + reader.Seek(0x4000, SeekOrigin.Begin); uint firstValue = reader.ReadUInt32(); uint secondValue = reader.ReadUInt32(); @@ -161,13 +162,13 @@ namespace NDecrypt.Nitro /// Process the secure ARM9 region of the file, if possible /// /// CommonHeader representing the DS header - /// BinaryReader representing the input stream - /// BinaryWriter representing the output stream - private void ProcessARM9(CommonHeader commonHeader, BinaryReader reader, BinaryWriter writer) + /// Stream representing the input + /// Stream representing the output + private void ProcessARM9(CommonHeader commonHeader, Stream reader, Stream writer) { // Seek to the beginning of the secure area - reader.BaseStream.Seek(0x4000, SeekOrigin.Begin); - writer.BaseStream.Seek(0x4000, SeekOrigin.Begin); + reader.Seek(0x4000, SeekOrigin.Begin); + writer.Seek(0x4000, SeekOrigin.Begin); // Grab the first two blocks uint p0 = reader.ReadUInt32(); @@ -195,8 +196,8 @@ namespace NDecrypt.Nitro } // Ensure alignment - reader.BaseStream.Seek(0x4008, SeekOrigin.Begin); - writer.BaseStream.Seek(0x4008, SeekOrigin.Begin); + reader.Seek(0x4008, SeekOrigin.Begin); + writer.Seek(0x4008, SeekOrigin.Begin); // Loop throgh the main encryption step uint size = 0x800 - 8; @@ -219,8 +220,8 @@ namespace NDecrypt.Nitro // Replace the header explicitly if we're encrypting if (decryptArgs.Encrypt) { - reader.BaseStream.Seek(0x4000, SeekOrigin.Begin); - writer.BaseStream.Seek(0x4000, SeekOrigin.Begin); + reader.Seek(0x4000, SeekOrigin.Begin); + writer.Seek(0x4000, SeekOrigin.Begin); p0 = reader.ReadUInt32(); p1 = reader.ReadUInt32(); @@ -361,26 +362,5 @@ namespace NDecrypt.Nitro _cardHash[i + 18 + 1] = tmp2; } } - - #region Serialization - - /// - /// Read from a stream and get an NDS/NDSi Cart, if possible - /// - /// BinaryReader representing the input stream - /// NDS/NDSi Cart object, null on error - private static Cart? ReadCart(BinaryReader reader) - { - try - { - return NitroDeserializer.DeserializeStream(reader.BaseStream); - } - catch - { - return null; - } - } - - #endregion } }