diff --git a/NDecrypt.N3DS/CommonOperations.cs b/NDecrypt.N3DS/CommonOperations.cs
index dc10973..7923d40 100644
--- a/NDecrypt.N3DS/CommonOperations.cs
+++ b/NDecrypt.N3DS/CommonOperations.cs
@@ -325,6 +325,33 @@ namespace NDecrypt.N3DS
public static uint GetExeFSSize(NCCHHeader header, uint mediaUnitSize)
=> header.ExeFSSizeInMediaUnits * mediaUnitSize;
+ ///
+ /// Get the size of a partition extended header
+ ///
+ /// Size of the partition extended header in bytes, 0 on error
+ public static uint GetExtendedHeaderSize(Cart cart, int index)
+ {
+ // Empty partitions array means no size is available
+ var partitions = cart.Partitions;
+ if (partitions == null)
+ return 0;
+
+ // Invalid partition header means no size is available
+ var header = partitions[index];
+ if (header == null)
+ return 0;
+
+ // Return the adjusted size
+ return GetExtendedHeaderSize(header);
+ }
+
+ ///
+ /// Get the size of a partition extended header
+ ///
+ /// Size of the partition extended header in bytes, 0 on error
+ public static uint GetExtendedHeaderSize(NCCHHeader header)
+ => header.ExtendedHeaderSizeInBytes;
+
///
/// Get the size of a partition RomFS
///
diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs
index ab02574..5d3713c 100644
--- a/NDecrypt.N3DS/ThreeDSTool.cs
+++ b/NDecrypt.N3DS/ThreeDSTool.cs
@@ -150,12 +150,10 @@ namespace NDecrypt.N3DS
// Determine the Keys to be used
SetEncryptionKeys(cart, index, encrypt);
- // Process the extended header
- ProcessExtendedHeader(cart, index, encrypt, input, output);
-
// If we're encrypting, encrypt the filesystems and update the flags
if (encrypt)
{
+ EncryptExtendedHeader(cart, index, input, output);
EncryptExeFS(cart, index, input, output);
EncryptRomFS(cart, index, input, output);
UpdateEncryptCryptoAndMasks(cart, index, output);
@@ -164,6 +162,7 @@ namespace NDecrypt.N3DS
// If we're decrypting, decrypt the filesystems and update the flags
else
{
+ DecryptExtendedHeader(cart, index, input, output);
DecryptExeFS(cart, index, input, output);
DecryptRomFS(cart, index, input, output);
UpdateDecryptCryptoAndMasks(cart, index, output);
@@ -205,54 +204,54 @@ namespace NDecrypt.N3DS
KeysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development);
}
- ///
- /// Process the extended header, if it exists
- ///
- /// Cart representing the 3DS file
- /// Index of the partition
- /// Indicates if the file should be encrypted or decrypted
- /// Stream representing the input
- /// Stream representing the output
- private bool ProcessExtendedHeader(Cart cart, int index, bool encrypt, Stream input, Stream output)
- {
- // Get required offsets
- uint partitionOffsetMU = cart.Header!.PartitionsTable![index]!.Offset;
- uint partitionOffset = partitionOffsetMU * cart.MediaUnitSize();
-
- if (cart.Partitions![index]!.ExtendedHeaderSizeInBytes > 0)
- {
- // Seek to the extended header
- input.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
- output.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
-
- Console.WriteLine($"Partition {index} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
-
- // Create the Plain AES cipher for this partition
- var cipher = CreateAESCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index), encrypt);
-
- // Process the extended header
- byte[] readBytes = input.ReadBytes(Constants.CXTExtendedDataHeaderLength);
- byte[] processedBytes = cipher.ProcessBytes(readBytes);
- output.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
- input.Seek(0, SeekOrigin.Begin);
-#endif
- output.Flush();
- return true;
- }
- else
- {
- Console.WriteLine($"Partition {index} ExeFS: No Extended Header... Skipping...");
- return false;
- }
- }
-
#endregion
#region Decrypt
+ ///
+ /// Decrypt the extended header, if it exists
+ ///
+ /// Cart representing the 3DS file
+ /// Index of the partition
+ /// Stream representing the input
+ /// Stream representing the output
+ private bool DecryptExtendedHeader(Cart cart, int index, Stream input, Stream output)
+ {
+ // Get required offsets
+ uint partitionOffset = GetPartitionOffset(cart, index);
+ if (partitionOffset == 0)
+ {
+ Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
+ return false;
+ }
+
+ uint extHeaderSize = GetExtendedHeaderSize(cart, index);
+ if (extHeaderSize == 0)
+ {
+ Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping...");
+ return false;
+ }
+
+ // Seek to the extended header
+ input.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
+ output.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
+
+ Console.WriteLine($"Partition {index} ExeFS: Decrypting: ExHeader");
+
+ // Create the Plain AES cipher for this partition
+ var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index));
+
+ // Process the extended header
+ PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null);
+
+#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
+ input.Seek(0, SeekOrigin.Begin);
+#endif
+ output.Flush();
+ return true;
+ }
+
///
/// Decrypt the ExeFS, if it exists
///
@@ -398,7 +397,7 @@ namespace NDecrypt.N3DS
/// Stream representing the output
private bool DecryptRomFS(Cart cart, int index, Stream input, Stream output)
{
- // Get required offsets
+ // Validate the RomFS
uint romFsOffset = GetRomFSOffset(cart, index);
if (romFsOffset == 0)
{
@@ -406,6 +405,13 @@ namespace NDecrypt.N3DS
return false;
}
+ uint romFsSize = GetRomFSSize(cart, index);
+ if (romFsSize == 0)
+ {
+ Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
+ return false;
+ }
+
// Seek to the RomFS
input.Seek(romFsOffset, SeekOrigin.Begin);
output.Seek(romFsOffset, SeekOrigin.Begin);
@@ -414,7 +420,6 @@ namespace NDecrypt.N3DS
var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey, cart.RomFSIV(index));
// Setup and perform the decryption
- uint romFsSize = GetRomFSSize(cart, index);
PerformAESOperation(romFsSize,
cipher,
input,
@@ -457,6 +462,50 @@ namespace NDecrypt.N3DS
#region Encrypt
+ ///
+ /// Encrypt the extended header, if it exists
+ ///
+ /// Cart representing the 3DS file
+ /// Index of the partition
+ /// Stream representing the input
+ /// Stream representing the output
+ private bool EncryptExtendedHeader(Cart cart, int index, Stream input, Stream output)
+ {
+ // Get required offsets
+ uint partitionOffset = GetPartitionOffset(cart, index);
+ if (partitionOffset == 0)
+ {
+ Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping...");
+ return false;
+ }
+
+ uint extHeaderSize = GetExtendedHeaderSize(cart, index);
+ if (extHeaderSize == 0)
+ {
+ Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping...");
+ return false;
+ }
+
+ // Seek to the extended header
+ input.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
+ output.Seek(partitionOffset + 0x200, SeekOrigin.Begin);
+
+ Console.WriteLine($"Partition {index} ExeFS: Encrypting: ExHeader");
+
+ // Create the Plain AES cipher for this partition
+ var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index));
+
+ // Process the extended header
+ PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null);
+
+#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
+ input.Seek(0, SeekOrigin.Begin);
+#endif
+ output.Flush();
+ return true;
+ }
+
///
/// Encrypt the ExeFS, if it exists
///
@@ -603,7 +652,7 @@ namespace NDecrypt.N3DS
/// Stream representing the output
private bool EncryptRomFS(Cart cart, int index, Stream input, Stream output)
{
- // Get required offset
+ // Validate the RomFS
uint romFsOffset = GetRomFSOffset(cart, index);
if (romFsOffset == 0)
{
@@ -611,6 +660,13 @@ namespace NDecrypt.N3DS
return false;
}
+ uint romFsSize = GetRomFSSize(cart, index);
+ if (romFsSize == 0)
+ {
+ Console.WriteLine($"Partition {index} RomFS: No Data... Skipping...");
+ return false;
+ }
+
// Seek to the RomFS
input.Seek(romFsOffset, SeekOrigin.Begin);
output.Seek(romFsOffset, SeekOrigin.Begin);
@@ -635,7 +691,6 @@ namespace NDecrypt.N3DS
var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey, cart.RomFSIV(index));
// Setup and perform the decryption
- uint romFsSize = GetRomFSSize(cart, index);
PerformAESOperation(romFsSize,
cipher,
input,