mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
move compilation steps into separate files and abstract behind an interface
This commit is contained in:
parent
af1d4ff609
commit
eb6bf289a7
20
Wabbajack.Lib/CompilationSteps/ACompilationStep.cs
Normal file
20
Wabbajack.Lib/CompilationSteps/ACompilationStep.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public abstract class ACompilationStep : ICompilationStep
|
||||
{
|
||||
protected Compiler _compiler;
|
||||
|
||||
public ACompilationStep(Compiler compiler)
|
||||
{
|
||||
_compiler = compiler;
|
||||
}
|
||||
|
||||
public abstract Directive Run(RawSourceFile source);
|
||||
}
|
||||
}
|
91
Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs
Normal file
91
Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Compression.BSA;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class DeconstructBSAs : ACompilationStep
|
||||
{
|
||||
private readonly List<ICompilationStep> _microstack;
|
||||
private readonly List<ICompilationStep> _microstackWithInclude;
|
||||
private readonly IEnumerable<string> _include_directly;
|
||||
|
||||
public DeconstructBSAs(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_include_directly = _compiler.ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(Consts.WABBAJACK_INCLUDE)) return true;
|
||||
if (general.comments != null && general.comments.Contains(Consts.WABBAJACK_INCLUDE)) return true;
|
||||
return false;
|
||||
})
|
||||
.Select(kv => $"mods\\{kv.Key}\\")
|
||||
.ToList();
|
||||
|
||||
_microstack = new List<ICompilationStep>
|
||||
{
|
||||
new DirectMatch(_compiler),
|
||||
new IncludePatches(_compiler),
|
||||
new DropAll(_compiler)
|
||||
};
|
||||
|
||||
_microstackWithInclude = new List<ICompilationStep>
|
||||
{
|
||||
new DirectMatch(_compiler),
|
||||
new IncludePatches(_compiler),
|
||||
new IncludeAll(_compiler)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!Consts.SupportedBSAs.Contains(Path.GetExtension(source.Path).ToLower())) return null;
|
||||
|
||||
var defaultInclude = false;
|
||||
if (source.Path.StartsWith("mods"))
|
||||
{
|
||||
if (_include_directly.Any(path => source.Path.StartsWith(path)))
|
||||
{
|
||||
defaultInclude = true;
|
||||
}
|
||||
}
|
||||
|
||||
var source_files = source.File.FileInArchive;
|
||||
|
||||
var stack = defaultInclude ? _microstackWithInclude : _microstack;
|
||||
|
||||
var id = Guid.NewGuid().ToString();
|
||||
|
||||
var matches = source_files.PMap(e => Compiler.RunStack(stack, new RawSourceFile(e)
|
||||
{
|
||||
Path = Path.Combine(Consts.BSACreationDir, id, e.Paths.Last())
|
||||
}));
|
||||
|
||||
|
||||
foreach (var match in matches)
|
||||
{
|
||||
if (match is IgnoredDirectly)
|
||||
Utils.Error($"File required for BSA {source.Path} creation doesn't exist: {match.To}");
|
||||
_compiler.ExtraFiles.Add(match);
|
||||
}
|
||||
|
||||
CreateBSA directive;
|
||||
using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
||||
{
|
||||
directive = new CreateBSA
|
||||
{
|
||||
To = source.Path,
|
||||
TempID = id,
|
||||
State = bsa.State,
|
||||
FileStates = bsa.Files.Select(f => f.State).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
return directive;
|
||||
}
|
||||
}
|
||||
}
|
29
Wabbajack.Lib/CompilationSteps/DirectMatch.cs
Normal file
29
Wabbajack.Lib/CompilationSteps/DirectMatch.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class DirectMatch : ACompilationStep
|
||||
{
|
||||
public DirectMatch(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_compiler.IndexedFiles.TryGetValue(source.Hash, out var found)) return null;
|
||||
var result = source.EvolveTo<FromArchive>();
|
||||
|
||||
var match = found.Where(f =>
|
||||
Path.GetFileName(f.Paths[f.Paths.Length - 1]) == Path.GetFileName(source.Path))
|
||||
.OrderBy(f => f.Paths.Length)
|
||||
.FirstOrDefault()
|
||||
?? found.OrderBy(f => f.Paths.Length).FirstOrDefault();
|
||||
|
||||
result.ArchiveHashPath = match.MakeRelativePaths();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
19
Wabbajack.Lib/CompilationSteps/DropAll.cs
Normal file
19
Wabbajack.Lib/CompilationSteps/DropAll.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class DropAll : ACompilationStep
|
||||
{
|
||||
public DropAll(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var result = source.EvolveTo<NoMatch>();
|
||||
result.Reason = "No Match in Stack";
|
||||
Utils.Log($"No match for: {source.Path}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
13
Wabbajack.Lib/CompilationSteps/IStackStep.cs
Normal file
13
Wabbajack.Lib/CompilationSteps/IStackStep.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public interface ICompilationStep
|
||||
{
|
||||
Directive Run(RawSourceFile source);
|
||||
}
|
||||
}
|
49
Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs
Normal file
49
Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreDisabledMods : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _allEnabledMods;
|
||||
|
||||
public IgnoreDisabledMods(Compiler compiler) : base(compiler)
|
||||
{
|
||||
var alwaysEnabled = _compiler.ModInis.Where(f => IsAlwaysEnabled(f.Value)).Select(f => f.Key).ToHashSet();
|
||||
|
||||
_allEnabledMods = _compiler.SelectedProfiles
|
||||
.SelectMany(p => File.ReadAllLines(Path.Combine(_compiler.MO2Folder, "profiles", p, "modlist.txt")))
|
||||
.Where(line => line.StartsWith("+") || line.EndsWith("_separator"))
|
||||
.Select(line => line.Substring(1))
|
||||
.Concat(alwaysEnabled)
|
||||
.Select(line => Path.Combine("mods", line) + "\\")
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith("mods") || _allEnabledMods.Any(mod => source.Path.StartsWith(mod)))
|
||||
return null;
|
||||
var r = source.EvolveTo<IgnoredDirectly>();
|
||||
r.Reason = "Disabled Mod";
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
private static bool IsAlwaysEnabled(dynamic data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
if (data.General != null && data.General.notes != null &&
|
||||
data.General.notes.Contains(
|
||||
Consts.WABBAJACK_ALWAYS_ENABLE))
|
||||
return true;
|
||||
if (data.General != null && data.General.comments != null &&
|
||||
data.General.notes.Contains(Consts.WABBAJACK_ALWAYS_ENABLE))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
29
Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs
Normal file
29
Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreEndsWith : ACompilationStep
|
||||
{
|
||||
private readonly string _reason;
|
||||
private readonly string _postfix;
|
||||
|
||||
public IgnoreEndsWith(Compiler compiler, string postfix) : base(compiler)
|
||||
{
|
||||
_postfix = postfix;
|
||||
_reason = $"Ignored because path ends with {postfix}";
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.EndsWith(_postfix)) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = _reason;
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
23
Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs
Normal file
23
Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreGameFiles : ACompilationStep
|
||||
{
|
||||
private readonly string _startDir;
|
||||
|
||||
public IgnoreGameFiles(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_startDir = Consts.GameFolderFilesDir + "\\";
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith(_startDir)) return null;
|
||||
var i = source.EvolveTo<IgnoredDirectly>();
|
||||
i.Reason = "Default game file";
|
||||
return i;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
29
Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs
Normal file
29
Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnorePathContains : ACompilationStep
|
||||
{
|
||||
private readonly string _pattern;
|
||||
private readonly string _reason;
|
||||
|
||||
public IgnorePathContains(Compiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_pattern = $"\\{pattern.Trim('\\')}\\";
|
||||
_reason = $"Ignored because path contains {_pattern}";
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.Contains(_pattern)) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = _reason;
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
27
Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs
Normal file
27
Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreRegex : ACompilationStep
|
||||
{
|
||||
private readonly string _reason;
|
||||
private string _pattern;
|
||||
private readonly Regex _regex;
|
||||
|
||||
public IgnoreRegex(Compiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_pattern = pattern;
|
||||
_reason = $"Ignored because path matches regex {pattern}";
|
||||
_regex = new Regex(pattern);
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_regex.IsMatch(source.Path)) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = _reason;
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
31
Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs
Normal file
31
Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreStartsWith : ACompilationStep
|
||||
{
|
||||
private readonly string _reason;
|
||||
private readonly string _prefix;
|
||||
|
||||
public IgnoreStartsWith(Compiler compiler, string prefix) : base(compiler)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_reason = string.Format("Ignored because path starts with {0}", _prefix);
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (source.Path.StartsWith(_prefix))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = _reason;
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class IgnoreWabbajackInstallCruft : ACompilationStep
|
||||
{
|
||||
private readonly HashSet<string> _cruftFiles;
|
||||
|
||||
public IgnoreWabbajackInstallCruft(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_cruftFiles = new HashSet<string>
|
||||
{
|
||||
"7z.dll", "7z.exe", "vfs_staged_files\\", "nexus.key_cache", "patch_cache\\",
|
||||
Consts.NexusCacheDirectory + "\\"
|
||||
};
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_cruftFiles.Any(f => source.Path.StartsWith(f))) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = "Wabbajack Cruft file";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
18
Wabbajack.Lib/CompilationSteps/IncludeAll.cs
Normal file
18
Wabbajack.Lib/CompilationSteps/IncludeAll.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeAll : ACompilationStep
|
||||
{
|
||||
public IncludeAll(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
}
|
||||
}
|
||||
}
|
20
Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs
Normal file
20
Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class IncludeAllConfigs : ACompilationStep
|
||||
{
|
||||
public IncludeAllConfigs(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path))) return null;
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
30
Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs
Normal file
30
Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeDummyESPs : ACompilationStep
|
||||
{
|
||||
public IncludeDummyESPs(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (Path.GetExtension(source.AbsolutePath) != ".esp" &&
|
||||
Path.GetExtension(source.AbsolutePath) != ".esm") return null;
|
||||
|
||||
var bsa = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
|
||||
Path.GetFileNameWithoutExtension(source.AbsolutePath) + ".bsa");
|
||||
var bsaTextures = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
|
||||
Path.GetFileNameWithoutExtension(source.AbsolutePath) + " - Textures.bsa");
|
||||
var espSize = new FileInfo(source.AbsolutePath).Length;
|
||||
|
||||
if (espSize > 250 || (!File.Exists(bsa) && !File.Exists(bsaTextures))) return null;
|
||||
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
24
Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs
Normal file
24
Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeLootFiles : ACompilationStep
|
||||
{
|
||||
private readonly string _prefix;
|
||||
|
||||
public IncludeLootFiles(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_prefix = Consts.LOOTFolderFilesDir + "\\";
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith(_prefix)) return null;
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath).ToBase64());
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
20
Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs
Normal file
20
Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class IncludeModIniData : ACompilationStep
|
||||
{
|
||||
public IncludeModIniData(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith("mods\\") || !source.Path.EndsWith("\\meta.ini")) return null;
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return e;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
31
Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs
Normal file
31
Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreOtherProfiles : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _profiles;
|
||||
|
||||
public IgnoreOtherProfiles(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_profiles = _compiler.SelectedProfiles
|
||||
.Select(p => Path.Combine("profiles", p) + "\\")
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith("profiles\\")) return null;
|
||||
if (_profiles.Any(profile => source.Path.StartsWith(profile))) return null;
|
||||
var c = source.EvolveTo<IgnoredDirectly>();
|
||||
c.Reason = "File not for selected profiles";
|
||||
return c;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
41
Wabbajack.Lib/CompilationSteps/IncludePatches.cs
Normal file
41
Wabbajack.Lib/CompilationSteps/IncludePatches.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludePatches : ACompilationStep
|
||||
{
|
||||
private readonly Dictionary<string, IGrouping<string, VirtualFile>> _indexed;
|
||||
|
||||
public IncludePatches(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_indexed = _compiler.IndexedFiles.Values
|
||||
.SelectMany(f => f)
|
||||
.GroupBy(f => Path.GetFileName(Enumerable.Last(f.Paths)).ToLower())
|
||||
.ToDictionary(f => f.Key);
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_indexed.TryGetValue(Path.GetFileName(Enumerable.Last(source.File.Paths).ToLower()), out var value))
|
||||
return null;
|
||||
|
||||
var found = value.OrderByDescending(f => (f.TopLevelArchive ?? f).LastModified).First();
|
||||
|
||||
var e = source.EvolveTo<PatchedFromArchive>();
|
||||
e.ArchiveHashPath = found.MakeRelativePaths();
|
||||
e.To = source.Path;
|
||||
e.Hash = source.File.Hash;
|
||||
|
||||
Utils.TryGetPatch(found.Hash, source.File.Hash, out var data);
|
||||
|
||||
if (data != null)
|
||||
e.PatchID = _compiler.IncludeFile(data);
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
43
Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs
Normal file
43
Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class IncludePropertyFiles : ACompilationStep
|
||||
{
|
||||
public IncludePropertyFiles(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var files = new HashSet<string>
|
||||
{
|
||||
_compiler.ModListImage, _compiler.ModListReadme
|
||||
};
|
||||
if (!files.Any(f => source.AbsolutePath.Equals(f))) return null;
|
||||
if (!File.Exists(source.AbsolutePath)) return null;
|
||||
var isBanner = source.AbsolutePath == _compiler.ModListImage;
|
||||
//var isReadme = source.AbsolutePath == ModListReadme;
|
||||
var result = source.EvolveTo<PropertyFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
if (isBanner)
|
||||
{
|
||||
result.Type = PropertyType.Banner;
|
||||
_compiler.ModListImage = result.SourceDataID;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Type = PropertyType.Readme;
|
||||
_compiler.ModListReadme = result.SourceDataID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
29
Wabbajack.Lib/CompilationSteps/IncludeRegex.cs
Normal file
29
Wabbajack.Lib/CompilationSteps/IncludeRegex.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeRegex : ACompilationStep
|
||||
{
|
||||
private readonly Regex _regex;
|
||||
|
||||
public IncludeRegex(Compiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_regex = new Regex(pattern);
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_regex.IsMatch(source.Path)) return null;
|
||||
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
42
Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs
Normal file
42
Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System.Text;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeStubbedConfigFiles : ACompilationStep
|
||||
{
|
||||
public IncludeStubbedConfigFiles(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
return Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path)) ? RemapFile(source) : null;
|
||||
}
|
||||
|
||||
private Directive RemapFile(RawSourceFile source)
|
||||
{
|
||||
var data = File.ReadAllText(source.AbsolutePath);
|
||||
var originalData = data;
|
||||
|
||||
data = data.Replace(_compiler.GamePath, Consts.GAME_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_compiler.GamePath.Replace("\\", "\\\\"), Consts.GAME_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_compiler.GamePath.Replace("\\", "/"), Consts.GAME_PATH_MAGIC_FORWARD);
|
||||
|
||||
data = data.Replace(_compiler.MO2Folder, Consts.MO2_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_compiler.MO2Folder.Replace("\\", "\\\\"), Consts.MO2_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_compiler.MO2Folder.Replace("\\", "/"), Consts.MO2_PATH_MAGIC_FORWARD);
|
||||
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder.Replace("\\", "\\\\"), Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder.Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
|
||||
|
||||
if (data == originalData)
|
||||
return null;
|
||||
var result = source.EvolveTo<RemappedInlineFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(Encoding.UTF8.GetBytes(data));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
39
Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs
Normal file
39
Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class IncludeTaggedMods : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _includeDirectly;
|
||||
private string _tag;
|
||||
|
||||
|
||||
public IncludeTaggedMods(Compiler compiler, string tag) : base(compiler)
|
||||
{
|
||||
_tag = tag;
|
||||
_includeDirectly = _compiler.ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(_tag))
|
||||
return true;
|
||||
return general.comments != null && general.comments.Contains(_tag);
|
||||
}).Select(kv => $"mods\\{kv.Key}\\");
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.StartsWith("mods")) return null;
|
||||
foreach (var modpath in _includeDirectly)
|
||||
{
|
||||
if (!source.Path.StartsWith(modpath)) continue;
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
39
Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs
Normal file
39
Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeThisProfile : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _correctProfiles;
|
||||
|
||||
public IncludeThisProfile(Compiler compiler) : base(compiler)
|
||||
{
|
||||
_correctProfiles = _compiler.SelectedProfiles.Select(p => Path.Combine("profiles", p) + "\\").ToList();
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (_correctProfiles.Any(p => source.Path.StartsWith(p)))
|
||||
{
|
||||
var data = source.Path.EndsWith("\\modlist.txt") ? ReadAndCleanModlist(source.AbsolutePath) : File.ReadAllBytes(source.AbsolutePath);
|
||||
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = _compiler.IncludeFile(data);
|
||||
return e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private static byte[] ReadAndCleanModlist(string absolutePath)
|
||||
{
|
||||
var lines = File.ReadAllLines(absolutePath);
|
||||
lines = (from line in lines
|
||||
where !(line.StartsWith("-") && !line.EndsWith("_separator"))
|
||||
select line).ToArray();
|
||||
return Encoding.UTF8.GetBytes(string.Join("\r\n", lines));
|
||||
}
|
||||
}
|
||||
}
|
40
Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs
Normal file
40
Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.IO;
|
||||
using Wabbajack.Common;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
class PatchStockESMs : ACompilationStep
|
||||
{
|
||||
public PatchStockESMs(Compiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var filename = Path.GetFileName(source.Path);
|
||||
var gameFile = Path.Combine(_compiler.GamePath, "Data", filename);
|
||||
if (!Consts.GameESMs.Contains(filename) || !source.Path.StartsWith("mods\\") ||
|
||||
!File.Exists(gameFile)) return null;
|
||||
|
||||
Utils.Log(
|
||||
$"A ESM named {filename} was found in a mod that shares a name with a core game ESMs, it is assumed this is a cleaned ESM and it will be binary patched.");
|
||||
var result = source.EvolveTo<CleanedESM>();
|
||||
result.SourceESMHash = _compiler.VFS.Lookup(gameFile).Hash;
|
||||
|
||||
Utils.Status($"Generating patch of {filename}");
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Utils.CreatePatch(File.ReadAllBytes(gameFile), File.ReadAllBytes(source.AbsolutePath), ms);
|
||||
var data = ms.ToArray();
|
||||
result.SourceDataID = _compiler.IncludeFile(data);
|
||||
Utils.Log($"Generated a {data.Length} byte patch for {filename}");
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using CommonMark;
|
||||
using Compression.BSA;
|
||||
using System;
|
||||
using System.CodeDom;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -12,6 +13,7 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
@ -498,12 +500,12 @@ namespace Wabbajack.Lib
|
||||
}
|
||||
|
||||
|
||||
private Directive RunStack(IEnumerable<Func<RawSourceFile, Directive>> stack, RawSourceFile source)
|
||||
public static Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
||||
{
|
||||
Status($"Compiling {source.Path}");
|
||||
foreach (var f in stack)
|
||||
Utils.Status($"Compiling {source.Path}");
|
||||
foreach (var step in stack)
|
||||
{
|
||||
var result = f(source);
|
||||
var result = step.Run(source);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
@ -517,631 +519,65 @@ namespace Wabbajack.Lib
|
||||
/// result included into the pack
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<Func<RawSourceFile, Directive>> MakeStack()
|
||||
private IEnumerable<ICompilationStep> MakeStack()
|
||||
{
|
||||
Info("Generating compilation stack");
|
||||
return new List<Func<RawSourceFile, Directive>>
|
||||
return new List<ICompilationStep>
|
||||
{
|
||||
IncludePropertyFiles(),
|
||||
IgnoreStartsWith("logs\\"),
|
||||
IncludeRegex("^downloads\\\\.*\\.meta"),
|
||||
IgnoreStartsWith("downloads\\"),
|
||||
IgnoreStartsWith("webcache\\"),
|
||||
IgnoreStartsWith("overwrite\\"),
|
||||
IgnorePathContains("temporary_logs"),
|
||||
IgnorePathContains("GPUCache"),
|
||||
IgnorePathContains("SSEEdit Cache"),
|
||||
IgnoreEndsWith(".pyc"),
|
||||
IgnoreEndsWith(".log"),
|
||||
IgnoreOtherProfiles(),
|
||||
IgnoreDisabledMods(),
|
||||
IncludeThisProfile(),
|
||||
new IncludePropertyFiles(this),
|
||||
new IgnoreStartsWith(this,"logs\\"),
|
||||
new IncludeRegex(this, "^downloads\\\\.*\\.meta"),
|
||||
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),
|
||||
// Ignore the ModOrganizer.ini file it contains info created by MO2 on startup
|
||||
IncludeStubbedConfigFiles(),
|
||||
IncludeLootFiles(),
|
||||
IgnoreStartsWith(Path.Combine(Consts.GameFolderFilesDir, "Data")),
|
||||
IgnoreStartsWith(Path.Combine(Consts.GameFolderFilesDir, "Papyrus Compiler")),
|
||||
IgnoreStartsWith(Path.Combine(Consts.GameFolderFilesDir, "Skyrim")),
|
||||
IgnoreRegex(Consts.GameFolderFilesDir + "\\\\.*\\.bsa"),
|
||||
IncludeModIniData(),
|
||||
DirectMatch(),
|
||||
IncludeTaggedFiles(Consts.WABBAJACK_INCLUDE),
|
||||
DeconstructBSAs(), // Deconstruct BSAs before building patches so we don't generate massive patch files
|
||||
IncludePatches(),
|
||||
IncludeDummyESPs(),
|
||||
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),
|
||||
|
||||
|
||||
// If we have no match at this point for a game folder file, skip them, we can't do anything about them
|
||||
IgnoreGameFiles(),
|
||||
new IgnoreGameFiles(this),
|
||||
|
||||
// There are some types of files that will error the compilation, because they're created on-the-fly via tools
|
||||
// so if we don't have a match by this point, just drop them.
|
||||
IgnoreEndsWith(".ini"),
|
||||
IgnoreEndsWith(".html"),
|
||||
IgnoreEndsWith(".txt"),
|
||||
new IgnoreEndsWith(this, ".ini"),
|
||||
new IgnoreEndsWith(this, ".html"),
|
||||
new IgnoreEndsWith(this, ".txt"),
|
||||
// Don't know why, but this seems to get copied around a bit
|
||||
IgnoreEndsWith("HavokBehaviorPostProcess.exe"),
|
||||
new IgnoreEndsWith(this, "HavokBehaviorPostProcess.exe"),
|
||||
// Theme file MO2 downloads somehow
|
||||
IgnoreEndsWith("splash.png"),
|
||||
new IgnoreEndsWith(this, "splash.png"),
|
||||
|
||||
IgnoreEndsWith(".bin"),
|
||||
IgnoreEndsWith(".refcache"),
|
||||
new IgnoreEndsWith(this, ".bin"),
|
||||
new IgnoreEndsWith(this, ".refcache"),
|
||||
|
||||
IgnoreWabbajackInstallCruft(),
|
||||
new IgnoreWabbajackInstallCruft(this),
|
||||
|
||||
PatchStockESMs(),
|
||||
new PatchStockESMs(this),
|
||||
|
||||
IncludeAllConfigs(),
|
||||
IncludeTaggedFiles(Consts.WABBAJACK_NOMATCH_INCLUDE),
|
||||
zEditIntegration.IncludezEditPatches(this),
|
||||
new IncludeAllConfigs(this),
|
||||
new IncludeTaggedMods(this, Consts.WABBAJACK_NOMATCH_INCLUDE),
|
||||
new zEditIntegration.IncludeZEditPatches(this),
|
||||
|
||||
DropAll()
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludePropertyFiles()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
var files = new HashSet<string>
|
||||
{
|
||||
ModListImage, ModListReadme
|
||||
};
|
||||
if (!files.Any(f => source.AbsolutePath.Equals(f))) return null;
|
||||
if (!File.Exists(source.AbsolutePath)) return null;
|
||||
var isBanner = source.AbsolutePath == ModListImage;
|
||||
//var isReadme = source.AbsolutePath == ModListReadme;
|
||||
var result = source.EvolveTo<PropertyFile>();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
if (isBanner)
|
||||
{
|
||||
result.Type = PropertyType.Banner;
|
||||
ModListImage = result.SourceDataID;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Type = PropertyType.Readme;
|
||||
ModListReadme = result.SourceDataID;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreWabbajackInstallCruft()
|
||||
{
|
||||
var cruft_files = new HashSet<string>
|
||||
{
|
||||
"7z.dll", "7z.exe", "vfs_staged_files\\", "nexus.key_cache", "patch_cache\\",
|
||||
Consts.NexusCacheDirectory + "\\"
|
||||
};
|
||||
return source =>
|
||||
{
|
||||
if (!cruft_files.Any(f => source.Path.StartsWith(f))) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = "Wabbajack Cruft file";
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeAllConfigs()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
if (!Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path))) return null;
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> PatchStockESMs()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
var filename = Path.GetFileName(source.Path);
|
||||
var game_file = Path.Combine(GamePath, "Data", filename);
|
||||
if (Consts.GameESMs.Contains(filename) && source.Path.StartsWith("mods\\") && File.Exists(game_file))
|
||||
{
|
||||
Info(
|
||||
$"A ESM named {filename} was found in a mod that shares a name with a core game ESMs, it is assumed this is a cleaned ESM and it will be binary patched.");
|
||||
var result = source.EvolveTo<CleanedESM>();
|
||||
result.SourceESMHash = VFS.Lookup(game_file).Hash;
|
||||
|
||||
Status($"Generating patch of {filename}");
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Utils.CreatePatch(File.ReadAllBytes(game_file), File.ReadAllBytes(source.AbsolutePath), ms);
|
||||
var data = ms.ToArray();
|
||||
result.SourceDataID = IncludeFile(data);
|
||||
Info($"Generated a {data.Length} byte patch for {filename}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeLootFiles()
|
||||
{
|
||||
var prefix = Consts.LOOTFolderFilesDir + "\\";
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith(prefix))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath).ToBase64());
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeStubbedConfigFiles()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
if (Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path)))
|
||||
return RemapFile(source, GamePath);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Directive RemapFile(RawSourceFile source, string gamePath)
|
||||
{
|
||||
var data = File.ReadAllText(source.AbsolutePath);
|
||||
var original_data = data;
|
||||
|
||||
data = data.Replace(GamePath, Consts.GAME_PATH_MAGIC_BACK);
|
||||
data = data.Replace(GamePath.Replace("\\", "\\\\"), Consts.GAME_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(GamePath.Replace("\\", "/"), Consts.GAME_PATH_MAGIC_FORWARD);
|
||||
|
||||
data = data.Replace(MO2Folder, Consts.MO2_PATH_MAGIC_BACK);
|
||||
data = data.Replace(MO2Folder.Replace("\\", "\\\\"), Consts.MO2_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(MO2Folder.Replace("\\", "/"), Consts.MO2_PATH_MAGIC_FORWARD);
|
||||
|
||||
data = data.Replace(MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
|
||||
data = data.Replace(MO2DownloadsFolder.Replace("\\", "\\\\"), Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(MO2DownloadsFolder.Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
|
||||
|
||||
if (data == original_data)
|
||||
return null;
|
||||
var result = source.EvolveTo<RemappedInlineFile>();
|
||||
result.SourceDataID = IncludeFile(Encoding.UTF8.GetBytes(data));
|
||||
return result;
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnorePathContains(string v)
|
||||
{
|
||||
v = $"\\{v.Trim('\\')}\\";
|
||||
var reason = $"Ignored because path contains {v}";
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.Contains(v))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = reason;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If a user includes WABBAJACK_INCLUDE directly in the notes or comments of a mod, the contents of that
|
||||
/// mod will be inlined into the installer. USE WISELY.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Func<RawSourceFile, Directive> IncludeTaggedFiles(string tag)
|
||||
{
|
||||
var include_directly = ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(tag))
|
||||
return true;
|
||||
if (general.comments != null && general.comments.Contains(tag))
|
||||
return true;
|
||||
return false;
|
||||
}).Select(kv => $"mods\\{kv.Key}\\");
|
||||
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith("mods"))
|
||||
foreach (var modpath in include_directly)
|
||||
if (source.Path.StartsWith(modpath))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Some tools like the Cathedral Asset Optimizer will create dummy ESPs whos only existance is to make
|
||||
/// sure a BSA with the same name is loaded. We don't have a good way to detect these, but if an ESP is
|
||||
/// less than 100 bytes in size and shares a name with a BSA it's a pretty good chance that it's a dummy
|
||||
/// and the contents are generated.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Func<RawSourceFile, Directive> IncludeDummyESPs()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
if (Path.GetExtension(source.AbsolutePath) == ".esp" || Path.GetExtension(source.AbsolutePath) == ".esm")
|
||||
{
|
||||
var bsa = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
|
||||
Path.GetFileNameWithoutExtension(source.AbsolutePath) + ".bsa");
|
||||
var bsa_textures = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
|
||||
Path.GetFileNameWithoutExtension(source.AbsolutePath) + " - Textures.bsa");
|
||||
var esp_size = new FileInfo(source.AbsolutePath).Length;
|
||||
if (esp_size <= 250 && (File.Exists(bsa) || File.Exists(bsa_textures)))
|
||||
{
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This function will search for a way to create a BSA in the installed mod list by assembling it from files
|
||||
/// found in archives. To do this we hash all the files in side the BSA then try to find matches and patches for
|
||||
/// all of the files.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Func<RawSourceFile, Directive> DeconstructBSAs()
|
||||
{
|
||||
var include_directly = ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(Consts.WABBAJACK_INCLUDE))
|
||||
return true;
|
||||
if (general.comments != null && general.comments.Contains(Consts.WABBAJACK_INCLUDE))
|
||||
return true;
|
||||
return false;
|
||||
}).Select(kv => $"mods\\{kv.Key}\\");
|
||||
|
||||
var microstack = new List<Func<RawSourceFile, Directive>>
|
||||
{
|
||||
DirectMatch(),
|
||||
IncludePatches(),
|
||||
DropAll()
|
||||
};
|
||||
|
||||
var microstack_with_include = new List<Func<RawSourceFile, Directive>>
|
||||
{
|
||||
DirectMatch(),
|
||||
IncludePatches(),
|
||||
IncludeALL()
|
||||
};
|
||||
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (!Consts.SupportedBSAs.Contains(Path.GetExtension(source.Path).ToLower())) return null;
|
||||
|
||||
var default_include = false;
|
||||
if (source.Path.StartsWith("mods"))
|
||||
foreach (var modpath in include_directly)
|
||||
if (source.Path.StartsWith(modpath))
|
||||
{
|
||||
default_include = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var source_files = source.File.FileInArchive;
|
||||
|
||||
var stack = default_include ? microstack_with_include : microstack;
|
||||
|
||||
var id = Guid.NewGuid().ToString();
|
||||
|
||||
var matches = source_files.PMap(e => RunStack(stack, new RawSourceFile(e)
|
||||
{
|
||||
Path = Path.Combine(Consts.BSACreationDir, id, e.Paths.Last())
|
||||
}));
|
||||
|
||||
|
||||
foreach (var match in matches)
|
||||
{
|
||||
if (match is IgnoredDirectly) Error($"File required for BSA {source.Path} creation doesn't exist: {match.To}");
|
||||
ExtraFiles.Add(match);
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
CreateBSA directive;
|
||||
using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
||||
{
|
||||
directive = new CreateBSA
|
||||
{
|
||||
To = source.Path,
|
||||
TempID = id,
|
||||
State = bsa.State,
|
||||
FileStates = bsa.Files.Select(f => f.State).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
return directive;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeALL()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreDisabledMods()
|
||||
{
|
||||
var always_enabled = ModInis.Where(f => IsAlwaysEnabled(f.Value)).Select(f => f.Key).ToHashSet();
|
||||
|
||||
var all_enabled_mods = SelectedProfiles
|
||||
.SelectMany(p => File.ReadAllLines(Path.Combine(MO2Folder, "profiles", p, "modlist.txt")))
|
||||
.Where(line => line.StartsWith("+") || line.EndsWith("_separator"))
|
||||
.Select(line => line.Substring(1))
|
||||
.Concat(always_enabled)
|
||||
.Select(line => Path.Combine("mods", line) + "\\")
|
||||
.ToList();
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (!source.Path.StartsWith("mods") || all_enabled_mods.Any(mod => source.Path.StartsWith(mod)))
|
||||
return null;
|
||||
var r = source.EvolveTo<IgnoredDirectly>();
|
||||
r.Reason = "Disabled Mod";
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsAlwaysEnabled(dynamic data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
if (data.General != null && data.General.notes != null &&
|
||||
data.General.notes.Contains(Consts.WABBAJACK_ALWAYS_ENABLE))
|
||||
return true;
|
||||
if (data.General != null && data.General.comments != null &&
|
||||
data.General.notes.Contains(Consts.WABBAJACK_ALWAYS_ENABLE))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This matches files based purely on filename, and then creates a binary patch.
|
||||
/// In practice this is fine, because a single file tends to only come from one archive.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Func<RawSourceFile, Directive> IncludePatches()
|
||||
{
|
||||
var indexed = IndexedFiles.Values
|
||||
.SelectMany(f => f)
|
||||
.GroupBy(f => Path.GetFileName(f.Paths.Last()).ToLower())
|
||||
.ToDictionary(f => f.Key);
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (!indexed.TryGetValue(Path.GetFileName(source.File.Paths.Last().ToLower()), out var value))
|
||||
return null;
|
||||
|
||||
var found = value.OrderByDescending(f => (f.TopLevelArchive ?? f).LastModified).First();
|
||||
|
||||
var e = source.EvolveTo<PatchedFromArchive>();
|
||||
e.ArchiveHashPath = found.MakeRelativePaths();
|
||||
e.To = source.Path;
|
||||
e.Hash = source.File.Hash;
|
||||
|
||||
Utils.TryGetPatch(found.Hash, source.File.Hash, out var data);
|
||||
|
||||
if (data != null)
|
||||
e.PatchID = IncludeFile(data);
|
||||
|
||||
return e;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeModIniData()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith("mods\\") && source.Path.EndsWith("\\meta.ini"))
|
||||
{
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return e;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreGameFiles()
|
||||
{
|
||||
var start_dir = Consts.GameFolderFilesDir + "\\";
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith(start_dir))
|
||||
{
|
||||
var i = source.EvolveTo<IgnoredDirectly>();
|
||||
i.Reason = "Default game file";
|
||||
return i;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeThisProfile()
|
||||
{
|
||||
var correct_profiles = SelectedProfiles.Select(p => Path.Combine("profiles", p) + "\\").ToList();
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (correct_profiles.Any(p => source.Path.StartsWith(p)))
|
||||
{
|
||||
byte[] data;
|
||||
if (source.Path.EndsWith("\\modlist.txt"))
|
||||
data = ReadAndCleanModlist(source.AbsolutePath);
|
||||
else
|
||||
data = File.ReadAllBytes(source.AbsolutePath);
|
||||
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceDataID = IncludeFile(data);
|
||||
return e;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] ReadAndCleanModlist(string absolutePath)
|
||||
{
|
||||
var lines = File.ReadAllLines(absolutePath);
|
||||
lines = (from line in lines
|
||||
where !(line.StartsWith("-") && !line.EndsWith("_separator"))
|
||||
select line).ToArray();
|
||||
return Encoding.UTF8.GetBytes(string.Join("\r\n", lines));
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreOtherProfiles()
|
||||
{
|
||||
var profiles = SelectedProfiles
|
||||
.Select(p => Path.Combine("profiles", p) + "\\")
|
||||
.ToList();
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith("profiles\\"))
|
||||
{
|
||||
if (profiles.Any(profile => source.Path.StartsWith(profile))) return null;
|
||||
var c = source.EvolveTo<IgnoredDirectly>();
|
||||
c.Reason = "File not for selected profiles";
|
||||
return c;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreEndsWith(string v)
|
||||
{
|
||||
var reason = string.Format("Ignored because path ends with {0}", v);
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.EndsWith(v))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = reason;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreRegex(string p)
|
||||
{
|
||||
var reason = string.Format("Ignored because path matches regex {0}", p);
|
||||
var regex = new Regex(p);
|
||||
return source =>
|
||||
{
|
||||
if (regex.IsMatch(source.Path))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = reason;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private Func<RawSourceFile, Directive> IncludeRegex(string pattern)
|
||||
{
|
||||
var regex = new Regex(pattern);
|
||||
return source =>
|
||||
{
|
||||
if (regex.IsMatch(source.Path))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private Func<RawSourceFile, Directive> DropAll()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
var result = source.EvolveTo<NoMatch>();
|
||||
result.Reason = "No Match in Stack";
|
||||
Info($"No match for: {source.Path}");
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> DirectMatch()
|
||||
{
|
||||
return source =>
|
||||
{
|
||||
if (IndexedFiles.TryGetValue(source.Hash, out var found))
|
||||
{
|
||||
var result = source.EvolveTo<FromArchive>();
|
||||
|
||||
var match = found.Where(f =>
|
||||
Path.GetFileName(f.Paths[f.Paths.Length - 1]) == Path.GetFileName(source.Path))
|
||||
.OrderBy(f => f.Paths.Length)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (match == null)
|
||||
match = found.OrderBy(f => f.Paths.Length).FirstOrDefault();
|
||||
|
||||
result.ArchiveHashPath = match.MakeRelativePaths();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Func<RawSourceFile, Directive> IgnoreStartsWith(string v)
|
||||
{
|
||||
var reason = string.Format("Ignored because path starts with {0}", v);
|
||||
return source =>
|
||||
{
|
||||
if (source.Path.StartsWith(v))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = reason;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
new DropAll(this)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,31 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CerasConfig.cs" />
|
||||
<Compile Include="CompilationSteps\ACompilationStep.cs" />
|
||||
<Compile Include="CompilationSteps\DeconstructBSAs.cs" />
|
||||
<Compile Include="CompilationSteps\DirectMatch.cs" />
|
||||
<Compile Include="CompilationSteps\DropAll.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreDisabledMods.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreEndsWith.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreGameFiles.cs" />
|
||||
<Compile Include="CompilationSteps\IgnorePathContains.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreRegex.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreStartsWith.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreWabbajackInstallCruft.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeAll.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeAllConfigs.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeDummyESPs.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeLootFiles.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeModIniData.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeOtherProfiles.cs" />
|
||||
<Compile Include="CompilationSteps\IncludePatches.cs" />
|
||||
<Compile Include="CompilationSteps\IncludePropertyFiles.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeRegex.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeStubbedConfigFiles.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeTaggedMods.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeThisProfile.cs" />
|
||||
<Compile Include="CompilationSteps\IStackStep.cs" />
|
||||
<Compile Include="CompilationSteps\PatchStockESMs.cs" />
|
||||
<Compile Include="Compiler.cs" />
|
||||
<Compile Include="Data.cs" />
|
||||
<Compile Include="Downloaders\AbstractDownloadState.cs" />
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
@ -29,14 +30,22 @@ namespace Wabbajack.Lib
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Func<RawSourceFile, Directive> IncludezEditPatches(Compiler compiler)
|
||||
public class IncludeZEditPatches : ACompilationStep
|
||||
{
|
||||
private Dictionary<string, zEditMerge> _mergesIndexed;
|
||||
|
||||
public IncludeZEditPatches(Compiler compiler) : base(compiler)
|
||||
{
|
||||
var zEditPath = FindzEditPath(compiler);
|
||||
var havezEdit = zEditPath != null;
|
||||
|
||||
Utils.Log(havezEdit ? $"Found zEdit at {zEditPath}" : $"zEdit not detected, disabling zEdit routines");
|
||||
|
||||
if (!havezEdit) return source => { return null; };
|
||||
if (!havezEdit)
|
||||
{
|
||||
_mergesIndexed = new Dictionary<string, zEditMerge>();
|
||||
return;
|
||||
}
|
||||
|
||||
var merges = Directory.EnumerateFiles(Path.Combine(zEditPath, "profiles"),
|
||||
DirectoryEnumerationOptions.Files | DirectoryEnumerationOptions.Recursive)
|
||||
@ -51,17 +60,15 @@ namespace Wabbajack.Lib
|
||||
$"WARNING, you have two patches named {m.Key.name}\\{m.Key.filename} in your zEdit profiles. We'll pick one at random, this probably isn't what you want.");
|
||||
});
|
||||
|
||||
var mergesIndexed =
|
||||
_mergesIndexed =
|
||||
merges.ToDictionary(
|
||||
m => Path.Combine(compiler.MO2Folder, "mods", m.Key.name, m.Key.filename),
|
||||
m => m.First());
|
||||
}
|
||||
|
||||
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (mergesIndexed.TryGetValue(source.AbsolutePath, out var merge))
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!_mergesIndexed.TryGetValue(source.AbsolutePath, out var merge)) return null;
|
||||
var result = source.EvolveTo<MergedPatch>();
|
||||
result.Sources = merge.plugins.Select(f =>
|
||||
{
|
||||
@ -72,8 +79,8 @@ namespace Wabbajack.Lib
|
||||
|
||||
return new SourcePatch
|
||||
{
|
||||
RelativePath = abs_path.RelativeTo(compiler.MO2Folder),
|
||||
Hash = compiler.VFS[abs_path].Hash
|
||||
RelativePath = abs_path.RelativeTo(_compiler.MO2Folder),
|
||||
Hash = _compiler.VFS[abs_path].Hash
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
@ -85,19 +92,14 @@ namespace Wabbajack.Lib
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Utils.CreatePatch(src_data, dst_data, ms);
|
||||
result.PatchID = compiler.IncludeFile(ms.ToArray());
|
||||
result.PatchID = _compiler.IncludeFile(ms.ToArray());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class zEditMerge
|
||||
{
|
||||
public string name;
|
||||
|
Loading…
Reference in New Issue
Block a user