Overhauled the StoreHandlers

This commit is contained in:
erri120 2020-01-03 18:03:09 +01:00
parent bd2fcef22f
commit 62c3178ff2
No known key found for this signature in database
GPG Key ID: A8C0A18D8D4D3135
3 changed files with 527 additions and 0 deletions

View File

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.IO;
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 string Name { get; internal set; }
public override string Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.GOG;
}
public class GOGHandler : AStoreHandler
{
public override List<AStoreGame> Games { get; set; }
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()
{
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();
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();
var game = new GOGGame() {ID = gameID, Name = gameName, Path = path};
var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g =>
g.GOGIDs.Contains(game.ID)
&&
g.RequiredFiles.TrueForAll(file =>
File.Exists(Path.Combine(game.Path, file))));
if (gameMeta == null)
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

@ -0,0 +1,301 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using DynamicData;
using Microsoft.Win32;
namespace Wabbajack.Common.StoreHandlers
{
public class SteamGame : AStoreGame
{
public override Game Game { get; internal set; }
public override string Name { get; internal set; }
public override string Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM;
public string Universe;
public List<SteamWorkshopItem> WorkshopItems;
public int WorkshopItemsSize;
}
public class SteamWorkshopItem
{
public SteamGame Game;
public int ItemID;
public int Size;
}
public class SteamHandler : AStoreHandler
{
public override List<AStoreGame> Games { get; set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM;
private const string SteamRegKey = @"Software\Valve\Steam";
private string SteamPath { get; set; }
private List<string> SteamUniverses { get; set; }
private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf");
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;
}
SteamPath = steamPathKey.ToString();
if (string.IsNullOrWhiteSpace(SteamPath))
{
Utils.Error(new StoreException("Path to the Steam Directory from registry is Null or Empty!"));
return false;
}
if (!Directory.Exists(SteamPath))
{
Utils.Error(new StoreException($"Path to the Steam Directory from registry does not exists: {SteamPath}"));
return false;
}
if (File.Exists(SteamConfig))
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 void LoadUniverses()
{
SteamUniverses = new List<string>();
File.ReadAllLines(SteamConfig).Do(l =>
{
if (!l.Contains("BaseInstallFolder_")) return;
var s = GetVdfValue(l);
s = Path.Combine(s, "steamapps");
if (!Directory.Exists(s))
{
Utils.Log($"Directory {s} does not exist, skipping");
return;
}
SteamUniverses.Add(s);
Utils.Log($"Steam Library found at {s}");
});
Utils.Log($"Total number of Steam Libraries found: {SteamUniverses.Count}");
// Default path in the Steam folder isn't in the configs
if(Directory.Exists(Path.Combine(SteamPath, "steamapps")))
SteamUniverses.Add(Path.Combine(SteamPath, "steamapps"));
}
public override bool LoadAllGames()
{
if(SteamUniverses == null)
LoadUniverses();
if (SteamUniverses.Count == 0)
{
Utils.Log("Could not find any Steam Libraries!");
return false;
}
SteamUniverses.Do(u =>
{
Directory.EnumerateFiles(u, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f =>
{
var game = new SteamGame();
var valid = false;
File.ReadAllLines(f).Do(l =>
{
if (l.Contains("\"appid\""))
{
if (!int.TryParse(GetVdfValue(l), out var id))
return;
game.ID = id;
}
if (l.Contains("\"name\""))
game.Name = GetVdfValue(l);
if (l.Contains("\"installdir\""))
{
var path = Path.Combine(u, "common", GetVdfValue(l));
if (Directory.Exists(path))
game.Path = path;
else
return;
}
valid = true;
});
if (!valid) return;
var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g =>
g.SteamIDs.Contains(game.ID)
&&
g.RequiredFiles.TrueForAll(file =>
File.Exists(Path.Combine(game.Path, file))));
if (gameMeta == null)
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 void LoadWorkshopItems(SteamGame game)
{
if(game.WorkshopItems == null)
game.WorkshopItems = new List<SteamWorkshopItem>();
var workshop = Path.Combine(game.Universe, "workshop");
if (!Directory.Exists(workshop))
return;
Directory.EnumerateFiles(workshop, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f =>
{
if (Path.GetFileName(f) != $"appworkshop{game.ID}.acf")
return;
Utils.Log($"Found workshop item file {f} for \"{game.Name}\"");
var lines = File.ReadAllLines(f);
var end = false;
var foundAppID = false;
var workshopItemsInstalled = 0;
var workshopItemDetails = 0;
var bracketStart = 0;
var bracketEnd = 0;
var currentItem = new SteamWorkshopItem();
lines.Do(l =>
{
if (end)
return;
if(currentItem == null)
currentItem = new SteamWorkshopItem();
var currentLine = lines.IndexOf(l);
if (l.Contains("\"appid\"") && !foundAppID)
{
if (!int.TryParse(GetVdfValue(l), out var appID))
return;
foundAppID = true;
if (appID != game.ID)
return;
}
if (!foundAppID)
return;
if (l.Contains("\"SizeOnDisk\""))
{
if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk))
return;
game.WorkshopItemsSize += sizeOnDisk;
}
if (l.Contains("\"WorkshopItemsInstalled\""))
workshopItemsInstalled = currentLine;
if (l.Contains("\"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 (!int.TryParse(GetVdfValue(l), out currentItem.Size))
return;
if (bracketStart == 0 || bracketEnd == 0 || currentItem.ItemID == 0 || currentItem.Size == 0)
return;
bracketStart = 0;
bracketEnd = 0;
currentItem.Game = game;
game.WorkshopItems.Add(currentItem);
Utils.Log($"Found WorkshopItem {currentItem.ItemID}");
currentItem = null;
end = true;
});
});
}
private static string GetVdfValue(string line)
{
var trim = line.Trim('\t').Replace("\t", "");
string[] s = trim.Split('\"');
return s[3].Replace("\\\\", "\\");
}
private static string GetSingleVdfValue(string line)
{
var trim = line.Trim('\t').Replace("\t", "");
return trim.Split('\"')[1];
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Wabbajack.Common.StoreHandlers
{
public enum StoreType
{
STEAM,
GOG
}
public class StoreHandler
{
private static readonly Lazy<StoreHandler> instance = new Lazy<StoreHandler>(() => new StoreHandler(), true);
public static StoreHandler Instance => instance.Value;
private static readonly Lazy<SteamHandler> _steamHandler = new Lazy<SteamHandler>(() => new SteamHandler());
public SteamHandler SteamHandler = _steamHandler.Value;
private static readonly Lazy<GOGHandler> _gogHandler = new Lazy<GOGHandler>(() => new GOGHandler());
public GOGHandler GOGHandler = _gogHandler.Value;
public List<AStoreGame> StoreGames;
public StoreHandler()
{
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!"));
}
}
public string GetGamePath(Game game)
{
return StoreGames.FirstOrDefault(g => g.Game == game)?.Path;
}
public string GetGamePath(int id)
{
return StoreGames.FirstOrDefault(g => g.ID == id)?.Path;
}
}
public abstract class AStoreGame
{
public abstract Game Game { get; internal set; }
public abstract string Name { get; internal set; }
public abstract string Path { get; internal set; }
public abstract int ID { get; internal set; }
public abstract StoreType Type { get; internal set; }
}
public abstract class AStoreHandler
{
public abstract List<AStoreGame> Games { get; set; }
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)
{
}
}
}