mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
commit
37d002f95a
14
Wabbajack.Common/Enums/ModManager.cs
Normal file
14
Wabbajack.Common/Enums/ModManager.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public enum ModManager
|
||||
{
|
||||
MO2,
|
||||
Vortex
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
|
@ -1,16 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public enum Game {
|
||||
//MO2 GAMES
|
||||
Morrowind,
|
||||
Oblivion,
|
||||
Fallout3,
|
||||
@ -18,16 +14,33 @@ namespace Wabbajack.Common
|
||||
Skyrim,
|
||||
SkyrimSpecialEdition,
|
||||
Fallout4,
|
||||
SkyrimVR
|
||||
SkyrimVR,
|
||||
//VORTEX GAMES
|
||||
DarkestDungeon,
|
||||
DivinityOriginalSin2,
|
||||
DivinityOriginalSin2DE, //definitive edition has its own nexus page but same Steam/GOG ids
|
||||
Starbound,
|
||||
SWKOTOR,
|
||||
SWKOTOR2,
|
||||
WITCHER,
|
||||
WITCHER2,
|
||||
WITCHER3
|
||||
}
|
||||
|
||||
public class GameMetaData
|
||||
{
|
||||
public ModManager SupportedModManager { get; internal set; }
|
||||
public string MO2ArchiveName { get; internal set; }
|
||||
public Game Game { get; internal set; }
|
||||
public string NexusName { get; internal set; }
|
||||
public string MO2Name { get; internal set; }
|
||||
public string GameLocationRegistryKey { get; internal set; }
|
||||
// to get steam ids: https://steamdb.info
|
||||
public List<int> SteamIDs { get; internal set; }
|
||||
// to get gog ids: https://www.gogdb.org
|
||||
public List<int> GOGIDs { get; internal set; }
|
||||
// these are additional folders when a game installs mods outside the game folder
|
||||
public List<string> AdditionalFolders { get; internal set; }
|
||||
|
||||
public string GameLocation
|
||||
{
|
||||
@ -53,6 +66,11 @@ namespace Wabbajack.Common
|
||||
return Games.Values.FirstOrDefault(g => g.MO2ArchiveName?.ToLower() == gamename);
|
||||
}
|
||||
|
||||
public static GameMetaData GetByNexusName(string gameName)
|
||||
{
|
||||
return Games.Values.FirstOrDefault(g => g.NexusName == gameName.ToLower());
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<Game, GameMetaData> Games = new Dictionary<Game, GameMetaData>
|
||||
{
|
||||
@ -62,72 +80,195 @@ namespace Wabbajack.Common
|
||||
{
|
||||
Game.Oblivion, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.Oblivion,
|
||||
NexusName = "oblivion",
|
||||
MO2Name = "Oblivion",
|
||||
MO2ArchiveName = "oblivion",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Oblivion"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Oblivion",
|
||||
SteamIDs = new List<int> {22330}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
Game.Fallout3, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.Fallout3,
|
||||
NexusName = "fallout3",
|
||||
MO2Name = "fallout3",
|
||||
MO2ArchiveName = "fallout3",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout3"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout3",
|
||||
SteamIDs = new List<int> {22300, 22370} // base game and GotY
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.FalloutNewVegas, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.FalloutNewVegas,
|
||||
NexusName = "newvegas",
|
||||
MO2Name = "New Vegas",
|
||||
MO2ArchiveName = "falloutnv",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\falloutnv"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\falloutnv",
|
||||
SteamIDs = new List<int> {22380}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.Skyrim, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.Skyrim,
|
||||
NexusName = "skyrim",
|
||||
MO2Name = "Skyrim",
|
||||
MO2ArchiveName = "skyrim",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\skyrim"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\skyrim",
|
||||
SteamIDs = new List<int> {72850}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.SkyrimSpecialEdition, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.SkyrimSpecialEdition,
|
||||
NexusName = "skyrimspecialedition",
|
||||
MO2Name = "Skyrim Special Edition",
|
||||
MO2ArchiveName = "skyrimse",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim Special Edition"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim Special Edition",
|
||||
SteamIDs = new List<int> {489830}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.Fallout4, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.Fallout4,
|
||||
NexusName = "fallout4",
|
||||
MO2Name = "Fallout 4",
|
||||
MO2ArchiveName = "fallout4",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout4"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout4",
|
||||
SteamIDs = new List<int> {377160}
|
||||
}
|
||||
},
|
||||
/*{
|
||||
Game.Fallout4VR, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.Fallout4VR,
|
||||
NexusName = "fallout4",
|
||||
MO2Name = "Fallout 4",
|
||||
MO2ArchiveName = "fallout4",
|
||||
SteamIDs = new List<int>{611660}
|
||||
}
|
||||
},*/
|
||||
{
|
||||
Game.SkyrimVR, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.MO2,
|
||||
Game = Game.SkyrimVR,
|
||||
NexusName = "skyrimspecialedition",
|
||||
MO2Name = "Skyrim VR",
|
||||
MO2ArchiveName = "skyrimse",
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim VR"
|
||||
GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim VR",
|
||||
SteamIDs = new List<int> {611670}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.DarkestDungeon, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.DarkestDungeon,
|
||||
NexusName = "darkestdungeon",
|
||||
SteamIDs = new List<int> {262060},
|
||||
GOGIDs = new List<int>{1450711444}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.DivinityOriginalSin2, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.DivinityOriginalSin2,
|
||||
NexusName = "divinityoriginalsin2",
|
||||
SteamIDs = new List<int> {435150},
|
||||
GOGIDs = new List<int>{1584823040},
|
||||
AdditionalFolders = new List<string>
|
||||
{
|
||||
"%documents%\\Larian Studios\\Divinity Original Sin 2\\Mods\\",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.DivinityOriginalSin2DE, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.DivinityOriginalSin2DE,
|
||||
NexusName = "divinityoriginalsin2definitiveedition",
|
||||
SteamIDs = new List<int> {435150},
|
||||
GOGIDs = new List<int>{1584823040},
|
||||
AdditionalFolders = new List<string>
|
||||
{
|
||||
"%documents%\\Larian Studios\\Divinity Original Sin 2 Definitive Edition\\Mods\\"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.Starbound, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.Starbound,
|
||||
NexusName = "starbound",
|
||||
SteamIDs = new List<int>{211820},
|
||||
GOGIDs = new List<int>{1452598881}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.SWKOTOR, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.SWKOTOR,
|
||||
NexusName = "kotor",
|
||||
SteamIDs = new List<int>{32370},
|
||||
GOGIDs = new List<int>{1207666283}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.SWKOTOR2, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.SWKOTOR2,
|
||||
NexusName = "kotor2",
|
||||
SteamIDs = new List<int>{208580},
|
||||
GOGIDs = new List<int>{1421404581}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.WITCHER, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.WITCHER,
|
||||
NexusName = "witcher",
|
||||
SteamIDs = new List<int>{20900},
|
||||
GOGIDs = new List<int>{1207658924}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.WITCHER2, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.WITCHER2,
|
||||
NexusName = "witcher2",
|
||||
SteamIDs = new List<int>{20920},
|
||||
GOGIDs = new List<int>{1207658930}
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.WITCHER3, new GameMetaData
|
||||
{
|
||||
SupportedModManager = ModManager.Vortex,
|
||||
Game = Game.WITCHER3,
|
||||
NexusName = "witcher3",
|
||||
SteamIDs = new List<int>{292030, 499450}, // normal and GotY
|
||||
GOGIDs = new List<int>{1207664643, 1495134320, 1207664663, 1640424747} // normal, GotY and both in packages
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -91,6 +91,8 @@
|
||||
<Compile Include="DynamicIniData.cs" />
|
||||
<Compile Include="Error States\ErrorResponse.cs" />
|
||||
<Compile Include="Error States\GetResponse.cs" />
|
||||
<Compile Include="Enums\ModManager.cs" />
|
||||
<Compile Include="Enums\RunMode.cs" />
|
||||
<Compile Include="ExtensionManager.cs" />
|
||||
<Compile Include="Extensions\DictionaryExt.cs" />
|
||||
<Compile Include="Extensions\HashHelper.cs" />
|
||||
|
42
Wabbajack.Lib/ACompiler.cs
Normal file
42
Wabbajack.Lib/ACompiler.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public abstract class ACompiler
|
||||
{
|
||||
public ModManager ModManager;
|
||||
|
||||
public string GamePath;
|
||||
|
||||
public string ModListOutputFolder;
|
||||
public string ModListOutputFile;
|
||||
|
||||
public List<Archive> SelectedArchives;
|
||||
public List<Directive> InstallDirectives;
|
||||
public List<RawSourceFile> AllFiles;
|
||||
public ModList ModList;
|
||||
public VirtualFileSystem VFS;
|
||||
public List<IndexedArchive> IndexedArchives;
|
||||
public Dictionary<string, IEnumerable<VirtualFile>> IndexedFiles;
|
||||
|
||||
public abstract void Info(string msg);
|
||||
public abstract void Status(string msg);
|
||||
public abstract void Error(string msg);
|
||||
|
||||
internal abstract string IncludeFile(byte[] data);
|
||||
internal abstract string IncludeFile(string data);
|
||||
|
||||
public abstract bool Compile();
|
||||
|
||||
public abstract Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source);
|
||||
public abstract IEnumerable<ICompilationStep> GetStack();
|
||||
public abstract IEnumerable<ICompilationStep> MakeStack();
|
||||
}
|
||||
}
|
@ -2,9 +2,9 @@
|
||||
{
|
||||
public abstract class ACompilationStep : ICompilationStep
|
||||
{
|
||||
protected Compiler _compiler;
|
||||
protected ACompiler _compiler;
|
||||
|
||||
public ACompilationStep(Compiler compiler)
|
||||
public ACompilationStep(ACompiler compiler)
|
||||
{
|
||||
_compiler = compiler;
|
||||
}
|
||||
|
@ -13,10 +13,12 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly IEnumerable<string> _include_directly;
|
||||
private readonly List<ICompilationStep> _microstack;
|
||||
private readonly List<ICompilationStep> _microstackWithInclude;
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public DeconstructBSAs(Compiler compiler) : base(compiler)
|
||||
public DeconstructBSAs(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_include_directly = _compiler.ModInis.Where(kv =>
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
_include_directly = _mo2Compiler.ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(Consts.WABBAJACK_INCLUDE)) return true;
|
||||
@ -28,16 +30,16 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
_microstack = new List<ICompilationStep>
|
||||
{
|
||||
new DirectMatch(_compiler),
|
||||
new IncludePatches(_compiler),
|
||||
new DropAll(_compiler)
|
||||
new DirectMatch(_mo2Compiler),
|
||||
new IncludePatches(_mo2Compiler),
|
||||
new DropAll(_mo2Compiler)
|
||||
};
|
||||
|
||||
_microstackWithInclude = new List<ICompilationStep>
|
||||
{
|
||||
new DirectMatch(_compiler),
|
||||
new IncludePatches(_compiler),
|
||||
new IncludeAll(_compiler)
|
||||
new DirectMatch(_mo2Compiler),
|
||||
new IncludePatches(_mo2Compiler),
|
||||
new IncludeAll(_mo2Compiler)
|
||||
};
|
||||
}
|
||||
|
||||
@ -61,7 +63,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
var id = Guid.NewGuid().ToString();
|
||||
|
||||
var matches = source_files.PMap(e => Compiler.RunStack(stack, new RawSourceFile(e)
|
||||
var matches = source_files.PMap(e => _mo2Compiler.RunStack(stack, new RawSourceFile(e)
|
||||
{
|
||||
Path = Path.Combine(Consts.BSACreationDir, id, e.Paths.Last())
|
||||
}));
|
||||
@ -71,7 +73,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
if (match is IgnoredDirectly)
|
||||
Utils.Error($"File required for BSA {source.Path} creation doesn't exist: {match.To}");
|
||||
_compiler.ExtraFiles.Add(match);
|
||||
_mo2Compiler.ExtraFiles.Add(match);
|
||||
}
|
||||
|
||||
CreateBSA directive;
|
||||
@ -92,7 +94,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("DeconstructBSAs")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new DeconstructBSAs(compiler);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class DirectMatch : ACompilationStep
|
||||
{
|
||||
public DirectMatch(Compiler compiler) : base(compiler)
|
||||
public DirectMatch(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("DirectMatch")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new DirectMatch(compiler);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class DropAll : ACompilationStep
|
||||
{
|
||||
public DropAll(Compiler compiler) : base(compiler)
|
||||
public DropAll(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("DropAll")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new DropAll(compiler);
|
||||
}
|
||||
|
@ -8,6 +8,6 @@
|
||||
|
||||
public interface IState
|
||||
{
|
||||
ICompilationStep CreateStep(Compiler compiler);
|
||||
ICompilationStep CreateStep(ACompiler compiler);
|
||||
}
|
||||
}
|
@ -9,13 +9,15 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
public class IgnoreDisabledMods : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _allEnabledMods;
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public IgnoreDisabledMods(Compiler compiler) : base(compiler)
|
||||
public IgnoreDisabledMods(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
var alwaysEnabled = _compiler.ModInis.Where(f => IsAlwaysEnabled(f.Value)).Select(f => f.Key).ToHashSet();
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
var alwaysEnabled = _mo2Compiler.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")))
|
||||
_allEnabledMods = _mo2Compiler.SelectedProfiles
|
||||
.SelectMany(p => File.ReadAllLines(Path.Combine(_mo2Compiler.MO2Folder, "profiles", p, "modlist.txt")))
|
||||
.Where(line => line.StartsWith("+") || line.EndsWith("_separator"))
|
||||
.Select(line => line.Substring(1))
|
||||
.Concat(alwaysEnabled)
|
||||
@ -55,7 +57,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IgnoreDisabledMods")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreDisabledMods(compiler);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly string _postfix;
|
||||
private readonly string _reason;
|
||||
|
||||
public IgnoreEndsWith(Compiler compiler, string postfix) : base(compiler)
|
||||
public IgnoreEndsWith(ACompiler compiler, string postfix) : base(compiler)
|
||||
{
|
||||
_postfix = postfix;
|
||||
_reason = $"Ignored because path ends with {postfix}";
|
||||
@ -40,7 +40,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Postfix { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreEndsWith(compiler, Postfix);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
private readonly string _startDir;
|
||||
|
||||
public IgnoreGameFiles(Compiler compiler) : base(compiler)
|
||||
public IgnoreGameFiles(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_startDir = Consts.GameFolderFilesDir + "\\";
|
||||
}
|
||||
@ -28,7 +28,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IgnoreGameFiles")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreGameFiles(compiler);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly string _pattern;
|
||||
private readonly string _reason;
|
||||
|
||||
public IgnorePathContains(Compiler compiler, string pattern) : base(compiler)
|
||||
public IgnorePathContains(ACompiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_pattern = $"\\{pattern.Trim('\\')}\\";
|
||||
_reason = $"Ignored because path contains {_pattern}";
|
||||
@ -40,7 +40,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Pattern { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnorePathContains(compiler, Pattern);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly Regex _regex;
|
||||
private readonly string _pattern;
|
||||
|
||||
public IgnoreRegex(Compiler compiler, string pattern) : base(compiler)
|
||||
public IgnoreRegex(ACompiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_pattern = pattern;
|
||||
_reason = $"Ignored because path matches regex {pattern}";
|
||||
@ -43,7 +43,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Pattern { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreRegex(compiler, Pattern);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly string _prefix;
|
||||
private readonly string _reason;
|
||||
|
||||
public IgnoreStartsWith(Compiler compiler, string prefix) : base(compiler)
|
||||
public IgnoreStartsWith(ACompiler compiler, string prefix) : base(compiler)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_reason = string.Format("Ignored because path starts with {0}", _prefix);
|
||||
@ -44,7 +44,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreStartsWith(compiler, Prefix);
|
||||
}
|
||||
|
40
Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs
Normal file
40
Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IgnoreVortex : ACompilationStep
|
||||
{
|
||||
private readonly VortexCompiler _vortex;
|
||||
|
||||
public IgnoreVortex(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_vortex = (VortexCompiler) compiler;
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (Path.GetDirectoryName(source.AbsolutePath) != _vortex.DownloadsFolder) return null;
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = "Ignored because it is a Vortex file";
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public override IState GetState()
|
||||
{
|
||||
return new State();
|
||||
}
|
||||
|
||||
[JsonObject("IgnoreVortex")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreVortex(compiler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
private readonly HashSet<string> _cruftFiles;
|
||||
|
||||
public IgnoreWabbajackInstallCruft(Compiler compiler) : base(compiler)
|
||||
public IgnoreWabbajackInstallCruft(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_cruftFiles = new HashSet<string>
|
||||
{
|
||||
@ -34,7 +34,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IgnoreWabbajackInstallCruft")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreWabbajackInstallCruft(compiler);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeAll : ACompilationStep
|
||||
{
|
||||
public IncludeAll(Compiler compiler) : base(compiler)
|
||||
public IncludeAll(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeAll")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeAll(compiler);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeAllConfigs : ACompilationStep
|
||||
{
|
||||
public IncludeAllConfigs(Compiler compiler) : base(compiler)
|
||||
public IncludeAllConfigs(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeAllConfigs")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeAllConfigs(compiler);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeDummyESPs : ACompilationStep
|
||||
{
|
||||
public IncludeDummyESPs(Compiler compiler) : base(compiler)
|
||||
public IncludeDummyESPs(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeDummyESPs")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeDummyESPs(compiler);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
private readonly string _prefix;
|
||||
|
||||
public IncludeLootFiles(Compiler compiler) : base(compiler)
|
||||
public IncludeLootFiles(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_prefix = Consts.LOOTFolderFilesDir + "\\";
|
||||
}
|
||||
@ -29,7 +29,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeLootFiles")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeLootFiles(compiler);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeModIniData : ACompilationStep
|
||||
{
|
||||
public IncludeModIniData(Compiler compiler) : base(compiler)
|
||||
public IncludeModIniData(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeModIniData")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeModIniData(compiler);
|
||||
}
|
||||
|
@ -8,10 +8,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
public class IgnoreOtherProfiles : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _profiles;
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public IgnoreOtherProfiles(Compiler compiler) : base(compiler)
|
||||
public IgnoreOtherProfiles(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_profiles = _compiler.SelectedProfiles
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
|
||||
_profiles = _mo2Compiler.SelectedProfiles
|
||||
.Select(p => Path.Combine("profiles", p) + "\\")
|
||||
.ToList();
|
||||
}
|
||||
@ -33,7 +36,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IgnoreOtherProfiles")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IgnoreOtherProfiles(compiler);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
private readonly Dictionary<string, IGrouping<string, VirtualFile>> _indexed;
|
||||
|
||||
public IncludePatches(Compiler compiler) : base(compiler)
|
||||
public IncludePatches(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_indexed = _compiler.IndexedFiles.Values
|
||||
.SelectMany(f => f)
|
||||
@ -47,7 +47,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludePatches")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludePatches(compiler);
|
||||
}
|
||||
|
@ -7,31 +7,34 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludePropertyFiles : ACompilationStep
|
||||
{
|
||||
public IncludePropertyFiles(Compiler compiler) : base(compiler)
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public IncludePropertyFiles(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var files = new HashSet<string>
|
||||
{
|
||||
_compiler.ModListImage, _compiler.ModListReadme
|
||||
_mo2Compiler.ModListImage, _mo2Compiler.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 isBanner = source.AbsolutePath == _mo2Compiler.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;
|
||||
_mo2Compiler.ModListImage = result.SourceDataID;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Type = PropertyType.Readme;
|
||||
_compiler.ModListReadme = result.SourceDataID;
|
||||
_mo2Compiler.ModListReadme = result.SourceDataID;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -45,7 +48,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludePropertyFiles")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludePropertyFiles(compiler);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
private readonly string _pattern;
|
||||
private readonly Regex _regex;
|
||||
|
||||
public IncludeRegex(Compiler compiler, string pattern) : base(compiler)
|
||||
public IncludeRegex(ACompiler compiler, string pattern) : base(compiler)
|
||||
{
|
||||
_pattern = pattern;
|
||||
_regex = new Regex(pattern);
|
||||
@ -43,7 +43,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Pattern { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeRegex(compiler, Pattern);
|
||||
}
|
||||
|
@ -7,8 +7,11 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeStubbedConfigFiles : ACompilationStep
|
||||
{
|
||||
public IncludeStubbedConfigFiles(Compiler compiler) : base(compiler)
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public IncludeStubbedConfigFiles(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
@ -26,18 +29,18 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
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(_mo2Compiler.GamePath, Consts.GAME_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_mo2Compiler.GamePath.Replace("\\", "\\\\"), Consts.GAME_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_mo2Compiler.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(_mo2Compiler.MO2Folder, Consts.MO2_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_mo2Compiler.MO2Folder.Replace("\\", "\\\\"), Consts.MO2_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_mo2Compiler.MO2Folder.Replace("\\", "/"), Consts.MO2_PATH_MAGIC_FORWARD);
|
||||
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder.Replace("\\", "\\\\"),
|
||||
data = data.Replace(_mo2Compiler.MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
|
||||
data = data.Replace(_mo2Compiler.MO2DownloadsFolder.Replace("\\", "\\\\"),
|
||||
Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK);
|
||||
data = data.Replace(_compiler.MO2DownloadsFolder.Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
|
||||
data = data.Replace(_mo2Compiler.MO2DownloadsFolder.Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
|
||||
|
||||
if (data == originalData)
|
||||
return null;
|
||||
@ -49,7 +52,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeStubbedConfigFiles")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeStubbedConfigFiles(compiler);
|
||||
}
|
||||
|
@ -9,12 +9,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
private readonly IEnumerable<string> _includeDirectly;
|
||||
private readonly string _tag;
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
|
||||
public IncludeTaggedMods(Compiler compiler, string tag) : base(compiler)
|
||||
public IncludeTaggedMods(ACompiler compiler, string tag) : base(compiler)
|
||||
{
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
_tag = tag;
|
||||
_includeDirectly = _compiler.ModInis.Where(kv =>
|
||||
_includeDirectly = _mo2Compiler.ModInis.Where(kv =>
|
||||
{
|
||||
var general = kv.Value.General;
|
||||
if (general.notes != null && general.notes.Contains(_tag))
|
||||
@ -56,7 +57,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public string Tag { get; set; }
|
||||
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeTaggedMods(compiler, Tag);
|
||||
}
|
||||
|
@ -9,10 +9,12 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
public class IncludeThisProfile : ACompilationStep
|
||||
{
|
||||
private readonly IEnumerable<string> _correctProfiles;
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public IncludeThisProfile(Compiler compiler) : base(compiler)
|
||||
public IncludeThisProfile(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_correctProfiles = _compiler.SelectedProfiles.Select(p => Path.Combine("profiles", p) + "\\").ToList();
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
_correctProfiles = _mo2Compiler.SelectedProfiles.Select(p => Path.Combine("profiles", p) + "\\").ToList();
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
@ -48,7 +50,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("IncludeThisProfile")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeThisProfile(compiler);
|
||||
}
|
||||
|
39
Wabbajack.Lib/CompilationSteps/IncludeVortexDeployment.cs
Normal file
39
Wabbajack.Lib/CompilationSteps/IncludeVortexDeployment.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class IncludeVortexDeployment : ACompilationStep
|
||||
{
|
||||
public IncludeVortexDeployment(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
if (!source.Path.EndsWith("vortex.deployment.msgpack") &&
|
||||
!source.Path.EndsWith("\\vortex.deployment.json")) return null;
|
||||
var inline = source.EvolveTo<InlineFile>();
|
||||
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
|
||||
return inline;
|
||||
}
|
||||
|
||||
public override IState GetState()
|
||||
{
|
||||
return new State();
|
||||
}
|
||||
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeVortexDeployment(compiler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,14 +8,17 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
public class PatchStockESMs : ACompilationStep
|
||||
{
|
||||
public PatchStockESMs(Compiler compiler) : base(compiler)
|
||||
private readonly Compiler _mo2Compiler;
|
||||
|
||||
public PatchStockESMs(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
}
|
||||
|
||||
public override Directive Run(RawSourceFile source)
|
||||
{
|
||||
var filename = Path.GetFileName(source.Path);
|
||||
var gameFile = Path.Combine(_compiler.GamePath, "Data", filename);
|
||||
var gameFile = Path.Combine(_mo2Compiler.GamePath, "Data", filename);
|
||||
if (!Consts.GameESMs.Contains(filename) || !source.Path.StartsWith("mods\\") ||
|
||||
!File.Exists(gameFile)) return null;
|
||||
|
||||
@ -44,7 +47,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
[JsonObject("PatchStockESMs")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new PatchStockESMs(compiler);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
.ToJSON(TypeNameHandling.Auto, TypeNameAssemblyFormatHandling.Simple);
|
||||
}
|
||||
|
||||
public static List<ICompilationStep> Deserialize(string stack, Compiler compiler)
|
||||
public static List<ICompilationStep> Deserialize(string stack, ACompiler compiler)
|
||||
{
|
||||
return stack.FromJSONString<List<IState>>(TypeNameHandling.Auto, TypeNameAssemblyFormatHandling.Simple)
|
||||
.Select(s => s.CreateStep(compiler)).ToList();
|
||||
|
@ -26,7 +26,7 @@ using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public class Compiler
|
||||
public class Compiler : ACompiler
|
||||
{
|
||||
private string _mo2DownloadsFolder;
|
||||
|
||||
@ -42,9 +42,23 @@ namespace Wabbajack.Lib
|
||||
|
||||
public Compiler(string mo2_folder)
|
||||
{
|
||||
ModManager = ModManager.MO2;
|
||||
|
||||
MO2Folder = mo2_folder;
|
||||
MO2Ini = Path.Combine(MO2Folder, "ModOrganizer.ini").LoadIniFile();
|
||||
GamePath = ((string)MO2Ini.General.gamePath).Replace("\\\\", "\\");
|
||||
|
||||
ModListOutputFolder = "output_folder";
|
||||
ModListOutputFile = MO2Profile + ExtensionManager.Extension;
|
||||
|
||||
SelectedArchives = new List<Archive>();
|
||||
InstallDirectives = new List<Directive>();
|
||||
AllFiles = new List<RawSourceFile>();
|
||||
ModList = new ModList();
|
||||
|
||||
VFS = VirtualFileSystem.VFS;
|
||||
IndexedArchives = new List<IndexedArchive>();
|
||||
IndexedFiles = new Dictionary<string, IEnumerable<VirtualFile>>();
|
||||
}
|
||||
|
||||
public dynamic MO2Ini { get; }
|
||||
@ -70,55 +84,43 @@ namespace Wabbajack.Lib
|
||||
|
||||
public string MO2ProfileDir => Path.Combine(MO2Folder, "profiles", MO2Profile);
|
||||
|
||||
public string ModListOutputFolder => "output_folder";
|
||||
public string ModListOutputFile => MO2Profile + ExtensionManager.Extension;
|
||||
|
||||
public List<Directive> InstallDirectives { get; private set; }
|
||||
internal UserStatus User { get; private set; }
|
||||
public List<Archive> SelectedArchives { get; private set; }
|
||||
public List<RawSourceFile> AllFiles { get; private set; }
|
||||
public ModList ModList { get; private set; }
|
||||
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
|
||||
public Dictionary<string, dynamic> ModInis { get; private set; }
|
||||
|
||||
public VirtualFileSystem VFS => VirtualFileSystem.VFS;
|
||||
|
||||
public List<IndexedArchive> IndexedArchives { get; private set; }
|
||||
public Dictionary<string, IEnumerable<VirtualFile>> IndexedFiles { get; private set; }
|
||||
|
||||
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
|
||||
|
||||
public void Info(string msg)
|
||||
public override void Info(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
}
|
||||
|
||||
public void Status(string msg)
|
||||
public override void Status(string msg)
|
||||
{
|
||||
WorkQueue.Report(msg, 0);
|
||||
}
|
||||
|
||||
private void Error(string msg)
|
||||
public override void Error(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
internal string IncludeFile(byte[] data)
|
||||
internal override string IncludeFile(byte[] data)
|
||||
{
|
||||
var id = Guid.NewGuid().ToString();
|
||||
File.WriteAllBytes(Path.Combine(ModListOutputFolder, id), data);
|
||||
return id;
|
||||
}
|
||||
|
||||
internal string IncludeFile(string data)
|
||||
internal override string IncludeFile(string data)
|
||||
{
|
||||
var id = Guid.NewGuid().ToString();
|
||||
File.WriteAllText(Path.Combine(ModListOutputFolder, id), data);
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool Compile()
|
||||
public override bool Compile()
|
||||
{
|
||||
VirtualFileSystem.Clean();
|
||||
Info("Looking for other profiles");
|
||||
@ -283,6 +285,7 @@ namespace Wabbajack.Lib
|
||||
GameType = GameRegistry.Games.Values.First(f => f.MO2Name == MO2Ini.General.gameName).Game,
|
||||
WabbajackVersion = WabbajackVersion,
|
||||
Archives = SelectedArchives,
|
||||
ModManager = ModManager.MO2,
|
||||
Directives = InstallDirectives,
|
||||
Name = ModListName ?? MO2Profile,
|
||||
Author = ModListAuthor ?? "",
|
||||
@ -533,7 +536,7 @@ namespace Wabbajack.Lib
|
||||
}
|
||||
|
||||
|
||||
public static Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
||||
public override Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
||||
{
|
||||
Utils.Status($"Compiling {source.Path}");
|
||||
foreach (var step in stack)
|
||||
@ -545,7 +548,7 @@ namespace Wabbajack.Lib
|
||||
throw new InvalidDataException("Data fell out of the compilation stack");
|
||||
}
|
||||
|
||||
public IEnumerable<ICompilationStep> GetStack()
|
||||
public override IEnumerable<ICompilationStep> GetStack()
|
||||
{
|
||||
var user_config = Path.Combine(MO2ProfileDir, "compilation_stack.yml");
|
||||
if (File.Exists(user_config))
|
||||
@ -566,7 +569,7 @@ namespace Wabbajack.Lib
|
||||
/// result included into the pack
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ICompilationStep> MakeStack()
|
||||
public override IEnumerable<ICompilationStep> MakeStack()
|
||||
{
|
||||
Utils.Log("Generating compilation stack");
|
||||
return new List<ICompilationStep>
|
||||
|
@ -40,6 +40,11 @@ namespace Wabbajack.Lib
|
||||
/// </summary>
|
||||
public List<Archive> Archives;
|
||||
|
||||
/// <summary>
|
||||
/// The Mod Manager used to create the modlist
|
||||
/// </summary>
|
||||
public ModManager ModManager;
|
||||
|
||||
/// <summary>
|
||||
/// The game variant to which this game applies
|
||||
/// </summary>
|
||||
@ -204,6 +209,7 @@ namespace Wabbajack.Lib
|
||||
/// </summary>
|
||||
public string Hash;
|
||||
|
||||
/// <summary>
|
||||
/// Meta INI for the downloaded archive
|
||||
/// </summary>
|
||||
public string Meta;
|
||||
|
@ -19,7 +19,8 @@ namespace Wabbajack.Lib.Downloaders
|
||||
if (general.modID != null && general.fileID != null && general.gameName != null)
|
||||
{
|
||||
var name = (string)general.gameName;
|
||||
var game = GameRegistry.GetByMO2ArchiveName(name).Game;
|
||||
var gameMeta = GameRegistry.GetByMO2ArchiveName(name);
|
||||
var game = gameMeta != null ? GameRegistry.GetByMO2ArchiveName(name).Game : GameRegistry.GetByNexusName(name).Game;
|
||||
var info = new NexusApiClient().GetModInfo(game, general.modID);
|
||||
return new State
|
||||
{
|
||||
|
@ -46,6 +46,12 @@ namespace Wabbajack.Lib.NexusApi
|
||||
public bool contains_adult_content;
|
||||
}
|
||||
|
||||
public class MD5Response
|
||||
{
|
||||
public ModInfo mod;
|
||||
public NexusFileInfo file_details;
|
||||
}
|
||||
|
||||
public class EndorsementResponse
|
||||
{
|
||||
public string message;
|
||||
|
@ -222,7 +222,6 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
}
|
||||
|
||||
|
||||
public string GetNexusDownloadLink(NexusDownloader.State archive, bool cache = false)
|
||||
{
|
||||
if (cache && TryGetCachedLink(archive, out var result))
|
||||
@ -269,6 +268,12 @@ namespace Wabbajack.Lib.NexusApi
|
||||
return GetCached<GetModFilesResponse>(url).files;
|
||||
}
|
||||
|
||||
public List<MD5Response> GetModInfoFromMD5(Game game, string md5Hash)
|
||||
{
|
||||
var url = $"https://api.nexusmods.com/v1/games/{GameRegistry.Games[game].NexusName}/mods/md5_search/{md5Hash}.json";
|
||||
return Get<List<MD5Response>>(url);
|
||||
}
|
||||
|
||||
public ModInfo GetModInfo(Game game, string modId)
|
||||
{
|
||||
var url = $"https://api.nexusmods.com/v1/games/{GameRegistry.Games[game].NexusName}/mods/{modId}.json";
|
||||
@ -365,4 +370,4 @@ namespace Wabbajack.Lib.NexusApi
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Wabbajack.Common;
|
||||
using System.Text.RegularExpressions;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.NexusApi
|
||||
{
|
||||
@ -6,6 +7,8 @@ namespace Wabbajack.Lib.NexusApi
|
||||
{
|
||||
public static string ConvertGameName(string gameName)
|
||||
{
|
||||
if (Regex.IsMatch(gameName, @"^[^a-z\s]+\.[^a-z\s]+$"))
|
||||
return gameName;
|
||||
return GameRegistry.GetByMO2ArchiveName(gameName)?.NexusName ?? gameName.ToLower();
|
||||
}
|
||||
|
||||
|
452
Wabbajack.Lib/VortexCompiler.cs
Normal file
452
Wabbajack.Lib/VortexCompiler.cs
Normal file
@ -0,0 +1,452 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.WindowsAPICodePack.Shell;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.ModListRegistry;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public class VortexCompiler : ACompiler
|
||||
{
|
||||
public Game Game { get; }
|
||||
public string GameName { get; }
|
||||
|
||||
public string VortexFolder { get; set; }
|
||||
public string StagingFolder { get; set; }
|
||||
public string DownloadsFolder { get; set; }
|
||||
|
||||
public bool IgnoreMissingFiles { get; set; }
|
||||
|
||||
public VortexCompiler(string gameName, string gamePath)
|
||||
{
|
||||
ModManager = ModManager.Vortex;
|
||||
|
||||
// TODO: only for testing
|
||||
IgnoreMissingFiles = true;
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
|
||||
GamePath = gamePath;
|
||||
GameName = gameName;
|
||||
Game = GameRegistry.GetByNexusName(GameName).Game;
|
||||
|
||||
//args: wabbajacke.exe gameName gamePath vortexfolder stagingfolder downloadsfolder
|
||||
VortexFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vortex");
|
||||
if (File.Exists(Path.Combine(VortexFolder, gameName, "mods", "__vortex_staging_folder")))
|
||||
StagingFolder = Path.Combine(VortexFolder, gameName, "mods");
|
||||
if (File.Exists(Path.Combine(VortexFolder, "downloads", "__vortex_downloads_folder")))
|
||||
DownloadsFolder = Path.Combine(VortexFolder, "downloads", gameName);
|
||||
|
||||
if (args.Length >= 4)
|
||||
StagingFolder = args[3];
|
||||
if (args.Length == 5)
|
||||
DownloadsFolder = args[4];
|
||||
|
||||
ModListOutputFolder = "output_folder";
|
||||
|
||||
// TODO: add custom modlist name
|
||||
ModListOutputFile = $"VORTEX_TEST_MODLIST{ExtensionManager.Extension}";
|
||||
|
||||
VFS = VirtualFileSystem.VFS;
|
||||
|
||||
SelectedArchives = new List<Archive>();
|
||||
AllFiles = new List<RawSourceFile>();
|
||||
IndexedArchives = new List<IndexedArchive>();
|
||||
IndexedFiles = new Dictionary<string, IEnumerable<VirtualFile>>();
|
||||
}
|
||||
|
||||
public override void Info(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
}
|
||||
|
||||
public override void Status(string msg)
|
||||
{
|
||||
WorkQueue.Report(msg, 0);
|
||||
}
|
||||
|
||||
public override void Error(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
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()
|
||||
{
|
||||
VirtualFileSystem.Clean();
|
||||
Info($"Starting Vortex compilation for {GameName} at {GamePath} with staging folder at {StagingFolder} and downloads folder at {DownloadsFolder}.");
|
||||
|
||||
Info("Starting pre-compilation steps");
|
||||
CreateMetaFiles();
|
||||
|
||||
Info($"Indexing {StagingFolder}");
|
||||
VFS.AddRoot(StagingFolder);
|
||||
|
||||
Info($"Indexing {GamePath}");
|
||||
VFS.AddRoot(GamePath);
|
||||
|
||||
Info($"Indexing {DownloadsFolder}");
|
||||
VFS.AddRoot(DownloadsFolder);
|
||||
|
||||
AddExternalFolder();
|
||||
|
||||
Info("Cleaning output folder");
|
||||
if (Directory.Exists(ModListOutputFolder)) Directory.Delete(ModListOutputFolder, true);
|
||||
Directory.CreateDirectory(ModListOutputFolder);
|
||||
|
||||
IEnumerable<RawSourceFile> vortexStagingFiles = Directory.EnumerateFiles(StagingFolder, "*", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists() && p != "__vortex_staging_folder")
|
||||
.Select(p => new RawSourceFile(VFS.Lookup(p))
|
||||
{Path = p.RelativeTo(StagingFolder)});
|
||||
|
||||
IEnumerable<RawSourceFile> vortexDownloads = Directory.EnumerateFiles(DownloadsFolder, "*", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists())
|
||||
.Select(p => new RawSourceFile(VFS.Lookup(p))
|
||||
{Path = p.RelativeTo(DownloadsFolder)});
|
||||
|
||||
IEnumerable<RawSourceFile> gameFiles = Directory.EnumerateFiles(GamePath, "*", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists())
|
||||
.Select(p => new RawSourceFile(VFS.Lookup(p))
|
||||
{ Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)) });
|
||||
|
||||
Info("Indexing Archives");
|
||||
IndexedArchives = Directory.EnumerateFiles(DownloadsFolder)
|
||||
.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");
|
||||
IDictionary<VirtualFile, IEnumerable<VirtualFile>> grouped = VFS.GroupedByArchive();
|
||||
IndexedFiles = IndexedArchives.Select(f => grouped.TryGetValue(f.File, out var result) ? result : 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");
|
||||
AllFiles = vortexStagingFiles.Concat(vortexDownloads)
|
||||
.Concat(gameFiles)
|
||||
.DistinctBy(f => f.Path)
|
||||
.ToList();
|
||||
|
||||
Info($"Found {AllFiles.Count} files to build into mod list");
|
||||
|
||||
Info("Verifying destinations");
|
||||
List<IGrouping<string, RawSourceFile>> 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");
|
||||
}
|
||||
|
||||
IEnumerable<ICompilationStep> stack = MakeStack();
|
||||
|
||||
Info("Running Compilation Stack");
|
||||
List<Directive> results = AllFiles.PMap(f => RunStack(stack.Where(s => s != null), f)).ToList();
|
||||
|
||||
IEnumerable<NoMatch> noMatch = results.OfType<NoMatch>().ToList();
|
||||
Info($"No match for {noMatch.Count()} files");
|
||||
foreach (var file in noMatch)
|
||||
Info($" {file.To}");
|
||||
if (noMatch.Any())
|
||||
{
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
|
||||
|
||||
// TODO: nexus stuff
|
||||
/*Info("Getting Nexus api_key, please click authorize if a browser window appears");
|
||||
if (IndexedArchives.Any(a => a.IniData?.General?.gameName != null))
|
||||
{
|
||||
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");
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
GatherArchives();
|
||||
|
||||
ModList = new ModList
|
||||
{
|
||||
Archives = SelectedArchives,
|
||||
ModManager = ModManager.Vortex,
|
||||
Directives = InstallDirectives,
|
||||
GameType = Game
|
||||
};
|
||||
|
||||
ExportModList();
|
||||
|
||||
Info("Done Building ModList");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some have mods outside their game folder located
|
||||
/// </summary>
|
||||
private void AddExternalFolder()
|
||||
{
|
||||
var currentGame = GameRegistry.Games[Game];
|
||||
if (currentGame.AdditionalFolders == null || currentGame.AdditionalFolders.Count == 0) return;
|
||||
currentGame.AdditionalFolders.Do(f =>
|
||||
{
|
||||
var path = f.Replace("%documents%", KnownFolders.Documents.Path);
|
||||
if (!Directory.Exists(path)) return;
|
||||
Info($"Indexing {path}");
|
||||
VFS.AddRoot(path);
|
||||
});
|
||||
}
|
||||
|
||||
private void ExportModList()
|
||||
{
|
||||
Utils.Log($"Exporting ModList to: {ModListOutputFolder}");
|
||||
|
||||
// using JSON for better debugging
|
||||
ModList.ToJSON(Path.Combine(ModListOutputFolder, "modlist.json"));
|
||||
//ModList.ToCERAS(Path.Combine(ModListOutputFolder, "modlist"), ref CerasConfig.Config);
|
||||
|
||||
if(File.Exists(ModListOutputFile))
|
||||
File.Delete(ModListOutputFile);
|
||||
|
||||
using (var fs = new FileStream(ModListOutputFile, FileMode.Create))
|
||||
{
|
||||
using (var za = new ZipArchive(fs, ZipArchiveMode.Create))
|
||||
{
|
||||
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("Exporting ModList metadata");
|
||||
var metadata = new ModlistMetadata.DownloadMetadata
|
||||
{
|
||||
Size = File.GetSize(ModListOutputFile),
|
||||
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);
|
||||
}
|
||||
|
||||
/*private void GenerateReport()
|
||||
{
|
||||
string css;
|
||||
using (var cssStream = Utils.GetResourceStream("Wabbajack.Lib.css-min.css"))
|
||||
using (var reader = new StreamReader(cssStream))
|
||||
{
|
||||
css = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
using (var fs = File.OpenWrite($"{ModList.Name}.md"))
|
||||
{
|
||||
fs.SetLength(0);
|
||||
using (var reporter = new ReportBuilder(fs, ModListOutputFolder))
|
||||
{
|
||||
reporter.Build(this, ModList);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
private void CreateMetaFiles()
|
||||
{
|
||||
Utils.Log("Getting Nexus api_key, please click authorize if a browser window appears");
|
||||
var nexusClient = new NexusApiClient();
|
||||
|
||||
Directory.EnumerateFiles(DownloadsFolder, "*", SearchOption.TopDirectoryOnly)
|
||||
.Where(f => File.Exists(f) && Path.GetExtension(f) != ".meta" && !File.Exists(f+".meta"))
|
||||
.Do(f =>
|
||||
{
|
||||
Utils.Log($"Trying to create meta file for {Path.GetFileName(f)}");
|
||||
var metaString = $"[General]\n" +
|
||||
$"repository=Nexus\n" +
|
||||
$"installed=true\n" +
|
||||
$"uninstalled=false\n" +
|
||||
$"paused=false\n" +
|
||||
$"removed=false\n" +
|
||||
$"gameName={GameName}\n";
|
||||
string hash;
|
||||
using(var md5 = MD5.Create())
|
||||
using (var stream = File.OpenRead(f))
|
||||
{
|
||||
Utils.Log($"Calculating hash for {Path.GetFileName(f)}");
|
||||
byte[] cH = md5.ComputeHash(stream);
|
||||
hash = BitConverter.ToString(cH).Replace("-", "").ToLowerInvariant();
|
||||
Utils.Log($"Hash is {hash}");
|
||||
}
|
||||
|
||||
List<MD5Response> md5Response = nexusClient.GetModInfoFromMD5(Game, hash);
|
||||
if (md5Response.Count >= 1)
|
||||
{
|
||||
var modInfo = md5Response[0].mod;
|
||||
metaString += $"modID={modInfo.mod_id}\ndescription={NexusApiUtils.FixupSummary(modInfo.summary)}\n" +
|
||||
$"modName={modInfo.name}\nfileID={md5Response[0].file_details.file_id}";
|
||||
File.WriteAllText(f+".meta",metaString, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("Error while getting information from nexusmods via MD5 hash!");
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void GatherArchives()
|
||||
{
|
||||
Info("Building a list of archives based on the files required");
|
||||
|
||||
var shas = InstallDirectives.OfType<FromArchive>()
|
||||
.Select(a => a.ArchiveHashPath[0])
|
||||
.Distinct();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private Archive ResolveArchive(string sha, IDictionary<string, IndexedArchive> archives)
|
||||
{
|
||||
if (archives.TryGetValue(sha, out var found))
|
||||
{
|
||||
if(found.IniData == null)
|
||||
Error($"No download metadata found for {found.Name}, please use MO2 to query info or add a .meta file and try again.");
|
||||
|
||||
var result = new Archive();
|
||||
result.State = (AbstractDownloadState) DownloadDispatcher.ResolveArchive(found.IniData);
|
||||
|
||||
if (result.State == null)
|
||||
Error($"{found.Name} could not be handled by any of the downloaders");
|
||||
|
||||
result.Name = found.Name;
|
||||
result.Hash = found.File.Hash;
|
||||
result.Meta = found.Meta;
|
||||
result.Size = found.File.Size;
|
||||
|
||||
Info($"Checking link for {found.Name}");
|
||||
|
||||
if (!result.State.Verify())
|
||||
Error(
|
||||
$"Unable to resolve link for {found.Name}. If this is hosted on the Nexus the file may have been removed.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Error($"No match found for Archive sha: {sha} this shouldn't happen");
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Directive RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
||||
{
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
public override IEnumerable<ICompilationStep> GetStack()
|
||||
{
|
||||
var s = Consts.TestMode ? DownloadsFolder : VortexFolder;
|
||||
var userConfig = Path.Combine(s, "compilation_stack.yml");
|
||||
if (File.Exists(userConfig))
|
||||
return Serialization.Deserialize(File.ReadAllText(userConfig), this);
|
||||
|
||||
IEnumerable<ICompilationStep> stack = MakeStack();
|
||||
|
||||
File.WriteAllText(Path.Combine(s, "_current_compilation_stack.yml"),
|
||||
Serialization.Serialize(stack));
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
public override IEnumerable<ICompilationStep> MakeStack()
|
||||
{
|
||||
Utils.Log("Generating compilation stack");
|
||||
return new List<ICompilationStep>
|
||||
{
|
||||
//new IncludePropertyFiles(this),
|
||||
new IncludeVortexDeployment(this),
|
||||
new IncludeRegex(this, "^*\\.meta"),
|
||||
new IgnoreVortex(this),
|
||||
|
||||
Game == Game.DarkestDungeon ? new IncludeRegex(this, "project\\.xml$") : null,
|
||||
|
||||
new IgnoreStartsWith(this, " __vortex_staging_folder"),
|
||||
new IgnoreEndsWith(this, "__vortex_staging_folder"),
|
||||
|
||||
new IgnoreGameFiles(this),
|
||||
|
||||
new DirectMatch(this),
|
||||
|
||||
new IgnoreGameFiles(this),
|
||||
|
||||
new IgnoreWabbajackInstallCruft(this),
|
||||
|
||||
new DropAll(this)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
307
Wabbajack.Lib/VortexInstaller.cs
Normal file
307
Wabbajack.Lib/VortexInstaller.cs
Normal file
@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public class VortexInstaller
|
||||
{
|
||||
public string ModListArchive { get; }
|
||||
public ModList ModList { get; }
|
||||
public Dictionary<string, string> HashedArchives { get; private set; }
|
||||
|
||||
public GameMetaData GameInfo { get; internal set; }
|
||||
|
||||
public string StagingFolder { get; set; }
|
||||
public string DownloadFolder { get; set; }
|
||||
|
||||
public VirtualFileSystem VFS => VirtualFileSystem.VFS;
|
||||
|
||||
public bool IgnoreMissingFiles { get; internal set; }
|
||||
|
||||
public VortexInstaller(string archive, ModList modList)
|
||||
{
|
||||
ModListArchive = archive;
|
||||
ModList = modList;
|
||||
|
||||
// TODO: only for testing
|
||||
IgnoreMissingFiles = true;
|
||||
|
||||
GameInfo = GameRegistry.Games[ModList.GameType];
|
||||
}
|
||||
|
||||
public void Info(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
}
|
||||
|
||||
public void Status(string msg)
|
||||
{
|
||||
WorkQueue.Report(msg, 0);
|
||||
}
|
||||
|
||||
private void Error(string msg)
|
||||
{
|
||||
Utils.Log(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
public 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))
|
||||
{
|
||||
var entry = ar.GetEntry("modlist");
|
||||
if (entry == null)
|
||||
{
|
||||
entry = ar.GetEntry("modlist.json");
|
||||
using (var e = entry.Open())
|
||||
return e.FromJSON<ModList>();
|
||||
}
|
||||
using (var e = entry.Open())
|
||||
return e.FromCERAS<ModList>(ref CerasConfig.Config);
|
||||
}
|
||||
}
|
||||
|
||||
public void Install()
|
||||
{
|
||||
Directory.CreateDirectory(DownloadFolder);
|
||||
|
||||
VirtualFileSystem.Clean();
|
||||
|
||||
HashArchives();
|
||||
DownloadArchives();
|
||||
HashArchives();
|
||||
|
||||
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
|
||||
if (missing.Count > 0)
|
||||
{
|
||||
foreach (var a in missing)
|
||||
Info($"Unable to download {a.Name}");
|
||||
if (IgnoreMissingFiles)
|
||||
Info("Missing some archives, but continuing anyways at the request of the user");
|
||||
else
|
||||
Error("Cannot continue, was unable to download one or more archives");
|
||||
}
|
||||
|
||||
PrimeVFS();
|
||||
|
||||
BuildFolderStructure();
|
||||
InstallArchives();
|
||||
InstallIncludedFiles();
|
||||
//InctallIncludedDownloadMetas();
|
||||
|
||||
Info("Installation complete! You may exit the program.");
|
||||
}
|
||||
|
||||
private void BuildFolderStructure()
|
||||
{
|
||||
Info("Building Folder Structure");
|
||||
ModList.Directives
|
||||
.OfType<FromArchive>()
|
||||
.Select(d => Path.Combine(StagingFolder, Path.GetDirectoryName(d.To)))
|
||||
.ToHashSet()
|
||||
.Do(f =>
|
||||
{
|
||||
if (Directory.Exists(f)) return;
|
||||
Directory.CreateDirectory(f);
|
||||
});
|
||||
}
|
||||
|
||||
private void InstallArchives()
|
||||
{
|
||||
Info("Installing Archives");
|
||||
Info("Grouping Install Files");
|
||||
var grouped = ModList.Directives
|
||||
.OfType<FromArchive>()
|
||||
.GroupBy(e => e.ArchiveHashPath[0])
|
||||
.ToDictionary(k => k.Key);
|
||||
var archives = ModList.Archives
|
||||
.Select(a => new { Archive = a, AbsolutePath = HashedArchives.GetOrDefault(a.Hash) })
|
||||
.Where(a => a.AbsolutePath != null)
|
||||
.ToList();
|
||||
|
||||
Info("Installing Archives");
|
||||
archives.PMap(a => InstallArchive(a.Archive, a.AbsolutePath, grouped[a.Archive.Hash]));
|
||||
}
|
||||
|
||||
private void InstallArchive(Archive archive, string absolutePath, IGrouping<string, FromArchive> grouping)
|
||||
{
|
||||
Status($"Extracting {archive.Name}");
|
||||
|
||||
var vFiles = grouping.Select(g =>
|
||||
{
|
||||
var file = VFS.FileForArchiveHashPath(g.ArchiveHashPath);
|
||||
g.FromFile = file;
|
||||
return g;
|
||||
}).ToList();
|
||||
|
||||
var onFinish = VFS.Stage(vFiles.Select(f => f.FromFile).Distinct());
|
||||
|
||||
Status($"Copying files for {archive.Name}");
|
||||
|
||||
void CopyFile(string from, string to, bool useMove)
|
||||
{
|
||||
if(File.Exists(to))
|
||||
File.Delete(to);
|
||||
if (useMove)
|
||||
File.Move(from, to);
|
||||
else
|
||||
File.Copy(from, to);
|
||||
}
|
||||
|
||||
vFiles.GroupBy(f => f.FromFile)
|
||||
.DoIndexed((idx, group) =>
|
||||
{
|
||||
Utils.Status("Installing files", idx * 100 / vFiles.Count);
|
||||
var firstDest = Path.Combine(StagingFolder, group.First().To);
|
||||
CopyFile(group.Key.StagedPath, firstDest, true);
|
||||
|
||||
foreach (var copy in group.Skip(1))
|
||||
{
|
||||
var nextDest = Path.Combine(StagingFolder, copy.To);
|
||||
CopyFile(firstDest, nextDest, false);
|
||||
}
|
||||
});
|
||||
|
||||
Status("Unstaging files");
|
||||
onFinish();
|
||||
}
|
||||
|
||||
private void InstallIncludedFiles()
|
||||
{
|
||||
Info("Writing inline files");
|
||||
ModList.Directives.OfType<InlineFile>()
|
||||
.PMap(directive =>
|
||||
{
|
||||
Status($"Writing included file {directive.To}");
|
||||
var outPath = Path.Combine(StagingFolder, directive.To);
|
||||
if(File.Exists(outPath)) File.Delete(outPath);
|
||||
File.WriteAllBytes(outPath, LoadBytesFromPath(directive.SourceDataID));
|
||||
});
|
||||
}
|
||||
|
||||
private void PrimeVFS()
|
||||
{
|
||||
HashedArchives.Do(a => VFS.AddKnown(new VirtualFile
|
||||
{
|
||||
Paths = new[] { a.Value },
|
||||
Hash = a.Key
|
||||
}));
|
||||
VFS.RefreshIndexes();
|
||||
|
||||
|
||||
ModList.Directives
|
||||
.OfType<FromArchive>()
|
||||
.Do(f =>
|
||||
{
|
||||
var updated_path = new string[f.ArchiveHashPath.Length];
|
||||
f.ArchiveHashPath.CopyTo(updated_path, 0);
|
||||
updated_path[0] = VFS.HashIndex[updated_path[0]].Where(e => e.IsConcrete).First().FullPath;
|
||||
VFS.AddKnown(new VirtualFile { Paths = updated_path });
|
||||
});
|
||||
|
||||
VFS.BackfillMissing();
|
||||
}
|
||||
|
||||
private void DownloadArchives()
|
||||
{
|
||||
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
|
||||
Info($"Missing {missing.Count} archives");
|
||||
|
||||
Info("Getting Nexus API Key, if a browser appears, please accept");
|
||||
|
||||
var dispatchers = missing.Select(m => m.State.GetDownloader()).Distinct();
|
||||
|
||||
foreach (var dispatcher in dispatchers)
|
||||
dispatcher.Prepare();
|
||||
|
||||
DownloadMissingArchives(missing);
|
||||
}
|
||||
|
||||
private void DownloadMissingArchives(List<Archive> missing, bool download = true)
|
||||
{
|
||||
if (download)
|
||||
{
|
||||
foreach (var a in missing.Where(a => a.State.GetType() == typeof(ManualDownloader.State)))
|
||||
{
|
||||
var output_path = Path.Combine(DownloadFolder, a.Name);
|
||||
a.State.Download(a, output_path);
|
||||
}
|
||||
}
|
||||
|
||||
missing.Where(a => a.State.GetType() != typeof(ManualDownloader.State))
|
||||
.PMap(archive =>
|
||||
{
|
||||
Info($"Downloading {archive.Name}");
|
||||
var output_path = Path.Combine(DownloadFolder, archive.Name);
|
||||
|
||||
if (!download) return DownloadArchive(archive, download);
|
||||
if (output_path.FileExists())
|
||||
File.Delete(output_path);
|
||||
|
||||
return DownloadArchive(archive, download);
|
||||
});
|
||||
}
|
||||
|
||||
public bool DownloadArchive(Archive archive, bool download)
|
||||
{
|
||||
try
|
||||
{
|
||||
archive.State.Download(archive, Path.Combine(DownloadFolder, archive.Name));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Log($"Download error for file {archive.Name}");
|
||||
Utils.Log(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void HashArchives()
|
||||
{
|
||||
HashedArchives = Directory.EnumerateFiles(DownloadFolder)
|
||||
.Where(e => !e.EndsWith(".sha"))
|
||||
.PMap(e => (HashArchive(e), e))
|
||||
.OrderByDescending(e => File.GetLastWriteTime(e.Item2))
|
||||
.GroupBy(e => e.Item1)
|
||||
.Select(e => e.First())
|
||||
.ToDictionary(e => e.Item1, e => e.Item2);
|
||||
}
|
||||
|
||||
private string HashArchive(string e)
|
||||
{
|
||||
var cache = e + ".sha";
|
||||
if (cache.FileExists() && new FileInfo(cache).LastWriteTime >= new FileInfo(e).LastWriteTime)
|
||||
return File.ReadAllText(cache);
|
||||
|
||||
Status($"Hashing {Path.GetFileName(e)}");
|
||||
File.WriteAllText(cache, e.FileHash());
|
||||
return HashArchive(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -77,6 +77,7 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ACompiler.cs" />
|
||||
<Compile Include="CerasConfig.cs" />
|
||||
<Compile Include="CompilationSteps\ACompilationStep.cs" />
|
||||
<Compile Include="CompilationSteps\DeconstructBSAs.cs" />
|
||||
@ -88,6 +89,7 @@
|
||||
<Compile Include="CompilationSteps\IgnorePathContains.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreRegex.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreStartsWith.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreVortex.cs" />
|
||||
<Compile Include="CompilationSteps\IgnoreWabbajackInstallCruft.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeAll.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeAllConfigs.cs" />
|
||||
@ -101,6 +103,7 @@
|
||||
<Compile Include="CompilationSteps\IncludeStubbedConfigFiles.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeTaggedMods.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeThisProfile.cs" />
|
||||
<Compile Include="CompilationSteps\IncludeVortexDeployment.cs" />
|
||||
<Compile Include="CompilationSteps\IStackStep.cs" />
|
||||
<Compile Include="CompilationSteps\PatchStockESMs.cs" />
|
||||
<Compile Include="CompilationSteps\Serialization.cs" />
|
||||
@ -131,6 +134,8 @@
|
||||
<Compile Include="Validation\DTOs.cs" />
|
||||
<Compile Include="Validation\ValidateModlist.cs" />
|
||||
<Compile Include="ViewModel.cs" />
|
||||
<Compile Include="VortexCompiler.cs" />
|
||||
<Compile Include="VortexInstaller.cs" />
|
||||
<Compile Include="WebAutomation\WebAutomation.cs" />
|
||||
<Compile Include="WebAutomation\WebAutomationWindow.xaml.cs">
|
||||
<DependentUpon>WebAutomationWindow.xaml</DependentUpon>
|
||||
|
@ -14,9 +14,12 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
public class zEditIntegration
|
||||
{
|
||||
public static string FindzEditPath(Compiler compiler)
|
||||
private static Compiler _mo2Compiler;
|
||||
|
||||
public static string FindzEditPath(ACompiler compiler)
|
||||
{
|
||||
var executables = compiler.MO2Ini.customExecutables;
|
||||
_mo2Compiler = (Compiler) compiler;
|
||||
var executables = _mo2Compiler.MO2Ini.customExecutables;
|
||||
if (executables.size == null) return null;
|
||||
|
||||
foreach (var idx in Enumerable.Range(1, int.Parse(executables.size)))
|
||||
@ -35,7 +38,7 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
private Dictionary<string, zEditMerge> _mergesIndexed;
|
||||
|
||||
public IncludeZEditPatches(Compiler compiler) : base(compiler)
|
||||
public IncludeZEditPatches(ACompiler compiler) : base(compiler)
|
||||
{
|
||||
var zEditPath = FindzEditPath(compiler);
|
||||
var havezEdit = zEditPath != null;
|
||||
@ -63,7 +66,7 @@ namespace Wabbajack.Lib
|
||||
|
||||
_mergesIndexed =
|
||||
merges.ToDictionary(
|
||||
m => Path.Combine(compiler.MO2Folder, "mods", m.Key.name, m.Key.filename),
|
||||
m => Path.Combine(_mo2Compiler.MO2Folder, "mods", m.Key.name, m.Key.filename),
|
||||
m => m.First());
|
||||
}
|
||||
|
||||
@ -89,12 +92,12 @@ namespace Wabbajack.Lib
|
||||
|
||||
return new SourcePatch
|
||||
{
|
||||
RelativePath = abs_path.RelativeTo(_compiler.MO2Folder),
|
||||
RelativePath = abs_path.RelativeTo(_mo2Compiler.MO2Folder),
|
||||
Hash = _compiler.VFS[abs_path].Hash
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
var src_data = result.Sources.Select(f => File.ReadAllBytes(Path.Combine(_compiler.MO2Folder, f.RelativePath)))
|
||||
var src_data = result.Sources.Select(f => File.ReadAllBytes(Path.Combine(_mo2Compiler.MO2Folder, f.RelativePath)))
|
||||
.ConcatArrays();
|
||||
|
||||
var dst_data = File.ReadAllBytes(source.AbsolutePath);
|
||||
@ -117,7 +120,7 @@ namespace Wabbajack.Lib
|
||||
[JsonObject("IncludeZEditPatches")]
|
||||
public class State : IState
|
||||
{
|
||||
public ICompilationStep CreateStep(Compiler compiler)
|
||||
public ICompilationStep CreateStep(ACompiler compiler)
|
||||
{
|
||||
return new IncludeZEditPatches(compiler);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Documents;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
71
Wabbajack.Test/AVortexCompilerTest.cs
Normal file
71
Wabbajack.Test/AVortexCompilerTest.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using VFS;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
public abstract class AVortexCompilerTest
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
protected TestUtils utils { get; set; }
|
||||
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
Consts.TestMode = true;
|
||||
|
||||
utils = new TestUtils
|
||||
{
|
||||
GameName = "darkestdungeon"
|
||||
};
|
||||
|
||||
Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f));
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
utils.Dispose();
|
||||
}
|
||||
|
||||
protected VortexCompiler ConfigureAndRunCompiler()
|
||||
{
|
||||
var vortexCompiler = MakeCompiler();
|
||||
vortexCompiler.VFS.Reset();
|
||||
vortexCompiler.DownloadsFolder = utils.DownloadsFolder;
|
||||
vortexCompiler.StagingFolder = utils.InstallFolder;
|
||||
Directory.CreateDirectory(utils.InstallFolder);
|
||||
Assert.IsTrue(vortexCompiler.Compile());
|
||||
return vortexCompiler;
|
||||
}
|
||||
|
||||
protected VortexCompiler MakeCompiler()
|
||||
{
|
||||
VirtualFileSystem.Reconfigure(utils.TestFolder);
|
||||
var vortexCompiler = new VortexCompiler(utils.GameName, utils.GameFolder);
|
||||
return vortexCompiler;
|
||||
}
|
||||
|
||||
protected ModList CompileAndInstall()
|
||||
{
|
||||
var vortexCompiler = ConfigureAndRunCompiler();
|
||||
Install(vortexCompiler);
|
||||
return vortexCompiler.ModList;
|
||||
}
|
||||
|
||||
protected void Install(VortexCompiler vortexCompiler)
|
||||
{
|
||||
var modList = Installer.LoadFromFile(vortexCompiler.ModListOutputFile);
|
||||
var installer = new Installer(vortexCompiler.ModListOutputFile, modList, utils.InstallFolder)
|
||||
{
|
||||
DownloadFolder = utils.DownloadsFolder,
|
||||
GameFolder = utils.GameFolder,
|
||||
};
|
||||
installer.Install();
|
||||
}
|
||||
}
|
||||
}
|
25
Wabbajack.Test/VortexTests.cs
Normal file
25
Wabbajack.Test/VortexTests.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class VortexTests : AVortexCompilerTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestVortexStackSerialization()
|
||||
{
|
||||
utils.AddMod("test");
|
||||
utils.Configure();
|
||||
|
||||
var vortexCompiler = ConfigureAndRunCompiler();
|
||||
var stack = vortexCompiler.MakeStack();
|
||||
|
||||
var serialized = Serialization.Serialize(stack);
|
||||
var rounded = Serialization.Serialize(Serialization.Deserialize(serialized, vortexCompiler));
|
||||
|
||||
Assert.AreEqual(serialized, rounded);
|
||||
Assert.IsNotNull(vortexCompiler.GetStack());
|
||||
}
|
||||
}
|
||||
}
|
@ -94,6 +94,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ACompilerTest.cs" />
|
||||
<Compile Include="AVortexCompilerTest.cs" />
|
||||
<Compile Include="CSP\ChannelTests.cs" />
|
||||
<Compile Include="CSP\CSPTests.cs" />
|
||||
<Compile Include="DownloaderTests.cs" />
|
||||
@ -105,6 +106,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ContentRightsManagementTests.cs" />
|
||||
<Compile Include="CompilationStackTests.cs" />
|
||||
<Compile Include="VortexTests.cs" />
|
||||
<Compile Include="WebAutomationTests.cs" />
|
||||
<Compile Include="zEditIntegrationTests.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -53,6 +53,7 @@ namespace Wabbajack
|
||||
public class InstallationSettings
|
||||
{
|
||||
public string InstallationLocation { get; set; }
|
||||
public string StagingLocation { get; set; }
|
||||
public string DownloadLocation { get; set; }
|
||||
}
|
||||
|
||||
|
@ -196,9 +196,30 @@ namespace Wabbajack
|
||||
|
||||
private async Task ExecuteBegin()
|
||||
{
|
||||
Compiler compiler;
|
||||
try
|
||||
if (false)
|
||||
{
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
var compiler = new VortexCompiler(args[1], args[2]);
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Compiling = true;
|
||||
try
|
||||
{
|
||||
compiler.Compile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Can't continue: {ex.ExceptionToString()}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Compiling = false;
|
||||
}
|
||||
});
|
||||
}else{
|
||||
Compiler compiler;
|
||||
try {
|
||||
compiler = new Compiler(this.Mo2Folder)
|
||||
{
|
||||
MO2Profile = this.MOProfile,
|
||||
@ -209,34 +230,35 @@ namespace Wabbajack
|
||||
ModListWebsite = this.Website,
|
||||
ModListReadme = this.ReadMeText.TargetPath,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Compiler error: {ex.ExceptionToString()}");
|
||||
return;
|
||||
}
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Compiling = true;
|
||||
try
|
||||
{
|
||||
compiler.Compile();
|
||||
if (compiler.ModList?.ReportHTML != null)
|
||||
{
|
||||
this.HTMLReport = compiler.ModList.ReportHTML;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Compiler error: {ex.ExceptionToString()}");
|
||||
return;
|
||||
}
|
||||
finally
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Compiling = false;
|
||||
}
|
||||
});
|
||||
Compiling = true;
|
||||
try
|
||||
{
|
||||
compiler.Compile();
|
||||
if (compiler.ModList?.ReportHTML != null)
|
||||
{
|
||||
this.HTMLReport = compiler.ModList.ReportHTML;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Compiler error: {ex.ExceptionToString()}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Compiling = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Syroot.Windows.IO;
|
||||
using Syroot.Windows.IO;
|
||||
using System;
|
||||
using ReactiveUI;
|
||||
using System.Diagnostics;
|
||||
@ -50,10 +50,15 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public bool InstallingMode { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool IsMO2ModList { get; set; }
|
||||
|
||||
public FilePickerVM Location { get; }
|
||||
|
||||
public FilePickerVM DownloadLocation { get; }
|
||||
|
||||
public FilePickerVM StagingLocation { get; }
|
||||
|
||||
private readonly ObservableAsPropertyHelper<float> _ProgressPercent;
|
||||
public float ProgressPercent => _ProgressPercent.Value;
|
||||
|
||||
@ -114,15 +119,25 @@ namespace Wabbajack
|
||||
this.DownloadLocation.AdditionalError = this.WhenAny(x => x.DownloadLocation.TargetPath)
|
||||
.Select(x => Utils.IsDirectoryPathValid(x));
|
||||
|
||||
StagingLocation = new FilePickerVM
|
||||
{
|
||||
DoExistsCheck = true,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select your Vortex Staging Folder",
|
||||
AdditionalError = this.WhenAny(x => x.StagingLocation.TargetPath)
|
||||
.Select(Utils.IsDirectoryPathValid)
|
||||
};
|
||||
|
||||
// Load settings
|
||||
InstallationSettings settings = this.MWVM.Settings.InstallationSettings.TryCreate(source);
|
||||
this.Location.TargetPath = settings.InstallationLocation;
|
||||
this.DownloadLocation.TargetPath = settings.DownloadLocation;
|
||||
var settings = MWVM.Settings.InstallationSettings.TryCreate(source);
|
||||
this.MWVM.Settings.SaveSignal
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
settings.InstallationLocation = this.Location.TargetPath;
|
||||
settings.DownloadLocation = this.DownloadLocation.TargetPath;
|
||||
settings.DownloadLocation = DownloadLocation.TargetPath;
|
||||
if (IsMO2ModList)
|
||||
settings.InstallationLocation = Location.TargetPath;
|
||||
else
|
||||
settings.StagingLocation = StagingLocation.TargetPath;
|
||||
})
|
||||
.DisposeWith(this.CompositeDisposable);
|
||||
|
||||
@ -148,6 +163,38 @@ namespace Wabbajack
|
||||
});
|
||||
return default(ModListVM);
|
||||
}
|
||||
if (modList.ModManager == ModManager.Vortex)
|
||||
{
|
||||
IsMO2ModList = false;
|
||||
StagingLocation.TargetPath = settings.StagingLocation;
|
||||
|
||||
var vortexFolder =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"Vortex");
|
||||
var stagingFolder = Path.Combine(vortexFolder, GameRegistry.Games[modList.GameType].NexusName,
|
||||
"mods");
|
||||
var downloadFolder = Path.Combine(vortexFolder, "downloads",
|
||||
GameRegistry.Games[modList.GameType].NexusName);
|
||||
MessageBox.Show(
|
||||
"The ModList you are about to install was compiled from a Vortex installation. " +
|
||||
"Vortex support is still very bleeding edge and installing this ModList WILL OVERRIDE your existing mods. " +
|
||||
"If you encounter any errors during installation go to our discord and ping erri120#2285 with your error and a log file.",
|
||||
"Important information regarding Vortex support", MessageBoxButton.OK, MessageBoxImage.Stop);
|
||||
|
||||
if (!Directory.Exists(vortexFolder)) return new ModListVM(modList, modListPath);
|
||||
if (Directory.Exists(stagingFolder) &&
|
||||
File.Exists(Path.Combine(stagingFolder, "__vortex_staging_folder")))
|
||||
StagingLocation.TargetPath = stagingFolder;
|
||||
if (Directory.Exists(Path.Combine(vortexFolder, "downloads")) &&
|
||||
File.Exists(Path.Combine(vortexFolder, "downloads", "__vortex_downloads_folder")))
|
||||
DownloadLocation.TargetPath = downloadFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
Location.TargetPath = settings.InstallationLocation;
|
||||
DownloadLocation.TargetPath = settings.DownloadLocation;
|
||||
IsMO2ModList = true;
|
||||
}
|
||||
return new ModListVM(modList, modListPath);
|
||||
})
|
||||
.ObserveOnGuiThread()
|
||||
@ -219,10 +266,13 @@ namespace Wabbajack
|
||||
this.WhenAny(x => x.Installing),
|
||||
this.WhenAny(x => x.Location.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
resultSelector: (installing, loc, download) =>
|
||||
this.WhenAny(x => x.StagingLocation.InError),
|
||||
resultSelector: (installing, loc, download, staging) =>
|
||||
{
|
||||
if (installing) return false;
|
||||
return !loc && !download;
|
||||
if (IsMO2ModList)
|
||||
return !loc && !download;
|
||||
return !staging && !download;
|
||||
})
|
||||
.ObserveOnGuiThread());
|
||||
this.VisitWebsiteCommand = ReactiveCommand.Create(
|
||||
@ -289,35 +339,68 @@ namespace Wabbajack
|
||||
|
||||
private void ExecuteBegin()
|
||||
{
|
||||
this.Installing = true;
|
||||
this.InstallingMode = true;
|
||||
var installer = new Installer(this.ModListPath, this.ModList.SourceModList, Location.TargetPath)
|
||||
Installing = true;
|
||||
InstallingMode = true;
|
||||
if (ModList.ModManager == ModManager.Vortex)
|
||||
{
|
||||
DownloadFolder = DownloadLocation.TargetPath
|
||||
};
|
||||
var th = new Thread(() =>
|
||||
var installer = new VortexInstaller(ModListPath, ModList.SourceModList)
|
||||
{
|
||||
StagingFolder = StagingLocation.TargetPath,
|
||||
DownloadFolder = DownloadLocation.TargetPath
|
||||
};
|
||||
var th = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
installer.Install();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Installing = false;
|
||||
}
|
||||
})
|
||||
{
|
||||
Priority = ThreadPriority.BelowNormal
|
||||
};
|
||||
th.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
var installer = new Installer(this.ModListPath, this.ModList.SourceModList, Location.TargetPath)
|
||||
{
|
||||
installer.Install();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
}
|
||||
finally
|
||||
DownloadFolder = DownloadLocation.TargetPath
|
||||
};
|
||||
var th = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
installer.Install();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log(ex.StackTrace);
|
||||
Utils.Log(ex.ToString());
|
||||
Utils.Log($"{ex.Message} - Can't continue");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
this.Installing = false;
|
||||
}
|
||||
})
|
||||
{
|
||||
Priority = ThreadPriority.BelowNormal
|
||||
};
|
||||
th.Start();
|
||||
this.Installing = false;
|
||||
}
|
||||
})
|
||||
{
|
||||
Priority = ThreadPriority.BelowNormal
|
||||
};
|
||||
th.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,8 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
@ -18,13 +13,14 @@ namespace Wabbajack
|
||||
{
|
||||
public ModList SourceModList { get; }
|
||||
public string ModListPath { get; }
|
||||
public string Name => this.SourceModList.Name;
|
||||
public string ReportHTML => this.SourceModList.ReportHTML;
|
||||
public string Readme => this.SourceModList.Readme;
|
||||
public string ImageURL => this.SourceModList.Image;
|
||||
public string Author => this.SourceModList.Author;
|
||||
public string Description => this.SourceModList.Description;
|
||||
public string Website => this.SourceModList.Website;
|
||||
public string Name => SourceModList.Name;
|
||||
public string ReportHTML => SourceModList.ReportHTML;
|
||||
public string Readme => SourceModList.Readme;
|
||||
public string ImageURL => SourceModList.Image;
|
||||
public string Author => SourceModList.Author;
|
||||
public string Description => SourceModList.Description;
|
||||
public string Website => SourceModList.Website;
|
||||
public ModManager ModManager => SourceModList.ModManager;
|
||||
|
||||
// Image isn't exposed as a direct property, but as an observable.
|
||||
// This acts as a caching mechanism, as interested parties will trigger it to be created,
|
||||
@ -33,10 +29,10 @@ namespace Wabbajack
|
||||
|
||||
public ModListVM(ModList sourceModList, string modListPath)
|
||||
{
|
||||
this.ModListPath = modListPath;
|
||||
this.SourceModList = sourceModList;
|
||||
ModListPath = modListPath;
|
||||
SourceModList = sourceModList;
|
||||
|
||||
this.ImageObservable = Observable.Return(this.ImageURL)
|
||||
ImageObservable = Observable.Return(this.ImageURL)
|
||||
.ObserveOn(RxApp.TaskpoolScheduler)
|
||||
.Select(url =>
|
||||
{
|
||||
|
@ -277,43 +277,93 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="80" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Installation Location"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding Location}"
|
||||
FontSize="14" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Download Location"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding DownloadLocation}"
|
||||
FontSize="14" />
|
||||
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Visibility="{Binding IsMO2ModList, Converter={StaticResource bool2VisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="40" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Installation Location"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding Location}"
|
||||
FontSize="14" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Download Location"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding DownloadLocation}"
|
||||
FontSize="14" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Visibility="{Binding IsMO2ModList, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="40" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Staging Folder"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding StagingLocation}"
|
||||
FontSize="14" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Download Folder"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding DownloadLocation}"
|
||||
FontSize="14" />
|
||||
</Grid>
|
||||
<local:BeginButton
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="4"
|
||||
Margin="0,0,25,0"
|
||||
HorizontalAlignment="Right"
|
||||
|
@ -185,7 +185,6 @@
|
||||
<DependentUpon>CompilerView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\IsNotNullVisibilityConverter.cs" />
|
||||
<Compile Include="Enums\RunMode.cs" />
|
||||
<Compile Include="Extensions\ReactiveUIExt.cs" />
|
||||
<Compile Include="Views\DownloadWindow.xaml.cs">
|
||||
<DependentUpon>DownloadWindow.xaml</DependentUpon>
|
||||
|
Loading…
Reference in New Issue
Block a user