|  | @@ -1,12 +1,15 @@
 | 
	
		
			
				|  |  |  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;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -38,8 +41,11 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  |                              var err = Encryption.GetDecryptionKeys(this, out keys);
 | 
	
		
			
				|  |  |                              if (err != 0)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                if(SkipErrors)
 | 
	
		
			
				|  |  | +                                    continue;
 | 
	
		
			
				|  |  |                                  return err;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                              arc = new WarpArc(stream, requestedFile =>
 | 
	
		
			
				|  |  |                              {
 | 
	
	
		
			
				|  | @@ -62,9 +68,14 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      if (arc == null)
 | 
	
		
			
				|  |  | -                        return Errors.Error(Errors.ErrorCodes.UnknownArcType, file.Name);
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        var err = Errors.Error(Errors.ErrorCodes.UnknownArcType, file.Name);
 | 
	
		
			
				|  |  | +                        if (SkipErrors)
 | 
	
		
			
				|  |  | +                            continue;
 | 
	
		
			
				|  |  | +                        return err;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    Console.WriteLine($"Extracting {arc.Name}:");
 | 
	
		
			
				|  |  | +                    Console.WriteLine($"Extracting {arc.Name}");
 | 
	
		
			
				|  |  |                      var arcDir = Path.Combine(Output, Path.GetFileNameWithoutExtension(arc.Name));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      Directory.CreateDirectory(arcDir);
 | 
	
	
		
			
				|  | @@ -78,6 +89,15 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |              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; }
 | 
	
	
		
			
				|  | @@ -86,13 +106,17 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |          public IEnumerable<string> Input { get; set; }
 | 
	
		
			
				|  |  |          public string Output { get; set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private void ExtractArchive(ArcEntry entry, string path)
 | 
	
		
			
				|  |  | +        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())
 | 
	
		
			
				|  |  |                      {
 | 
	
	
		
			
				|  | @@ -102,9 +126,9 @@ namespace ArcToolkitCLI.Commands
 | 
	
		
			
				|  |  |                      break;
 | 
	
		
			
				|  |  |                  case ArcDirectoryEntry de:
 | 
	
		
			
				|  |  |                      var subfolderPath = Path.Combine(path, de.Name);
 | 
	
		
			
				|  |  | -                    Directory.CreateDirectory(subfolderPath);
 | 
	
		
			
				|  |  | +                    var realSubfolderPath = Path.Combine(internalPath, de.Name);
 | 
	
		
			
				|  |  |                      foreach (var arcEntry in de.Children)
 | 
	
		
			
				|  |  | -                        ExtractArchive(arcEntry, subfolderPath);
 | 
	
		
			
				|  |  | +                        ExtractArchive(arcEntry, subfolderPath, realSubfolderPath);
 | 
	
		
			
				|  |  |                      break;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 |