wabbajack/Wabbajack.Common/StoreHandlers/SteamHandler.cs

310 lines
10 KiB
C#
Raw Normal View History

2020-01-03 17:03:09 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using Microsoft.Win32;
2020-04-10 01:29:53 +00:00
#nullable enable
2020-01-03 17:03:09 +00:00
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;
2020-01-03 17:03:09 +00:00
public readonly List<SteamWorkshopItem> WorkshopItems = new List<SteamWorkshopItem>();
public int WorkshopItemsSizeOnDisk;
2020-01-03 17:03:09 +00:00
}
public class SteamWorkshopItem
{
2020-04-10 01:29:53 +00:00
public readonly SteamGame Game;
2020-01-03 17:03:09 +00:00
public int ItemID;
public int Size;
2020-04-10 01:29:53 +00:00
public SteamWorkshopItem(SteamGame game)
{
Game = game;
}
2020-01-03 17:03:09 +00:00
}
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; }
2020-01-03 17:03:09 +00:00
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))
2020-01-03 17:03:09 +00:00
{
Utils.Error(new StoreException("Path to the Steam Directory from registry is Null or Empty!"));
return false;
}
SteamPath = new AbsolutePath(steamPath);
if (!SteamPath.Exists)
2020-01-03 17:03:09 +00:00
{
Utils.Error(new StoreException($"Path to the Steam Directory from registry does not exists: {SteamPath}"));
return false;
}
if (SteamConfig.Exists)
2020-01-03 17:03:09 +00:00
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()
2020-01-03 17:03:09 +00:00
{
var ret = new List<AbsolutePath>();
2020-01-03 17:03:09 +00:00
SteamConfig.ReadAllLines().Do(l =>
2020-01-03 17:03:09 +00:00
{
if (!l.ContainsCaseInsensitive("BaseInstallFolder_")) return;
var s = new AbsolutePath(GetVdfValue(l));
var path = new RelativePath("steamapps").RelativeTo(s);
if (!path.Exists)
2020-01-03 17:03:09 +00:00
{
Utils.Log($"Directory {path} does not exist, skipping");
2020-01-03 17:03:09 +00:00
return;
}
ret.Add(path);
Utils.Log($"Steam Library found at {path}");
2020-01-03 17:03:09 +00:00
});
Utils.Log($"Total number of Steam Libraries found: {ret.Count}");
2020-01-03 17:03:09 +00:00
// 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;
2020-01-03 17:03:09 +00:00
}
public override bool LoadAllGames()
{
SteamUniverses ??= LoadUniverses();
2020-01-03 17:03:09 +00:00
if (SteamUniverses.Count == 0)
{
2020-01-13 21:11:07 +00:00
Utils.Log("Could not find any Steam Libraries");
2020-01-03 17:03:09 +00:00
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 =>
2020-01-03 17:03:09 +00:00
{
var game = new SteamGame();
var gotID = false;
2020-01-03 17:03:09 +00:00
f.ReadAllLines().Do(l =>
2020-01-03 17:03:09 +00:00
{
if (l.ContainsCaseInsensitive("\"appid\""))
2020-01-03 17:03:09 +00:00
{
if (!int.TryParse(GetVdfValue(l), out var id))
return;
game.ID = id;
gotID = true;
2020-01-03 17:03:09 +00:00
}
if (l.ContainsCaseInsensitive("\"name\""))
2020-01-03 17:03:09 +00:00
game.Name = GetVdfValue(l);
if (!l.ContainsCaseInsensitive("\"installdir\""))
return;
var path = new RelativePath("common").Combine(GetVdfValue(l)).RelativeTo(u);
if (path.Exists)
game.Path = path;
2020-01-03 17:03:09 +00:00
});
2020-03-25 22:30:43 +00:00
if (!gotID || !game.Path.IsDirectory) return;
2020-01-03 17:03:09 +00:00
var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g =>
{
return (g.SteamIDs?.Contains(game.ID) ?? false)
2020-03-25 22:30:43 +00:00
&& (g.RequiredFiles?.TrueForAll(file => game.Path.Combine(file).Exists) ?? true);
});
2020-01-03 17:03:09 +00:00
if (gameMeta == null)
{
2020-01-13 21:11:07 +00:00
Utils.Log($"Steam Game \"{game.Name}\" ({game.ID}) is not supported, skipping");
2020-01-03 17:03:09 +00:00
return;
}
2020-01-03 17:03:09 +00:00
game.Game = gameMeta.Game;
game.Universe = u;
2020-01-13 21:11:07 +00:00
Utils.Log($"Found Steam Game: \"{game.Name}\" ({game.ID}) at {game.Path}");
2020-01-03 17:03:09 +00:00
LoadWorkshopItems(game);
Games.Add(game);
});
});
Utils.Log($"Total number of Steam Games found: {Games.Count}");
return true;
}
private static void LoadWorkshopItems(SteamGame game)
2020-01-03 17:03:09 +00:00
{
var workshop = new RelativePath("workshop").RelativeTo(game.Universe);
if (!workshop.Exists)
2020-01-03 17:03:09 +00:00
return;
workshop.EnumerateFiles(false, "*.acf")
.Where(f => f.Exists)
.Where(f => f.IsFile)
.Do(f =>
2020-01-03 17:03:09 +00:00
{
if (f.FileName.ToString() != $"appworkshop{game.ID}.acf")
2020-01-03 17:03:09 +00:00
return;
2020-01-13 21:11:07 +00:00
Utils.Log($"Found Steam Workshop item file {f} for \"{game.Name}\"");
2020-01-03 17:03:09 +00:00
var lines = f.ReadAllLines().ToList();
2020-01-03 17:03:09 +00:00
var end = false;
var foundAppID = false;
var workshopItemsInstalled = 0;
var workshopItemDetails = 0;
var bracketStart = 0;
var bracketEnd = 0;
2020-04-10 01:29:53 +00:00
SteamWorkshopItem? currentItem = new SteamWorkshopItem(game);
2020-01-03 17:03:09 +00:00
lines.Do(l =>
{
if (end)
return;
currentItem ??= new SteamWorkshopItem(game);
2020-01-03 17:03:09 +00:00
var currentLine = lines.IndexOf(l);
if (l.ContainsCaseInsensitive("\"appid\"") && !foundAppID)
2020-01-03 17:03:09 +00:00
{
if (!int.TryParse(GetVdfValue(l), out var appID))
return;
foundAppID = true;
if (appID != game.ID)
return;
}
if (!foundAppID)
return;
if (l.ContainsCaseInsensitive("\"SizeOnDisk\""))
2020-01-03 17:03:09 +00:00
{
if (!int.TryParse(GetVdfValue(l), out var sizeOnDisk))
return;
game.WorkshopItemsSizeOnDisk += sizeOnDisk;
2020-01-03 17:03:09 +00:00
}
if (l.ContainsCaseInsensitive("\"WorkshopItemsInstalled\""))
2020-01-03 17:03:09 +00:00
workshopItemsInstalled = currentLine;
if (l.ContainsCaseInsensitive("\"WorkshopItemDetails\""))
2020-01-03 17:03:09 +00:00
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;
game.WorkshopItems.Add(currentItem);
2020-01-13 21:11:07 +00:00
Utils.Log($"Found Steam Workshop item {currentItem.ItemID}");
2020-01-03 17:03:09 +00:00
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];
}
}
}