|  | @@ -1,103 +1,99 @@
 | 
	
		
			
				|  |  |  using System;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  |  using System.IO;
 | 
	
		
			
				|  |  | -using System.Net;
 | 
	
		
			
				|  |  |  using System.Text;
 | 
	
		
			
				|  |  |  using ArcToolkitCLI.Commands.Options;
 | 
	
		
			
				|  |  |  using ArcToolkitCLI.Util;
 | 
	
		
			
				|  |  |  using COM3D2.Toolkit.Arc;
 | 
	
		
			
				|  |  |  using COM3D2.Toolkit.Arc.Files;
 | 
	
		
			
				|  |  |  using CommandLine;
 | 
	
		
			
				|  |  | -using GlobExpressions;
 | 
	
		
			
				|  |  | -using Glob = ArcToolkitCLI.Util.Glob;
 | 
	
		
			
				|  |  | +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 Glob.EnumerateFiles(Input))
 | 
	
		
			
				|  |  | +            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;
 | 
	
		
			
				|  |  | +                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 (headerString == "warp")
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    if (keys == null)
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  | -                        if (keys == null)
 | 
	
		
			
				|  |  | +                        var err = Encryption.GetDecryptionKeys(this, out keys);
 | 
	
		
			
				|  |  | +                        if (err != 0)
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  | -                            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);
 | 
	
		
			
				|  |  | -                            });
 | 
	
		
			
				|  |  | +                            if (SkipErrors)
 | 
	
		
			
				|  |  | +                                continue;
 | 
	
		
			
				|  |  | +                            return err;
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    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;
 | 
	
		
			
				|  |  | +                        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));
 | 
	
		
			
				|  |  | +                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);
 | 
	
		
			
				|  |  | +                Directory.CreateDirectory(arcDir);
 | 
	
		
			
				|  |  | +                foreach (var arcEntry in arc.Entries)
 | 
	
		
			
				|  |  | +                    ExtractArchive(arcEntry, arcDir);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    arc.Dispose();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                arc.Dispose();
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return 0;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        [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 GlobExpressions.Glob extractGlob = null;
 | 
	
		
			
				|  |  | -        private GlobExpressions.Glob ExtractGlob => extractGlob ?? (extractGlob = new GlobExpressions.Glob(ExtractFilePattern));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          public string ArcDirectory { get; set; }
 | 
	
		
			
				|  |  |          public string DecryptionKey { get; set; }
 | 
	
		
			
				|  |  |          public string KeyFile { get; set; }
 | 
	
	
		
			
				|  | @@ -118,8 +114,8 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |                      Console.WriteLine(fullName);
 | 
	
		
			
				|  |  |                      Directory.CreateDirectory(Path.GetDirectoryName(fullName));
 | 
	
		
			
				|  |  |                      using (var file = File.Create(fullName))
 | 
	
		
			
				|  |  | -                    using (var data = fe.GetDataStream())
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  | +                        using var data = fe.GetDataStream();
 | 
	
		
			
				|  |  |                          data.CopyTo(file);
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |  
 |