diff --git a/Wabbajack.Common/StoreHandlers/BethNetHandler.cs b/Wabbajack.Common/StoreHandlers/BethNetHandler.cs deleted file mode 100644 index 7c94b91a..00000000 --- a/Wabbajack.Common/StoreHandlers/BethNetHandler.cs +++ /dev/null @@ -1,213 +0,0 @@ -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 AbsolutePath InstallPath; - - } - - 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); - }) ?? true); - - 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/EpicGameStoreHandler.cs b/Wabbajack.Common/StoreHandlers/EpicGameStoreHandler.cs deleted file mode 100644 index 10c152c3..00000000 --- a/Wabbajack.Common/StoreHandlers/EpicGameStoreHandler.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Linq; -using Microsoft.Win32; - -namespace Wabbajack.Common.StoreHandlers -{ - public class EpicGameStoreHandler : AStoreHandler - { - public override StoreType Type { get; internal set; } - - public string BaseRegKey = @"SOFTWARE\Epic Games\EOS"; - public override bool Init() - { - return true; - } - - public override bool LoadAllGames() - { - try - { - using var eosKey = Registry.CurrentUser.OpenSubKey(BaseRegKey); - if (eosKey == null) - { - Utils.Log("Epic Game Store is not installed"); - return false; - } - - var name = eosKey.GetValue("ModSdkMetadataDir"); - if (name == null) - { - Utils.Log("Registry key entry does not exist for Epic Game store"); - return false; - } - - var byID = GameRegistry.Games.SelectMany(g => g.Value.EpicGameStoreIDs - .Select(id => (id, g.Value.Game))) - .GroupBy(t => t.id) - .ToDictionary(t => t.Key, t => t.First().Game); - - foreach (var itm in ((AbsolutePath)(string)(name!)).EnumerateFiles(false, "*.item")) - { - try - { - var item = itm.FromJson(); - Utils.Log($"Found Epic Game Store Game: {item.DisplayName} at {item.InstallLocation}"); - - if (byID.TryGetValue(item.CatalogItemId, out var game)) - { - Games.Add(new EpicStoreGame(game, item)); - } - } - catch (Newtonsoft.Json.JsonReaderException) - { - Utils.Log($"Failure parsing Epic Game Store manifest: {itm}"); - } - - } - } - catch (NullReferenceException) - { - Utils.Log("Epic Game Store is does not appear to be installed"); - return false; - } - - return true; - } - - public class EpicStoreGame : AStoreGame - { - public EpicStoreGame(Game game, EpicGameItem item) - { - Type = StoreType.EpicGameStore; - Game = game; - Path = (AbsolutePath)item.InstallLocation; - Name = game.MetaData().HumanFriendlyGameName; - - } - - public override Game Game { get; internal set; } - public override StoreType Type { get; internal set; } - } - - public class EpicGameItem - { - public string DisplayName { get; set; } = ""; - public string InstallationGuid { get; set; } = ""; - public string CatalogItemId { get; set; } = ""; - public string CatalogNamespace { get; set; } = ""; - public string InstallSessionId { get; set; } = ""; - public string InstallLocation { get; set; } = ""; - } - } -} diff --git a/Wabbajack.Common/StoreHandlers/GOGHandler.cs b/Wabbajack.Common/StoreHandlers/GOGHandler.cs deleted file mode 100644 index f475fe1f..00000000 --- a/Wabbajack.Common/StoreHandlers/GOGHandler.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Linq; -using System.Security; -using Microsoft.Win32; - -namespace Wabbajack.Common.StoreHandlers -{ - public class GOGGame : AStoreGame - { - public override Game Game { get; internal set; } - public override StoreType Type { get; internal set; } = StoreType.GOG; - } - - public class GOGHandler : AStoreHandler - { - public override StoreType Type { get; internal set; } - - private const string GOGRegKey = @"Software\GOG.com\Games"; - private const string GOG64RegKey = @"Software\WOW6432Node\GOG.com\Games"; - - private RegistryKey? GOGKey { get; set; } - - public override bool Init() - { - try - { - var gogKey = Registry.LocalMachine.OpenSubKey(GOGRegKey) ?? - Registry.LocalMachine.OpenSubKey(GOG64RegKey); - - if (gogKey == null) - { - Utils.Error(new StoreException("Could not open the GOG registry key!")); - return false; - } - - GOGKey = gogKey; - return true; - } - catch (SecurityException se) - { - Utils.Error(se, "GOGHandler could not read from registry!"); - } - catch (UnauthorizedAccessException uae) - { - Utils.Error(uae, "GOGHandler could not read from registry!"); - } - - return false; - } - - public override bool LoadAllGames() - { - if (GOGKey == null) - { - Utils.Error("GOGHandler could not read from registry!"); - return false; - } - try - { - string[] keys = GOGKey.GetSubKeyNames(); - Utils.Log($"Found {keys.Length} SubKeys for GOG"); - - keys.Do(key => - { - if (!int.TryParse(key, out var gameID)) - { - Utils.Error(new StoreException($"Could not read gameID for key {key}")); - return; - } - - var subKey = GOGKey.OpenSubKey(key); - if (subKey == null) - { - Utils.Error(new StoreException($"Could not open SubKey for {key}")); - return; - } - - var gameNameValue = subKey.GetValue("GAMENAME"); - if (gameNameValue == null) - { - Utils.Error(new StoreException($"Could not get GAMENAME for {gameID} at {key}")); - return; - } - - var gameName = gameNameValue.ToString() ?? string.Empty; - - var pathValue = subKey.GetValue("PATH"); - if (pathValue == null) - { - Utils.Error(new StoreException($"Could not get PATH for {gameID} at {key}")); - return; - } - - var path = pathValue.ToString() ?? string.Empty; - - var game = new GOGGame - { - ID = gameID, - Name = gameName, - Path = (AbsolutePath)path - }; - - var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g => (g.GOGIDs?.Contains(gameID) ?? false)); - - if (gameMeta == null) - { - Utils.Log($"GOG Game \"{gameName}\" ({gameID}) is not supported, skipping"); - return; - } - - game.Game = gameMeta.Game; - - Utils.Log($"Found GOG Game: \"{game.Name}\" ({game.ID}) at {game.Path}"); - - Games.Add(game); - }); - } - catch (SecurityException se) - { - Utils.Error(se, "GOGHandler could not read from registry!"); - } - catch (UnauthorizedAccessException uae) - { - Utils.Error(uae, "GOGHandler could not read from registry!"); - } - catch (Exception e) - { - Utils.ErrorThrow(e); - } - - Utils.Log($"Total number of GOG Games found: {Games.Count}"); - - return true; - } - } -} diff --git a/Wabbajack.Common/StoreHandlers/OriginHandler.cs b/Wabbajack.Common/StoreHandlers/OriginHandler.cs index 7eba2dea..3945f487 100644 --- a/Wabbajack.Common/StoreHandlers/OriginHandler.cs +++ b/Wabbajack.Common/StoreHandlers/OriginHandler.cs @@ -7,16 +7,16 @@ using Microsoft.Win32; namespace Wabbajack.Common.StoreHandlers { - public class OriginHandler : AStoreHandler + public class OriginHandler { private AbsolutePath OriginDataPath = (AbsolutePath)@"C:\ProgramData\Origin\LocalContent"; private Extension MFSTExtension = new Extension(".mfst"); private HashSet KnownMFSTs = new(); - public override StoreType Type { get; internal set; } = StoreType.Origin; - + public List Games = new(); + private static Regex SplitRegex = new Regex("(.*)([0-9]+)(@subscription)?", RegexOptions.RightToLeft); - public override bool Init() + public bool Init() { try { @@ -66,7 +66,7 @@ namespace Wabbajack.Common.StoreHandlers } } - public override bool LoadAllGames() + public bool LoadAllGames() { try { @@ -90,7 +90,7 @@ namespace Wabbajack.Common.StoreHandlers } } - public sealed class OriginGame : AStoreGame + public sealed class OriginGame { private string _mfst; private GameMetaData _metaData; @@ -101,10 +101,9 @@ namespace Wabbajack.Common.StoreHandlers Game = game; _metaData = metaData; } - public override Game Game { get; internal set; } - public override StoreType Type { get; internal set; } = StoreType.Origin; + public Game Game { get; internal set; } - public override AbsolutePath Path + public AbsolutePath Path { get { diff --git a/Wabbajack.Common/StoreHandlers/SteamHandler.cs b/Wabbajack.Common/StoreHandlers/SteamHandler.cs deleted file mode 100644 index 72211510..00000000 --- a/Wabbajack.Common/StoreHandlers/SteamHandler.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security; -using Microsoft.Win32; -#nullable enable - -namespace Wabbajack.Common.StoreHandlers -{ - public class SteamGame : AStoreGame - { - public override Game Game { get; internal set; } - public override StoreType Type { get; internal set; } = StoreType.STEAM; - - public AbsolutePath Universe; - - public readonly List WorkshopItems = new List(); - public int WorkshopItemsSizeOnDisk; - } - - public class SteamWorkshopItem - { - public readonly SteamGame Game; - public int ItemID; - public long Size; - - public SteamWorkshopItem(SteamGame game) - { - Game = game; - } - } - - public class SteamHandler : AStoreHandler - { - public override StoreType Type { get; internal set; } = StoreType.STEAM; - - private const string SteamRegKey = @"Software\Valve\Steam"; - - public AbsolutePath SteamPath { get; set; } - private AbsolutePath SteamConfig => new RelativePath("config//config.vdf").RelativeTo(SteamPath); - private List? SteamUniverses { get; set; } - - public override bool Init() - { - try - { - var steamKey = Registry.CurrentUser.OpenSubKey(SteamRegKey); - - var steamPathKey = steamKey?.GetValue("SteamPath"); - if (steamPathKey == null) - { - Utils.Error(new StoreException("Could not open the SteamPath registry key!")); - return false; - } - - 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; - } - - 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 (SteamConfig.Exists) - return true; - - Utils.Error(new StoreException($"The Steam config file could not be read: {SteamConfig}")); - return false; - - } - catch (SecurityException se) - { - Utils.Error(se, "SteamHandler could not read from registry!"); - } - catch (UnauthorizedAccessException uae) - { - Utils.Error(uae, "SteamHandler could not read from registry!"); - } - - return false; - } - - private List LoadUniverses() - { - var ret = new List(); - - SteamConfig.ReadAllLines().Do(l => - { - if (!l.ContainsCaseInsensitive("BaseInstallFolder_")) return; - var s = new AbsolutePath(GetVdfValue(l)); - var path = new RelativePath("steamapps").RelativeTo(s); - - if (!path.Exists) - { - Utils.Log($"Directory {path} does not exist, skipping"); - return; - } - - 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 - var defaultPath = new RelativePath("steamapps").RelativeTo(SteamPath); - if(defaultPath.Exists) - ret.Add(defaultPath); - - return ret; - } - - public override bool LoadAllGames() - { - SteamUniverses ??= LoadUniverses(); - - if (SteamUniverses.Count == 0) - { - Utils.Log("Could not find any Steam Libraries"); - return false; - } - - SteamUniverses.Do(u => - { - Utils.Log($"Searching for Steam Games in {u}"); - - u.EnumerateFiles(false, "*.acf") - .Where(a => a.Exists) - .Where(a => a.IsFile) - .Do(f => - { - var game = new SteamGame(); - var gotID = false; - - f.ReadAllLines().Do(l => - { - if (l.ContainsCaseInsensitive("\"appid\"")) - { - if (!int.TryParse(GetVdfValue(l), out var id)) - return; - game.ID = id; - gotID = true; - } - - if (l.ContainsCaseInsensitive("\"name\"")) - game.Name = GetVdfValue(l); - - if (!l.ContainsCaseInsensitive("\"installdir\"")) - return; - - var value = GetVdfValue(l); - AbsolutePath absPath; - - if (Path.IsPathRooted(value)) - { - absPath = (AbsolutePath)value; - } - else - { - absPath = new RelativePath("common").Combine(GetVdfValue(l)).RelativeTo(u); - } - - if (absPath.Exists) - game.Path = absPath; - - }); - - if (!gotID || !game.Path.IsDirectory) return; - - var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g => g.SteamIDs?.Contains(game.ID) ?? false); - - if (gameMeta == null) - { - Utils.Log($"Steam Game \"{game.Name}\" ({game.ID}) is not supported, skipping"); - return; - } - - game.Game = gameMeta.Game; - game.Universe = u; - - Utils.Log($"Found Steam Game: \"{game.Name}\" ({game.ID}) at {game.Path}"); - - LoadWorkshopItems(game); - - Games.Add(game); - }); - }); - - Utils.Log($"Total number of Steam Games found: {Games.Count}"); - - return true; - } - - private static void LoadWorkshopItems(SteamGame game) - { - var workshop = new RelativePath("workshop").RelativeTo(game.Universe); - if (!workshop.Exists) - return; - - workshop.EnumerateFiles(false, "*.acf") - .Where(f => f.Exists) - .Where(f => f.IsFile) - .Do(f => - { - if (f.FileName.ToString() != $"appworkshop_{game.ID}.acf") - return; - - Utils.Log($"Found Steam Workshop item file {f} for \"{game.Name}\""); - - var lines = f.ReadAllLines().ToList(); - var end = false; - var foundAppID = false; - var workshopItemsInstalled = 0; - var workshopItemDetails = 0; - var bracketStart = 0; - var bracketEnd = 0; - - SteamWorkshopItem? currentItem = new SteamWorkshopItem(game); - - lines.Do(l => - { - if (end) - return; - - currentItem ??= new SteamWorkshopItem(game); - - var currentLine = lines.IndexOf(l); - if (l.ContainsCaseInsensitive("\"appid\"") && !foundAppID) - { - if (!int.TryParse(GetVdfValue(l), out var appID)) - return; - - foundAppID = true; - - if (appID != game.ID) - return; - } - - if (!foundAppID) - return; - - if (l.ContainsCaseInsensitive("\"SizeOnDisk\"")) - { - if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk)) - return; - - game.WorkshopItemsSizeOnDisk += sizeOnDisk; - } - - if (l.ContainsCaseInsensitive("\"WorkshopItemsInstalled\"")) - workshopItemsInstalled = currentLine; - - if (l.ContainsCaseInsensitive("\"WorkshopItemDetails\"")) - workshopItemDetails = currentLine; - - if (workshopItemsInstalled == 0) - return; - - if (currentLine <= workshopItemsInstalled + 1 && currentLine >= workshopItemDetails - 1) - return; - - if (currentItem.ItemID == 0) - if (!int.TryParse(GetSingleVdfValue(l), out currentItem.ItemID)) - return; - - if (currentItem.ItemID == 0) - return; - - if (bracketStart == 0 && l.Contains("{")) - bracketStart = currentLine; - - if (bracketEnd == 0 && l.Contains("}")) - bracketEnd = currentLine; - - if (bracketStart == 0) - return; - - if (currentLine == bracketStart + 1) - if (!long.TryParse(GetVdfValue(l), out currentItem.Size)) - return; - - if (bracketStart == 0 || bracketEnd == 0 || currentItem.ItemID == 0 || currentItem.Size == 0) - return; - - bracketStart = 0; - bracketEnd = 0; - game.WorkshopItems.Add(currentItem); - - Utils.Log($"Found Steam Workshop item {currentItem.ItemID}"); - - currentItem = null; - end = true; - }); - }); - } - - private static string GetVdfValue(string line) - { - var trim = line.Trim('\t').Replace("\t", ""); - var split = trim.Split('\"'); - return split.Length >= 4 ? split[3].Replace("\\\\", "\\") : string.Empty; - } - - private static string GetSingleVdfValue(string line) - { - var trim = line.Trim('\t').Replace("\t", ""); - var split = trim.Split('\"'); - return split.Length >= 2 ? split[1] : string.Empty; - } - } -} diff --git a/Wabbajack.Common/StoreHandlers/StoreHandler.cs b/Wabbajack.Common/StoreHandlers/StoreHandler.cs index 96fe27c5..4ead029a 100644 --- a/Wabbajack.Common/StoreHandlers/StoreHandler.cs +++ b/Wabbajack.Common/StoreHandlers/StoreHandler.cs @@ -2,108 +2,101 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using GameFinder; +using GameFinder.StoreHandlers.BethNet; +using GameFinder.StoreHandlers.EGS; +using GameFinder.StoreHandlers.GOG; +using GameFinder.StoreHandlers.Steam; namespace Wabbajack.Common.StoreHandlers { - public enum StoreType - { - STEAM, - GOG, - BethNet, - EpicGameStore, - Origin - } - public class StoreHandler { - private static readonly Lazy _instance = new Lazy(() => new StoreHandler(), isThreadSafe: true); + private static readonly Lazy _instance = new(() => new StoreHandler(), isThreadSafe: true); public static StoreHandler Instance => _instance.Value; - private static readonly Lazy _steamHandler = new Lazy(() => new SteamHandler()); + private static readonly Lazy _steamHandler = new(() => new SteamHandler()); public SteamHandler SteamHandler = _steamHandler.Value; - private static readonly Lazy _gogHandler = new Lazy(() => new GOGHandler()); + private static readonly Lazy _gogHandler = new(() => new GOGHandler()); public GOGHandler GOGHandler = _gogHandler.Value; - private static readonly Lazy _bethNetHandler = new Lazy(() => new BethNetHandler()); + private static readonly Lazy _bethNetHandler = new(() => new BethNetHandler()); public BethNetHandler BethNetHandler = _bethNetHandler.Value; - private static readonly Lazy _epicGameStoreHandler = new Lazy(() => new EpicGameStoreHandler()); - public EpicGameStoreHandler EpicGameStoreHandler = _epicGameStoreHandler.Value; + private static readonly Lazy _epicGameStoreHandler = new(() => new EGSHandler()); + public EGSHandler EpicGameStoreHandler = _epicGameStoreHandler.Value; - private static readonly Lazy _originHandler = new Lazy(() => new OriginHandler()); + private static readonly Lazy _originHandler = new(() => new OriginHandler()); public OriginHandler OriginHandler = _originHandler.Value; public List StoreGames; + public Dictionary Games = new(); + + private void FindGames(THandler handler, string name) + where THandler : AStoreHandler + where TGame : AStoreGame + { + try + { + handler.FindAllGames(); + foreach (var game in handler.Games) + { + Utils.Log($"{handler.StoreType}: Found game {game}"); + StoreGames.Add(game); + } + } + catch (Exception e) + { + Utils.Error(e, $"Could not load all Games from the {name}"); + } + } + public StoreHandler() { StoreGames = new List(); - - if (SteamHandler.Init()) - { - if(SteamHandler.LoadAllGames()) - StoreGames.AddRange(SteamHandler.Games); - else - Utils.Error(new StoreException("Could not load all Games from the SteamHandler, check previous error messages!")); - } - else - { - Utils.Error(new StoreException("Could not Init the SteamHandler, check previous error messages!")); - } - - if (GOGHandler.Init()) - { - if(GOGHandler.LoadAllGames()) - StoreGames.AddRange(GOGHandler.Games); - else - Utils.Error(new StoreException("Could not load all Games from the GOGHandler, check previous error messages!")); - } - else - { - 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!")); - } - if (EpicGameStoreHandler.Init()) - { - if (EpicGameStoreHandler.LoadAllGames()) - StoreGames.AddRange(EpicGameStoreHandler.Games); - else - Utils.Error(new StoreException("Could not load all Games from the EpicGameStoreHandler, check previous error messages!")); - } - else - { - Utils.Error(new StoreException("Could not Init the EpicGameStoreHandler, check previous error messages!")); - } + FindGames(SteamHandler, "SteamHandler"); + FindGames(GOGHandler, "GOGHandler"); + FindGames(BethNetHandler, "BethNetHandler"); + FindGames(EpicGameStoreHandler, "EpicGameStoreHandler"); if (OriginHandler.Init()) { - if (OriginHandler.LoadAllGames()) - StoreGames.AddRange(OriginHandler.Games); - else + if (!OriginHandler.LoadAllGames()) Utils.Error(new StoreException("Could not load all Games from the OriginHandler, check previous error messages!")); } else { Utils.Error(new StoreException("Could not Init the OriginHandler, check previous error messages!")); } + + foreach (var storeGame in StoreGames) + { + IEnumerable>? enumerable = storeGame switch + { + SteamGame steamGame => GameRegistry.Games.Where(y => y.Value.SteamIDs?.Contains(steamGame.ID) ?? false), + GOGGame gogGame => GameRegistry.Games.Where(y => y.Value.GOGIDs?.Contains(gogGame.GameID) ?? false), + BethNetGame bethNetGame => GameRegistry.Games.Where(y => y.Value.BethNetID.Equals((int)bethNetGame.ID)), + EGSGame egsGame => GameRegistry.Games.Where(y => y.Value.EpicGameStoreIDs.Contains(egsGame.CatalogItemId ?? string.Empty)), + _ => null + }; + + if (enumerable == null) continue; + + var list = enumerable.ToList(); + if (list.Count == 0) continue; + + Games.Add(list.First().Key, storeGame); + } } public AbsolutePath? TryGetGamePath(Game game) { - return StoreGames.FirstOrDefault(g => g.Game == game)?.Path; + if (Games.TryGetValue(game, out var storeGame)) + return (AbsolutePath) storeGame.Path; + return OriginHandler.Games.FirstOrDefault(x => x.Game == game)?.Path; } public static void Warmup() @@ -111,27 +104,7 @@ namespace Wabbajack.Common.StoreHandlers Task.Run(() => _instance.Value).FireAndForget(); } } - - public abstract class AStoreGame - { - public abstract Game Game { get; internal set; } - public virtual string Name { get; internal set; } = string.Empty; - public virtual AbsolutePath Path { get; internal set; } - public virtual int ID { get; internal set; } - public abstract StoreType Type { get; internal set; } - } - - public abstract class AStoreHandler - { - public List Games { get; } = new List(); - - public abstract StoreType Type { get; internal set; } - - public abstract bool Init(); - - public abstract bool LoadAllGames(); - } - + public class StoreException : Exception { public StoreException(string msg) : base(msg) diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj index c87244cf..c636b94c 100644 --- a/Wabbajack.Common/Wabbajack.Common.csproj +++ b/Wabbajack.Common/Wabbajack.Common.csproj @@ -48,6 +48,10 @@ + + + + diff --git a/Wabbajack.Lib/CompilationSteps/IncludeSteamWorkshopItems.cs b/Wabbajack.Lib/CompilationSteps/IncludeSteamWorkshopItems.cs deleted file mode 100644 index 5b670a35..00000000 --- a/Wabbajack.Lib/CompilationSteps/IncludeSteamWorkshopItems.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Alphaleonis.Win32.Filesystem; -using Wabbajack.Common; -using Wabbajack.Common.StoreHandlers; - -namespace Wabbajack.Lib.CompilationSteps -{ - public class IncludeSteamWorkshopItems : ACompilationStep - { - private readonly Regex _regex = new Regex("steamWorkshopItem_\\d*\\.meta$"); - private readonly bool _isGenericGame; - private readonly SteamGame? _game; - - public IncludeSteamWorkshopItems(ACompiler compiler) : base(compiler) - { - var mo2Compiler = (MO2Compiler)compiler; - _isGenericGame = mo2Compiler.CompilingGame.IsGenericMO2Plugin; - _game = (SteamGame?)StoreHandler.Instance.SteamHandler.Games.FirstOrDefault(x => - mo2Compiler.CompilingGame.SteamIDs!.Contains(x.ID)); - } - - public override async ValueTask Run(RawSourceFile source) - { - if (!_isGenericGame) - return null; - - if (_game == null) - return null; - - if (!_regex.IsMatch((string)source.Path)) - return null; - - try - { - var lines = await source.AbsolutePath.ReadAllLinesAsync(); - var sID = lines.FirstOrDefault(l => l.StartsWith("itemID="))?.Replace("itemID=", ""); - if (string.IsNullOrEmpty(sID)) - { - Utils.Error($"Found no itemID= in file {source.AbsolutePath}!"); - return null; - } - - if(!int.TryParse(sID, out var id)) - { - Utils.Error($"Unable to parse int {sID} in {source.AbsolutePath}"); - return null; - } - - //Get-ChildItem -Name -Directory | ForEach-Object -Process {Out-File -FilePath .\steamWorkshopItem_$_.meta -InputObject "itemID=$($_)" -Encoding utf8} - if (id == 0) - return null; - - SteamWorkshopItem? item = _game.WorkshopItems.FirstOrDefault(x => x.ItemID == id); - if (item == null) - { - Utils.Error($"Unable to find workshop item with ID {id} in loaded workshop item list!"); - return null; - } - - var fromSteam = source.EvolveTo(); - fromSteam.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath); - fromSteam.ItemID = item.ItemID; - fromSteam.Size = item.Size; - return fromSteam; - } - catch (Exception e) - { - Utils.Error(e, $"Exception while trying to evolve source to FromSteam"); - return null; - } - } - } -} diff --git a/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs b/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs index 4d5c8543..2a7516a6 100644 --- a/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs +++ b/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs @@ -35,7 +35,6 @@ namespace Wabbajack.Lib.Downloaders typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State), - typeof(SteamWorkshopDownloader.State), typeof(VectorPlexusDownloader.State), typeof(DeadlyStreamDownloader.State), typeof(TESAllianceDownloader.State), diff --git a/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs b/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs deleted file mode 100644 index 27820b72..00000000 --- a/Wabbajack.Lib/Downloaders/SteamWorkshopDownloader.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Wabbajack.Common; -using Wabbajack.Common.StoreHandlers; -using Wabbajack.Lib.Validation; - -namespace Wabbajack.Lib.Downloaders -{ - public class SteamWorkshopDownloader : IUrlDownloader - { - public async Task GetDownloaderState(dynamic archiveINI, bool quickMode) - { - var id = archiveINI?.General?.itemID; - var steamID = archiveINI?.General?.steamID; - var size = archiveINI?.General?.itemSize; - if (steamID == null) - { - throw new ArgumentException("Steam workshop item had no steam ID."); - } - var item = new SteamWorkshopItem(GameRegistry.GetBySteamID(int.Parse(steamID))) - { - ItemID = id != null ? int.Parse(id) : 0, - Size = size != null ? int.Parse(size) : 0, - }; - return new State(item); - } - - public async Task Prepare() - { - } - - public AbstractDownloadState GetDownloaderState(string url) - { - throw new NotImplementedException(); - } - - public class State : AbstractDownloadState - { - public SteamWorkshopItem Item { get; } - - public override object[] PrimaryKey => new object[] { Item.Game, Item.ItemID }; - - public State(SteamWorkshopItem item) - { - Item = item; - } - - public override bool IsWhitelisted(ServerWhitelist whitelist) - { - return true; - } - - public override async Task Download(Archive a, AbsolutePath destination) - { - var currentLib = Item.Game.Universe; - - 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 = new RelativePath("steam.exe").RelativeTo(StoreHandler.Instance.SteamHandler.SteamPath).ToString(), - CreateNoWindow = true, - Arguments = $"console +workshop_download_item {Item.Game.ID} {Item.ItemID}" - } - }; - - p.Start(); - - //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(!itemDownloadPath.Exists) - if(itemContentPath.Exists) - finished = true; - - Thread.Sleep(1000); - } - - return true; - } - - public override async Task Verify(Archive a, CancellationToken? token) - { - //TODO: find a way to verify steam workshop items - throw new NotImplementedException(); - } - - public override IDownloader GetDownloader() - { - return DownloadDispatcher.GetInstance(); - } - - public override string GetManifestURL(Archive a) - { - return $"https://steamcommunity.com/sharedfiles/filedetails/?id={Item.ItemID}"; - } - - public override string[] GetMetaIni() - { - return new[] - { - "[General]", - $"itemID={Item.ItemID}", - $"steamID={Item.Game.Game.MetaData().SteamIDs!.First()}", - $"itemSize={Item.Size}" - }; - } - } - } -}