|
@@ -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);
|
|
|
}
|
|
|
|