add support for ESM cleaning

This commit is contained in:
Timothy Baldridge 2019-08-24 21:46:32 -06:00
parent 40950b442c
commit bf181cdc2d
4 changed files with 84 additions and 20 deletions

View File

@ -43,5 +43,7 @@ namespace Wabbajack.Common
public static String AppName = "Wabbajack";
public static string HashCacheName = "Wabbajack.hash_cache";
public static HashSet<string> GameESMs = new HashSet<string>() { "Skyrim.esm", "Update.esm", "Dawnguard.esm", "HearthFires.esm", "Dragonborn.esm" };
}
}

View File

@ -205,6 +205,7 @@ namespace Wabbajack
var stack = MakeStack();
Info("Running Compilation Stack");
var results = AllFiles.PMap(f => RunStack(stack, f)).ToList();
@ -363,26 +364,7 @@ namespace Wabbajack
Archive result;
if (general.modID != null && general.fileID != null && general.gameName != null)
{
result = new NexusMod()
{
GameName = general.gameName,
FileID = general.fileID,
ModID = general.modID
};
Status($"Getting Nexus info for {found.Name}");
try
{
var link = NexusAPI.GetNexusDownloadLink((NexusMod)result, NexusKey, true);
}
catch (Exception ex)
{
Error($"Unable to resolve {found.Name} on the Nexus was the file removed?");
}
}
else if (general.directURL != null && general.directURL.StartsWith("https://drive.google.com"))
if (general.directURL != null && general.directURL.StartsWith("https://drive.google.com"))
{
var regex = new Regex("((?<=id=)[a-zA-Z0-9_-]*)|(?<=\\/file\\/d\\/)[a-zA-Z0-9_-]*");
var match = regex.Match(general.directURL);
@ -452,6 +434,25 @@ namespace Wabbajack
URL = general.manualURL.ToString()
};
}
else if (general.modID != null && general.fileID != null && general.gameName != null)
{
result = new NexusMod()
{
GameName = general.gameName,
FileID = general.fileID,
ModID = general.modID
};
Status($"Getting Nexus info for {found.Name}");
try
{
var link = NexusAPI.GetNexusDownloadLink((NexusMod)result, NexusKey, true);
}
catch (Exception ex)
{
Error($"Unable to resolve {found.Name} on the Nexus was the file removed?");
}
}
else
{
Error("No way to handle archive {0} but it's required by the modpack", found.Name);
@ -532,10 +533,40 @@ namespace Wabbajack
IgnoreEndsWith("HavokBehaviorPostProcess.exe"),
// Theme file MO2 downloads somehow
IgnoreEndsWith("splash.png"),
PatchStockESMs(),
DropAll()
};
}
private Func<RawSourceFile, Directive> PatchStockESMs()
{
return source =>
{
string filename = Path.GetFileName(source.Path);
string 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()) {
BSDiff.Create(File.ReadAllBytes(game_file), File.ReadAllBytes(source.AbsolutePath), ms);
result.SourceData = ms.ToArray().ToBase64();
}
Info($"Generated a {result.SourceData.Length} byte patch for {filename}");
return result;
}
return null;
};
}
private Func<RawSourceFile, Directive> IncludeLootFiles()
{
var prefix = Consts.LOOTFolderFilesDir + "\\";

View File

@ -96,6 +96,11 @@ namespace Wabbajack
public string SourceData;
}
public class CleanedESM : InlineFile
{
public string SourceESMHash;
}
/// <summary>
/// A file that has the game and MO2 folders remapped on installation
/// </summary>

View File

@ -217,6 +217,10 @@ namespace Wabbajack
{
WriteRemappedFile((RemappedInlineFile)directive);
}
else if (directive is CleanedESM)
{
GenerateCleanedESM((CleanedESM)directive);
}
else
{
File.WriteAllBytes(out_path, directive.SourceData.FromBase64());
@ -224,6 +228,28 @@ namespace Wabbajack
});
}
private void GenerateCleanedESM(CleanedESM directive)
{
var filename = Path.GetFileName(directive.To);
var game_file = Path.Combine(GameFolder, "Data", filename);
Info($"Generating cleaned ESM for {filename}");
if (!File.Exists(game_file))
{
throw new InvalidDataException($"Missing {filename} at {game_file}");
}
Status($"Hashing game version of {filename}");
var sha = Utils.FileSHA256(game_file);
if (sha != directive.SourceESMHash)
throw new InvalidDataException($"Cannot patch {filename} from the game folder hashes don't match have you already cleaned the file?");
var patch_data = directive.SourceData.FromBase64();
var to_file = Path.Combine(Outputfolder, directive.To);
Status($"Patching {filename}");
using (var output = File.OpenWrite(to_file)) {
BSDiff.Apply(File.OpenRead(game_file), () => new MemoryStream(patch_data), output);
}
}
private void WriteRemappedFile(RemappedInlineFile directive)
{
var data = Encoding.UTF8.GetString(directive.SourceData.FromBase64());