123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using ArcToolkitCLI.Commands.Options;
- using ArcToolkitCLI.Util;
- using COM3D2.Toolkit.Arc;
- using COM3D2.Toolkit.Arc.Files;
- using CommandLine;
- using Glob = GlobExpressions.Glob;
- namespace ArcToolkitCLI.Commands
- {
- [Verb("extract", HelpText = "Extract the contents of the given ARC files")]
- public class ExtractCommand : ICommand, IInputOptions, IDecryptionOptions, IOutputOptions
- {
- private Glob extractGlob;
- [Option('s', "skip-errors", Required = false,
- HelpText = "If specified, skips ARC files that failed to extract.", Default = false)]
- public bool SkipErrors { get; set; }
- [Option('p', "pattern", Required = false, HelpText = "Pattern to select which files to extract", Default = "*")]
- public string ExtractFilePattern { get; set; }
- private Glob ExtractGlob => extractGlob ?? (extractGlob = new Glob(ExtractFilePattern));
- public int Run()
- {
- Dictionary<string, byte[]> keys = null;
- Directory.CreateDirectory(Output);
- foreach (var file in Util.Glob.EnumerateFiles(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);
- var headerString = Encoding.ASCII.GetString(header);
- stream.Position = 0;
- WarcArc arc = null;
- if (headerString == "warp")
- {
- if (keys == null)
- {
- var err = Encryption.GetDecryptionKeys(this, out keys);
- if (err != 0)
- {
- if (SkipErrors)
- continue;
- return err;
- }
- arc = new WarpArc(stream, requestedFile =>
- {
- if (keys.TryGetValue("*", out var key))
- return key;
- if (keys.TryGetValue(requestedFile, out key))
- return key;
- if (Directory.Exists(ArcDirectory) &&
- File.Exists(Path.Combine(ArcDirectory, requestedFile)))
- return Encryption.ReadKeyFromFile(Path.Combine(ArcDirectory, requestedFile));
- if (File.Exists(WarcFile))
- return Encryption.ReadKeyFromFile(WarcFile);
- throw new FileNotFoundException("No key found for the requested ARC", requestedFile);
- });
- }
- }
- else if (headerString == "warc")
- arc = new WarcArc(stream);
- if (arc == null)
- {
- var err = Errors.Error(Errors.ErrorCodes.UnknownArcType, file.Name);
- if (SkipErrors)
- continue;
- return err;
- }
- Console.WriteLine($"Extracting {arc.Name}");
- var arcDir = Path.Combine(Output, Path.GetFileNameWithoutExtension(arc.Name));
- Directory.CreateDirectory(arcDir);
- foreach (var arcEntry in arc.Entries)
- ExtractArchive(arcEntry, arcDir);
- arc.Dispose();
- }
- return 0;
- }
- 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; }
- public string Output { get; set; }
- private void ExtractArchive(ArcEntry entry, string path, string internalPath = ".")
- {
- switch (entry)
- {
- case ArcFileEntry fe:
- var fullName = Path.Combine(path, fe.Name);
- var fullInternalName = Path.Combine(internalPath, fe.Name);
- if (!ExtractGlob.IsMatch(fullInternalName))
- return;
- Console.WriteLine(fullName);
- Directory.CreateDirectory(Path.GetDirectoryName(fullName));
- using (var file = File.Create(fullName))
- {
- using var data = fe.GetDataStream();
- data.CopyTo(file);
- }
- break;
- case ArcDirectoryEntry de:
- var subfolderPath = Path.Combine(path, de.Name);
- var realSubfolderPath = Path.Combine(internalPath, de.Name);
- foreach (var arcEntry in de.Children)
- ExtractArchive(arcEntry, subfolderPath, realSubfolderPath);
- break;
- }
- }
- }
- }
|