Merge pull request #1394 from erri120/use-gamefinder-lib

Add GameFinder lib
This commit is contained in:
Timothy Baldridge 2021-06-03 06:31:54 -07:00 committed by GitHub
commit 031f4c3943
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 85 additions and 1056 deletions

View File

@ -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<BethNetGame> possibleGames = new List<BethNetGame>();
// 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;
}
}
}

View File

@ -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<EpicGameItem>();
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; } = "";
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<string> KnownMFSTs = new();
public override StoreType Type { get; internal set; } = StoreType.Origin;
public List<OriginGame> 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
{

View File

@ -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<SteamWorkshopItem> WorkshopItems = new List<SteamWorkshopItem>();
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<AbsolutePath>? 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<AbsolutePath> LoadUniverses()
{
var ret = new List<AbsolutePath>();
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;
}
}
}

View File

@ -2,108 +2,111 @@
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<StoreHandler> _instance = new Lazy<StoreHandler>(() => new StoreHandler(), isThreadSafe: true);
private static readonly Lazy<StoreHandler> _instance = new(() => new StoreHandler(), isThreadSafe: true);
public static StoreHandler Instance => _instance.Value;
private static readonly Lazy<SteamHandler> _steamHandler = new Lazy<SteamHandler>(() => new SteamHandler());
private static readonly Lazy<SteamHandler> _steamHandler = new(() => new SteamHandler());
public SteamHandler SteamHandler = _steamHandler.Value;
private static readonly Lazy<GOGHandler> _gogHandler = new Lazy<GOGHandler>(() => new GOGHandler());
private static readonly Lazy<GOGHandler> _gogHandler = new(() => new GOGHandler());
public GOGHandler GOGHandler = _gogHandler.Value;
private static readonly Lazy<BethNetHandler> _bethNetHandler = new Lazy<BethNetHandler>(() => new BethNetHandler());
private static readonly Lazy<BethNetHandler> _bethNetHandler = new(() => new BethNetHandler());
public BethNetHandler BethNetHandler = _bethNetHandler.Value;
private static readonly Lazy<EpicGameStoreHandler> _epicGameStoreHandler = new Lazy<EpicGameStoreHandler>(() => new EpicGameStoreHandler());
public EpicGameStoreHandler EpicGameStoreHandler = _epicGameStoreHandler.Value;
private static readonly Lazy<EGSHandler> _epicGameStoreHandler = new(() => new EGSHandler());
public EGSHandler EpicGameStoreHandler = _epicGameStoreHandler.Value;
private static readonly Lazy<OriginHandler> _originHandler = new Lazy<OriginHandler>(() => new OriginHandler());
private static readonly Lazy<OriginHandler> _originHandler = new(() => new OriginHandler());
public OriginHandler OriginHandler = _originHandler.Value;
public List<AStoreGame> StoreGames;
private List<AStoreGame> _storeGames;
public Dictionary<Game, AStoreGame> Games = new();
private void FindGames<THandler, TGame>(Lazy<THandler> lazyHandler, string name)
where THandler : AStoreHandler<TGame>
where TGame : AStoreGame
{
try
{
var handler = lazyHandler.Value;
var res = handler.FindAllGames();
if (res.HasErrors)
{
Utils.Error($"Errors while finding Games from {name}\n{res.ErrorsToString()}");
}
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 {name}");
}
}
public StoreHandler()
{
StoreGames = new List<AStoreGame>();
_storeGames = new List<AStoreGame>();
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, SteamGame>(_steamHandler, "SteamHandler");
FindGames<GOGHandler, GOGGame>(_gogHandler, "GOGHandler");
FindGames<BethNetHandler, BethNetGame>(_bethNetHandler, "BethNetHandler");
FindGames<EGSHandler, EGSGame>(_epicGameStoreHandler, "EGSHandler");
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<KeyValuePair<Game, GameMetaData>>? 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;
var game = list.First().Key;
if (Games.ContainsKey(game)) continue;
Games.Add(game, 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()
@ -112,26 +115,6 @@ namespace Wabbajack.Common.StoreHandlers
}
}
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<AStoreGame> Games { get; } = new List<AStoreGame>();
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)

View File

@ -48,6 +48,10 @@
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GameFinder.StoreHandlers.BethNet" Version="1.5.0" />
<PackageReference Include="GameFinder.StoreHandlers.EGS" Version="1.5.0" />
<PackageReference Include="GameFinder.StoreHandlers.GOG" Version="1.5.0" />
<PackageReference Include="GameFinder.StoreHandlers.Steam" Version="1.5.0" />
<PackageReference Include="Genbox.AlphaFS" Version="2.2.2.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />

View File

@ -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<Directive?> 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<SteamMeta>();
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;
}
}
}
}

View File

@ -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),

View File

@ -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<AbstractDownloadState?> 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<bool> 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<bool> Verify(Archive a, CancellationToken? token)
{
//TODO: find a way to verify steam workshop items
throw new NotImplementedException();
}
public override IDownloader GetDownloader()
{
return DownloadDispatcher.GetInstance<SteamWorkshopDownloader>();
}
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}"
};
}
}
}
}