mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-02-04 05:35:53 +00:00
Use CommandLine library for executable
This commit is contained in:
194
NDecrypt/BaseFeature.cs
Normal file
194
NDecrypt/BaseFeature.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NDecrypt.Core;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
internal abstract class BaseFeature : Feature
|
||||
{
|
||||
#region Common Inputs
|
||||
|
||||
protected const string ConfigName = "config";
|
||||
protected readonly StringInput ConfigString = new(ConfigName, ["-c", "--config"], "Path to config.json");
|
||||
|
||||
protected const string DevelopmentName = "development";
|
||||
protected readonly FlagInput DevelopmentFlag = new(DevelopmentName, ["-d", "--development"], "Enable using development keys, if available");
|
||||
|
||||
protected const string ForceName = "force";
|
||||
protected readonly FlagInput ForceFlag = new(ForceName, ["-f", "--force"], "Force operation by avoiding sanity checks");
|
||||
|
||||
protected const string HashName = "hash";
|
||||
protected readonly FlagInput HashFlag = new(HashName, "--hash", "Output size and hashes to a companion file");
|
||||
|
||||
protected const string OverwriteName = "overwrite";
|
||||
protected readonly FlagInput OverwriteFlag = new(OverwriteName, ["-o", "--overwrite"], "Overwrite input files instead of creating new ones");
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of reusable tools
|
||||
/// </summary>
|
||||
private readonly Dictionary<FileType, ITool> _tools = [];
|
||||
|
||||
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
|
||||
: base(name, flags, description, detailed)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
// Initialize required pieces
|
||||
InitializeTools();
|
||||
|
||||
for (int i = 0; i < Inputs.Count; i++)
|
||||
{
|
||||
if (File.Exists(Inputs[i]))
|
||||
{
|
||||
ProcessFile(Inputs[i]);
|
||||
}
|
||||
else if (Directory.Exists(Inputs[i]))
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(Inputs[i], "*", SearchOption.AllDirectories))
|
||||
{
|
||||
ProcessFile(file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{Inputs[i]} is not a file or folder. Please check your spelling and formatting and try again.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => Inputs.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Process a single file path
|
||||
/// </summary>
|
||||
/// <param name="input">File path to process</param>
|
||||
protected abstract void ProcessFile(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the tools to be used by the feature
|
||||
/// </summary>
|
||||
private void InitializeTools()
|
||||
{
|
||||
|
||||
var decryptArgs = new DecryptArgs(GetString(ConfigName));
|
||||
_tools[FileType.NDS] = new DSTool(decryptArgs);
|
||||
_tools[FileType.N3DS] = new ThreeDSTool(GetBoolean(DevelopmentName), decryptArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the encryption tool to be used for the given file
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the tool from</param>
|
||||
protected ITool? DeriveTool(string filename)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Console.WriteLine($"{filename} does not exist! Skipping...");
|
||||
return null;
|
||||
}
|
||||
|
||||
FileType type = DetermineFileType(filename);
|
||||
return type switch
|
||||
{
|
||||
FileType.NDS => _tools[FileType.NDS],
|
||||
FileType.NDSi => _tools[FileType.NDS],
|
||||
FileType.iQueDS => _tools[FileType.NDS],
|
||||
FileType.N3DS => _tools[FileType.N3DS],
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive an output filename from the input, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the input file to derive from</param>
|
||||
/// <param name="extension">Preferred extension set by the feature implementation</param>
|
||||
/// <returns>Output filename based on the input</returns>
|
||||
protected static string GetOutputFile(string filename, string extension)
|
||||
{
|
||||
// Empty filenames are passed back
|
||||
if (filename.Length == 0)
|
||||
return filename;
|
||||
|
||||
// TODO: Replace the suffix instead of just appending
|
||||
// TODO: Ensure that the input and output aren't the same
|
||||
|
||||
// If the extension does not include a leading period
|
||||
if (!extension.StartsWith("."))
|
||||
extension = $".{extension}";
|
||||
|
||||
// Append the extension and return
|
||||
return $"{filename}{extension}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out the hashes of a file to a named file
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to get hashes for/param>
|
||||
protected static void WriteHashes(string filename)
|
||||
{
|
||||
// If the file doesn't exist, don't try anything
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
// Get the hash string from the file
|
||||
string? hashString = HashingHelper.GetInfo(filename);
|
||||
if (hashString == null)
|
||||
return;
|
||||
|
||||
// Open the output file and write the hashes
|
||||
using var fs = File.Open(Path.GetFullPath(filename) + ".hash", FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
using var sw = new StreamWriter(fs);
|
||||
sw.Write(hashString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the file type from the filename extension
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the type from</param>
|
||||
/// <returns>FileType value, if possible</returns>
|
||||
private FileType DetermineFileType(string filename)
|
||||
{
|
||||
if (filename.EndsWith(".nds", StringComparison.OrdinalIgnoreCase) // Standard carts
|
||||
|| filename.EndsWith(".nds.dec", StringComparison.OrdinalIgnoreCase) // Carts/images with secure area decrypted
|
||||
|| filename.EndsWith(".nds.enc", StringComparison.OrdinalIgnoreCase) // Carts/images with secure area encrypted
|
||||
|| filename.EndsWith(".srl", StringComparison.OrdinalIgnoreCase)) // Development carts/images
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo DS");
|
||||
return FileType.NDS;
|
||||
}
|
||||
else if (filename.EndsWith(".dsi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo DSi");
|
||||
return FileType.NDSi;
|
||||
}
|
||||
else if (filename.EndsWith(".ids", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine("File recognized as iQue DS");
|
||||
return FileType.iQueDS;
|
||||
}
|
||||
else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase) // Standard carts
|
||||
|| filename.EndsWith(".3ds.dec", StringComparison.OrdinalIgnoreCase) // Decrypted carts/images
|
||||
|| filename.EndsWith(".3ds.enc", StringComparison.OrdinalIgnoreCase) // Encrypted carts/images
|
||||
|| filename.EndsWith(".cci", StringComparison.OrdinalIgnoreCase)) // Development carts/images
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo 3DS");
|
||||
return FileType.N3DS;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds, *.cci");
|
||||
return FileType.NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
NDecrypt/DecryptFeature.cs
Normal file
57
NDecrypt/DecryptFeature.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
internal sealed class DecryptFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "decrypt";
|
||||
|
||||
private static readonly string[] _flags = ["d", "decrypt"];
|
||||
|
||||
private const string _description = "Decrypt the input files";
|
||||
|
||||
#endregion
|
||||
|
||||
public DecryptFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
RequiresInputs = true;
|
||||
|
||||
Add(ConfigString);
|
||||
Add(DevelopmentFlag);
|
||||
Add(ForceFlag);
|
||||
Add(HashFlag);
|
||||
|
||||
// TODO: Include this when enabled
|
||||
// Add(OverwriteFlag);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ProcessFile(string input)
|
||||
{
|
||||
// Attempt to derive the tool for the path
|
||||
var tool = DeriveTool(input);
|
||||
if (tool == null)
|
||||
return;
|
||||
|
||||
// Derive the output filename, if required
|
||||
string? output = null;
|
||||
if (!GetBoolean(OverwriteName))
|
||||
output = GetOutputFile(input, ".dec");
|
||||
|
||||
Console.WriteLine($"Processing {input}");
|
||||
|
||||
if (!tool.DecryptFile(input, output, GetBoolean(ForceName)))
|
||||
{
|
||||
Console.WriteLine("Decryption failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Output the file hashes, if expected
|
||||
if (GetBoolean(HashName))
|
||||
WriteHashes(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
NDecrypt/EncryptFeature.cs
Normal file
57
NDecrypt/EncryptFeature.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
internal sealed class EncryptFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "encrypt";
|
||||
|
||||
private static readonly string[] _flags = ["e", "encrypt"];
|
||||
|
||||
private const string _description = "Encrypt the input files";
|
||||
|
||||
#endregion
|
||||
|
||||
public EncryptFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
RequiresInputs = true;
|
||||
|
||||
Add(ConfigString);
|
||||
Add(DevelopmentFlag);
|
||||
Add(ForceFlag);
|
||||
Add(HashFlag);
|
||||
|
||||
// TODO: Include this when enabled
|
||||
// Add(OverwriteFlag);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ProcessFile(string input)
|
||||
{
|
||||
// Attempt to derive the tool for the path
|
||||
var tool = DeriveTool(input);
|
||||
if (tool == null)
|
||||
return;
|
||||
|
||||
// Derive the output filename, if required
|
||||
string? output = null;
|
||||
if (!GetBoolean(OverwriteName))
|
||||
output = GetOutputFile(input, ".enc");
|
||||
|
||||
Console.WriteLine($"Processing {input}");
|
||||
|
||||
if (!tool.EncryptFile(input, output, GetBoolean(ForceName)))
|
||||
{
|
||||
Console.WriteLine("Encryption failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Output the file hashes, if expected
|
||||
if (GetBoolean(HashName))
|
||||
WriteHashes(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ namespace NDecrypt
|
||||
/// <summary>
|
||||
/// Functionality to use from the program
|
||||
/// </summary>
|
||||
internal enum Feature
|
||||
internal enum FeatureFlag
|
||||
{
|
||||
NULL,
|
||||
Decrypt,
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace NDecrypt
|
||||
+ $"CRC-32: {(hashDict.ContainsKey(HashType.CRC32) ? hashDict[HashType.CRC32] : string.Empty)}\n"
|
||||
+ $"MD5: {(hashDict.ContainsKey(HashType.MD5) ? hashDict[HashType.MD5] : string.Empty)}\n"
|
||||
+ $"SHA-1: {(hashDict.ContainsKey(HashType.SHA1) ? hashDict[HashType.SHA1] : string.Empty)}\n"
|
||||
+ $"CSHA-256: {(hashDict.ContainsKey(HashType.SHA256) ? hashDict[HashType.SHA256] : string.Empty)}\n";
|
||||
+ $"SHA-256: {(hashDict.ContainsKey(HashType.SHA256) ? hashDict[HashType.SHA256] : string.Empty)}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
NDecrypt/InfoFeature.cs
Normal file
45
NDecrypt/InfoFeature.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
internal sealed class InfoFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "info";
|
||||
|
||||
private static readonly string[] _flags = ["i", "info"];
|
||||
|
||||
private const string _description = "Output file information";
|
||||
|
||||
#endregion
|
||||
|
||||
public InfoFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
RequiresInputs = true;
|
||||
|
||||
Add(HashFlag);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ProcessFile(string input)
|
||||
{
|
||||
// Attempt to derive the tool for the path
|
||||
var tool = DeriveTool(input);
|
||||
if (tool == null)
|
||||
return;
|
||||
|
||||
Console.WriteLine($"Processing {input}");
|
||||
|
||||
string? infoString = tool.GetInformation(input);
|
||||
infoString ??= "There was a problem getting file information!";
|
||||
|
||||
Console.WriteLine(infoString);
|
||||
|
||||
// Output the file hashes, if expected
|
||||
if (GetBoolean(HashName))
|
||||
WriteHashes(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NDecrypt
|
||||
/// <summary>
|
||||
/// Feature to process input files with
|
||||
/// </summary>
|
||||
public Feature Feature { get; private set; }
|
||||
public FeatureFlag Feature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to config.json
|
||||
@@ -77,17 +77,17 @@ namespace NDecrypt
|
||||
|
||||
case "d":
|
||||
case "decrypt":
|
||||
options.Feature = Feature.Decrypt;
|
||||
options.Feature = FeatureFlag.Decrypt;
|
||||
break;
|
||||
|
||||
case "e":
|
||||
case "encrypt":
|
||||
options.Feature = Feature.Encrypt;
|
||||
options.Feature = FeatureFlag.Encrypt;
|
||||
break;
|
||||
|
||||
case "i":
|
||||
case "info":
|
||||
options.Feature = Feature.Info;
|
||||
options.Feature = FeatureFlag.Info;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -190,7 +190,7 @@ namespace NDecrypt
|
||||
Console.WriteLine("-d, --development Enable using development keys, if available");
|
||||
Console.WriteLine("-f, --force Force operation by avoiding sanity checks");
|
||||
Console.WriteLine("--hash Output size and hashes to a companion file");
|
||||
// Console.WriteLine("-o, --overwrite Overwrite input files instead of creating new ones"); // TODO: Print this when enabled
|
||||
// Console.WriteLine("-o, --overwrite Overwrite input files instead of creating new ones");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("<path> can be any file or folder that contains uncompressed items.");
|
||||
Console.WriteLine("More than one path can be specified at a time.");
|
||||
|
||||
@@ -1,204 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NDecrypt.Core;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Features;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Mapping of reusable tools
|
||||
/// </summary>
|
||||
private static readonly Dictionary<FileType, ITool> _tools = [];
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Get the options from the arguments
|
||||
var options = Options.ParseOptions(args);
|
||||
var commandSet = CreateCommands();
|
||||
|
||||
// If we have an invalid state
|
||||
if (options == null)
|
||||
// If we have no args, show the help and quit
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
Options.DisplayHelp();
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the decrypt args, if possible
|
||||
var decryptArgs = new DecryptArgs(options.ConfigPath); ;
|
||||
// Get the first argument as a feature flag
|
||||
string featureName = args[0];
|
||||
|
||||
// Create reusable tools
|
||||
_tools[FileType.NDS] = new DSTool(decryptArgs);
|
||||
_tools[FileType.N3DS] = new ThreeDSTool(options.Development, decryptArgs);
|
||||
|
||||
for (int i = 0; i < options.InputPaths.Count; i++)
|
||||
// Get the associated feature
|
||||
var topLevel = commandSet.GetTopLevel(featureName);
|
||||
if (topLevel == null || topLevel is not Feature feature)
|
||||
{
|
||||
if (File.Exists(options.InputPaths[i]))
|
||||
{
|
||||
ProcessFile(options.InputPaths[i], options);
|
||||
}
|
||||
else if (Directory.Exists(options.InputPaths[i]))
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(options.InputPaths[i], "*", SearchOption.AllDirectories))
|
||||
{
|
||||
ProcessFile(file, options);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{options.InputPaths[i]} is not a file or folder. Please check your spelling and formatting and try again.");
|
||||
}
|
||||
Console.WriteLine($"'{featureName}' is not valid feature flag");
|
||||
commandSet.OutputFeatureHelp(featureName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle default help functionality
|
||||
if (topLevel is Help helpFeature)
|
||||
{
|
||||
helpFeature.ProcessArgs(args, 0, commandSet);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now verify that all other flags are valid
|
||||
if (!feature.ProcessArgs(args, 1))
|
||||
return;
|
||||
|
||||
// If inputs are required
|
||||
if (feature.RequiresInputs && !feature.VerifyInputs())
|
||||
{
|
||||
commandSet.OutputFeatureHelp(topLevel.Name);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
// Now execute the current feature
|
||||
if (!feature.Execute())
|
||||
{
|
||||
Console.Error.WriteLine("An error occurred during processing!");
|
||||
commandSet.OutputFeatureHelp(topLevel.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a single file path
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
/// <param name="input">File path to process</param>
|
||||
/// <param name="options">Options indicating how to process the file</param>
|
||||
private static void ProcessFile(string input, Options options)
|
||||
private static CommandSet CreateCommands()
|
||||
{
|
||||
// Attempt to derive the tool for the path
|
||||
var tool = DeriveTool(input);
|
||||
if (tool == null)
|
||||
return;
|
||||
List<string> header = [
|
||||
"Cart Image Encrypt/Decrypt Tool",
|
||||
string.Empty,
|
||||
"NDecrypt <operation> [options] <path> ...",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
Console.WriteLine($"Processing {input}");
|
||||
List<string> footer = [
|
||||
string.Empty,
|
||||
"<path> can be any file or folder that contains uncompressed items.",
|
||||
"More than one path can be specified at a time.",
|
||||
];
|
||||
|
||||
// Derive the output filename, if required
|
||||
string? output = null;
|
||||
if (!options.Overwrite)
|
||||
output = GetOutputFile(input, options);
|
||||
var commandSet = new CommandSet(header, footer);
|
||||
|
||||
// Encrypt or decrypt the file as requested
|
||||
if (options.Feature == Feature.Encrypt && !tool.EncryptFile(input, output, options.Force))
|
||||
{
|
||||
Console.WriteLine("Encryption failed!");
|
||||
return;
|
||||
}
|
||||
else if (options.Feature == Feature.Decrypt && !tool.DecryptFile(input, output, options.Force))
|
||||
{
|
||||
Console.WriteLine("Decryption failed!");
|
||||
return;
|
||||
}
|
||||
else if (options.Feature == Feature.Info)
|
||||
{
|
||||
string? infoString = tool.GetInformation(input);
|
||||
infoString ??= "There was a problem getting file information!";
|
||||
commandSet.Add(new Help());
|
||||
commandSet.Add(new EncryptFeature());
|
||||
commandSet.Add(new DecryptFeature());
|
||||
commandSet.Add(new InfoFeature());
|
||||
|
||||
Console.WriteLine(infoString);
|
||||
}
|
||||
|
||||
// Output the file hashes, if expected
|
||||
if (options.OutputHashes)
|
||||
WriteHashes(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the encryption tool to be used for the given file
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the tool from</param>
|
||||
private static ITool? DeriveTool(string filename)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Console.WriteLine($"{filename} does not exist! Skipping...");
|
||||
return null;
|
||||
}
|
||||
|
||||
FileType type = DetermineFileType(filename);
|
||||
return type switch
|
||||
{
|
||||
FileType.NDS => _tools[FileType.NDS],
|
||||
FileType.NDSi => _tools[FileType.NDS],
|
||||
FileType.iQueDS => _tools[FileType.NDS],
|
||||
FileType.N3DS => _tools[FileType.N3DS],
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the file type from the filename extension
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the type from</param>
|
||||
/// <returns>FileType value, if possible</returns>
|
||||
private static FileType DetermineFileType(string filename)
|
||||
{
|
||||
if (filename.EndsWith(".nds", StringComparison.OrdinalIgnoreCase) // Standard carts
|
||||
|| filename.EndsWith(".nds.dec", StringComparison.OrdinalIgnoreCase) // Carts/images with secure area decrypted
|
||||
|| filename.EndsWith(".nds.enc", StringComparison.OrdinalIgnoreCase) // Carts/images with secure area encrypted
|
||||
|| filename.EndsWith(".srl", StringComparison.OrdinalIgnoreCase)) // Development carts/images
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo DS");
|
||||
return FileType.NDS;
|
||||
}
|
||||
else if (filename.EndsWith(".dsi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo DSi");
|
||||
return FileType.NDSi;
|
||||
}
|
||||
else if (filename.EndsWith(".ids", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine("File recognized as iQue DS");
|
||||
return FileType.iQueDS;
|
||||
}
|
||||
else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase) // Standard carts
|
||||
|| filename.EndsWith(".3ds.dec", StringComparison.OrdinalIgnoreCase) // Decrypted carts/images
|
||||
|| filename.EndsWith(".3ds.enc", StringComparison.OrdinalIgnoreCase) // Encrypted carts/images
|
||||
|| filename.EndsWith(".cci", StringComparison.OrdinalIgnoreCase)) // Development carts/images
|
||||
{
|
||||
Console.WriteLine("File recognized as Nintendo 3DS");
|
||||
return FileType.N3DS;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds, *.cci");
|
||||
return FileType.NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive an output filename from the input, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the input file to derive from</param>
|
||||
/// <param name="options">Options indicating how to process the file</param>
|
||||
/// <returns>Output filename based on the input</returns>
|
||||
private static string GetOutputFile(string filename, Options options)
|
||||
{
|
||||
// Empty filenames are passed back
|
||||
if (filename.Length == 0)
|
||||
return filename;
|
||||
|
||||
// TODO: Replace the suffix instead of just appending
|
||||
// TODO: Ensure that the input and output aren't the same
|
||||
|
||||
// Append '.enc' or '.dec' based on the feature
|
||||
if (options.Feature == Feature.Decrypt)
|
||||
filename += ".dec";
|
||||
else if (options.Feature == Feature.Encrypt)
|
||||
filename += ".enc";
|
||||
|
||||
// Return the reformatted name
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out the hashes of a file to a named file
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to get hashes for/param>
|
||||
private static void WriteHashes(string filename)
|
||||
{
|
||||
// If the file doesn't exist, don't try anything
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
// Get the hash string from the file
|
||||
string? hashString = HashingHelper.GetInfo(filename);
|
||||
if (hashString == null)
|
||||
return;
|
||||
|
||||
// Open the output file and write the hashes
|
||||
using var fs = File.Create(Path.GetFullPath(filename) + ".hash");
|
||||
using var sw = new StreamWriter(fs);
|
||||
sw.WriteLine(hashString);
|
||||
return commandSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user