mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #1394 from erri120/use-gamefinder-lib
Add GameFinder lib
This commit is contained in:
commit
031f4c3943
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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; } = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,16 +7,16 @@ using Microsoft.Win32;
|
|||||||
|
|
||||||
namespace Wabbajack.Common.StoreHandlers
|
namespace Wabbajack.Common.StoreHandlers
|
||||||
{
|
{
|
||||||
public class OriginHandler : AStoreHandler
|
public class OriginHandler
|
||||||
{
|
{
|
||||||
private AbsolutePath OriginDataPath = (AbsolutePath)@"C:\ProgramData\Origin\LocalContent";
|
private AbsolutePath OriginDataPath = (AbsolutePath)@"C:\ProgramData\Origin\LocalContent";
|
||||||
private Extension MFSTExtension = new Extension(".mfst");
|
private Extension MFSTExtension = new Extension(".mfst");
|
||||||
private HashSet<string> KnownMFSTs = new();
|
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);
|
private static Regex SplitRegex = new Regex("(.*)([0-9]+)(@subscription)?", RegexOptions.RightToLeft);
|
||||||
public override bool Init()
|
public bool Init()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -66,7 +66,7 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool LoadAllGames()
|
public bool LoadAllGames()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -90,7 +90,7 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class OriginGame : AStoreGame
|
public sealed class OriginGame
|
||||||
{
|
{
|
||||||
private string _mfst;
|
private string _mfst;
|
||||||
private GameMetaData _metaData;
|
private GameMetaData _metaData;
|
||||||
@ -101,10 +101,9 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
Game = game;
|
Game = game;
|
||||||
_metaData = metaData;
|
_metaData = metaData;
|
||||||
}
|
}
|
||||||
public override Game Game { get; internal set; }
|
public Game Game { get; internal set; }
|
||||||
public override StoreType Type { get; internal set; } = StoreType.Origin;
|
|
||||||
|
|
||||||
public override AbsolutePath Path
|
public AbsolutePath Path
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,108 +2,111 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
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
|
namespace Wabbajack.Common.StoreHandlers
|
||||||
{
|
{
|
||||||
public enum StoreType
|
|
||||||
{
|
|
||||||
STEAM,
|
|
||||||
GOG,
|
|
||||||
BethNet,
|
|
||||||
EpicGameStore,
|
|
||||||
Origin
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StoreHandler
|
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;
|
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;
|
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;
|
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;
|
public BethNetHandler BethNetHandler = _bethNetHandler.Value;
|
||||||
|
|
||||||
private static readonly Lazy<EpicGameStoreHandler> _epicGameStoreHandler = new Lazy<EpicGameStoreHandler>(() => new EpicGameStoreHandler());
|
private static readonly Lazy<EGSHandler> _epicGameStoreHandler = new(() => new EGSHandler());
|
||||||
public EpicGameStoreHandler EpicGameStoreHandler = _epicGameStoreHandler.Value;
|
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 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()
|
public StoreHandler()
|
||||||
{
|
{
|
||||||
StoreGames = new List<AStoreGame>();
|
_storeGames = new List<AStoreGame>();
|
||||||
|
|
||||||
if (SteamHandler.Init())
|
FindGames<SteamHandler, SteamGame>(_steamHandler, "SteamHandler");
|
||||||
{
|
FindGames<GOGHandler, GOGGame>(_gogHandler, "GOGHandler");
|
||||||
if(SteamHandler.LoadAllGames())
|
FindGames<BethNetHandler, BethNetGame>(_bethNetHandler, "BethNetHandler");
|
||||||
StoreGames.AddRange(SteamHandler.Games);
|
FindGames<EGSHandler, EGSGame>(_epicGameStoreHandler, "EGSHandler");
|
||||||
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!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OriginHandler.Init())
|
if (OriginHandler.Init())
|
||||||
{
|
{
|
||||||
if (OriginHandler.LoadAllGames())
|
if (!OriginHandler.LoadAllGames())
|
||||||
StoreGames.AddRange(OriginHandler.Games);
|
|
||||||
else
|
|
||||||
Utils.Error(new StoreException("Could not load all Games from the OriginHandler, check previous error messages!"));
|
Utils.Error(new StoreException("Could not load all Games from the OriginHandler, check previous error messages!"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Utils.Error(new StoreException("Could not Init the OriginHandler, check previous error messages!"));
|
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)
|
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()
|
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 class StoreException : Exception
|
||||||
{
|
{
|
||||||
public StoreException(string msg) : base(msg)
|
public StoreException(string msg) : base(msg)
|
||||||
|
@ -48,6 +48,10 @@
|
|||||||
<Folder Include="Properties\" />
|
<Folder Include="Properties\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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="Genbox.AlphaFS" Version="2.2.2.1" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||||
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,7 +35,6 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
typeof(MegaDownloader.State),
|
typeof(MegaDownloader.State),
|
||||||
typeof(ModDBDownloader.State),
|
typeof(ModDBDownloader.State),
|
||||||
typeof(NexusDownloader.State),
|
typeof(NexusDownloader.State),
|
||||||
typeof(SteamWorkshopDownloader.State),
|
|
||||||
typeof(VectorPlexusDownloader.State),
|
typeof(VectorPlexusDownloader.State),
|
||||||
typeof(DeadlyStreamDownloader.State),
|
typeof(DeadlyStreamDownloader.State),
|
||||||
typeof(TESAllianceDownloader.State),
|
typeof(TESAllianceDownloader.State),
|
||||||
|
@ -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}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user