diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs
index 5f3f195..8a32e9a 100644
--- a/NDecrypt.N3DS/ThreeDSTool.cs
+++ b/NDecrypt.N3DS/ThreeDSTool.cs
@@ -256,15 +256,24 @@ 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);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
+ // Create the Plain AES cipher for this partition
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.PlainIV(partitionIndex), decryptArgs.Encrypt);
+
+ // Process the extended header
byte[] readBytes = reader.ReadBytes(Constants.CXTExtendedDataHeaderLength);
byte[] processedBytes = cipher.ProcessBytes(readBytes);
writer.Write(processedBytes);
+
+#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);
+#endif
writer.Flush();
return true;
}
@@ -289,9 +298,11 @@ namespace NDecrypt.N3DS
{
// Get required offsets
uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset;
- uint exeFsOffset = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsOffsetMU = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsHeaderOffset = (partitionOffsetMU + exeFsOffsetMU) * cart.MediaUnitSize();
+ uint exeFsOffset = (partitionOffsetMU + exeFsOffsetMU + 1) * cart.MediaUnitSize();
- reader.BaseStream.Seek((partitionOffsetMU + exeFsOffset) * cart.MediaUnitSize(), SeekOrigin.Begin);
+ reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
var exefsHeader = Serializer.ReadExeFSHeader(reader);
// If the header failed to read, log and return
@@ -307,18 +318,21 @@ namespace NDecrypt.N3DS
if (fileHeader == null || !fileHeader.IsCodeBinary())
continue;
+ // Get MiB-aligned block count and extra byte count
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
uint ctroffset = ((fileHeader.FileOffset + cart.MediaUnitSize()) / 0x10);
+ // Create the ExeFS AES ciphers for this partition
byte[] exefsIVWithOffsetForHeader = AddToByteArray(cart.ExeFSIV(partitionIndex), (int)ctroffset);
-
var firstCipher = CreateAESCipher(NormalKey[partitionIndex], exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
var secondCipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);
- reader.BaseStream.Seek(((partitionOffsetMU + exeFsOffset + 1) * cart.MediaUnitSize()) + fileHeader.FileOffset, SeekOrigin.Begin);
- writer.BaseStream.Seek(((partitionOffsetMU + exeFsOffset + 1) * cart.MediaUnitSize()) + fileHeader.FileOffset, SeekOrigin.Begin);
+ // Seek to the file entry
+ reader.BaseStream.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin);
+ writer.BaseStream.Seek(exeFsOffset + fileHeader.FileOffset, SeekOrigin.Begin);
+ // Process MiB-aligned data
if (datalenM > 0)
{
for (int i = 0; i < datalenM; i++)
@@ -332,6 +346,7 @@ namespace NDecrypt.N3DS
}
}
+ // Process additional data
if (datalenB > 0)
{
byte[] readBytes = reader.ReadBytes((int)datalenB);
@@ -359,14 +374,19 @@ namespace NDecrypt.N3DS
{
// Get required offsets
uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset;
- uint exeFsOffset = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsOffsetMU = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsHeaderOffset = (partitionOffsetMU + exeFsOffsetMU) * cart.MediaUnitSize();
- reader.BaseStream.Seek((partitionOffsetMU + exeFsOffset) * cart.MediaUnitSize(), SeekOrigin.Begin);
- writer.BaseStream.Seek((partitionOffsetMU + exeFsOffset) * cart.MediaUnitSize(), SeekOrigin.Begin);
+ // Seek to the ExeFS header
+ reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
+ writer.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
Console.WriteLine($"Partition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
+ // Create the ExeFS AES cipher for this partition
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], cart.ExeFSIV(partitionIndex), decryptArgs.Encrypt);
+
+ // Process the filename table
byte[] readBytes = reader.ReadBytes((int)cart.MediaUnitSize());
byte[] processedBytes = cipher.ProcessBytes(readBytes);
writer.Write(processedBytes);
@@ -385,25 +405,38 @@ namespace NDecrypt.N3DS
/// Index of the partition
/// BinaryReader representing the input stream
/// BinaryWriter representing the output stream
- private void ProcessExeFS(Cart cart,
+ private bool ProcessExeFS(Cart cart,
int partitionIndex,
BinaryReader reader,
BinaryWriter writer)
{
// Get required offsets
uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset;
- uint exeFsOffset = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsOffsetMU = cart.Partitions![partitionIndex]!.ExeFSOffsetInMediaUnits;
+ uint exeFsOffset = (partitionOffsetMU + exeFsOffsetMU + 1) * cart.MediaUnitSize();
- int exefsSizeM = (int)((long)((cart.Partitions![partitionIndex]!.ExeFSSizeInMediaUnits - 1) * cart.MediaUnitSize()) / (1024 * 1024));
- int exefsSizeB = (int)((long)((cart.Partitions![partitionIndex]!.ExeFSSizeInMediaUnits - 1) * cart.MediaUnitSize()) % (1024 * 1024));
+ // If the RomFS offset is 0, we log and return
+ if (exeFsOffsetMU == 0)
+ {
+ Console.WriteLine($"Partition {partitionIndex} RomFS: No Data... Skipping...");
+ return false;
+ }
+
+ // Get MiB-aligned block count and extra byte count
+ uint exeFsSize = (cart.Partitions![partitionIndex]!.ExeFSSizeInMediaUnits - 1) * cart.MediaUnitSize();
+ int exefsSizeM = (int)((long)exeFsSize / (1024 * 1024));
+ int exefsSizeB = (int)((long)exeFsSize % (1024 * 1024));
int ctroffsetE = (int)(cart.MediaUnitSize() / 0x10);
+ // Create the ExeFS AES cipher for this partition
byte[] exefsIVWithOffset = AddToByteArray(cart.ExeFSIV(partitionIndex), ctroffsetE);
-
var cipher = CreateAESCipher(NormalKey2C[partitionIndex], exefsIVWithOffset, decryptArgs.Encrypt);
- reader.BaseStream.Seek((partitionOffsetMU + exeFsOffset + 1) * cart.MediaUnitSize(), SeekOrigin.Begin);
- writer.BaseStream.Seek((partitionOffsetMU + exeFsOffset + 1) * cart.MediaUnitSize(), SeekOrigin.Begin);
+ // Seek to the ExeFS
+ reader.BaseStream.Seek(exeFsOffset, SeekOrigin.Begin);
+ writer.BaseStream.Seek(exeFsOffset, SeekOrigin.Begin);
+
+ // Process MiB-aligned data
if (exefsSizeM > 0)
{
for (int i = 0; i < exefsSizeM; i++)
@@ -415,6 +448,8 @@ namespace NDecrypt.N3DS
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
}
}
+
+ // Process additional data
if (exefsSizeB > 0)
{
byte[] readBytes = reader.ReadBytes(exefsSizeB);
@@ -424,6 +459,7 @@ namespace NDecrypt.N3DS
}
Console.Write($"\rPartition {partitionIndex} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
+ return true;
}
#endregion
@@ -468,7 +504,7 @@ namespace NDecrypt.N3DS
/// BinaryReader representing the input stream
/// BinaryWriter representing the output stream
/// TODO: See how much can be extracted into a common method with Encrypt
- private void DecryptRomFS(Cart cart,
+ private bool DecryptRomFS(Cart cart,
int partitionIndex,
BinaryReader reader,
BinaryWriter writer)
@@ -482,17 +518,22 @@ namespace NDecrypt.N3DS
if (romFsOffsetMU == 0)
{
Console.WriteLine($"Partition {partitionIndex} RomFS: No Data... Skipping...");
- return;
+ return false;
}
+ // Get MiB-aligned block count and extra byte count
uint romFsSize = cart.Partitions![partitionIndex]!.RomFSSizeInMediaUnits * cart.MediaUnitSize();
long romfsSizeM = (int)((long)romFsSize / (1024 * 1024));
int romfsSizeB = (int)((long)romFsSize % (1024 * 1024));
+ // Create the RomFS AES cipher for this partition
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);
+
+ // Process MiB-aligned data
if (romfsSizeM > 0)
{
for (int i = 0; i < romfsSizeM; i++)
@@ -504,6 +545,8 @@ namespace NDecrypt.N3DS
Console.Write($"\rPartition {partitionIndex} RomFS: Decrypting: {i} / {romfsSizeM + 1} mb");
}
}
+
+ // Process additional data
if (romfsSizeB > 0)
{
byte[] readBytes = reader.ReadBytes(romfsSizeB);
@@ -513,6 +556,7 @@ namespace NDecrypt.N3DS
}
Console.Write($"\rPartition {partitionIndex} RomFS: Decrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n");
+ return true;
}
///
@@ -521,7 +565,7 @@ namespace NDecrypt.N3DS
/// Cart representing the 3DS file
/// Index of the partition
/// BinaryWriter representing the output stream
- private void UpdateDecryptCryptoAndMasks(Cart cart, int partitionIndex, BinaryWriter writer)
+ private static void UpdateDecryptCryptoAndMasks(Cart cart, int partitionIndex, BinaryWriter writer)
{
// Get required offsets
uint partitionOffsetMU = cart.Header!.PartitionsTable![partitionIndex]!.Offset;
@@ -532,8 +576,10 @@ namespace NDecrypt.N3DS
writer.Write((byte)CryptoMethod.Original);
writer.Flush();
- // Write the new BitMasks flag
+ // Seek to the BitMasks location
writer.BaseStream.Seek(partitionOffset + 0x18F, SeekOrigin.Begin);
+
+ // Write the new BitMasks flag
BitMasks flag = cart.Partitions![partitionIndex]!.Flags!.BitMasks;
flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
flag |= BitMasks.NoCrypto;
@@ -586,7 +632,7 @@ namespace NDecrypt.N3DS
/// BinaryReader representing the input stream
/// BinaryWriter representing the output stream
/// TODO: See how much can be extracted into a common method with Decrypt
- private void EncryptRomFS(Cart cart,
+ private bool EncryptRomFS(Cart cart,
int partitionIndex,
BinaryReader reader,
BinaryWriter writer)
@@ -603,9 +649,10 @@ namespace NDecrypt.N3DS
if (romFsOffsetMU == 0)
{
Console.WriteLine($"Partition {partitionIndex} RomFS: No Data... Skipping...");
- return;
+ return false;
}
+ // Get MiB-aligned block count and extra byte count
uint romFsSize = cart.Partitions![partitionIndex]!.RomFSSizeInMediaUnits * cart.MediaUnitSize();
long romfsSizeM = (int)((long)romFsSize / (1024 * 1024));
int romfsSizeB = (int)((long)romFsSize % (1024 * 1024));
@@ -624,10 +671,14 @@ namespace NDecrypt.N3DS
}
}
+ // Create the RomFS AES cipher for this partition
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);
+
+ // Process MiB-aligned data
if (romfsSizeM > 0)
{
for (int i = 0; i < romfsSizeM; i++)
@@ -639,6 +690,8 @@ namespace NDecrypt.N3DS
Console.Write($"\rPartition {partitionIndex} RomFS: Encrypting: {i} / {romfsSizeM + 1} mb");
}
}
+
+ // Process additional data
if (romfsSizeB > 0)
{
byte[] readBytes = reader.ReadBytes(romfsSizeB);
@@ -648,6 +701,7 @@ namespace NDecrypt.N3DS
}
Console.Write($"\rPartition {partitionIndex} RomFS: Encrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n");
+ return true;
}
///
@@ -669,17 +723,17 @@ namespace NDecrypt.N3DS
writer.BaseStream.Seek(partitionOffset + 0x18B, SeekOrigin.Begin);
// For partitions 1 and up, set crypto-method to 0x00
+ // If partition 0, restore crypto-method from backup flags
if (partitionIndex > 0)
writer.Write((byte)CryptoMethod.Original);
-
- // If partition 0, restore crypto-method from backup flags
else
writer.Write((byte)backupHeader!.Flags!.CryptoMethod);
-
writer.Flush();
- // Write the new BitMasks flag
+ // Seek to the BitMasks location
writer.BaseStream.Seek(partitionOffset + 0x18F, SeekOrigin.Begin);
+
+ // Write the new BitMasks flag
BitMasks flag = cart.Partitions![partitionIndex]!.Flags!.BitMasks;
flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;
flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & backupHeader!.Flags!.BitMasks;