diff --git a/NDecrypt.Core/DSTool.cs b/NDecrypt.Core/DSTool.cs
index 167aab1..b9c948d 100644
--- a/NDecrypt.Core/DSTool.cs
+++ b/NDecrypt.Core/DSTool.cs
@@ -1,30 +1,71 @@
using System;
using System.IO;
using System.Text;
-#if NETFRAMEWORK || NETSTANDARD2_0_OR_GREATER
+using SabreTools.Hashing;
using SabreTools.IO.Extensions;
-#endif
using SabreTools.Serialization.Wrappers;
namespace NDecrypt.Core
{
public class DSTool : ITool
{
- ///
- /// Decryption args to use while processing
- ///
- private readonly DecryptArgs _decryptArgs;
+ #region Properties
- public DSTool(DecryptArgs decryptArgs)
+ ///
+ /// Blowfish Table
+ ///
+ public byte[] BlowfishTable
{
- _decryptArgs = decryptArgs;
- }
+ get;
+ set
+ {
+ // Ignore missing encryption data
+ if (value.Length == 0)
+ return;
+
+ // Validate the blowfish table data
+ byte[]? actual = HashTool.GetByteArrayHashArray(value, HashType.SHA512);
+ if (actual is null || !actual.EqualsExactly(ExpectedNitroSha512Hash))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
+
+ #endregion
+
+ #region Internal Test Values
+
+ ///
+ /// Expected hash for NitroEncryptionData
+ ///
+ private static readonly byte[] ExpectedNitroSha512Hash =
+ [
+ 0x1A, 0xD6, 0x40, 0x21, 0xFC, 0x3D, 0x1A, 0x9A,
+ 0x9B, 0xC0, 0x88, 0x8E, 0x2E, 0x68, 0xDE, 0x4E,
+ 0x8A, 0x60, 0x6B, 0x86, 0x63, 0x22, 0xD2, 0xC7,
+ 0xC6, 0xD7, 0xD6, 0xCE, 0x65, 0xF5, 0xBA, 0xA7,
+ 0xEA, 0x69, 0x63, 0x7E, 0xC9, 0xE4, 0x57, 0x7B,
+ 0x01, 0xFD, 0xCE, 0xC2, 0x26, 0x3B, 0xD9, 0x0D,
+ 0x84, 0x57, 0xC2, 0x00, 0xB8, 0x56, 0x9F, 0xE5,
+ 0x56, 0xDA, 0x8D, 0xDE, 0x84, 0xB8, 0x8E, 0xE4,
+ ];
+
+ #endregion
#region Encrypt
///
public bool EncryptFile(string input, string? output, bool force)
{
+ // If the blowfish table is not set, do not process
+ if (BlowfishTable.Length == 0)
+ {
+ Console.WriteLine("Error: Nitro encryption data not provided!");
+ return false;
+ }
+
try
{
// If the output is provided, copy the input file
@@ -52,7 +93,7 @@ namespace NDecrypt.Core
}
// Encrypt the secure area
- nitro.EncryptSecureArea(_decryptArgs.NitroEncryptionData, force);
+ nitro.EncryptSecureArea(BlowfishTable, force);
// Write the encrypted secure area
using var writer = File.Open(output, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
@@ -77,6 +118,13 @@ namespace NDecrypt.Core
///
public bool DecryptFile(string input, string? output, bool force)
{
+ // If the blowfish table is not set, do not process
+ if (BlowfishTable.Length == 0)
+ {
+ Console.WriteLine("Error: Nitro encryption data not provided!");
+ return false;
+ }
+
try
{
// If the output is provided, copy the input file
@@ -104,7 +152,7 @@ namespace NDecrypt.Core
}
// Decrypt the secure area
- nitro.DecryptSecureArea(_decryptArgs.NitroEncryptionData, force);
+ nitro.DecryptSecureArea(BlowfishTable, force);
// Write the decrypted secure area
using var writer = File.Open(output, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
diff --git a/NDecrypt.Core/PartitionKeys.cs b/NDecrypt.Core/PartitionKeys.cs
index 929bcd3..75f300a 100644
--- a/NDecrypt.Core/PartitionKeys.cs
+++ b/NDecrypt.Core/PartitionKeys.cs
@@ -22,7 +22,7 @@ namespace NDecrypt.Core
///
/// Decryption args to use while processing
///
- private readonly DecryptArgs _decryptArgs;
+ private readonly ThreeDSDecryptArgs _decryptArgs;
///
/// Indicates if development images are expected
@@ -37,11 +37,9 @@ namespace NDecrypt.Core
/// BitMasks from the partition or backup header
/// CryptoMethod from the partition or backup header
/// Determine if development keys are used
- public PartitionKeys(DecryptArgs args, byte[]? signature, BitMasks masks, CryptoMethod method, bool development)
+ public PartitionKeys(ThreeDSDecryptArgs args, byte[]? signature, BitMasks masks, CryptoMethod method, bool development)
{
// Validate inputs
- if (args.IsReady != true)
- throw new InvalidOperationException($"{nameof(args)} must be initialized before use");
if (signature is not null && signature.Length < 16)
throw new ArgumentOutOfRangeException(nameof(signature), $"{nameof(signature)} must be at least 16 bytes");
@@ -51,7 +49,7 @@ namespace NDecrypt.Core
// Set the standard KeyX values
KeyX = new byte[16];
- KeyX2C = development ? args.DevKeyX0x2C : args.KeyX0x2C;
+ KeyX2C = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C;
// Backup headers can't have a KeyY value set
KeyY = new byte[16];
@@ -63,7 +61,7 @@ namespace NDecrypt.Core
NormalKey2C = KeyX2C.RotateLeft(2);
NormalKey2C = NormalKey2C.Xor(KeyY);
- NormalKey2C = NormalKey2C.Add(add: args.AESHardwareConstant);
+ NormalKey2C = NormalKey2C.Add(add: _decryptArgs.AESHardwareConstant);
NormalKey2C = NormalKey2C.RotateLeft(87);
// Special case for zero-key
@@ -84,22 +82,22 @@ namespace NDecrypt.Core
{
case CryptoMethod.Original:
Console.WriteLine("Encryption Method: Key 0x2C");
- KeyX = development ? args.DevKeyX0x2C : args.KeyX0x2C;
+ KeyX = development ? _decryptArgs.DevKeyX0x2C : _decryptArgs.KeyX0x2C;
break;
case CryptoMethod.Seven:
Console.WriteLine("Encryption Method: Key 0x25");
- KeyX = development ? args.DevKeyX0x25 : args.KeyX0x25;
+ KeyX = development ? _decryptArgs.DevKeyX0x25 : _decryptArgs.KeyX0x25;
break;
case CryptoMethod.NineThree:
Console.WriteLine("Encryption Method: Key 0x18");
- KeyX = development ? args.DevKeyX0x18 : args.KeyX0x18;
+ KeyX = development ? _decryptArgs.DevKeyX0x18 : _decryptArgs.KeyX0x18;
break;
case CryptoMethod.NineSix:
Console.WriteLine("Encryption Method: Key 0x1B");
- KeyX = development ? args.DevKeyX0x1B : args.KeyX0x1B;
+ KeyX = development ? _decryptArgs.DevKeyX0x1B : _decryptArgs.KeyX0x1B;
break;
// This should never happen
@@ -111,7 +109,7 @@ namespace NDecrypt.Core
// Set the normal key based on the new KeyX value
NormalKey = KeyX.RotateLeft(2);
NormalKey = NormalKey.Xor(KeyY);
- NormalKey = NormalKey.Add(args.AESHardwareConstant);
+ NormalKey = NormalKey.Add(_decryptArgs.AESHardwareConstant);
NormalKey = NormalKey.RotateLeft(87);
}
diff --git a/NDecrypt.Core/DecryptArgs.cs b/NDecrypt.Core/ThreeDSDecryptArgs.cs
similarity index 61%
rename from NDecrypt.Core/DecryptArgs.cs
rename to NDecrypt.Core/ThreeDSDecryptArgs.cs
index 347c839..7cfe218 100644
--- a/NDecrypt.Core/DecryptArgs.cs
+++ b/NDecrypt.Core/ThreeDSDecryptArgs.cs
@@ -1,97 +1,205 @@
-using System;
-using System.IO;
-using SabreTools.Hashing;
using SabreTools.IO.Encryption;
using SabreTools.IO.Extensions;
namespace NDecrypt.Core
{
- public class DecryptArgs
+ ///
+ /// Nintendo 3DS encryption/decryption values
+ ///
+ public class ThreeDSDecryptArgs
{
- #region Common Fields
-
- ///
- /// Represents if all of the keys have been initialized properly
- ///
- public bool? IsReady { get; private set; }
-
- #endregion
-
- #region DS-Specific Fields
-
- ///
- /// Blowfish Table
- ///
- public byte[] NitroEncryptionData { get; private set; } = [];
-
- #endregion
-
- #region 3DS-Specific Fields
-
///
/// AES Hardware Constant
///
- public byte[] AESHardwareConstant { get; private set; } = [];
+ /// TODO: Validate this value on assignment
+ public byte[] AESHardwareConstant { get; set; } = [];
///
/// KeyX 0x18 (New 3DS 9.3)
///
- public byte[] KeyX0x18 { get; private set; } = [];
+ public byte[] KeyX0x18
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(KeyX0x18, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedKeyX0x18))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// Dev KeyX 0x18 (New 3DS 9.3)
///
- public byte[] DevKeyX0x18 { get; private set; } = [];
+ public byte[] DevKeyX0x18
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x18, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedDevKeyX0x18))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// KeyX 0x1B (New 3DS 9.6)
///
- public byte[] KeyX0x1B { get; private set; } = [];
+ public byte[] KeyX0x1B
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(KeyX0x1B, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedKeyX0x1B))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// Dev KeyX 0x1B New 3DS 9.6)
///
- public byte[] DevKeyX0x1B { get; private set; } = [];
+ public byte[] DevKeyX0x1B
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x1B, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedDevKeyX0x1B))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// KeyX 0x25 (> 7.x)
///
- public byte[] KeyX0x25 { get; private set; } = [];
+ public byte[] KeyX0x25
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(KeyX0x25, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedKeyX0x25))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// Dev KeyX 0x25 (> 7.x)
///
- public byte[] DevKeyX0x25 { get; private set; } = [];
+ public byte[] DevKeyX0x25
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x25, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedDevKeyX0x25))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// KeyX 0x2C (< 6.x)
///
- public byte[] KeyX0x2C { get; private set; } = [];
+ public byte[] KeyX0x2C
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
+
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(KeyX0x2C, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedKeyX0x2C))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
///
/// Dev KeyX 0x2C (< 6.x)
///
- public byte[] DevKeyX0x2C { get; private set; } = [];
+ public byte[] DevKeyX0x2C
+ {
+ get;
+ set
+ {
+ // Ignore missing key data
+ if (value.Length == 0)
+ return;
- #endregion
+ // Validate the key data
+ var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x2C, TestIV);
+ byte[] actual = cipher.ProcessBytes(TestPattern);
+ if (!actual.EqualsExactly(ExpectedDevKeyX0x2C))
+ return;
+
+ // Assign the validated value
+ field = value;
+ }
+ } = [];
#region Internal Test Values
- ///
- /// Expected hash for NitroEncryptionData
- ///
- private static readonly byte[] ExpectedNitroSha512Hash =
- [
- 0x1A, 0xD6, 0x40, 0x21, 0xFC, 0x3D, 0x1A, 0x9A,
- 0x9B, 0xC0, 0x88, 0x8E, 0x2E, 0x68, 0xDE, 0x4E,
- 0x8A, 0x60, 0x6B, 0x86, 0x63, 0x22, 0xD2, 0xC7,
- 0xC6, 0xD7, 0xD6, 0xCE, 0x65, 0xF5, 0xBA, 0xA7,
- 0xEA, 0x69, 0x63, 0x7E, 0xC9, 0xE4, 0x57, 0x7B,
- 0x01, 0xFD, 0xCE, 0xC2, 0x26, 0x3B, 0xD9, 0x0D,
- 0x84, 0x57, 0xC2, 0x00, 0xB8, 0x56, 0x9F, 0xE5,
- 0x56, 0xDA, 0x8D, 0xDE, 0x84, 0xB8, 0x8E, 0xE4,
- ];
-
///
/// Initial value for key validation tests
///
@@ -237,155 +345,5 @@ namespace NDecrypt.Core
];
#endregion
-
- ///
- /// Setup all of the necessary constants
- ///
- /// Path to the keyfile
- public DecryptArgs(string? config)
- {
- if (config is null || !File.Exists(config))
- {
- IsReady = false;
- return;
- }
-
- // Try to read the configuration file
- var configObj = Configuration.Create(config);
- if (configObj is null)
- {
- IsReady = false;
- return;
- }
-
- // Set the fields from the configuration
- NitroEncryptionData = configObj.NitroEncryptionData.FromHexString() ?? [];
- AESHardwareConstant = configObj.AESHardwareConstant.FromHexString() ?? [];
- KeyX0x18 = configObj.KeyX0x18.FromHexString() ?? [];
- KeyX0x1B = configObj.KeyX0x1B.FromHexString() ?? [];
- KeyX0x25 = configObj.KeyX0x25.FromHexString() ?? [];
- KeyX0x2C = configObj.KeyX0x2C.FromHexString() ?? [];
- DevKeyX0x18 = configObj.DevKeyX0x18.FromHexString() ?? [];
- DevKeyX0x1B = configObj.DevKeyX0x1B.FromHexString() ?? [];
- DevKeyX0x25 = configObj.DevKeyX0x25.FromHexString() ?? [];
- DevKeyX0x2C = configObj.DevKeyX0x2C.FromHexString() ?? [];
-
- IsReady = true;
- ValidateKeys();
- }
-
- ///
- /// Validate that all keys provided are going to be valid
- ///
- /// Does not know what the keys are, just the result
- private void ValidateKeys()
- {
- // NitroEncryptionData
- if (NitroEncryptionData.Length > 0)
- {
- byte[]? actual = HashTool.GetByteArrayHashArray(NitroEncryptionData, HashType.SHA512);
- if (actual is null || !actual.EqualsExactly(ExpectedNitroSha512Hash))
- {
- Console.WriteLine($"NitroEncryptionData invalid value, disabling...");
- NitroEncryptionData = [];
- }
- }
-
- // KeyX0x18
- if (KeyX0x18.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(KeyX0x18, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedKeyX0x18))
- {
- Console.WriteLine($"KeyX0x18 invalid value, disabling...");
- KeyX0x18 = [];
- }
- }
-
- // DevKeyX0x18
- if (DevKeyX0x18.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x18, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedDevKeyX0x18))
- {
- Console.WriteLine($"DevKeyX0x18 invalid value, disabling...");
- DevKeyX0x18 = [];
- }
- }
-
- // KeyX0x1B
- if (KeyX0x1B.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(KeyX0x1B, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedKeyX0x1B))
- {
- Console.WriteLine($"KeyX0x1B invalid value, disabling...");
- KeyX0x1B = [];
- }
- }
-
- // DevKeyX0x1B
- if (DevKeyX0x1B.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x1B, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedDevKeyX0x1B))
- {
- Console.WriteLine($"DevKeyX0x1B invalid value, disabling...");
- DevKeyX0x1B = [];
- }
- }
-
- // KeyX0x25
- if (KeyX0x25.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(KeyX0x25, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedKeyX0x25))
- {
- Console.WriteLine($"KeyX0x25 invalid value, disabling...");
- KeyX0x25 = [];
- }
- }
-
- // DevKeyX0x25
- if (DevKeyX0x25.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x25, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedDevKeyX0x25))
- {
- Console.WriteLine($"DevKeyX0x25 invalid value, disabling...");
- DevKeyX0x25 = [];
- }
- }
-
- // KeyX0x2C
- if (KeyX0x2C.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(KeyX0x2C, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedKeyX0x2C))
- {
- Console.WriteLine($"KeyX0x2C invalid value, disabling...");
- KeyX0x2C = [];
- }
- }
-
- // DevKeyX0x2C
- if (DevKeyX0x2C.Length > 0)
- {
- var cipher = AESCTR.CreateEncryptionCipher(DevKeyX0x2C, TestIV);
- byte[] actual = cipher.ProcessBytes(TestPattern);
- if (!actual.EqualsExactly(ExpectedDevKeyX0x2C))
- {
- Console.WriteLine($"DevKeyX0x2C invalid value, disabling...");
- DevKeyX0x2C = [];
- }
- }
- }
}
}
diff --git a/NDecrypt.Core/ThreeDSTool.cs b/NDecrypt.Core/ThreeDSTool.cs
index f07c1f9..f2d2cfa 100644
--- a/NDecrypt.Core/ThreeDSTool.cs
+++ b/NDecrypt.Core/ThreeDSTool.cs
@@ -14,7 +14,7 @@ namespace NDecrypt.Core
///
/// Decryption args to use while processing
///
- private readonly DecryptArgs _decryptArgs;
+ private readonly ThreeDSDecryptArgs _decryptArgs;
///
/// Indicates if development images are expected
@@ -26,7 +26,7 @@ namespace NDecrypt.Core
///
private readonly PartitionKeys[] _keysMap = new PartitionKeys[8];
- public ThreeDSTool(bool development, DecryptArgs decryptArgs)
+ public ThreeDSTool(bool development, ThreeDSDecryptArgs decryptArgs)
{
_development = development;
_decryptArgs = decryptArgs;
@@ -37,13 +37,6 @@ namespace NDecrypt.Core
///
public bool DecryptFile(string input, string? output, bool force)
{
- // Ensure the constants are all set
- if (_decryptArgs.IsReady != true)
- {
- Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
- return false;
- }
-
try
{
// If the output is provided, copy the input file
@@ -456,13 +449,6 @@ namespace NDecrypt.Core
///
public bool EncryptFile(string input, string? output, bool force)
{
- // Ensure the constants are all set
- if (_decryptArgs.IsReady != true)
- {
- Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
- return false;
- }
-
try
{
// If the output is provided, copy the input file
diff --git a/NDecrypt.Core/Configuration.cs b/NDecrypt/Configuration.cs
similarity index 98%
rename from NDecrypt.Core/Configuration.cs
rename to NDecrypt/Configuration.cs
index 69a5e64..7a257c6 100644
--- a/NDecrypt.Core/Configuration.cs
+++ b/NDecrypt/Configuration.cs
@@ -1,7 +1,7 @@
using System.IO;
using Newtonsoft.Json;
-namespace NDecrypt.Core
+namespace NDecrypt
{
internal class Configuration
{
diff --git a/NDecrypt/Features/BaseFeature.cs b/NDecrypt/Features/BaseFeature.cs
index 0a6d975..18588bc 100644
--- a/NDecrypt/Features/BaseFeature.cs
+++ b/NDecrypt/Features/BaseFeature.cs
@@ -4,6 +4,7 @@ using System.IO;
using NDecrypt.Core;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Inputs;
+using SabreTools.IO.Extensions;
namespace NDecrypt.Features
{
@@ -31,7 +32,7 @@ namespace NDecrypt.Features
///
/// Mapping of reusable tools
///
- private readonly Dictionary _tools = [];
+ private readonly Dictionary _tools = [];
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
: base(name, flags, description, detailed)
@@ -80,10 +81,41 @@ namespace NDecrypt.Features
///
private void InitializeTools()
{
+ // Set default values for tools
+ _tools[FileType.NDS] = null;
+ _tools[FileType.N3DS] = null;
- var decryptArgs = new DecryptArgs(GetString(ConfigName, "config.json"));
- _tools[FileType.NDS] = new DSTool(decryptArgs);
- _tools[FileType.N3DS] = new ThreeDSTool(GetBoolean(DevelopmentName), decryptArgs);
+ // Check the configuration path
+ string? configPath = GetString(ConfigName, "config.json");
+ if (configPath is null || !File.Exists(configPath))
+ return;
+
+ // Try to read the configuration file
+ var config = Configuration.Create(configPath);
+ if (config is null)
+ return;
+
+ // Create the DS tool
+ _tools[FileType.NDS] = new DSTool
+ {
+ BlowfishTable = config.NitroEncryptionData.FromHexString() ?? [],
+ };
+
+ // Create the 3DS tool
+ bool development = GetBoolean(DevelopmentName);
+ var decryptArgs = new ThreeDSDecryptArgs
+ {
+ AESHardwareConstant = config.AESHardwareConstant.FromHexString() ?? [],
+ KeyX0x18 = config.KeyX0x18.FromHexString() ?? [],
+ KeyX0x1B = config.KeyX0x1B.FromHexString() ?? [],
+ KeyX0x25 = config.KeyX0x25.FromHexString() ?? [],
+ KeyX0x2C = config.KeyX0x2C.FromHexString() ?? [],
+ DevKeyX0x18 = config.DevKeyX0x18.FromHexString() ?? [],
+ DevKeyX0x1B = config.DevKeyX0x1B.FromHexString() ?? [],
+ DevKeyX0x25 = config.DevKeyX0x25.FromHexString() ?? [],
+ DevKeyX0x2C = config.DevKeyX0x2C.FromHexString() ?? []
+ };
+ _tools[FileType.N3DS] = new ThreeDSTool(development, decryptArgs);
}
///