diff --git a/Wabbajack.Common/GameMetaData.cs b/Wabbajack.Common/GameMetaData.cs index c1e74a9d..3744f62c 100644 --- a/Wabbajack.Common/GameMetaData.cs +++ b/Wabbajack.Common/GameMetaData.cs @@ -4,8 +4,6 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Alphaleonis.Win32.Filesystem; -using Microsoft.Win32; using Wabbajack.Common.StoreHandlers; namespace Wabbajack.Common @@ -28,28 +26,9 @@ namespace Wabbajack.Common Fallout4, [Description("Skyrim VR")] SkyrimVR, - //VORTEX GAMES - [Description("Darkest Dungeon")] - DarkestDungeon, - [Description("Divinity Original Sin 2")] - DivinityOriginalSin2, - [Description("Divinity Original Sin 2 Definitive Edition")] - DivinityOriginalSin2DE, //definitive edition has its own nexus page but same Steam/GOG ids - Starbound, - [Description("Star Wars: Knights of the Old Republic")] - SWKOTOR, - [Description("Star Wars: Knights of the Old Republic 2")] - SWKOTOR2, - Witcher, - [Description("Witcher 2")] - Witcher2, - [Description("Witcher 3")] - Witcher3, - [Description("Stardew Valley")] - StardewValley } - public static class GameExtentions + public static class GameExtensions { public static GameMetaData MetaData(this Game game) { @@ -59,30 +38,36 @@ namespace Wabbajack.Common public class GameMetaData { - public ModManager SupportedModManager { get; internal set; } - public string? MO2ArchiveName { get; internal set; } public Game Game { get; internal set; } + public ModManager SupportedModManager { get; internal set; } + + public string? MO2ArchiveName { get; internal set; } public string? NexusName { get; internal set; } // Nexus DB id for the game, used in some specific situations public long NexusGameId { get; internal set; } public string? MO2Name { get; internal set; } - public string HumanFriendlyGameName => Game.GetDescription(); - - public string? GameLocationRegistryKey { get; internal set; } // to get steam ids: https://steamdb.info public List? SteamIDs { get; internal set; } + // to get gog ids: https://www.gogdb.org public List? GOGIDs { get; internal set; } - // these are additional folders when a game installs mods outside the game folder - public List? AdditionalFolders { get; internal set; } + + // to get BethNet IDs: check the registry + public int BethNetID { get; internal set; } + //for BethNet games only! + public string RegString { get; internal set; } = string.Empty; + // file to check if the game is present, useful when steamIds and gogIds dont help public List? RequiredFiles { get; internal set; } - public bool Disabled { get; internal set; } + + public string? MainExecutable { get; internal set; } // Games that this game are commonly confused with, for example Skyrim SE vs Skyrim LE public Game[] CommonlyConfusedWith { get; set; } = Array.Empty(); - + + public string HumanFriendlyGameName => Game.GetDescription(); + public string InstalledVersion { get @@ -98,8 +83,6 @@ namespace Wabbajack.Common public bool IsInstalled => TryGetGameLocation() != null; - public string? MainExecutable { get; internal set; } - public AbsolutePath? TryGetGameLocation() { return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.TryGetGamePath(Game); @@ -113,11 +96,9 @@ namespace Wabbajack.Common path = ret.Value; return true; } - else - { - path = default; - return false; - } + + path = default; + return false; } public AbsolutePath GameLocation() @@ -146,16 +127,12 @@ namespace Wabbajack.Common throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue)); } var memberInfo = type.GetMember(enumerationValue.ToString()!); - if(memberInfo.Length > 0) - { - var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + if (memberInfo.Length <= 0) + return enumerationValue.ToString()!; - if(attrs.Length > 0) - { - return ((DescriptionAttribute)attrs[0]).Description; - } - } - return enumerationValue.ToString()!; + var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + + return attrs.Length > 0 ? ((DescriptionAttribute)attrs[0]).Description : enumerationValue.ToString(); } } @@ -163,8 +140,8 @@ namespace Wabbajack.Common { public static GameMetaData GetByMO2ArchiveName(string gameName) { - var gamename = gameName.ToLower(); - return Games.Values.FirstOrDefault(g => g.MO2ArchiveName?.ToLower() == gamename); + gameName = gameName.ToLower(); + return Games.Values.FirstOrDefault(g => g.MO2ArchiveName?.ToLower() == gameName); } public static GameMetaData GetByNexusName(string gameName) @@ -183,6 +160,7 @@ namespace Wabbajack.Common /// Name to query /// GameMetaData found /// If string could not be translated to a game + /// public static GameMetaData GetByFuzzyName(string someName) { return TryGetByFuzzyName(someName) ?? throw new ArgumentNullException($"{someName} could not be translated to a game"); @@ -192,6 +170,7 @@ namespace Wabbajack.Common /// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name. /// Name to query /// GameMetaData if found, otherwise null + /// public static GameMetaData? TryGetByFuzzyName(string someName) { if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata!).MetaData(); @@ -225,13 +204,14 @@ namespace Wabbajack.Common { SupportedModManager = ModManager.MO2, Game = Game.Morrowind, - Disabled = false, SteamIDs = new List{22320}, GOGIDs = new List{1440163901, 1435828767}, NexusName = "morrowind", NexusGameId = 100, MO2Name = "Morrowind", MO2ArchiveName = "morrowind", + BethNetID = 31, + RegString = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\The Elder Scrolls III: Morrowind Game of the Year Edition", RequiredFiles = new List { "Morrowind.exe" @@ -248,7 +228,6 @@ namespace Wabbajack.Common NexusGameId = 101, MO2Name = "Oblivion", MO2ArchiveName = "oblivion", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Oblivion", SteamIDs = new List {22330}, GOGIDs = new List{1458058109}, RequiredFiles = new List @@ -268,7 +247,6 @@ namespace Wabbajack.Common NexusGameId = 120, MO2Name = "fallout3", MO2ArchiveName = "fallout3", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout3", SteamIDs = new List {22300, 22370}, // base game and GotY RequiredFiles = new List { @@ -287,7 +265,6 @@ namespace Wabbajack.Common NexusGameId = 130, MO2Name = "New Vegas", MO2ArchiveName = "falloutnv", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\falloutnv", SteamIDs = new List {22380, 22490}, // normal and RU version GOGIDs = new List{1454587428}, RequiredFiles = new List @@ -306,7 +283,6 @@ namespace Wabbajack.Common NexusGameId = 110, MO2Name = "Skyrim", MO2ArchiveName = "skyrim", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\skyrim", SteamIDs = new List {72850}, RequiredFiles = new List { @@ -325,7 +301,6 @@ namespace Wabbajack.Common NexusGameId = 1704, MO2Name = "Skyrim Special Edition", MO2ArchiveName = "skyrimse", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim Special Edition", SteamIDs = new List {489830}, RequiredFiles = new List { @@ -344,7 +319,6 @@ namespace Wabbajack.Common NexusGameId = 1151, MO2Name = "Fallout 4", MO2ArchiveName = "fallout4", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout4", SteamIDs = new List {377160}, RequiredFiles = new List { @@ -353,17 +327,6 @@ namespace Wabbajack.Common MainExecutable = "Fallout4.exe" } }, - /*{ - Game.Fallout4VR, new GameMetaData - { - SupportedModManager = ModManager.MO2, - Game = Game.Fallout4VR, - NexusName = "fallout4", - MO2Name = "Fallout 4", - MO2ArchiveName = "fallout4", - SteamIDs = new List{611660} - } - },*/ { Game.SkyrimVR, new GameMetaData { @@ -373,7 +336,6 @@ namespace Wabbajack.Common NexusGameId = 1704, MO2Name = "Skyrim VR", MO2ArchiveName = "skyrimse", - GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim VR", SteamIDs = new List {611670}, RequiredFiles = new List { @@ -383,155 +345,6 @@ namespace Wabbajack.Common CommonlyConfusedWith = new []{Game.Skyrim, Game.SkyrimSpecialEdition} } }, - { - Game.DarkestDungeon, new GameMetaData - { - Game = Game.DarkestDungeon, - NexusName = "darkestdungeon", - NexusGameId = 804, - SteamIDs = new List {262060}, - GOGIDs = new List{1450711444}, - RequiredFiles = new List - { - "_windows\\Darkest.exe" - } - } - }, - { - Game.DivinityOriginalSin2DE, new GameMetaData - { - Game = Game.DivinityOriginalSin2DE, - NexusName = "divinityoriginalsin2definitiveedition", - NexusGameId = 2569, - SteamIDs = new List {435150}, - GOGIDs = new List{1584823040}, - AdditionalFolders = new List - { - "%documents%\\Larian Studios\\Divinity Original Sin 2 Definitive Edition\\Mods\\" - }, - RequiredFiles = new List - { - "DefEd\\bin\\SupportTool.exe" - } - } - }, - { - Game.DivinityOriginalSin2, new GameMetaData - { - Game = Game.DivinityOriginalSin2, - NexusName = "divinityoriginalsin2", - NexusGameId = 1661, - SteamIDs = new List {435150}, - GOGIDs = new List{1584823040}, - AdditionalFolders = new List - { - "%documents%\\Larian Studios\\Divinity Original Sin 2\\Mods\\", - }, - RequiredFiles = new List - { - "bin\\SupportTool.exe" - } - } - }, - { - Game.Starbound, new GameMetaData - { - Game = Game.Starbound, - NexusName = "starbound", - NexusGameId = 242, - SteamIDs = new List{211820}, - GOGIDs = new List{1452598881}, - RequiredFiles = new List - { - "win64\\starbound.exe" - } - } - }, - { - Game.SWKOTOR, new GameMetaData - { - Game = Game.SWKOTOR, - NexusName = "kotor", - NexusGameId = 234, - SteamIDs = new List{32370}, - GOGIDs = new List{1207666283}, - RequiredFiles = new List - { - "swkotor.exe" - } - } - }, - { - Game.SWKOTOR2, new GameMetaData - { - Game = Game.SWKOTOR2, - NexusName = "kotor2", - NexusGameId = 198, - SteamIDs = new List{208580}, - GOGIDs = new List{1421404581}, - RequiredFiles = new List - { - "swkotor2.exe" - } - } - }, - { - Game.Witcher, new GameMetaData - { - Game = Game.Witcher, - NexusName = "witcher", - NexusGameId = 150, - SteamIDs = new List{20900}, - GOGIDs = new List{1207658924}, - RequiredFiles = new List - { - "system\\witcher.exe" - } - } - }, - { - Game.Witcher2, new GameMetaData - { - Game = Game.Witcher2, - NexusName = "witcher2", - NexusGameId = 153, - SteamIDs = new List{20920}, - GOGIDs = new List{1207658930}, - RequiredFiles = new List - { - "bin\\witcher2.exe", - "bin\\userContentManager.exe" - } - } - }, - { - Game.Witcher3, new GameMetaData - { - Game = Game.Witcher3, - NexusName = "witcher3", - NexusGameId = 952, - SteamIDs = new List{292030, 499450}, // normal and GotY - GOGIDs = new List{1207664643, 1495134320, 1207664663, 1640424747}, // normal, GotY and both in packages - RequiredFiles = new List - { - "bin\\x64\\witcher2.exe" - } - } - }, - { - Game.StardewValley, new GameMetaData - { - Game = Game.StardewValley, - NexusName = "stardewvalley", - NexusGameId = 1303, - SteamIDs = new List{413150}, - GOGIDs = new List{1453375253}, - RequiredFiles = new List - { - "Stardew Valley.exe" - } - } - }, { Game.Enderal, new GameMetaData { diff --git a/Wabbajack.Common/Paths.cs b/Wabbajack.Common/Paths.cs index 5073ca4b..c4fa3bf3 100644 --- a/Wabbajack.Common/Paths.cs +++ b/Wabbajack.Common/Paths.cs @@ -220,12 +220,13 @@ namespace Wabbajack.Common /// Assuming the path is a folder, enumerate all the files in the folder /// /// if true, also returns files in sub-folders + /// pattern to match against /// - public IEnumerable EnumerateFiles(bool recursive = true) + public IEnumerable EnumerateFiles(bool recursive = true, string pattern = "*") { if (!IsDirectory) return new AbsolutePath[0]; return Directory - .EnumerateFiles(_path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .EnumerateFiles(_path, pattern, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) .Select(path => new AbsolutePath(path, true)); } diff --git a/Wabbajack.Common/StoreHandlers/BethNetHandler.cs b/Wabbajack.Common/StoreHandlers/BethNetHandler.cs new file mode 100644 index 00000000..9a02134d --- /dev/null +++ b/Wabbajack.Common/StoreHandlers/BethNetHandler.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using Microsoft.Win32; + +#nullable enable + +namespace Wabbajack.Common.StoreHandlers +{ + public class BethNetGame : AStoreGame + { + public override Game Game { get; internal set; } + public override StoreType Type { get; internal set; } = StoreType.BethNet; + } + + public class BethNetHandler : AStoreHandler + { + public override StoreType Type { get; internal set; } = StoreType.BethNet; + + private const string RegKey = @"SOFTWARE\WOW6432Node\bethesda softworks\Bethesda.net"; + + public AbsolutePath BethPath { get; set; } + private AbsolutePath Launcher => new RelativePath("BethesdaNetLauncher.exe").RelativeTo(BethPath); + private AbsolutePath GamesFolder => new RelativePath("games").RelativeTo(BethPath); + + public override bool Init() + { + try + { + var key = Registry.LocalMachine.OpenSubKey(RegKey); + + var pathKey = key?.GetValue("installLocation"); + if (pathKey == null) + { + Utils.Error(new StoreException("Could not open the BethNetPath registry key!")); + return false; + } + + var bethPath = pathKey.ToString() ?? string.Empty; + if (string.IsNullOrWhiteSpace(bethPath)) + { + Utils.Error(new StoreException("Path to the BethNet Directory from registry is Null or Empty!")); + return false; + } + + BethPath = (AbsolutePath)bethPath; + + if (!BethPath.IsDirectory || !BethPath.Exists) + { + Utils.Error(new StoreException($"Path to the BethNet Directory from registry does not exists: {BethPath}")); + return false; + } + + if (Launcher.Exists && Launcher.IsFile) + return true; + + Utils.Error(new StoreException($"The BethNet Launcher could not be located: {Launcher}")); + return false; + } + catch (SecurityException se) + { + Utils.Error(se, "BethNetHandler could not read from registry!"); + } + catch (UnauthorizedAccessException uae) + { + Utils.Error(uae, "BethNetHandler could not read from registry!"); + } + + return false; + } + + public override bool LoadAllGames() + { + List possibleGames = new List(); + // games folder + + if (!GamesFolder.Exists) + { + Utils.Error(new StoreException($"The GamesFolder for BethNet at {GamesFolder} does not exist!")); + return false; + } + + if (GamesFolder.Exists && GamesFolder.IsDirectory) + { + GamesFolder.EnumerateDirectories(false).Do(d => + { + var files = d.EnumerateFiles(); + var game = GameRegistry.Games.Values + .FirstOrDefault(g => g.RequiredFiles.All(f => + { + var absPath = new RelativePath(f).RelativeTo(d); + return files.Contains(absPath); + })); + + if (game != null) + { + possibleGames.Add(new BethNetGame + { + Game = game.Game, + ID = game.BethNetID, + Name = game.Game.ToString(), + Path = d, + Type = StoreType.BethNet + }); + } + else + { + Utils.Log($"BethNet Game at {d} is not supported!"); + } + }); + } + + possibleGames.Do(g => + { + try + { + var regString = g.Game.MetaData().RegString; + var regKey = Registry.LocalMachine.OpenSubKey(regString); + regString = @"HKEY_LOCAL_MACHINE\" + regString; + if (regKey == null) + { + Utils.Error(new StoreException($@"Could not open registry key at {regString}")); + return; + } + + var pathValue = regKey.GetValue("Path"); + var uninstallStringValue = regKey.GetValue("UninstallString"); + + if (pathValue == null || uninstallStringValue == null) + { + Utils.Error(new StoreException($@"Could not get Value from either {regString}\Path or UninstallString")); + return; + } + + var path = pathValue.ToString() ?? string.Empty; + var uninstallString = uninstallStringValue.ToString() ?? string.Empty; + + if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(uninstallString)) + { + Utils.Error(new StoreException($@"Path or UninstallString is null or empty for {regString}!")); + return; + } + + path = FixRegistryValue(path); + + if ((AbsolutePath)path != g.Path) + { + Utils.Error(new StoreException($"Path from registry does not equal game path: {path} != {g.Path} at {regString}")); + return; + } + + var split = uninstallString.Split("\""); + if (split.Length != 3) + { + Utils.Error(new StoreException($"UninstallString at {regString} can not be split into 3 parts!")); + return; + } + + var updaterPath = (AbsolutePath)split[1]; + var args = split[2].Trim(); + + if (!updaterPath.Exists) + { + Utils.Error(new StoreException($"UpdaterPath from {regString} does not exist at {updaterPath}")); + return; + } + + if (updaterPath.Parent != BethPath) + { + Utils.Error(new StoreException($"Parent of UpdatePath from {regString} is not BethPath: {updaterPath.Parent} != {BethPath}")); + return; + } + + if (!args.Equals($"bethesdanet://uninstall/{g.ID}", StringComparison.InvariantCultureIgnoreCase)) + { + Utils.Error(new StoreException($"Uninstall arguments from {regString} is not valid: {args}")); + return; + } + + Utils.Log($"Found BethNet game \"{g.Game}\" ({g.ID}) at {g.Path}"); + + Games.Add(g); + } + catch (SecurityException se) + { + Utils.Error(se, "BethNetHandler could not read from registry!"); + } + catch (UnauthorizedAccessException uae) + { + Utils.Error(uae, "BethNetHandler could not read from registry!"); + } + }); + + Utils.Log($"Total number of BethNet Games found: {Games.Count}"); + + return Games.Count != 0; + } + + private static string FixRegistryValue(string value) + { + var s = value; + if (s.StartsWith("\"")) + s = s[1..]; + if (s.EndsWith("\"")) + s = s[..^1]; + return s; + } + } +} diff --git a/Wabbajack.Common/StoreHandlers/GOGHandler.cs b/Wabbajack.Common/StoreHandlers/GOGHandler.cs index 8634688e..f475fe1f 100644 --- a/Wabbajack.Common/StoreHandlers/GOGHandler.cs +++ b/Wabbajack.Common/StoreHandlers/GOGHandler.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using System.Security; using Microsoft.Win32; diff --git a/Wabbajack.Common/StoreHandlers/SteamHandler.cs b/Wabbajack.Common/StoreHandlers/SteamHandler.cs index 6f9af723..3fec8415 100644 --- a/Wabbajack.Common/StoreHandlers/SteamHandler.cs +++ b/Wabbajack.Common/StoreHandlers/SteamHandler.cs @@ -1,9 +1,7 @@ using System; -using System.IO; using System.Collections.Generic; using System.Linq; using System.Security; -using DynamicData; using Microsoft.Win32; #nullable enable @@ -14,7 +12,7 @@ namespace Wabbajack.Common.StoreHandlers public override Game Game { get; internal set; } public override StoreType Type { get; internal set; } = StoreType.STEAM; - public string Universe = string.Empty; + public AbsolutePath Universe; public readonly List WorkshopItems = new List(); public int WorkshopItemsSizeOnDisk; @@ -38,10 +36,9 @@ namespace Wabbajack.Common.StoreHandlers private const string SteamRegKey = @"Software\Valve\Steam"; - public string SteamPath { get; set; } = string.Empty; - private List? SteamUniverses { get; set; } - - private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf"); + public AbsolutePath SteamPath { get; set; } + private AbsolutePath SteamConfig => new RelativePath("config//config.vdf").RelativeTo(SteamPath); + private List? SteamUniverses { get; set; } public override bool Init() { @@ -56,20 +53,22 @@ namespace Wabbajack.Common.StoreHandlers return false; } - SteamPath = steamPathKey.ToString() ?? string.Empty; - if (string.IsNullOrWhiteSpace(SteamPath)) + var steamPath = steamPathKey.ToString() ?? string.Empty; + if (string.IsNullOrWhiteSpace(steamPath)) { Utils.Error(new StoreException("Path to the Steam Directory from registry is Null or Empty!")); return false; } - if (!Directory.Exists(SteamPath)) + SteamPath = new AbsolutePath(steamPath); + + if (!SteamPath.Exists) { Utils.Error(new StoreException($"Path to the Steam Directory from registry does not exists: {SteamPath}")); return false; } - if (File.Exists(SteamConfig)) + if (SteamConfig.Exists) return true; Utils.Error(new StoreException($"The Steam config file could not be read: {SteamConfig}")); @@ -88,38 +87,39 @@ namespace Wabbajack.Common.StoreHandlers return false; } - private List LoadUniverses() + private List LoadUniverses() { - var ret = new List(); + var ret = new List(); - File.ReadAllLines(SteamConfig).Do(l => + SteamConfig.ReadAllLines().Do(l => { if (!l.ContainsCaseInsensitive("BaseInstallFolder_")) return; - var s = GetVdfValue(l); - s = Path.Combine(s, "steamapps"); - if (!Directory.Exists(s)) + var s = new AbsolutePath(GetVdfValue(l)); + var path = new RelativePath("steamapps").RelativeTo(s); + + if (!path.Exists) { - Utils.Log($"Directory {s} does not exist, skipping"); + Utils.Log($"Directory {path} does not exist, skipping"); return; } - ret.Add(s); - Utils.Log($"Steam Library found at {s}"); + ret.Add(path); + Utils.Log($"Steam Library found at {path}"); }); Utils.Log($"Total number of Steam Libraries found: {ret.Count}"); // Default path in the Steam folder isn't in the configs - if (Directory.Exists(Path.Combine(SteamPath, "steamapps"))) - ret.Add(Path.Combine(SteamPath, "steamapps")); + var defaultPath = new RelativePath("steamapps").RelativeTo(SteamPath); + if(defaultPath.Exists) + ret.Add(defaultPath); return ret; } public override bool LoadAllGames() { - if (SteamUniverses == null) - SteamUniverses = LoadUniverses(); + SteamUniverses ??= LoadUniverses(); if (SteamUniverses.Count == 0) { @@ -131,12 +131,15 @@ namespace Wabbajack.Common.StoreHandlers { Utils.Log($"Searching for Steam Games in {u}"); - Directory.EnumerateFiles(u, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f => + u.EnumerateFiles(false, "*.acf") + .Where(a => a.Exists) + .Where(a => a.IsFile) + .Do(f => { var game = new SteamGame(); var gotID = false; - File.ReadAllLines(f).Do(l => + f.ReadAllLines().Do(l => { if (l.ContainsCaseInsensitive("\"appid\"")) { @@ -152,9 +155,9 @@ namespace Wabbajack.Common.StoreHandlers if (!l.ContainsCaseInsensitive("\"installdir\"")) return; - var path = Path.Combine(u, "common", GetVdfValue(l)); - if (Directory.Exists(path)) - game.Path = (AbsolutePath)path; + var path = new RelativePath($"common//{GetVdfValue(l)}").RelativeTo(u); + if (path.Exists) + game.Path = path; }); if (!gotID || !game.Path.IsDirectory) return; @@ -189,18 +192,21 @@ namespace Wabbajack.Common.StoreHandlers private static void LoadWorkshopItems(SteamGame game) { - var workshop = Path.Combine(game.Universe, "workshop"); - if (!Directory.Exists(workshop)) + var workshop = new RelativePath("workshop").RelativeTo(game.Universe); + if (!workshop.Exists) return; - Directory.EnumerateFiles(workshop, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f => + workshop.EnumerateFiles(false, "*.acf") + .Where(f => f.Exists) + .Where(f => f.IsFile) + .Do(f => { - if (Path.GetFileName(f) != $"appworkshop{game.ID}.acf") + if (f.FileName.ToString() != $"appworkshop{game.ID}.acf") return; Utils.Log($"Found Steam Workshop item file {f} for \"{game.Name}\""); - var lines = File.ReadAllLines(f); + var lines = f.ReadAllLines().ToList(); var end = false; var foundAppID = false; var workshopItemsInstalled = 0; @@ -214,8 +220,8 @@ namespace Wabbajack.Common.StoreHandlers { if (end) return; - if (currentItem == null) - currentItem = new SteamWorkshopItem(game); + + currentItem ??= new SteamWorkshopItem(game); var currentLine = lines.IndexOf(l); if (l.ContainsCaseInsensitive("\"appid\"") && !foundAppID) diff --git a/Wabbajack.Common/StoreHandlers/StoreHandler.cs b/Wabbajack.Common/StoreHandlers/StoreHandler.cs index 0547aa28..dd1220c5 100644 --- a/Wabbajack.Common/StoreHandlers/StoreHandler.cs +++ b/Wabbajack.Common/StoreHandlers/StoreHandler.cs @@ -7,13 +7,14 @@ namespace Wabbajack.Common.StoreHandlers public enum StoreType { STEAM, - GOG + GOG, + BethNet } public class StoreHandler { - private static readonly Lazy instance = new Lazy(() => new StoreHandler(), true); - public static StoreHandler Instance => instance.Value; + private static readonly Lazy _instance = new Lazy(() => new StoreHandler(), true); + public static StoreHandler Instance => _instance.Value; private static readonly Lazy _steamHandler = new Lazy(() => new SteamHandler()); public SteamHandler SteamHandler = _steamHandler.Value; @@ -21,6 +22,9 @@ namespace Wabbajack.Common.StoreHandlers private static readonly Lazy _gogHandler = new Lazy(() => new GOGHandler()); public GOGHandler GOGHandler = _gogHandler.Value; + private static readonly Lazy _bethNetHandler = new Lazy(() => new BethNetHandler()); + public BethNetHandler BethNetHandler = _bethNetHandler.Value; + public List StoreGames; public StoreHandler() @@ -50,6 +54,18 @@ namespace Wabbajack.Common.StoreHandlers { Utils.Error(new StoreException("Could not Init the GOGHandler, check previous error messages!")); } + + if (BethNetHandler.Init()) + { + if (BethNetHandler.LoadAllGames()) + StoreGames.AddRange(BethNetHandler.Games); + else + Utils.Error(new StoreException("Could not load all Games from the BethNetHandler, check previous error messages!")); + } + else + { + Utils.Error(new StoreException("Could not Init the BethNetHandler, check previous error messages!")); + } } public AbsolutePath? TryGetGamePath(Game game) diff --git a/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs b/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs index cdf1ad5a..20db6b44 100644 --- a/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs +++ b/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Wabbajack.Common; @@ -60,13 +57,13 @@ namespace Wabbajack.Lib.Downloaders { var currentLib = Item.Game.Universe; - var downloadFolder = Path.Combine(currentLib, "workshop", "downloads", Item.Game.ID.ToString()); - var contentFolder = Path.Combine(currentLib, "workshop", "content", Item.Game.ID.ToString()); + var downloadFolder = new RelativePath($"workshop//downloads//{Item.Game.ID}").RelativeTo(currentLib); + var contentFolder = new RelativePath($"workshop//content//{Item.Game.ID}").RelativeTo(currentLib); var p = new Process { StartInfo = new ProcessStartInfo { - FileName = Path.Combine(StoreHandler.Instance.SteamHandler.SteamPath, "steam.exe"), + FileName = new RelativePath("steam.exe").RelativeTo(StoreHandler.Instance.SteamHandler.SteamPath).ToString(), CreateNoWindow = true, Arguments = $"console +workshop_download_item {Item.Game.ID} {Item.ItemID}" } @@ -76,10 +73,12 @@ namespace Wabbajack.Lib.Downloaders //TODO: async var finished = false; + var itemDownloadPath = new RelativePath(Item.ItemID.ToString()).RelativeTo(downloadFolder); + var itemContentPath = new RelativePath(Item.ItemID.ToString()).RelativeTo(contentFolder); while (!finished) { - if(!Directory.Exists(Path.Combine(downloadFolder, Item.ItemID.ToString()))) - if (Directory.Exists(Path.Combine(contentFolder, Item.ItemID.ToString()))) + if(!itemDownloadPath.Exists) + if(itemContentPath.Exists) finished = true; Thread.Sleep(1000); diff --git a/Wabbajack/App.xaml.cs b/Wabbajack/App.xaml.cs index d0b62a49..033b3fa5 100644 --- a/Wabbajack/App.xaml.cs +++ b/Wabbajack/App.xaml.cs @@ -8,6 +8,7 @@ using System.Windows; using System.Windows.Interop; using System.Windows.Media; using Wabbajack.Common; +using Wabbajack.Common.StoreHandlers; using Wabbajack.Util; namespace Wabbajack @@ -24,6 +25,7 @@ namespace Wabbajack CLIOld.ParseOptions(Environment.GetCommandLineArgs()); if (CLIArguments.Help) CLIOld.DisplayHelpText(); + var storeHandler = new StoreHandler(); } } }