mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
modlists are now .zip files, several bug fixes found in testing
This commit is contained in:
parent
eb827f5909
commit
4597135de5
@ -2,6 +2,9 @@
|
||||
|
||||
#### Version 0.9.4 - ???
|
||||
* Point github icon to https://github.com/wabbajack-tools/wabbajack
|
||||
* Add game registry entry for Skyrim VR
|
||||
* Modlists are now .zip files.
|
||||
* Modlists now end with `.modlist_v1` to enable better version control
|
||||
|
||||
#### Version 0.9.3 - 9/30/2019
|
||||
* Add WABBAJACK_NOMATCH_INCLUDE works like WABBAJACK_INCLUDE but only includes files that are found to be missing at the end of compilation
|
||||
|
@ -7,6 +7,7 @@ namespace Wabbajack.Common
|
||||
{
|
||||
public static class Consts
|
||||
{
|
||||
public static string ModlistExtension = ".modlist_v1";
|
||||
public static string GameFolderFilesDir = "Game Folder Files";
|
||||
public static string LOOTFolderFilesDir = "LOOT Config Files";
|
||||
public static string ModListMagic = "Celebration!, Cheese for Everyone!";
|
||||
|
@ -14,7 +14,8 @@ namespace Wabbajack.Common
|
||||
FalloutNewVegas,
|
||||
Skyrim,
|
||||
SkyrimSpecialEdition,
|
||||
Fallout4
|
||||
Fallout4,
|
||||
SkyrimVR
|
||||
}
|
||||
|
||||
public class GameMetaData
|
||||
@ -102,6 +103,16 @@ namespace Wabbajack.Common
|
||||
MO2ArchiveName = "fallout4",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout4"
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.SkyrimVR, new GameMetaData
|
||||
{
|
||||
Game = Game.SkyrimVR,
|
||||
NexusName = "skyrimspecialedition",
|
||||
MO2Name = "Skyrim VR",
|
||||
MO2ArchiveName = "skyrimse",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim VR"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -202,7 +202,8 @@ namespace Wabbajack.Common
|
||||
public static T FromJSON<T>(this Stream data)
|
||||
{
|
||||
var s = Encoding.UTF8.GetString(data.ReadAll());
|
||||
return JsonConvert.DeserializeObject<T>(s);
|
||||
return JsonConvert.DeserializeObject<T>(s,
|
||||
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
|
||||
}
|
||||
|
||||
public static bool FileExists(this string filename)
|
||||
@ -490,6 +491,12 @@ namespace Wabbajack.Common
|
||||
return dict.TryGetValue(key, out var result) ? result : default;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ButLast<T>(this IEnumerable<T> coll)
|
||||
{
|
||||
var lst = coll.ToList();
|
||||
return lst.Take(lst.Count() - 1);
|
||||
}
|
||||
|
||||
public static byte[] ConcatArrays(this IEnumerable<byte[]> arrays)
|
||||
{
|
||||
var outarr = new byte[arrays.Sum(a => a.Length)];
|
||||
|
@ -100,7 +100,6 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="7z.dll.gz" />
|
||||
<EmbeddedResource Include="7z.exe.gz" />
|
||||
<None Include="innounp.exe.gz" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -116,7 +115,6 @@
|
||||
<ItemGroup>
|
||||
<Content Include="7z.dll" />
|
||||
<Content Include="7z.exe" />
|
||||
<None Include="innounp.exe" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
Binary file not shown.
Binary file not shown.
@ -160,7 +160,7 @@ namespace Wabbajack.Test
|
||||
// Error due to patched file
|
||||
modlist.Directives[0] = new PatchedFromArchive
|
||||
{
|
||||
Patch = new byte[]{0, 1, 3},
|
||||
PatchID = Guid.NewGuid().ToString(),
|
||||
ArchiveHashPath = new[] {"DEADBEEF", "foo\\bar\\baz.pex"},
|
||||
};
|
||||
|
||||
@ -176,7 +176,15 @@ namespace Wabbajack.Test
|
||||
errors = validate.Validate(modlist);
|
||||
Assert.AreEqual(errors.Count(), 1);
|
||||
|
||||
// No error since we're just installing the .bsa, not extracting it
|
||||
modlist.Directives[0] = new FromArchive
|
||||
{
|
||||
ArchiveHashPath = new[] { "DEADBEEF", "foo.bsa"},
|
||||
};
|
||||
|
||||
errors = validate.Validate(modlist);
|
||||
Assert.AreEqual(0, errors.Count());
|
||||
|
||||
// Error due to game conversion
|
||||
modlist.GameType = Game.SkyrimSpecialEdition;
|
||||
modlist.Directives[0] = new FromArchive
|
||||
|
@ -103,7 +103,8 @@ namespace Wabbajack.Test
|
||||
|
||||
private void Install(Compiler compiler)
|
||||
{
|
||||
var installer = new Installer(compiler.ModList, utils.InstallFolder);
|
||||
var modlist = Installer.LoadFromFile(compiler.ModListOutputFile);
|
||||
var installer = new Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder);
|
||||
installer.DownloadFolder = utils.DownloadsFolder;
|
||||
installer.GameFolder = utils.GameFolder;
|
||||
installer.Install();
|
||||
|
@ -280,6 +280,8 @@ namespace Wabbajack
|
||||
}
|
||||
|
||||
public string _splashScreenSummary = "";
|
||||
private string _modListPath;
|
||||
|
||||
public string SplashScreenSummary
|
||||
{
|
||||
get => _splashScreenSummary;
|
||||
@ -379,9 +381,10 @@ namespace Wabbajack
|
||||
|
||||
public bool Running { get; set; } = true;
|
||||
|
||||
internal void ConfigureForInstall(ModList modlist)
|
||||
internal void ConfigureForInstall(string source, ModList modlist)
|
||||
{
|
||||
_modList = modlist;
|
||||
_modListPath = source;
|
||||
Mode = "Installing";
|
||||
ModListName = _modList.Name;
|
||||
HTMLReport = _modList.ReportHTML;
|
||||
@ -481,7 +484,7 @@ namespace Wabbajack
|
||||
UIReady = false;
|
||||
if (Mode == "Installing")
|
||||
{
|
||||
var installer = new Installer(_modList, Location);
|
||||
var installer = new Installer(_modListPath, _modList, Location);
|
||||
|
||||
installer.DownloadFolder = DownloadLocation;
|
||||
var th = new Thread(() =>
|
||||
|
@ -14,6 +14,7 @@ using Compression.BSA;
|
||||
using K4os.Compression.LZ4;
|
||||
using K4os.Compression.LZ4.Streams;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO.Compression;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.NexusApi;
|
||||
@ -67,6 +68,9 @@ namespace Wabbajack
|
||||
|
||||
public string MO2ProfileDir => Path.Combine(MO2Folder, "profiles", MO2Profile);
|
||||
|
||||
public string ModListOutputFolder => "output_folder";
|
||||
public string ModListOutputFile => MO2Profile + Consts.ModlistExtension;
|
||||
|
||||
public List<Directive> InstallDirectives { get; private set; }
|
||||
internal UserStatus User { get; private set; }
|
||||
public List<Archive> SelectedArchives { get; private set; }
|
||||
@ -98,6 +102,20 @@ namespace Wabbajack
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
internal string IncludeFile(byte[] data)
|
||||
{
|
||||
var id = Guid.NewGuid().ToString();
|
||||
File.WriteAllBytes(Path.Combine(ModListOutputFolder, id), data);
|
||||
return id;
|
||||
}
|
||||
|
||||
internal string IncludeFile(string data)
|
||||
{
|
||||
var id = Guid.NewGuid().ToString();
|
||||
File.WriteAllText(Path.Combine(ModListOutputFolder, id), data);
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool Compile()
|
||||
{
|
||||
VirtualFileSystem.Clean();
|
||||
@ -117,6 +135,12 @@ namespace Wabbajack
|
||||
Info($"Indexing {MO2DownloadsFolder}");
|
||||
VFS.AddRoot(MO2DownloadsFolder);
|
||||
|
||||
Info("Cleaning output folder");
|
||||
if (Directory.Exists(ModListOutputFolder))
|
||||
Directory.Delete(ModListOutputFolder, true);
|
||||
|
||||
Directory.CreateDirectory(ModListOutputFolder);
|
||||
|
||||
var mo2_files = Directory.EnumerateFiles(MO2Folder, "*", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists())
|
||||
.Select(p => new RawSourceFile(VFS.Lookup(p)) {Path = p.RelativeTo(MO2Folder)});
|
||||
@ -274,20 +298,35 @@ namespace Wabbajack
|
||||
|
||||
private void ExportModlist()
|
||||
{
|
||||
var out_path = MO2Profile + ".modlist";
|
||||
Utils.Log($"Exporting Modlist to : {ModListOutputFile}");
|
||||
|
||||
Utils.Log($"Exporting Modlist to : {out_path}");
|
||||
ModList.ToJSON(Path.Combine(ModListOutputFolder, "modlist.json"));
|
||||
|
||||
using (var os = File.OpenWrite(out_path))
|
||||
using (var bw = new BinaryWriter(os))
|
||||
if (File.Exists(ModListOutputFile))
|
||||
File.Delete(ModListOutputFile);
|
||||
|
||||
using (var fs = new FileStream(ModListOutputFile, FileMode.Create))
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
using (var compressed = LZ4Stream.Encode(bw.BaseStream,
|
||||
new LZ4EncoderSettings { CompressionLevel = LZ4Level.L10_OPT }, true))
|
||||
using (var za = new ZipArchive(fs, ZipArchiveMode.Create))
|
||||
{
|
||||
formatter.Serialize(compressed, ModList);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Utils.Log("Removing modlist staging folder");
|
||||
Directory.Delete(ModListOutputFolder, true);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void ShowReport()
|
||||
@ -313,7 +352,7 @@ namespace Wabbajack
|
||||
using (var fs = File.OpenWrite($"{ModList.Name}.md"))
|
||||
{
|
||||
fs.SetLength(0);
|
||||
using (var reporter = new ReportBuilder(fs))
|
||||
using (var reporter = new ReportBuilder(fs, ModListOutputFolder))
|
||||
{
|
||||
reporter.Build(ModList);
|
||||
}
|
||||
@ -342,7 +381,7 @@ namespace Wabbajack
|
||||
{
|
||||
Info("Gathering patch files");
|
||||
var groups = InstallDirectives.OfType<PatchedFromArchive>()
|
||||
.Where(p => p.Patch == null)
|
||||
.Where(p => p.PatchID == null)
|
||||
.GroupBy(p => p.ArchiveHashPath[0])
|
||||
.ToList();
|
||||
|
||||
@ -350,7 +389,7 @@ namespace Wabbajack
|
||||
var absolute_paths = AllFiles.ToDictionary(e => e.Path, e => e.AbsolutePath);
|
||||
groups.PMap(group => BuildArchivePatches(group.Key, group, absolute_paths));
|
||||
|
||||
if (InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.Patch == null) != null)
|
||||
if (InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.PatchID == null) != null)
|
||||
Error("Missing patches after generation, this should not happen");
|
||||
}
|
||||
|
||||
@ -372,8 +411,9 @@ namespace Wabbajack
|
||||
var a = origin.ReadAll();
|
||||
var b = LoadDataForTo(entry.To, absolute_paths);
|
||||
Utils.CreatePatch(a, b, output);
|
||||
entry.Patch = output.ToArray();
|
||||
Info($"Patch size {entry.Patch.Length} for {entry.To}");
|
||||
entry.PatchID = IncludeFile(output.ToArray());
|
||||
var file_size = File.GetSize(Path.Combine(ModListOutputFolder, entry.PatchID));
|
||||
Info($"Patch size {file_size} for {entry.To}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -534,7 +574,7 @@ namespace Wabbajack
|
||||
|
||||
Info($"Checking link for {found.Name}");
|
||||
|
||||
var installer = new Installer(null, "");
|
||||
var installer = new Installer("", null, "");
|
||||
|
||||
if (!installer.DownloadArchive(result, false))
|
||||
Error(
|
||||
@ -650,7 +690,7 @@ namespace Wabbajack
|
||||
{
|
||||
if (!Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path))) return null;
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
@ -672,10 +712,12 @@ namespace Wabbajack
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Utils.CreatePatch(File.ReadAllBytes(game_file), File.ReadAllBytes(source.AbsolutePath), ms);
|
||||
result.SourceData = ms.ToArray().ToBase64();
|
||||
var data = ms.ToArray().ToBase64();
|
||||
result.SourceDataID = IncludeFile(data);
|
||||
Info($"Generated a {data.Length} byte patch for {filename}");
|
||||
|
||||
}
|
||||
|
||||
Info($"Generated a {result.SourceData.Length} byte patch for {filename}");
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -693,7 +735,7 @@ namespace Wabbajack
|
||||
if (source.Path.StartsWith(prefix))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath).ToBase64());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -731,7 +773,7 @@ namespace Wabbajack
|
||||
if (data == original_data)
|
||||
return null;
|
||||
var result = source.EvolveTo<RemappedInlineFile>();
|
||||
result.SourceData = Encoding.UTF8.GetBytes(data).ToBase64();
|
||||
result.SourceDataID = IncludeFile(Encoding.UTF8.GetBytes(data));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -778,7 +820,7 @@ namespace Wabbajack
|
||||
if (source.Path.StartsWith(modpath))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -808,7 +850,7 @@ namespace Wabbajack
|
||||
if (esp_size <= 250 && (File.Exists(bsa) || File.Exists(bsa_textures)))
|
||||
{
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
inline.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
}
|
||||
}
|
||||
@ -908,7 +950,7 @@ namespace Wabbajack
|
||||
return source =>
|
||||
{
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
inline.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
};
|
||||
}
|
||||
@ -971,7 +1013,12 @@ namespace Wabbajack
|
||||
e.ArchiveHashPath = found.MakeRelativePaths();
|
||||
e.To = source.Path;
|
||||
e.Hash = source.File.Hash;
|
||||
Utils.TryGetPatch(found.Hash, source.File.Hash, out e.Patch);
|
||||
|
||||
Utils.TryGetPatch(found.Hash, source.File.Hash, out var data);
|
||||
|
||||
if (data != null)
|
||||
e.PatchID = IncludeFile(data);
|
||||
|
||||
return e;
|
||||
};
|
||||
}
|
||||
@ -983,7 +1030,7 @@ namespace Wabbajack
|
||||
if (source.Path.StartsWith("mods\\") && source.Path.EndsWith("\\meta.ini"))
|
||||
{
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
e.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -1022,7 +1069,7 @@ namespace Wabbajack
|
||||
data = File.ReadAllBytes(source.AbsolutePath);
|
||||
|
||||
var e = source.EvolveTo<InlineFile>();
|
||||
e.SourceData = data.ToBase64();
|
||||
e.SourceDataID = IncludeFile(data);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -1101,7 +1148,7 @@ namespace Wabbajack
|
||||
if (regex.IsMatch(source.Path))
|
||||
{
|
||||
var result = source.EvolveTo<InlineFile>();
|
||||
result.SourceData = File.ReadAllBytes(source.AbsolutePath).ToBase64();
|
||||
result.SourceDataID = IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1162,32 +1209,6 @@ namespace Wabbajack
|
||||
};
|
||||
}
|
||||
|
||||
internal void PatchExecutable()
|
||||
{
|
||||
Utils.Log("Exporting Installer");
|
||||
var settings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto};
|
||||
var executable = Assembly.GetExecutingAssembly().Location;
|
||||
var out_path = Path.Combine(Path.GetDirectoryName(executable), MO2Profile + ".exe");
|
||||
Info($"Patching Executable {Path.GetFileName(out_path)}");
|
||||
File.Copy(executable, out_path, true);
|
||||
using (var os = File.OpenWrite(out_path))
|
||||
using (var bw = new BinaryWriter(os))
|
||||
{
|
||||
var orig_pos = os.Length;
|
||||
os.Position = os.Length;
|
||||
var formatter = new BinaryFormatter();
|
||||
|
||||
using (var compressed = LZ4Stream.Encode(bw.BaseStream,
|
||||
new LZ4EncoderSettings {CompressionLevel = LZ4Level.L10_OPT}, true))
|
||||
{
|
||||
formatter.Serialize(compressed, ModList);
|
||||
}
|
||||
|
||||
bw.Write(orig_pos);
|
||||
bw.Write(Encoding.ASCII.GetBytes(Consts.ModListMagic));
|
||||
}
|
||||
}
|
||||
|
||||
public class IndexedFileMatch
|
||||
{
|
||||
public IndexedArchive Archive;
|
||||
|
@ -88,7 +88,7 @@ namespace Wabbajack
|
||||
/// <summary>
|
||||
/// Data that will be written as-is to the destination location;
|
||||
/// </summary>
|
||||
public string SourceData;
|
||||
public string SourceDataID;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@ -150,7 +150,7 @@ namespace Wabbajack
|
||||
/// <summary>
|
||||
/// The file to apply to the source file to patch it
|
||||
/// </summary>
|
||||
public byte[] Patch;
|
||||
public String PatchID;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@ -165,7 +165,7 @@ namespace Wabbajack
|
||||
{
|
||||
public List<SourcePatch> Sources;
|
||||
public string Hash;
|
||||
public byte[] Patch;
|
||||
public string PatchID;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
@ -12,6 +12,7 @@ using System.Windows;
|
||||
using CG.Web.MegaApiClient;
|
||||
using Compression.BSA;
|
||||
using K4os.Compression.LZ4.Streams;
|
||||
using System.IO.Compression;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.NexusApi;
|
||||
@ -27,8 +28,9 @@ namespace Wabbajack
|
||||
{
|
||||
private string _downloadsFolder;
|
||||
|
||||
public Installer(ModList mod_list, string output_folder)
|
||||
public Installer(string archive, ModList mod_list, string output_folder)
|
||||
{
|
||||
ModListArchive = archive;
|
||||
Outputfolder = output_folder;
|
||||
ModList = mod_list;
|
||||
}
|
||||
@ -43,6 +45,7 @@ namespace Wabbajack
|
||||
set => _downloadsFolder = value;
|
||||
}
|
||||
|
||||
public string ModListArchive { get; }
|
||||
public ModList ModList { get; }
|
||||
public Dictionary<string, string> HashedArchives { get; private set; }
|
||||
|
||||
@ -70,6 +73,31 @@ namespace Wabbajack
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
private byte[] LoadBytesFromPath(string path)
|
||||
{
|
||||
using (var fs = new FileStream(ModListArchive, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var ar = new ZipArchive(fs,ZipArchiveMode.Read))
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var entry = ar.GetEntry(path);
|
||||
using (var e = entry.Open())
|
||||
e.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static ModList LoadFromFile(string path)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var entry = ar.GetEntry("modlist.json");
|
||||
using (var e = entry.Open())
|
||||
return e.FromJSON<ModList>();
|
||||
}
|
||||
}
|
||||
|
||||
public void Install()
|
||||
{
|
||||
|
||||
@ -268,7 +296,7 @@ namespace Wabbajack
|
||||
else if (directive is CleanedESM)
|
||||
GenerateCleanedESM((CleanedESM) directive);
|
||||
else
|
||||
File.WriteAllBytes(out_path, directive.SourceData.FromBase64());
|
||||
File.WriteAllBytes(out_path, LoadBytesFromPath(directive.SourceDataID));
|
||||
});
|
||||
}
|
||||
|
||||
@ -284,7 +312,7 @@ namespace Wabbajack
|
||||
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 patch_data = LoadBytesFromPath(directive.SourceDataID);
|
||||
var to_file = Path.Combine(Outputfolder, directive.To);
|
||||
Status($"Patching {filename}");
|
||||
using (var output = File.OpenWrite(to_file))
|
||||
@ -295,7 +323,7 @@ namespace Wabbajack
|
||||
|
||||
private void WriteRemappedFile(RemappedInlineFile directive)
|
||||
{
|
||||
var data = Encoding.UTF8.GetString(directive.SourceData.FromBase64());
|
||||
var data = Encoding.UTF8.GetString(LoadBytesFromPath(directive.SourceDataID));
|
||||
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_BACK, GameFolder);
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_DOUBLE_BACK, GameFolder.Replace("\\", "\\\\"));
|
||||
@ -377,7 +405,7 @@ namespace Wabbajack
|
||||
Status($"Patching {Path.GetFileName(to_patch.To)}");
|
||||
// Read in the patch data
|
||||
|
||||
var patch_data = to_patch.Patch;
|
||||
var patch_data = LoadBytesFromPath(to_patch.PatchID);
|
||||
|
||||
var to_file = Path.Combine(Outputfolder, to_patch.To);
|
||||
var old_data = new MemoryStream(File.ReadAllBytes(to_file));
|
||||
@ -629,31 +657,5 @@ namespace Wabbajack
|
||||
File.WriteAllText(cache, e.FileSHA256());
|
||||
return HashArchive(e);
|
||||
}
|
||||
|
||||
public static ModList LoadModlist(string file)
|
||||
{
|
||||
Utils.Log("Reading Modlist, this may take a moment");
|
||||
try
|
||||
{
|
||||
using (var s = File.OpenRead(file))
|
||||
{
|
||||
using (var br = new BinaryReader(s))
|
||||
{
|
||||
using (var dc = LZ4Stream.Decode(br.BaseStream, leaveOpen: true))
|
||||
{
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
var list = formatter.Deserialize(dc);
|
||||
Utils.Log("Modlist loaded.");
|
||||
return (ModList) list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Utils.Log("Error Loading modlist");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace Wabbajack
|
||||
else if (mode == RunMode.Install)
|
||||
{
|
||||
context.UIReady = false;
|
||||
var modlist = Installer.LoadModlist(source);
|
||||
var modlist = Installer.LoadFromFile(source);
|
||||
if (modlist == null)
|
||||
{
|
||||
MessageBox.Show("Invalid Modlist, or file not found.", "Invalid Modlist", MessageBoxButton.OK,
|
||||
@ -71,7 +71,7 @@ namespace Wabbajack
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ConfigureForInstall(modlist);
|
||||
context.ConfigureForInstall(source, modlist);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using Wabbajack.Common;
|
||||
using static Wabbajack.MainWindow;
|
||||
|
||||
namespace Wabbajack
|
||||
@ -49,7 +50,7 @@ namespace Wabbajack
|
||||
|
||||
private void InstallModlist_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var file = UIUtils.OpenFileDialog("Wabbajack Modlist (*.modlist)|*.modlist");
|
||||
var file = UIUtils.OpenFileDialog($"Wabbajack Modlist (*{Consts.ModlistExtension})|*{Consts.ModlistExtension}");
|
||||
if (file != null)
|
||||
{
|
||||
ShutdownOnClose = false;
|
||||
|
@ -267,7 +267,19 @@ namespace Wabbajack.NexusApi
|
||||
|
||||
return Directory.EnumerateFiles(Consts.NexusCacheDirectory)
|
||||
.Where(f => f.EndsWith(".json"))
|
||||
.Select(f => f.FromJSON<ModInfo>())
|
||||
.Select(f =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return f.FromJSON<ModInfo>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
File.Delete(f);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.Where(m => m != null)
|
||||
.Where(m => m._internal_version == CACHED_VERSION_NUMBER && m.picture_url != null)
|
||||
.Select(m => new SlideShowItem
|
||||
{
|
||||
|
@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.NexusApi;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
@ -11,9 +13,11 @@ namespace Wabbajack
|
||||
{
|
||||
private const int WRAP_SIZE = 80;
|
||||
private readonly StreamWriter wtr;
|
||||
private string _output_folder;
|
||||
|
||||
public ReportBuilder(Stream str)
|
||||
public ReportBuilder(Stream str, string output_folder)
|
||||
{
|
||||
_output_folder = output_folder;
|
||||
wtr = new StreamWriter(str);
|
||||
}
|
||||
|
||||
@ -82,7 +86,7 @@ namespace Wabbajack
|
||||
Text($"#### Summary of ({patched.Count}) patches");
|
||||
foreach (var directive in patched)
|
||||
NoWrapText(
|
||||
$"* Applying {directive.Patch.Length} byte patch `{directive.FullPath}` to create `{directive.To}`");
|
||||
$"* Applying {SizeForID(directive.PatchID)} byte patch `{directive.FullPath}` to create `{directive.To}`");
|
||||
|
||||
|
||||
var files = lst.Directives.OrderBy(d => d.To).ToList();
|
||||
@ -101,7 +105,7 @@ namespace Wabbajack
|
||||
NoWrapText($"* `{i.To}` by remapping the contents of an inline file");
|
||||
break;
|
||||
case InlineFile i:
|
||||
NoWrapText($"* `{i.To}` from `{i.SourceData.Length.ToFileSizeString()}` file included in modlist");
|
||||
NoWrapText($"* `{i.To}` from `{SizeForID(i.SourceDataID).ToFileSizeString()}` file included in modlist");
|
||||
break;
|
||||
case CreateBSA i:
|
||||
NoWrapText(
|
||||
@ -110,20 +114,25 @@ namespace Wabbajack
|
||||
}
|
||||
|
||||
var inlined = lst.Directives.OfType<InlineFile>()
|
||||
.Select(f => (f.To, "inlined", f.SourceData.Length))
|
||||
.Select(f => (f.To, "inlined", SizeForID(f.SourceDataID)))
|
||||
.Concat(lst.Directives
|
||||
.OfType<PatchedFromArchive>()
|
||||
.Select(f => (f.To, "patched", f.Patch.Length)))
|
||||
.Select(f => (f.To, "patched", SizeForID(f.PatchID))))
|
||||
.ToHashSet()
|
||||
.OrderByDescending(f => f.Length);
|
||||
.OrderByDescending(f => f.Item3);
|
||||
|
||||
NoWrapText("\n\n### Summary of inlined files in this installer");
|
||||
foreach (var inline in inlined)
|
||||
{
|
||||
NoWrapText($"* {inline.Length.ToFileSizeString()} for {inline.Item2} file {inline.To}");
|
||||
NoWrapText($"* {inline.Item3.ToFileSizeString()} for {inline.Item2} file {inline.To}");
|
||||
}
|
||||
}
|
||||
|
||||
private long SizeForID(string id)
|
||||
{
|
||||
return File.GetSize(Path.Combine(_output_folder, id));
|
||||
}
|
||||
|
||||
private IEnumerable<Archive> SortArchives(List<Archive> lstArchives)
|
||||
{
|
||||
var lst = lstArchives.OfType<NexusMod>().OrderBy(m => m.Author).ThenBy(m => m.Name);
|
||||
|
@ -48,12 +48,14 @@ namespace Wabbajack.Validation
|
||||
using (var result = new StringReader(client.GetStringSync(Consts.ModPermissionsURL)))
|
||||
{
|
||||
AuthorPermissions = d.Deserialize<Dictionary<string, Author>>(result);
|
||||
Utils.Log($"Loaded permissions for {AuthorPermissions.Count} authors");
|
||||
}
|
||||
|
||||
Utils.Log("Loading Server Whitelist");
|
||||
using (var result = new StringReader(client.GetStringSync(Consts.ServerWhitelistURL)))
|
||||
{
|
||||
ServerWhitelist = d.Deserialize<ServerWhitelist>(result);
|
||||
Utils.Log($"Loaded permissions for {ServerWhitelist.AllowedPrefixes.Count} servers and {ServerWhitelist.GoogleIDs.Count} GDrive files");
|
||||
}
|
||||
|
||||
}
|
||||
@ -62,13 +64,17 @@ namespace Wabbajack.Validation
|
||||
{
|
||||
var validator = new ValidateModlist();
|
||||
validator.LoadListsFromGithub();
|
||||
|
||||
Utils.Log("Running validation checks");
|
||||
var errors = validator.Validate(modlist);
|
||||
errors.Do(e => Utils.Log(e));
|
||||
if (errors.Count() > 0)
|
||||
{
|
||||
Utils.Log($"{errors.Count()} validation errors found, cannot continue.");
|
||||
throw new AccessViolationException();
|
||||
throw new Exception($"{errors.Count()} validation errors found, cannot continue.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Log("No Validation failures");
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +140,7 @@ namespace Wabbajack.Validation
|
||||
if (nexus_mod_permissions.TryGetValue(p.ArchiveHashPath[0], out var archive))
|
||||
{
|
||||
if (!(archive.permissions.CanExtractBSAs ?? true) &&
|
||||
p.ArchiveHashPath.Skip(1).Any(a => Consts.SupportedBSAs.Contains(Path.GetExtension(a))))
|
||||
p.ArchiveHashPath.Skip(1).ButLast().Any(a => Consts.SupportedBSAs.Contains(Path.GetExtension(a))))
|
||||
{
|
||||
ValidationErrors.Push($"{p.To} from {archive.archive.NexusURL} is set to disallow BSA Extraction");
|
||||
}
|
||||
|
@ -143,6 +143,7 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Design" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -88,7 +88,7 @@ namespace Wabbajack
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Utils.CreatePatch(src_data, dst_data, ms);
|
||||
result.Patch = ms.ToArray();
|
||||
result.PatchID = compiler.IncludeFile(ms.ToArray());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
Loading…
Reference in New Issue
Block a user