using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Alphaleonis.Win32.Filesystem; using Microsoft.Win32; using Wabbajack.Common.StoreHandlers; namespace Wabbajack.Common { public enum Game { FalloutShelter, //MO2 GAMES Morrowind, Oblivion, [Description("Fallout 3")] Fallout3, [Description("Fallout New Vegas")] FalloutNewVegas, [Description("Skyrim Legendary Edition")] Skyrim, Enderal, [Description("Skyrim Special Edition")] SkyrimSpecialEdition, [Description("Fallout 4")] Fallout4, [Description("Skyrim VR")] SkyrimVR, //VORTEX GAMES [Description("Darkest Dungeon")] DarkestDungeon, [Description("Divinity Original Sin 2")] DivinityOriginalSin2, [Description("Divinity Original Sin 2 Definitive Edition")] DivinityOriginalSin2DE, //definitive edition has its own nexus page but same Steam/GOG ids Starbound, [Description("Star Wars: Knights of the Old Republic")] SWKOTOR, [Description("Star Wars: Knights of the Old Republic 2")] SWKOTOR2, Witcher, [Description("Witcher 2")] Witcher2, [Description("Witcher 3")] Witcher3, [Description("Stardew Valley")] StardewValley } public static class GameExtentions { public static GameMetaData MetaData(this Game game) { return GameRegistry.Games[game]; } } public class GameMetaData { public ModManager SupportedModManager { get; internal set; } public string? MO2ArchiveName { get; internal set; } public Game Game { get; internal set; } public string? NexusName { get; internal set; } // Nexus DB id for the game, used in some specific situations public long NexusGameId { get; internal set; } public string? MO2Name { get; internal set; } public string HumanFriendlyGameName => Game.GetDescription(); public string? GameLocationRegistryKey { get; internal set; } // to get steam ids: https://steamdb.info public List? SteamIDs { get; internal set; } // to get gog ids: https://www.gogdb.org public List? GOGIDs { get; internal set; } public int BethNetID { get; internal set; } //for BethNet games only! public string RegString { get; internal set; } = string.Empty; // these are additional folders when a game installs mods outside the game folder public List? AdditionalFolders { get; internal set; } // file to check if the game is present, useful when steamIds and gogIds dont help public List? RequiredFiles { get; internal set; } public bool Disabled { get; internal set; } // Games that this game are commonly confused with, for example Skyrim SE vs Skyrim LE public Game[] CommonlyConfusedWith { get; set; } = Array.Empty(); public string InstalledVersion { get { if (!TryGetGameLocation(out var gameLoc)) throw new GameNotInstalledException(this); if (MainExecutable == null) throw new NotImplementedException(); return FileVersionInfo.GetVersionInfo((string)gameLoc.Combine(MainExecutable)).ProductVersion; } } public bool IsInstalled => TryGetGameLocation() != null; public string? MainExecutable { get; internal set; } public AbsolutePath? TryGetGameLocation() { return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.TryGetGamePath(Game); } public bool TryGetGameLocation(out AbsolutePath path) { var ret = TryGetGameLocation(); if (ret != null) { path = ret.Value; return true; } else { path = default; return false; } } public AbsolutePath GameLocation() { var ret = TryGetGameLocation(); if (ret == null) throw new ArgumentNullException(); return ret.Value; } } public class GameNotInstalledException : Exception { public GameNotInstalledException(GameMetaData gameMetaData) : base($"Game {gameMetaData.Game} does not appear to be installed.") { } } public static class EnumExtensions { public static string GetDescription(this T enumerationValue) where T : Enum { var type = enumerationValue.GetType(); if(!type.IsEnum) { throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue)); } var memberInfo = type.GetMember(enumerationValue.ToString()!); if(memberInfo.Length > 0) { var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); if(attrs.Length > 0) { return ((DescriptionAttribute)attrs[0]).Description; } } return enumerationValue.ToString()!; } } public class GameRegistry { public static GameMetaData GetByMO2ArchiveName(string gameName) { var gamename = gameName.ToLower(); return Games.Values.FirstOrDefault(g => g.MO2ArchiveName?.ToLower() == gamename); } public static GameMetaData GetByNexusName(string gameName) { return Games.Values.FirstOrDefault(g => g.NexusName == gameName.ToLower()); } public static GameMetaData GetBySteamID(int id) { return Games.Values .FirstOrDefault(g => g.SteamIDs != null && g.SteamIDs.Count > 0 && g.SteamIDs.Any(i => i == id)); } /// /// Parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name. /// Name to query /// GameMetaData found /// If string could not be translated to a game public static GameMetaData GetByFuzzyName(string someName) { return TryGetByFuzzyName(someName) ?? throw new ArgumentNullException($"{someName} could not be translated to a game"); } /// /// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name. /// Name to query /// GameMetaData if found, otherwise null public static GameMetaData? TryGetByFuzzyName(string someName) { if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata!).MetaData(); GameMetaData? result = GetByNexusName(someName); if (result != null) return result; result = GetByMO2ArchiveName(someName); if (result != null) return result; return int.TryParse(someName, out int id) ? GetBySteamID(id) : null; } public static bool TryGetByFuzzyName(string someName, [MaybeNullWhen(false)] out GameMetaData gameMetaData) { var result = TryGetByFuzzyName(someName); if (result == null) { gameMetaData = Games.Values.First(); return false; } gameMetaData = result; return true; } public static IReadOnlyDictionary Games = new Dictionary { { Game.Morrowind, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Morrowind, Disabled = false, SteamIDs = new List{22320}, GOGIDs = new List{1440163901, 1435828767}, NexusName = "morrowind", NexusGameId = 100, MO2Name = "Morrowind", MO2ArchiveName = "morrowind", BethNetID = 31, RegString = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\The Elder Scrolls III: Morrowind Game of the Year Edition", RequiredFiles = new List { "Morrowind.exe" }, MainExecutable = "Morrowind.exe" } }, { Game.Oblivion, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Oblivion, NexusName = "oblivion", NexusGameId = 101, MO2Name = "Oblivion", MO2ArchiveName = "oblivion", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Oblivion", SteamIDs = new List {22330}, GOGIDs = new List{1458058109}, RequiredFiles = new List { "oblivion.exe" }, MainExecutable = "Oblivion.exe" } }, { Game.Fallout3, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Fallout3, NexusName = "fallout3", NexusGameId = 120, MO2Name = "fallout3", MO2ArchiveName = "fallout3", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout3", SteamIDs = new List {22300, 22370}, // base game and GotY RequiredFiles = new List { "falloutlauncher.exe", "data\\fallout3.esm" }, MainExecutable = "Fallout3.exe" } }, { Game.FalloutNewVegas, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.FalloutNewVegas, NexusName = "newvegas", NexusGameId = 130, MO2Name = "New Vegas", MO2ArchiveName = "falloutnv", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\falloutnv", SteamIDs = new List {22380, 22490}, // normal and RU version GOGIDs = new List{1454587428}, RequiredFiles = new List { "FalloutNV.exe" }, MainExecutable = "FalloutNV.exe" } }, { Game.Skyrim, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Skyrim, NexusName = "skyrim", NexusGameId = 110, MO2Name = "Skyrim", MO2ArchiveName = "skyrim", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\skyrim", SteamIDs = new List {72850}, RequiredFiles = new List { "tesv.exe" }, MainExecutable = "TESV.exe", CommonlyConfusedWith = new [] {Game.SkyrimSpecialEdition, Game.SkyrimVR} } }, { Game.SkyrimSpecialEdition, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.SkyrimSpecialEdition, NexusName = "skyrimspecialedition", NexusGameId = 1704, MO2Name = "Skyrim Special Edition", MO2ArchiveName = "skyrimse", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim Special Edition", SteamIDs = new List {489830}, RequiredFiles = new List { "SkyrimSE.exe" }, MainExecutable = "SkyrimSE.exe", CommonlyConfusedWith = new []{Game.Skyrim, Game.SkyrimVR} } }, { Game.Fallout4, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Fallout4, NexusName = "fallout4", NexusGameId = 1151, MO2Name = "Fallout 4", MO2ArchiveName = "fallout4", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Fallout4", SteamIDs = new List {377160}, RequiredFiles = new List { "Fallout4.exe" }, MainExecutable = "Fallout4.exe" } }, /*{ Game.Fallout4VR, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Fallout4VR, NexusName = "fallout4", MO2Name = "Fallout 4", MO2ArchiveName = "fallout4", SteamIDs = new List{611660} } },*/ { Game.SkyrimVR, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.SkyrimVR, NexusName = "skyrimspecialedition", NexusGameId = 1704, MO2Name = "Skyrim VR", MO2ArchiveName = "skyrimse", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Skyrim VR", SteamIDs = new List {611670}, RequiredFiles = new List { "SkyrimVR.exe" }, MainExecutable = "SkyrimVR.exe", CommonlyConfusedWith = new []{Game.Skyrim, Game.SkyrimSpecialEdition} } }, { Game.DarkestDungeon, new GameMetaData { Game = Game.DarkestDungeon, NexusName = "darkestdungeon", NexusGameId = 804, SteamIDs = new List {262060}, GOGIDs = new List{1450711444}, RequiredFiles = new List { "_windows\\Darkest.exe" } } }, { Game.DivinityOriginalSin2DE, new GameMetaData { Game = Game.DivinityOriginalSin2DE, NexusName = "divinityoriginalsin2definitiveedition", NexusGameId = 2569, SteamIDs = new List {435150}, GOGIDs = new List{1584823040}, AdditionalFolders = new List { "%documents%\\Larian Studios\\Divinity Original Sin 2 Definitive Edition\\Mods\\" }, RequiredFiles = new List { "DefEd\\bin\\SupportTool.exe" } } }, { Game.DivinityOriginalSin2, new GameMetaData { Game = Game.DivinityOriginalSin2, NexusName = "divinityoriginalsin2", NexusGameId = 1661, SteamIDs = new List {435150}, GOGIDs = new List{1584823040}, AdditionalFolders = new List { "%documents%\\Larian Studios\\Divinity Original Sin 2\\Mods\\", }, RequiredFiles = new List { "bin\\SupportTool.exe" } } }, { Game.Starbound, new GameMetaData { Game = Game.Starbound, NexusName = "starbound", NexusGameId = 242, SteamIDs = new List{211820}, GOGIDs = new List{1452598881}, RequiredFiles = new List { "win64\\starbound.exe" } } }, { Game.SWKOTOR, new GameMetaData { Game = Game.SWKOTOR, NexusName = "kotor", NexusGameId = 234, SteamIDs = new List{32370}, GOGIDs = new List{1207666283}, RequiredFiles = new List { "swkotor.exe" } } }, { Game.SWKOTOR2, new GameMetaData { Game = Game.SWKOTOR2, NexusName = "kotor2", NexusGameId = 198, SteamIDs = new List{208580}, GOGIDs = new List{1421404581}, RequiredFiles = new List { "swkotor2.exe" } } }, { Game.Witcher, new GameMetaData { Game = Game.Witcher, NexusName = "witcher", NexusGameId = 150, SteamIDs = new List{20900}, GOGIDs = new List{1207658924}, RequiredFiles = new List { "system\\witcher.exe" } } }, { Game.Witcher2, new GameMetaData { Game = Game.Witcher2, NexusName = "witcher2", NexusGameId = 153, SteamIDs = new List{20920}, GOGIDs = new List{1207658930}, RequiredFiles = new List { "bin\\witcher2.exe", "bin\\userContentManager.exe" } } }, { Game.Witcher3, new GameMetaData { Game = Game.Witcher3, NexusName = "witcher3", NexusGameId = 952, SteamIDs = new List{292030, 499450}, // normal and GotY GOGIDs = new List{1207664643, 1495134320, 1207664663, 1640424747}, // normal, GotY and both in packages RequiredFiles = new List { "bin\\x64\\witcher2.exe" } } }, { Game.StardewValley, new GameMetaData { Game = Game.StardewValley, NexusName = "stardewvalley", NexusGameId = 1303, SteamIDs = new List{413150}, GOGIDs = new List{1453375253}, RequiredFiles = new List { "Stardew Valley.exe" } } }, { Game.Enderal, new GameMetaData { SupportedModManager = ModManager.MO2, Game = Game.Enderal, NexusName = "enderal", MO2Name = "Enderal", MO2ArchiveName = "enderal", SteamIDs = new List{1027920}, RequiredFiles = new List { "TESV.exe" }, MainExecutable = "TESV.exe" } } }; } }