wabbajack/Wabbajack.Lib/Compiler.cs

630 lines
24 KiB
C#
Raw Normal View History

2019-10-07 17:33:34 +00:00
using CommonMark;
using Compression.BSA;
using System;
using System.CodeDom;
2019-07-26 20:59:14 +00:00
using System.Collections.Concurrent;
2019-07-21 04:40:54 +00:00
using System.Collections.Generic;
2019-08-30 23:57:56 +00:00
using System.Diagnostics;
2019-07-21 04:40:54 +00:00
using System.IO;
2019-10-07 17:33:34 +00:00
using System.IO.Compression;
2019-07-21 04:40:54 +00:00
using System.Linq;
using System.Reflection;
2019-07-21 04:40:54 +00:00
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VFS;
2019-07-21 04:40:54 +00:00
using Wabbajack.Common;
using Wabbajack.Lib.CompilationSteps;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.ModListRegistry;
using Wabbajack.Lib.NexusApi;
using Wabbajack.Lib.Validation;
using Directory = Alphaleonis.Win32.Filesystem.Directory;
using File = Alphaleonis.Win32.Filesystem.File;
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
using Path = Alphaleonis.Win32.Filesystem.Path;
2019-07-21 04:40:54 +00:00
namespace Wabbajack.Lib
2019-07-21 04:40:54 +00:00
{
public class Compiler : ACompiler
2019-07-21 04:40:54 +00:00
{
private string _mo2DownloadsFolder;
2019-07-23 04:27:26 +00:00
public Dictionary<string, IEnumerable<IndexedFileMatch>> DirectMatchIndex;
2019-07-21 04:40:54 +00:00
public string MO2Folder;
public string MO2Profile;
2019-10-11 12:56:55 +00:00
public string ModListName, ModListAuthor, ModListDescription, ModListWebsite, ModListImage, ModListReadme;
2019-10-23 17:00:45 +00:00
public string WabbajackVersion;
2019-09-26 22:32:15 +00:00
public Compiler(string mo2_folder)
{
ModManager = ModManager.MO2;
MO2Folder = mo2_folder;
MO2Ini = Path.Combine(MO2Folder, "ModOrganizer.ini").LoadIniFile();
2019-10-07 17:33:34 +00:00
GamePath = ((string)MO2Ini.General.gamePath).Replace("\\\\", "\\");
ModListOutputFolder = "output_folder";
ModListOutputFile = MO2Profile + ExtensionManager.Extension;
}
2019-07-21 04:40:54 +00:00
public dynamic MO2Ini { get; }
2019-09-24 04:20:24 +00:00
public bool ShowReportWhenFinished { get; set; } = true;
public bool IgnoreMissingFiles { get; set; }
2019-07-22 22:17:46 +00:00
public string MO2DownloadsFolder
{
2019-07-21 04:40:54 +00:00
get
{
if (_mo2DownloadsFolder != null) return _mo2DownloadsFolder;
if (MO2Ini != null)
if (MO2Ini.Settings != null)
if (MO2Ini.Settings.download_directory != null)
return MO2Ini.Settings.download_directory.Replace("/", "\\");
2019-07-21 04:40:54 +00:00
return Path.Combine(MO2Folder, "downloads");
}
set => _mo2DownloadsFolder = value;
2019-07-21 04:40:54 +00:00
}
public string MO2ProfileDir => Path.Combine(MO2Folder, "profiles", MO2Profile);
2019-07-21 12:42:29 +00:00
internal UserStatus User { get; private set; }
2019-07-26 20:59:14 +00:00
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
public Dictionary<string, dynamic> ModInis { get; private set; }
2019-07-21 04:40:54 +00:00
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
public override void Info(string msg)
2019-07-21 04:40:54 +00:00
{
2019-09-26 22:32:15 +00:00
Utils.Log(msg);
2019-07-21 04:40:54 +00:00
}
public override void Status(string msg)
2019-07-22 22:17:46 +00:00
{
WorkQueue.Report(msg, 0);
}
public override void Error(string msg)
2019-07-21 22:47:17 +00:00
{
2019-09-26 22:32:15 +00:00
Utils.Log(msg);
2019-07-21 22:47:17 +00:00
throw new Exception(msg);
}
internal override string IncludeFile(byte[] data)
{
var id = Guid.NewGuid().ToString();
File.WriteAllBytes(Path.Combine(ModListOutputFolder, id), data);
return id;
}
internal override string IncludeFile(string data)
{
var id = Guid.NewGuid().ToString();
File.WriteAllText(Path.Combine(ModListOutputFolder, id), data);
return id;
}
public override bool Compile()
2019-07-21 04:40:54 +00:00
{
2019-09-16 22:47:15 +00:00
VirtualFileSystem.Clean();
Info("Looking for other profiles");
var other_profiles_path = Path.Combine(MO2ProfileDir, "otherprofiles.txt");
SelectedProfiles = new HashSet<string>();
if (File.Exists(other_profiles_path)) SelectedProfiles = File.ReadAllLines(other_profiles_path).ToHashSet();
SelectedProfiles.Add(MO2Profile);
Info("Using Profiles: " + string.Join(", ", SelectedProfiles.OrderBy(p => p)));
Info($"Indexing {MO2Folder}");
VFS.AddRoot(MO2Folder);
Info($"Indexing {GamePath}");
VFS.AddRoot(GamePath);
Info($"Indexing {MO2DownloadsFolder}");
VFS.AddRoot(MO2DownloadsFolder);
Info("Cleaning output folder");
if (Directory.Exists(ModListOutputFolder))
Directory.Delete(ModListOutputFolder, true);
Directory.CreateDirectory(ModListOutputFolder);
2019-07-21 04:40:54 +00:00
var mo2_files = Directory.EnumerateFiles(MO2Folder, "*", SearchOption.AllDirectories)
.Where(p => p.FileExists())
2019-10-07 17:33:34 +00:00
.Select(p => new RawSourceFile(VFS.Lookup(p)) { Path = p.RelativeTo(MO2Folder) });
2019-07-21 04:40:54 +00:00
var game_files = Directory.EnumerateFiles(GamePath, "*", SearchOption.AllDirectories)
.Where(p => p.FileExists())
.Select(p => new RawSourceFile(VFS.Lookup(p))
2019-10-07 17:33:34 +00:00
{ Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)) });
2019-07-21 04:40:54 +00:00
var loot_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"LOOT");
2019-08-24 23:59:22 +00:00
2019-09-24 04:34:21 +00:00
// TODO: make this generic so we can add more paths
IEnumerable<RawSourceFile> loot_files = new List<RawSourceFile>();
if (Directory.Exists(loot_path))
{
Info($"Indexing {loot_path}");
VFS.AddRoot(loot_path);
loot_files = Directory.EnumerateFiles(loot_path, "userlist.yaml", SearchOption.AllDirectories)
.Where(p => p.FileExists())
.Select(p => new RawSourceFile(VFS.Lookup(p))
2019-10-07 17:33:34 +00:00
{ Path = Path.Combine(Consts.LOOTFolderFilesDir, p.RelativeTo(loot_path)) });
2019-09-24 04:34:21 +00:00
}
2019-08-24 23:59:22 +00:00
Info("Indexing Archives");
IndexedArchives = Directory.EnumerateFiles(MO2DownloadsFolder)
.Where(f => File.Exists(f + ".meta"))
.Select(f => new IndexedArchive
{
File = VFS.Lookup(f),
Name = Path.GetFileName(f),
IniData = (f + ".meta").LoadIniFile(),
Meta = File.ReadAllText(f + ".meta")
})
.ToList();
Info("Indexing Files");
var grouped = VFS.GroupedByArchive();
IndexedFiles = IndexedArchives.Select(f =>
{
if (grouped.TryGetValue(f.File, out var result))
return result;
return new List<VirtualFile>();
})
.SelectMany(fs => fs)
.Concat(IndexedArchives.Select(f => f.File))
.OrderByDescending(f => f.TopLevelArchive.LastModified)
.GroupBy(f => f.Hash)
.ToDictionary(f => f.Key, f => f.AsEnumerable());
Info("Searching for mod files");
2019-08-24 23:59:22 +00:00
AllFiles = mo2_files.Concat(game_files)
.Concat(loot_files)
2019-09-23 21:37:10 +00:00
.DistinctBy(f => f.Path)
.ToList();
2019-09-26 22:32:15 +00:00
Info($"Found {AllFiles.Count} files to build into mod list");
2019-07-21 04:40:54 +00:00
Info("Verifying destinations");
var dups = AllFiles.GroupBy(f => f.Path)
.Where(fs => fs.Count() > 1)
.Select(fs =>
{
Utils.Log($"Duplicate files installed to {fs.Key} from : {String.Join(", ", fs.Select(f => f.AbsolutePath))}");
return fs;
}).ToList();
if (dups.Count > 0)
{
Error($"Found {dups.Count} duplicates, exiting");
}
2019-07-26 20:59:14 +00:00
ExtraFiles = new ConcurrentBag<Directive>();
ModInis = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods"))
.Select(f =>
{
var mod_name = Path.GetFileName(f);
var meta_path = Path.Combine(f, "meta.ini");
if (File.Exists(meta_path))
return (mod_name, meta_path.LoadIniFile());
return (null, null);
})
.Where(f => f.Item2 != null)
.ToDictionary(f => f.Item1, f => f.Item2);
var stack = MakeStack();
2019-07-21 04:40:54 +00:00
2019-08-25 03:46:32 +00:00
2019-07-22 22:17:46 +00:00
Info("Running Compilation Stack");
var results = AllFiles.PMap(f => RunStack(stack, f)).ToList();
2019-07-21 04:40:54 +00:00
2019-07-26 20:59:14 +00:00
// Add the extra files that were generated by the stack
Info($"Adding {ExtraFiles.Count} that were generated by the stack");
results = results.Concat(ExtraFiles).ToList();
2019-07-21 04:40:54 +00:00
var nomatch = results.OfType<NoMatch>();
2019-09-26 22:32:15 +00:00
Info($"No match for {nomatch.Count()} files");
2019-07-21 04:40:54 +00:00
foreach (var file in nomatch)
2019-09-26 22:32:15 +00:00
Info($" {file.To}");
2019-07-26 20:59:14 +00:00
if (nomatch.Count() > 0)
{
if (IgnoreMissingFiles)
{
Info("Continuing even though files were missing at the request of the user.");
}
else
{
Info("Exiting due to no way to compile these files");
2019-09-24 04:20:24 +00:00
return false;
}
}
2019-07-21 04:40:54 +00:00
2019-07-21 22:47:17 +00:00
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
2019-09-24 15:26:44 +00:00
Info("Getting Nexus api_key, please click authorize if a browser window appears");
2019-09-26 03:18:36 +00:00
if (IndexedArchives.Any(a => a.IniData?.General?.gameName != null))
2019-09-24 04:20:24 +00:00
{
var nexusClient = new NexusApiClient();
if (!nexusClient.IsPremium) Error($"User {nexusClient.Username} is not a premium Nexus user, so we cannot access the necessary API calls, cannot continue");
2019-09-24 04:20:24 +00:00
}
2019-09-23 21:37:10 +00:00
zEditIntegration.VerifyMerges(this);
2019-07-21 22:47:17 +00:00
GatherArchives();
IncludeArchiveMetadata();
2019-07-22 03:36:25 +00:00
BuildPatches();
2019-07-23 04:27:26 +00:00
ModList = new ModList
2019-07-23 04:27:26 +00:00
{
GameType = GameRegistry.Games.Values.First(f => f.MO2Name == MO2Ini.General.gameName).Game,
2019-10-23 17:00:45 +00:00
WabbajackVersion = WabbajackVersion,
2019-07-23 04:27:26 +00:00
Archives = SelectedArchives,
ModManager = ModManager.MO2,
2019-08-02 23:04:04 +00:00
Directives = InstallDirectives,
Name = ModListName ?? MO2Profile,
Author = ModListAuthor ?? "",
Description = ModListDescription ?? "",
2019-10-11 12:56:55 +00:00
Readme = ModListReadme ?? "",
2019-10-11 11:09:34 +00:00
Image = ModListImage ?? "",
Website = ModListWebsite ?? ""
2019-07-23 04:27:26 +00:00
};
ValidateModlist.RunValidation(ModList);
2019-08-30 23:57:56 +00:00
GenerateReport();
2019-09-27 04:07:54 +00:00
ExportModlist();
2019-07-23 04:27:26 +00:00
ResetMembers();
2019-08-30 23:57:56 +00:00
ShowReport();
2019-09-27 04:07:54 +00:00
Info("Done Building Modlist");
2019-09-24 04:20:24 +00:00
return true;
2019-07-21 04:40:54 +00:00
}
private void IncludeArchiveMetadata()
{
Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads");
SelectedArchives.Do(a =>
{
var source = Path.Combine(MO2DownloadsFolder, a.Name + ".meta");
InstallDirectives.Add(new ArchiveMeta()
{
SourceDataID = IncludeFile(File.ReadAllText(source)),
Size = File.GetSize(source),
Hash = source.FileHash(),
To = Path.GetFileName(source)
});
});
}
2019-09-27 04:07:54 +00:00
private void ExportModlist()
{
Utils.Log($"Exporting Modlist to : {ModListOutputFile}");
//ModList.ToJSON(Path.Combine(ModListOutputFolder, "modlist.json"));
2019-10-27 12:12:35 +00:00
ModList.ToCERAS(Path.Combine(ModListOutputFolder, "modlist"), ref CerasConfig.Config);
2019-09-27 04:07:54 +00:00
if (File.Exists(ModListOutputFile))
File.Delete(ModListOutputFile);
2019-09-27 04:07:54 +00:00
using (var fs = new FileStream(ModListOutputFile, FileMode.Create))
2019-09-27 04:07:54 +00:00
{
using (var za = new ZipArchive(fs, ZipArchiveMode.Create))
2019-09-27 04:07:54 +00:00
{
Directory.EnumerateFiles(ModListOutputFolder, "*.*")
.DoProgress("Compressing Modlist",
f =>
{
var ze = za.CreateEntry(Path.GetFileName(f));
using (var os = ze.Open())
using (var ins = File.OpenRead(f))
{
ins.CopyTo(os);
}
});
2019-09-27 04:07:54 +00:00
}
}
Utils.Log("Exporting Modlist metadata");
var metadata = new ModlistMetadata.DownloadMetadata
{
Size = File.GetSize(ModListOutputFile),
2019-11-06 13:21:39 +00:00
Hash = ModListOutputFile.FileHash(),
NumberOfArchives = ModList.Archives.Count,
SizeOfArchives = ModList.Archives.Sum(a => a.Size),
NumberOfInstalledFiles = ModList.Directives.Count,
SizeOfInstalledFiles = ModList.Directives.Sum(a => a.Size)
};
metadata.ToJSON(ModListOutputFile + ".meta.json");
Utils.Log("Removing modlist staging folder");
Directory.Delete(ModListOutputFolder, true);
2019-09-27 04:07:54 +00:00
}
2019-08-30 23:57:56 +00:00
private void ShowReport()
{
2019-09-24 04:20:24 +00:00
if (!ShowReportWhenFinished) return;
2019-08-30 23:57:56 +00:00
var file = Path.GetTempFileName() + ".html";
File.WriteAllText(file, ModList.ReportHTML);
Process.Start(file);
}
private void GenerateReport()
{
string css = "";
2019-10-16 03:17:27 +00:00
using (Stream cssStream = Utils.GetResourceStream("Wabbajack.Lib.css-min.css"))
{
using (StreamReader reader = new StreamReader(cssStream))
{
css = reader.ReadToEnd();
}
}
2019-08-30 23:57:56 +00:00
using (var fs = File.OpenWrite($"{ModList.Name}.md"))
{
fs.SetLength(0);
using (var reporter = new ReportBuilder(fs, ModListOutputFolder))
{
2019-10-01 23:06:15 +00:00
reporter.Build(this, ModList);
}
2019-08-30 23:57:56 +00:00
}
2019-10-07 17:33:34 +00:00
ModList.ReportHTML = "<style>" + css + "</style>"
+ CommonMarkConverter.Convert(File.ReadAllText($"{ModList.Name}.md"));
2019-08-30 23:57:56 +00:00
}
2019-07-21 04:40:54 +00:00
2019-07-23 04:27:26 +00:00
/// <summary>
/// Clear references to lists that hold a lot of data.
2019-07-23 04:27:26 +00:00
/// </summary>
private void ResetMembers()
{
AllFiles = null;
InstallDirectives = null;
SelectedArchives = null;
2019-07-26 20:59:14 +00:00
ExtraFiles = null;
2019-07-23 04:27:26 +00:00
}
2019-07-22 03:36:25 +00:00
/// <summary>
/// Fills in the Patch fields in files that require them
2019-07-22 03:36:25 +00:00
/// </summary>
private void BuildPatches()
{
Info("Gathering patch files");
2019-07-22 03:36:25 +00:00
var groups = InstallDirectives.OfType<PatchedFromArchive>()
.Where(p => p.PatchID == null)
.GroupBy(p => p.ArchiveHashPath[0])
.ToList();
2019-07-22 03:36:25 +00:00
2019-09-26 22:32:15 +00:00
Info($"Patching building patches from {groups.Count} archives");
2019-07-22 03:36:25 +00:00
var absolute_paths = AllFiles.ToDictionary(e => e.Path, e => e.AbsolutePath);
2019-07-22 22:17:46 +00:00
groups.PMap(group => BuildArchivePatches(group.Key, group, absolute_paths));
2019-07-22 03:36:25 +00:00
if (InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.PatchID == null) != null)
2019-07-22 03:36:25 +00:00
Error("Missing patches after generation, this should not happen");
}
private void BuildArchivePatches(string archive_sha, IEnumerable<PatchedFromArchive> group,
Dictionary<string, string> absolute_paths)
2019-07-22 03:36:25 +00:00
{
var archive = VFS.HashIndex[archive_sha];
using (var files = VFS.StageWith(group.Select(g => VFS.FileForArchiveHashPath(g.ArchiveHashPath))))
2019-07-22 03:36:25 +00:00
{
var by_path = files.GroupBy(f => string.Join("|", f.Paths.Skip(1)))
.ToDictionary(f => f.Key, f => f.First());
// Now Create the patches
group.PMap(entry =>
2019-07-22 03:36:25 +00:00
{
2019-09-26 22:32:15 +00:00
Info($"Patching {entry.To}");
2019-10-11 23:31:36 +00:00
Status($"Patching {entry.To}");
using (var origin = by_path[string.Join("|", entry.ArchiveHashPath.Skip(1))].OpenRead())
using (var output = new MemoryStream())
{
var a = origin.ReadAll();
var b = LoadDataForTo(entry.To, absolute_paths).Result;
Utils.CreatePatch(a, b, output);
entry.PatchID = IncludeFile(output.ToArray());
var file_size = File.GetSize(Path.Combine(ModListOutputFolder, entry.PatchID));
Info($"Patch size {file_size} for {entry.To}");
}
});
}
2019-07-22 03:36:25 +00:00
}
private async Task<byte[]> LoadDataForTo(string to, Dictionary<string, string> absolute_paths)
2019-07-26 20:59:14 +00:00
{
if (absolute_paths.TryGetValue(to, out var absolute))
return File.ReadAllBytes(absolute);
if (to.StartsWith(Consts.BSACreationDir))
{
var bsa_id = to.Split('\\')[1];
var bsa = InstallDirectives.OfType<CreateBSA>().First(b => b.TempID == bsa_id);
using (var a = await BSADispatch.OpenRead(Path.Combine(MO2Folder, bsa.To)))
2019-07-26 20:59:14 +00:00
{
2019-10-11 23:31:36 +00:00
var find = Path.Combine(to.Split('\\').Skip(2).ToArray());
var file = a.Files.First(e => e.Path.Replace('/', '\\') == find);
using (var ms = new MemoryStream())
{
await file.CopyDataToAsync(ms);
2019-10-11 23:31:36 +00:00
return ms.ToArray();
}
2019-07-26 20:59:14 +00:00
}
}
2019-07-26 20:59:14 +00:00
Error($"Couldn't load data for {to}");
return null;
}
2019-07-21 22:47:17 +00:00
private void GatherArchives()
{
Info("Building a list of archives based on the files required");
2019-07-21 22:47:17 +00:00
var shas = InstallDirectives.OfType<FromArchive>()
.Select(a => a.ArchiveHashPath[0])
.Distinct();
2019-07-21 22:47:17 +00:00
var archives = IndexedArchives.OrderByDescending(f => f.File.LastModified)
.GroupBy(f => f.File.Hash)
.ToDictionary(f => f.Key, f => f.First());
SelectedArchives = shas.PMap(sha => ResolveArchive(sha, archives));
2019-07-21 22:47:17 +00:00
}
private Archive ResolveArchive(string sha, IDictionary<string, IndexedArchive> archives)
2019-07-21 22:47:17 +00:00
{
2019-07-22 22:17:46 +00:00
if (archives.TryGetValue(sha, out var found))
2019-07-21 22:47:17 +00:00
{
if (found.IniData == null)
2019-09-26 22:32:15 +00:00
Error($"No download metadata found for {found.Name}, please use MO2 to query info or add a .meta file and try again.");
2019-07-21 22:47:17 +00:00
var result = new Archive();
result.State = (AbstractDownloadState)DownloadDispatcher.ResolveArchive(found.IniData);
2019-07-21 22:47:17 +00:00
if (result.State == null)
Error($"{found.Name} could not be handled by any of the downloaders");
2019-07-21 22:47:17 +00:00
result.Name = found.Name;
result.Hash = found.File.Hash;
2019-07-21 22:47:17 +00:00
result.Meta = found.Meta;
result.Size = found.File.Size;
2019-07-21 22:47:17 +00:00
Info($"Checking link for {found.Name}");
if (!result.State.Verify())
Error(
2019-09-24 15:26:44 +00:00
$"Unable to resolve link for {found.Name}. If this is hosted on the Nexus the file may have been removed.");
2019-07-21 22:47:17 +00:00
return result;
}
2019-09-26 22:32:15 +00:00
Error($"No match found for Archive sha: {sha} this shouldn't happen");
2019-07-21 22:47:17 +00:00
return null;
}
public override Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
2019-07-21 04:40:54 +00:00
{
Utils.Status($"Compiling {source.Path}");
foreach (var step in stack)
{
var result = step.Run(source);
if (result != null) return result;
}
throw new InvalidDataException("Data fell out of the compilation stack");
2019-07-21 04:40:54 +00:00
}
public override IEnumerable<ICompilationStep> GetStack()
{
var user_config = Path.Combine(MO2ProfileDir, "compilation_stack.yml");
if (File.Exists(user_config))
return Serialization.Deserialize(File.ReadAllText(user_config), this);
var stack = MakeStack();
File.WriteAllText(Path.Combine(MO2ProfileDir, "_current_compilation_stack.yml"),
Serialization.Serialize(stack));
return stack;
}
2019-07-21 04:40:54 +00:00
/// <summary>
/// Creates a execution stack. The stack should be passed into Run stack. Each function
/// in this stack will be run in-order and the first to return a non-null result will have its
/// result included into the pack
2019-07-21 04:40:54 +00:00
/// </summary>
/// <returns></returns>
public override IEnumerable<ICompilationStep> MakeStack()
2019-07-21 04:40:54 +00:00
{
Utils.Log("Generating compilation stack");
return new List<ICompilationStep>
{
new IncludePropertyFiles(this),
new IgnoreStartsWith(this,"logs\\"),
new IgnoreStartsWith(this, "downloads\\"),
new IgnoreStartsWith(this,"webcache\\"),
new IgnoreStartsWith(this, "overwrite\\"),
new IgnorePathContains(this,"temporary_logs"),
new IgnorePathContains(this, "GPUCache"),
new IgnorePathContains(this, "SSEEdit Cache"),
new IgnoreEndsWith(this, ".pyc"),
new IgnoreEndsWith(this, ".log"),
new IgnoreOtherProfiles(this),
new IgnoreDisabledMods(this),
new IncludeThisProfile(this),
2019-07-21 04:40:54 +00:00
// Ignore the ModOrganizer.ini file it contains info created by MO2 on startup
new IncludeStubbedConfigFiles(this),
new IncludeLootFiles(this),
new IgnoreStartsWith(this, Path.Combine(Consts.GameFolderFilesDir, "Data")),
new IgnoreStartsWith(this, Path.Combine(Consts.GameFolderFilesDir, "Papyrus Compiler")),
new IgnoreStartsWith(this, Path.Combine(Consts.GameFolderFilesDir, "Skyrim")),
new IgnoreRegex(this, Consts.GameFolderFilesDir + "\\\\.*\\.bsa"),
new IncludeModIniData(this),
new DirectMatch(this),
new IncludeTaggedMods(this, Consts.WABBAJACK_INCLUDE),
new DeconstructBSAs(this), // Deconstruct BSAs before building patches so we don't generate massive patch files
new IncludePatches(this),
new IncludeDummyESPs(this),
2019-07-21 12:42:29 +00:00
2019-07-26 20:59:14 +00:00
2019-07-21 12:42:29 +00:00
// If we have no match at this point for a game folder file, skip them, we can't do anything about them
new IgnoreGameFiles(this),
2019-07-26 20:59:14 +00:00
2019-09-26 23:08:10 +00:00
// There are some types of files that will error the compilation, because they're created on-the-fly via tools
2019-07-26 20:59:14 +00:00
// so if we don't have a match by this point, just drop them.
new IgnoreEndsWith(this, ".ini"),
new IgnoreEndsWith(this, ".html"),
new IgnoreEndsWith(this, ".txt"),
2019-07-26 20:59:14 +00:00
// Don't know why, but this seems to get copied around a bit
new IgnoreEndsWith(this, "HavokBehaviorPostProcess.exe"),
2019-08-03 17:37:32 +00:00
// Theme file MO2 downloads somehow
new IgnoreEndsWith(this, "splash.png"),
2019-07-23 04:27:26 +00:00
new IgnoreEndsWith(this, ".bin"),
new IgnoreEndsWith(this, ".refcache"),
2019-09-02 22:36:57 +00:00
new IgnoreWabbajackInstallCruft(this),
2019-07-21 22:47:17 +00:00
new PatchStockESMs(this),
new IncludeAllConfigs(this),
new zEditIntegration.IncludeZEditPatches(this),
2019-11-02 18:36:38 +00:00
new IncludeTaggedMods(this, Consts.WABBAJACK_NOMATCH_INCLUDE),
new DropAll(this)
2019-07-21 04:40:54 +00:00
};
}
2019-07-22 22:17:46 +00:00
public class IndexedFileMatch
{
public IndexedArchive Archive;
public IndexedArchiveEntry Entry;
public DateTime LastModified;
}
2019-07-21 04:40:54 +00:00
}
}