wabbajack/Wabbajack.CLI/Verbs/ChangeDownload.cs

326 lines
13 KiB
C#
Raw Normal View History

2020-02-19 14:49:06 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using CommandLine;
using Wabbajack.Common;
using Wabbajack.Lib;
using Directory = Alphaleonis.Win32.Filesystem.Directory;
using File = Alphaleonis.Win32.Filesystem.File;
using Path = Alphaleonis.Win32.Filesystem.Path;
namespace Wabbajack.CLI.Verbs
{
[Verb("change-download", HelpText = "Move or Copy all used Downloads from a Modlist to another directory")]
public class ChangeDownload : AVerb
{
[IsDirectory(CustomMessage = "Downloads folder %1 does not exist!")]
2020-02-19 15:24:50 +00:00
[Option("input", Required = true, HelpText = "Input folder containing the downloads you want to move")]
2020-04-09 22:36:07 +00:00
public string Input { get; set; } = "";
2020-02-19 14:49:06 +00:00
[IsDirectory(Create = true)]
2020-02-19 15:24:50 +00:00
[Option("output", Required = true, HelpText = "Output folder the downloads should be transferred to")]
2020-04-09 22:36:07 +00:00
public string Output { get; set; } = "";
2020-02-19 14:49:06 +00:00
[IsFile(CustomMessage = "Modlist file %1 does not exist!")]
2020-02-19 14:49:06 +00:00
[Option("modlist", Required = true, HelpText = "The Modlist, can either be a .wabbajack or a modlist.txt file")]
2020-04-09 22:36:07 +00:00
public string Modlist { get; set; } = "";
2020-02-19 14:49:06 +00:00
2020-04-09 22:36:07 +00:00
[Option("mods", Required = false,
HelpText = "Mods folder location if the provided modlist file is an MO2 modlist.txt")]
public string Mods { get; set; } = "";
2020-02-19 14:49:06 +00:00
2020-02-20 10:40:20 +00:00
[Option("copy", Default = true, HelpText = "Whether to copy the files", SetName = "copy")]
2020-02-19 14:49:06 +00:00
public bool Copy { get; set; }
2020-02-20 10:40:20 +00:00
[Option("move", Default = false, HelpText = "Whether to move the files", SetName = "move")]
2020-02-19 14:49:06 +00:00
public bool Move { get; set; }
[Option("overwrite", Default = false, HelpText = "Whether to overwrite the file if it already exists")]
public bool Overwrite { get; set; }
[Option("meta", Default = true, HelpText = "Whether to also transfer the meta file for the archive")]
public bool IncludeMeta { get; set; }
private struct TransferFile
{
public readonly string Input;
public readonly string Output;
public readonly bool IsMeta;
public TransferFile(string input, string output, bool isMeta = false)
{
Input = input;
Output = output;
IsMeta = isMeta;
}
}
2020-04-06 17:14:46 +00:00
protected override async Task<ExitCode> Run()
2020-02-19 14:49:06 +00:00
{
2020-04-09 22:36:07 +00:00
var modListPath = (AbsolutePath)Modlist;
if (modListPath.Extension != Consts.ModListExtension && modListPath.FileName != (RelativePath)"modlist.txt")
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit($"The file {Modlist} is not a valid modlist file!", ExitCode.BadArguments);
2020-02-19 14:49:06 +00:00
if (Copy && Move)
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit("You can't set both copy and move flags!", ExitCode.BadArguments);
2020-04-09 22:36:07 +00:00
var isModlist = modListPath.Extension == Consts.ModListExtension;
2020-02-19 14:49:06 +00:00
var list = new List<TransferFile>();
if (isModlist)
{
ModList modlist;
try
{
2020-04-09 22:36:07 +00:00
modlist = AInstaller.LoadFromFile(modListPath);
2020-02-19 14:49:06 +00:00
}
catch (Exception e)
{
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit($"Error while loading the Modlist!\n{e}", ExitCode.Error);
2020-02-19 14:49:06 +00:00
}
if (modlist == null)
{
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit("The Modlist could not be loaded!", ExitCode.Error);
2020-02-19 14:49:06 +00:00
}
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Modlist contains {modlist.Archives.Count} archives.");
2020-02-19 14:49:06 +00:00
modlist.Archives.Do(a =>
{
var inputPath = Path.Combine(Input, a.Name);
var outputPath = Path.Combine(Output, a.Name);
if (!File.Exists(inputPath))
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"File {inputPath} does not exist, skipping.");
2020-02-19 14:49:06 +00:00
return;
}
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Adding {inputPath} to the transfer list.");
2020-02-19 14:49:06 +00:00
list.Add(new TransferFile(inputPath, outputPath));
var metaInputPath = Path.Combine(inputPath, ".meta");
var metaOutputPath = Path.Combine(outputPath, ".meta");
if (File.Exists(metaInputPath))
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found meta file {metaInputPath}");
2020-02-19 14:49:06 +00:00
if (IncludeMeta)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Adding {metaInputPath} to the transfer list.");
2020-02-19 14:49:06 +00:00
list.Add(new TransferFile(metaInputPath, metaOutputPath));
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Meta file {metaInputPath} will be ignored.");
2020-02-19 14:49:06 +00:00
}
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found no meta file for {inputPath}");
2020-02-19 14:49:06 +00:00
if (IncludeMeta)
{
if (string.IsNullOrWhiteSpace(a.Meta))
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Meta for {a.Name} is empty, this should not be possible but whatever.");
2020-02-19 14:49:06 +00:00
return;
}
2020-02-24 16:50:58 +00:00
CLIUtils.Log("Adding meta from archive info the transfer list");
2020-02-19 14:49:06 +00:00
list.Add(new TransferFile(a.Meta, metaOutputPath, true));
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Meta will be ignored for {a.Name}");
2020-02-19 14:49:06 +00:00
}
}
});
}
else
{
2020-02-19 15:24:50 +00:00
if (!Directory.Exists(Mods))
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit($"Mods directory {Mods} does not exist!", ExitCode.BadArguments);
2020-02-19 14:49:06 +00:00
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Reading modlist.txt from {Modlist}");
2020-02-19 14:49:06 +00:00
string[] modlist = File.ReadAllLines(Modlist);
if (modlist == null || modlist.Length == 0)
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit($"Provided modlist.txt file at {Modlist} is empty or could not be read!", ExitCode.BadArguments);
2020-02-19 14:49:06 +00:00
var mods = modlist.Where(s => s.StartsWith("+")).Select(s => s.Substring(1)).ToHashSet();
2020-02-24 17:05:42 +00:00
2020-02-19 14:49:06 +00:00
if (mods.Count == 0)
2020-04-06 13:09:17 +00:00
return CLIUtils.Exit("Counted mods from modlist.txt are 0!", ExitCode.BadArguments);
2020-02-19 14:49:06 +00:00
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found {mods.Count} mods in modlist.txt");
2020-02-19 14:49:06 +00:00
var downloads = new HashSet<string>();
Directory.EnumerateDirectories(Mods, "*", SearchOption.TopDirectoryOnly)
.Where(d => mods.Contains(Path.GetRelativePath(Path.GetDirectoryName(d), d)))
.Do(d =>
{
var meta = Path.Combine(d, "meta.ini");
if (!File.Exists(meta))
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Mod meta file {meta} does not exist, skipping");
2020-02-19 14:49:06 +00:00
return;
}
string[] ini = File.ReadAllLines(meta);
if (ini == null || ini.Length == 0)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Mod meta file {meta} could not be read or is empty!");
2020-02-19 14:49:06 +00:00
return;
}
ini.Where(i => !string.IsNullOrWhiteSpace(i) && i.StartsWith("installationFile="))
.Select(i => i.Replace("installationFile=", ""))
.Do(i =>
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found installationFile {i}");
2020-02-19 14:49:06 +00:00
downloads.Add(i);
});
});
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found {downloads.Count} installationFiles from mod metas.");
2020-02-19 14:49:06 +00:00
Directory.EnumerateFiles(Input, "*", SearchOption.TopDirectoryOnly)
.Where(f => downloads.Contains(Path.GetFileNameWithoutExtension(f)))
.Do(f =>
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found archive {f}");
2020-02-19 14:49:06 +00:00
var outputPath = Path.Combine(Output, Path.GetFileName(f));
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Adding {f} to the transfer list");
2020-02-19 14:49:06 +00:00
list.Add(new TransferFile(f, outputPath));
var metaInputPath = Path.Combine(f, ".meta");
if (File.Exists(metaInputPath))
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found meta file for {f} at {metaInputPath}");
2020-02-19 14:49:06 +00:00
if (IncludeMeta)
{
var metaOutputPath = Path.Combine(outputPath, ".meta");
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Adding {metaInputPath} to the transfer list.");
2020-02-19 14:49:06 +00:00
list.Add(new TransferFile(metaInputPath, metaOutputPath));
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log("Meta file will be ignored");
2020-02-19 14:49:06 +00:00
}
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Found no meta file for {f}");
2020-02-19 14:49:06 +00:00
}
});
}
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Transfer list contains {list.Count} items");
2020-02-19 14:49:06 +00:00
var success = 0;
var failed = 0;
var skipped = 0;
list.Do(f =>
{
if (File.Exists(f.Output))
{
if (Overwrite)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Output file {f.Output} already exists, it will be overwritten");
2020-02-19 14:49:06 +00:00
if (f.IsMeta || Move)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Deleting file at {f.Output}");
2020-02-19 14:49:06 +00:00
try
{
File.Delete(f.Output);
}
catch (Exception e)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Could not delete file {f.Output}!\n{e}");
2020-02-19 14:49:06 +00:00
failed++;
}
}
}
else
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Output file {f.Output} already exists, skipping");
2020-02-19 14:49:06 +00:00
skipped++;
return;
}
}
if (f.IsMeta)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Writing meta data to {f.Output}");
2020-02-19 14:49:06 +00:00
try
{
File.WriteAllText(f.Output, f.Input, Encoding.UTF8);
success++;
}
catch (Exception e)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Error while writing meta data to {f.Output}!\n{e}");
2020-02-19 14:49:06 +00:00
failed++;
}
}
else
{
if (Copy)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Copying file {f.Input} to {f.Output}");
2020-02-19 14:49:06 +00:00
try
{
2020-02-19 15:24:50 +00:00
File.Copy(f.Input, f.Output, Overwrite ? CopyOptions.None : CopyOptions.FailIfExists, CopyMoveProgressHandler, null);
2020-02-19 14:49:06 +00:00
success++;
}
catch (Exception e)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Error while copying file {f.Input} to {f.Output}!\n{e}");
2020-02-19 14:49:06 +00:00
failed++;
}
}
else if(Move)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Moving file {f.Input} to {f.Output}");
2020-02-19 14:49:06 +00:00
try
{
2020-02-19 15:24:50 +00:00
File.Move(f.Input, f.Output, Overwrite ? MoveOptions.ReplaceExisting : MoveOptions.None, CopyMoveProgressHandler, null);
2020-02-19 14:49:06 +00:00
success++;
}
catch (Exception e)
{
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Error while moving file {f.Input} to {f.Output}!\n{e}");
2020-02-19 14:49:06 +00:00
failed++;
}
}
}
});
2020-02-24 16:50:58 +00:00
CLIUtils.Log($"Skipped transfers: {skipped}");
CLIUtils.Log($"Failed transfers: {failed}");
CLIUtils.Log($"Successful transfers: {success}");
2020-02-19 14:49:06 +00:00
return 0;
}
2020-02-19 15:24:50 +00:00
private static CopyMoveProgressResult CopyMoveProgressHandler(long totalfilesize, long totalbytestransferred, long streamsize, long streambytestransferred, int streamnumber, CopyMoveProgressCallbackReason callbackreason, object userdata)
{
Console.Write($"\r{' ', 100}");
Console.Write(totalfilesize == totalbytestransferred
? "\rTransfer complete!\n"
: $"\rTotal Size: {totalfilesize}, Transferred: {totalbytestransferred}");
return CopyMoveProgressResult.Continue;
}
2020-02-19 14:49:06 +00:00
}
}