mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #333 from erri120/store-handlers-overhaul
Store handlers overhaul
This commit is contained in:
commit
4e7b3a8b6a
@ -1,124 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public class GOGGame
|
||||
{
|
||||
public int GameID;
|
||||
public string Path;
|
||||
public string GameName;
|
||||
public Game? Game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class for all GOG operations
|
||||
/// </summary>
|
||||
public class GOGHandler
|
||||
{
|
||||
private static readonly Lazy<GOGHandler> instance = new Lazy<GOGHandler>(
|
||||
() => new GOGHandler(true),
|
||||
isThreadSafe: true);
|
||||
public static GOGHandler Instance => instance.Value;
|
||||
|
||||
private const string GOGRegKey = @"Software\GOG.com\Games";
|
||||
private const string GOG64RegKey = @"Software\WOW6432Node\GOG.com\Games";
|
||||
|
||||
public HashSet<GOGGame> Games { get; internal set; }
|
||||
public RegistryKey GOGKey { get; internal set; }
|
||||
|
||||
public GOGHandler(bool init)
|
||||
{
|
||||
var gogKey = Registry.LocalMachine.OpenSubKey(GOGRegKey) ?? Registry.LocalMachine.OpenSubKey(GOG64RegKey);
|
||||
if (gogKey == null)
|
||||
{
|
||||
Utils.ErrorThrow(new Exception("Could not find GOG in registry!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Log($"GOG registry key is ${gogKey}");
|
||||
GOGKey = gogKey;
|
||||
if (!init) return;
|
||||
LoadAllGames();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the installation path of a GOG game by ID
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the GOG game</param>
|
||||
/// <returns></returns>
|
||||
public string GetGamePathById(int id)
|
||||
{
|
||||
return Games.FirstOrDefault(f => f.GameID == id)?.Path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through all subkeys found in the GOG registry entry to get all
|
||||
/// GOG games
|
||||
/// </summary>
|
||||
public void LoadAllGames()
|
||||
{
|
||||
Games = new HashSet<GOGGame>();
|
||||
if (GOGKey == null) return;
|
||||
string[] keys = GOGKey.GetSubKeyNames();
|
||||
Utils.Log($"Found {keys.Length} SubKeys for GOG");
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (!int.TryParse(key, out var gameID))
|
||||
{
|
||||
Utils.ErrorThrow(new Exception($"Could not read gameID for key {key}"));
|
||||
}
|
||||
|
||||
var subKey = GOGKey.OpenSubKey(key);
|
||||
if (subKey == null)
|
||||
{
|
||||
Utils.ErrorThrow(new Exception($"Could not open SubKey for {key}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var gameNameValue = subKey.GetValue("GAMENAME");
|
||||
if (gameNameValue == null)
|
||||
{
|
||||
Utils.ErrorThrow(new Exception($"Could not get GAMENAME for {gameID} at {key}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var gameName = gameNameValue.ToString();
|
||||
|
||||
var pathValue = subKey.GetValue("PATH");
|
||||
if (pathValue == null)
|
||||
{
|
||||
Utils.ErrorThrow(new Exception($"Could not get PATH for {gameID} at {key}"));
|
||||
return;
|
||||
}
|
||||
|
||||
var path = pathValue.ToString();
|
||||
|
||||
|
||||
var game = new GOGGame
|
||||
{
|
||||
GameID = gameID,
|
||||
GameName = gameName,
|
||||
Path = path
|
||||
};
|
||||
|
||||
Utils.Log($"Found GOG Game: {gameName}({gameID}) at {path}");
|
||||
|
||||
game.Game = GameRegistry.Games.Values
|
||||
.FirstOrDefault(g => g.GOGIDs != null && g.GOGIDs.Contains(game.GameID)
|
||||
&&
|
||||
g.RequiredFiles.TrueForAll(s => File.Exists(Path.Combine(game.Path, s))))?.Game;
|
||||
|
||||
if (game.Game == null)
|
||||
{
|
||||
Utils.Log("Found no matching game, continuing");
|
||||
}
|
||||
Games.Add(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Microsoft.Win32;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
@ -71,11 +72,7 @@ namespace Wabbajack.Common
|
||||
|
||||
public string GameLocation()
|
||||
{
|
||||
if (Consts.TestMode)
|
||||
return Directory.GetCurrentDirectory();
|
||||
|
||||
return SteamHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.InstallDir ??
|
||||
GOGHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.Path;
|
||||
return Consts.TestMode ? Directory.GetCurrentDirectory() : StoreHandler.Instance.GetGamePath(Game);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,278 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DynamicData;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||
public class SteamGame
|
||||
{
|
||||
public int AppId;
|
||||
public string Name;
|
||||
public string InstallDir;
|
||||
public Game? Game;
|
||||
|
||||
public HashSet<SteamWorkshopItem> WorkshopItems;
|
||||
/// <summary>
|
||||
/// Combined size of all installed workshop items on disk in bytes
|
||||
/// </summary>
|
||||
public int WorkshopItemsSize;
|
||||
}
|
||||
|
||||
public class SteamWorkshopItem
|
||||
{
|
||||
public SteamGame Game;
|
||||
public int ItemID;
|
||||
/// <summary>
|
||||
/// Size is in bytes
|
||||
/// </summary>
|
||||
public int Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class for all Steam operations
|
||||
/// </summary>
|
||||
public class SteamHandler
|
||||
{
|
||||
private static readonly Lazy<SteamHandler> instance = new Lazy<SteamHandler>(
|
||||
() => new SteamHandler(true),
|
||||
isThreadSafe: true);
|
||||
public static SteamHandler Instance => instance.Value;
|
||||
|
||||
private const string SteamRegKey = @"Software\Valve\Steam";
|
||||
|
||||
/// <summary>
|
||||
/// Path to the Steam folder
|
||||
/// </summary>
|
||||
public string SteamPath { get; internal set; }
|
||||
/// <summary>
|
||||
/// HashSet of all known Steam Libraries
|
||||
/// </summary>
|
||||
public HashSet<string> InstallFolders { get; internal set; }
|
||||
/// <summary>
|
||||
/// HashSet of all known SteamGames
|
||||
/// </summary>
|
||||
public HashSet<SteamGame> Games { get; internal set; }
|
||||
|
||||
private string SteamConfig => Path.Combine(SteamPath, "config", "config.vdf");
|
||||
|
||||
public SteamHandler(bool init)
|
||||
{
|
||||
var steamKey = Registry.CurrentUser.OpenSubKey(SteamRegKey);
|
||||
SteamPath = steamKey?.GetValue("SteamPath").ToString();
|
||||
if(string.IsNullOrWhiteSpace(SteamPath) || steamKey == null || !Directory.Exists(SteamPath))
|
||||
Utils.ErrorThrow(new Exception("Could not find the Steam folder!"));
|
||||
if(!init) return;
|
||||
LoadInstallFolders();
|
||||
LoadAllSteamGames();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the installation path of a Steam game by ID
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the Steam game</param>
|
||||
/// <returns></returns>
|
||||
public string GetGamePathById(int id)
|
||||
{
|
||||
return Games.FirstOrDefault(f => f.AppId == id)?.InstallDir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the config file and adds all found installation folders to the HashSet
|
||||
/// </summary>
|
||||
public void LoadInstallFolders()
|
||||
{
|
||||
var paths = new HashSet<string>();
|
||||
|
||||
File.ReadLines(SteamConfig, Encoding.UTF8).Do(l =>
|
||||
{
|
||||
if (!l.Contains("BaseInstallFolder_")) return;
|
||||
var s = GetVdfValue(l);
|
||||
s = Path.Combine(s, "steamapps");
|
||||
if (!Directory.Exists(s))
|
||||
return;
|
||||
|
||||
paths.Add(s);
|
||||
Utils.Log($"Steam Library found at {s}");
|
||||
});
|
||||
|
||||
Utils.Log($"Total number of Steam Libraries found: {paths.Count}");
|
||||
|
||||
// Default path in the Steam folder isn't in the configs
|
||||
if(Directory.Exists(Path.Combine(SteamPath, "steamapps")))
|
||||
paths.Add(Path.Combine(SteamPath, "steamapps"));
|
||||
|
||||
InstallFolders = paths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through all Steam Libraries to find and read all .afc files, adding the found game
|
||||
/// to the HashSet
|
||||
/// </summary>
|
||||
public void LoadAllSteamGames()
|
||||
{
|
||||
var games = new HashSet<SteamGame>();
|
||||
|
||||
InstallFolders.Do(p =>
|
||||
{
|
||||
Directory.EnumerateFiles(p, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f =>
|
||||
{
|
||||
var steamGame = new SteamGame();
|
||||
var valid = false;
|
||||
File.ReadAllLines(f, Encoding.UTF8).Do(l =>
|
||||
{
|
||||
if(l.Contains("\"appid\""))
|
||||
if (!int.TryParse(GetVdfValue(l), out steamGame.AppId))
|
||||
return;
|
||||
if(l.Contains("\"name\""))
|
||||
steamGame.Name = GetVdfValue(l);
|
||||
if (l.Contains("\"installdir\""))
|
||||
{
|
||||
var path = Path.Combine(p, "common", GetVdfValue(l));
|
||||
steamGame.InstallDir = Directory.Exists(path) ? path : null;
|
||||
}
|
||||
|
||||
if (steamGame.AppId != 0 && !string.IsNullOrWhiteSpace(steamGame.Name) &&
|
||||
!string.IsNullOrWhiteSpace(steamGame.InstallDir))
|
||||
valid = true;
|
||||
});
|
||||
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
steamGame.Game = GameRegistry.Games.Values
|
||||
.FirstOrDefault(g =>
|
||||
g.SteamIDs.Contains(steamGame.AppId)
|
||||
&&
|
||||
g.RequiredFiles.TrueForAll(s => File.Exists(Path.Combine(steamGame.InstallDir, s)))
|
||||
)?.Game;
|
||||
games.Add(steamGame);
|
||||
|
||||
Utils.Log($"Found Game: {steamGame.Name} ({steamGame.AppId}) at {steamGame.InstallDir}");
|
||||
});
|
||||
});
|
||||
|
||||
Utils.Log($"Total number of Steam Games found: {games.Count}");
|
||||
|
||||
Games = games;
|
||||
}
|
||||
|
||||
public void LoadWorkshopItems(SteamGame game)
|
||||
{
|
||||
if(game.WorkshopItems == null)
|
||||
game.WorkshopItems = new HashSet<SteamWorkshopItem>();
|
||||
|
||||
InstallFolders.Do(p =>
|
||||
{
|
||||
var workshop = Path.Combine(p, "workshop");
|
||||
if(!Directory.Exists(workshop))
|
||||
return;
|
||||
|
||||
Directory.EnumerateFiles(workshop, "*.acf", SearchOption.TopDirectoryOnly).Where(File.Exists).Do(f =>
|
||||
{
|
||||
if (Path.GetFileName(f) != $"appworkshop_{game.AppId}.acf")
|
||||
return;
|
||||
|
||||
var foundAppID = false;
|
||||
var workshopItemsInstalled = 0;
|
||||
var workshopItemDetails = 0;
|
||||
var currentItem = new SteamWorkshopItem();
|
||||
var bracketStart = 0;
|
||||
var bracketEnd = 0;
|
||||
var lines = File.ReadAllLines(f, Encoding.UTF8);
|
||||
var end = false;
|
||||
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.AppId)
|
||||
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);
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
143
Wabbajack.Common/StoreHandlers/GOGHandler.cs
Normal file
143
Wabbajack.Common/StoreHandlers/GOGHandler.cs
Normal file
@ -0,0 +1,143 @@
|
||||
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()
|
||||
{
|
||||
if(Games == null)
|
||||
Games = new List<AStoreGame>();
|
||||
|
||||
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 != null
|
||||
&&
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
304
Wabbajack.Common/StoreHandlers/SteamHandler.cs
Normal file
304
Wabbajack.Common/StoreHandlers/SteamHandler.cs
Normal file
@ -0,0 +1,304 @@
|
||||
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";
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
if(Games == null)
|
||||
Games = new List<AStoreGame>();
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
98
Wabbajack.Common/StoreHandlers/StoreHandler.cs
Normal file
98
Wabbajack.Common/StoreHandlers/StoreHandler.cs
Normal file
@ -0,0 +1,98 @@
|
||||
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(Game game, StoreType type)
|
||||
{
|
||||
return StoreGames.FirstOrDefault(g => g.Type == type && 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
|
||||
namespace Wabbajack.Lib.CompilationSteps
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
using Wabbajack.Lib.Validation;
|
||||
|
||||
namespace Wabbajack.Lib.Downloaders
|
||||
@ -50,18 +51,17 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public override async Task Download(Archive a, string destination)
|
||||
{
|
||||
var currentLib = "";
|
||||
SteamHandler.Instance.InstallFolders.Where(f => f.Contains(Item.Game.InstallDir)).Do(s => currentLib = s);
|
||||
var currentLib = Item.Game.Universe;
|
||||
|
||||
var downloadFolder = Path.Combine(currentLib, "workshop", "downloads", Item.Game.AppId.ToString());
|
||||
var contentFolder = Path.Combine(currentLib, "workshop", "content", Item.Game.AppId.ToString());
|
||||
var downloadFolder = Path.Combine(currentLib, "workshop", "downloads", Item.Game.ID.ToString());
|
||||
var contentFolder = Path.Combine(currentLib, "workshop", "content", Item.Game.ID.ToString());
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(SteamHandler.Instance.SteamPath, "steam.exe"),
|
||||
FileName = Path.Combine(StoreHandler.Instance.SteamHandler.SteamPath, "steam.exe"),
|
||||
CreateNoWindow = true,
|
||||
Arguments = $"console +workshop_download_item {Item.Game.AppId} {Item.ItemID}"
|
||||
Arguments = $"console +workshop_download_item {Item.Game.ID} {Item.ItemID}"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@ using System.Threading;
|
||||
using Microsoft.WindowsAPICodePack.Shell;
|
||||
using Newtonsoft.Json;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
@ -70,11 +71,10 @@ namespace Wabbajack.Lib
|
||||
ActiveArchives = new List<string>();
|
||||
|
||||
// there can be max one game after filtering
|
||||
SteamHandler.Instance.Games.Where(g => g.Game != null && g.Game == game).Do(g =>
|
||||
StoreHandler.Instance.StoreGames.Where(g => g.Game == game && g.Type == StoreType.STEAM).Do(g =>
|
||||
{
|
||||
_isSteamGame = true;
|
||||
_steamGame = g;
|
||||
SteamHandler.Instance.LoadWorkshopItems(_steamGame);
|
||||
_steamGame = (SteamGame)g;
|
||||
_hasSteamWorkshopItems = _steamGame.WorkshopItems.Count > 0;
|
||||
});
|
||||
}
|
||||
@ -431,7 +431,7 @@ namespace Wabbajack.Lib
|
||||
var metaString = "[General]\n" +
|
||||
"repository=Steam\n" +
|
||||
$"gameName={GameName}\n" +
|
||||
$"steamID={_steamGame.AppId}\n" +
|
||||
$"steamID={_steamGame.ID}\n" +
|
||||
$"itemID={item.ItemID}\n" +
|
||||
$"itemSize={item.Size}\n";
|
||||
try
|
||||
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
@ -157,7 +158,7 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
//var currentLib = "";
|
||||
SteamGame currentSteamGame = null;
|
||||
SteamHandler.Instance.Games.Where(g => g.Game == GameInfo.Game).Do(s => currentSteamGame = s);
|
||||
StoreHandler.Instance.SteamHandler.Games.Where(g => g.Game == GameInfo.Game).Do(s => currentSteamGame = (SteamGame)s);
|
||||
/*SteamHandler.Instance.InstallFolders.Where(f => f.Contains(currentSteamGame.InstallDir)).Do(s => currentLib = s);
|
||||
|
||||
var downloadFolder = Path.Combine(currentLib, "workshop", "downloads", currentSteamGame.AppId.ToString());
|
||||
@ -188,9 +189,9 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = System.IO.Path.Combine(SteamHandler.Instance.SteamPath, "steam.exe"),
|
||||
FileName = Path.Combine(StoreHandler.Instance.SteamHandler.SteamPath, "steam.exe"),
|
||||
CreateNoWindow = true,
|
||||
Arguments = $"console +workshop_download_item {currentSteamGame.AppId} {item.ItemID}"
|
||||
Arguments = $"console +workshop_download_item {currentSteamGame.ID} {currentSteamGame.ID}"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StoreHandlers;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
namespace Wabbajack
|
||||
@ -172,14 +173,12 @@ namespace Wabbajack
|
||||
|
||||
private void SetGameToSteamLocation()
|
||||
{
|
||||
var steamGame = SteamHandler.Instance.Games.FirstOrDefault(g => g.Game.HasValue && g.Game == SelectedGame.Game);
|
||||
GameLocation.TargetPath = steamGame?.InstallDir;
|
||||
GameLocation.TargetPath = StoreHandler.Instance.GetGamePath(SelectedGame.Game, StoreType.STEAM);
|
||||
}
|
||||
|
||||
private void SetGameToGogLocation()
|
||||
{
|
||||
var gogGame = GOGHandler.Instance.Games.FirstOrDefault(g => g.Game.HasValue && g.Game == SelectedGame.Game);
|
||||
GameLocation.TargetPath = gogGame?.Path;
|
||||
GameLocation.TargetPath = StoreHandler.Instance.GetGamePath(SelectedGame.Game, StoreType.GOG);
|
||||
}
|
||||
|
||||
public async Task Compile()
|
||||
|
Loading…
Reference in New Issue
Block a user