123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.IO.Abstractions;
- using System.Linq;
- using System.Text;
- using CommandLine;
- using COM3D2.Toolkit.Arc;
- using Ganss.IO;
- namespace ArcToolkitCLI
- {
- internal interface IOptions
- {
- [Value(0, MetaName = "input", HelpText = "Input ARC files")]
- IEnumerable<string> Input { get; set; }
- }
- internal interface IDecryptionOptions
- {
- [Option("arc-search-dir", Required = false, HelpText = "Directory where to search ARC file to decrypt the WARP file")]
- string ArcDirectory { get; set; }
- [Option('k', "key", HelpText = "Decryption key as a base64 string (applied to all inputs)", Required = false)]
- string DecryptionKey { get; set; }
- [Option("key-file",
- HelpText = "A file with decryption keys on each line. Format of the file is <decryption arc name>:<key in base64>.",
- Required = false)]
- string KeyFile { get; set; }
- [Option("warc", Required = false, HelpText = "WARC file to use for decryption")]
- string WarcFile { get; set; }
- }
- [Verb("extract", HelpText = "Extract the contents of the given ARC files")]
- internal class ExtractOptions : IOptions, IDecryptionOptions
- {
- [Option('o', "output", HelpText = "Output directory", Required = true)]
- public string Output { get; set; }
- public string ArcDirectory { get; set; }
- public string DecryptionKey { get; set; }
- public string KeyFile { get; set; }
- public string WarcFile { get; set; }
- public IEnumerable<string> Input { get; set; }
- }
- [Verb("info", HelpText = "Display information about the given ARC files")]
- internal class InfoOptions : IOptions
- {
- [Option("only-key",
- HelpText = "If the archive is a WARC file, output only the decryption key as a base64 string",
- Required = false)]
- public bool OnlyKey { get; set; }
- public IEnumerable<string> Input { get; set; }
- }
- [Verb("decrypt", HelpText = "Decrypts the provided WARP files")]
- internal class DecryptOptions : IOptions, IDecryptionOptions
- {
- [Option('o', "output", HelpText = "Output directory", Default = ".")]
- public string Output { get; set; }
- public string ArcDirectory { get; set; }
- public string DecryptionKey { get; set; }
- public string KeyFile { get; set; }
- public string WarcFile { get; set; }
- public IEnumerable<string> Input { get; set; }
- }
- internal static class Errors
- {
- public enum ErrorCodes
- {
- NotAFile = 100,
- UnknownArcType,
- KeyNotFound,
- InvalidKeyFile,
- NoCorrectKey
- }
- public static readonly string[] ErrorMessages =
- {
- "{0} is not a file",
- "{0} is not a known ARC file",
- "No key specified or the key file does not exist",
- "The provided keyfile is not valid",
- "The ARC {0} needs a key from {1}"
- };
- public static int Error(ErrorCodes code, params object[] args)
- {
- Console.Error.WriteLine(ErrorMessages[code - ErrorCodes.NotAFile], args);
- return (int) code;
- }
- }
- internal static class Util
- {
- private static readonly char[] GlobCharacters = {'*', '{', '}', '[', ']', '?'};
- public static IEnumerable<FileSystemInfoBase> EnumerateFiles(IEnumerable<string> patterns)
- {
- foreach (string pattern in patterns)
- if (IsGlob(pattern))
- foreach (var fileSystemInfoBase in Glob.Expand(pattern))
- yield return fileSystemInfoBase;
- else
- yield return new FileInfoWrapper(new FileSystem(), new FileInfo(pattern));
- }
- private static bool IsGlob(string path)
- {
- return path.IndexOfAny(GlobCharacters) >= 0;
- }
- }
- internal class Program
- {
- private static int Main(string[] args)
- {
- return Parser.Default.ParseArguments<InfoOptions, ExtractOptions, DecryptOptions>(args)
- .MapResult((InfoOptions opts) => DisplayInfo(opts),
- (ExtractOptions opts) => Extract(opts),
- (DecryptOptions opts) => Decrypt(opts),
- errs => 1);
- }
- private static int DisplayInfo(InfoOptions opts)
- {
- bool first = true;
- foreach (var file in Util.EnumerateFiles(opts.Input))
- {
- if (!file.Exists)
- return Errors.Error(Errors.ErrorCodes.NotAFile, file.Name);
- if (!first && !opts.OnlyKey)
- Console.WriteLine();
- first = false;
- using (var stream = File.OpenRead(file.FullName))
- {
- var header = new byte[4];
- stream.Read(header, 0, header.Length);
- string headerString = Encoding.ASCII.GetString(header);
- stream.Position = 0;
- if (headerString == "warc")
- {
- var key = new byte[2048];
- stream.Read(key, 0, key.Length);
- string keyBase64 = Convert.ToBase64String(key);
- if (opts.OnlyKey)
- {
- Console.WriteLine($"{Path.GetFileName(file.FullName)}:{keyBase64}");
- continue;
- }
- stream.Position = 0;
- using (var warc = new WarcArc(stream))
- {
- Console.WriteLine($"File name: {file.Name}");
- Console.WriteLine("ARC type: WARC");
- Console.WriteLine($"ARC Name: {warc.Name}");
- Console.WriteLine($"File count: {warc.Entries.Count()}");
- Console.WriteLine($"Decryption key: {keyBase64}");
- }
- }
- else if (headerString == "warp")
- {
- if (opts.OnlyKey)
- continue;
- Console.WriteLine($"File name: {file.Name}");
- Console.WriteLine("ARC type: WARP");
- using (var br = new BinaryReader(stream))
- Console.WriteLine($"Needs a decryption key from the following ARC: {WarpArc.GetKeyWarpName(br)}");
- }
- else
- {
- return Errors.Error(Errors.ErrorCodes.UnknownArcType, file.Name);
- }
- }
- }
- return 0;
- }
- private static int Extract(ExtractOptions opts)
- {
- return 0;
- }
- private static byte[] ReadKeyFromFile(string filename)
- {
- using (var br = new BinaryReader(File.OpenRead(filename)))
- return br.ReadBytes(2048);
- }
- private static int Decrypt(DecryptOptions opts)
- {
- var keysDict = new Dictionary<string, byte[]>(StringComparer.InvariantCultureIgnoreCase);
- if (!string.IsNullOrWhiteSpace(opts.KeyFile))
- {
- if (!File.Exists(opts.KeyFile))
- return Errors.Error(Errors.ErrorCodes.KeyNotFound);
- foreach (string line in File.ReadAllLines(opts.KeyFile))
- {
- var parts = line.Split(':');
- if (parts.Length != 2)
- return Errors.Error(Errors.ErrorCodes.InvalidKeyFile);
- keysDict[parts[0].Trim()] = Convert.FromBase64String(parts[1].Trim());
- }
- }
- else if (!string.IsNullOrWhiteSpace(opts.DecryptionKey))
- {
- keysDict["*"] = Convert.FromBase64String(opts.DecryptionKey);
- }
- else if (string.IsNullOrWhiteSpace(opts.ArcDirectory) && string.IsNullOrWhiteSpace(opts.WarcFile))
- {
- return Errors.Error(Errors.ErrorCodes.KeyNotFound);
- }
- Directory.CreateDirectory(opts.Output);
- foreach (var file in Util.EnumerateFiles(opts.Input))
- {
- if (!file.Exists)
- return Errors.Error(Errors.ErrorCodes.NotAFile, file.Name);
- using (var stream = File.OpenRead(file.FullName))
- {
- var header = new byte[4];
- stream.Read(header, 0, header.Length);
- string headerString = Encoding.ASCII.GetString(header);
- stream.Position = 0;
- if (headerString == "warc")
- {
- Console.WriteLine($"{file.Name} is a WARC file, skipping...");
- }
- else if (headerString == "warp")
- {
- try
- {
- var arcStream = WarpArc.DecryptWarp(stream,
- requestedFile =>
- {
- if (keysDict.TryGetValue("*", out var key))
- return key;
- if (keysDict.TryGetValue(requestedFile, out key))
- return key;
- if (Directory.Exists(opts.ArcDirectory)
- && File.Exists(Path.Combine(opts.ArcDirectory, requestedFile)))
- return ReadKeyFromFile(Path.Combine(opts.ArcDirectory, requestedFile));
- if (File.Exists(opts.WarcFile))
- return ReadKeyFromFile(opts.WarcFile);
- throw new FileNotFoundException("No key found for the requested ARC",
- requestedFile);
- });
- using (var output =
- File.Create(Path.Combine(Path.GetDirectoryName(opts.Output), Path.GetFileNameWithoutExtension(file.Name))))
- arcStream.CopyTo(output);
- }
- catch (FileNotFoundException fe)
- {
- return Errors.Error(Errors.ErrorCodes.NoCorrectKey, file.Name, fe.FileName);
- }
- }
- else
- {
- return Errors.Error(Errors.ErrorCodes.UnknownArcType, file.Name);
- }
- }
- }
- return 0;
- }
- }
- }
|