diff --git a/Wabbajack.Common/GOGHandler.cs b/Wabbajack.Common/GOGHandler.cs new file mode 100644 index 00000000..3012e625 --- /dev/null +++ b/Wabbajack.Common/GOGHandler.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.Win32; + +namespace Wabbajack.Common +{ + public class GOGGame + { + public int GameID; + public string Path; + public string GameName; + } + + /// <summary> + /// Class for all GOG operations + /// </summary> + public class GOGHandler + { + 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); + 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>(); + string[] keys = GOGKey.GetSubKeyNames(); + foreach (var key in keys) + { + var game = new GOGGame + { + GameID = int.Parse(key), + GameName = GOGKey.OpenSubKey(key)?.GetValue("GAMENAME").ToString(), + Path = GOGKey.OpenSubKey(key)?.GetValue("PATH").ToString() + }; + + Games.Add(game); + } + } + } +} diff --git a/Wabbajack.Common/GameMetaData.cs b/Wabbajack.Common/GameMetaData.cs index 01594316..34b3cf13 100644 --- a/Wabbajack.Common/GameMetaData.cs +++ b/Wabbajack.Common/GameMetaData.cs @@ -50,22 +50,22 @@ namespace Wabbajack.Common public static GameMetaData GetByMO2ArchiveName(string gameName) { var gamename = gameName.ToLower(); - return Games.Values.FirstOrDefault(g => g.MO2ArchiveName == gamename); + return Games.Values.FirstOrDefault(g => g.MO2ArchiveName?.ToLower() == gamename); } public static Dictionary<Game, GameMetaData> Games = new Dictionary<Game, GameMetaData> { - { + /*{ Game.Morrowind, new GameMetaData() - }, + },*/ { Game.Oblivion, new GameMetaData { Game = Game.Oblivion, NexusName = "oblivion", MO2Name = "Oblivion", - MO2ArchiveName = "Oblivion", + MO2ArchiveName = "oblivion", GameLocationRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Bethesda Softworks\Oblivion" } }, diff --git a/Wabbajack.Common/SteamHandler.cs b/Wabbajack.Common/SteamHandler.cs new file mode 100644 index 00000000..cf542ca9 --- /dev/null +++ b/Wabbajack.Common/SteamHandler.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Win32; + +namespace Wabbajack.Common +{ + public class SteamGame + { + public int AppId; + public string Name; + public string InstallDir; + } + + /// <summary> + /// Class for all Steam operations + /// </summary> + public class SteamHandler + { + 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(!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"); + paths.Add(s); + }); + + 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).Do(f => + { + var steamGame = new SteamGame(); + File.ReadAllLines(f, Encoding.UTF8).Do(l => + { + if(l.Contains("\"appid\"")) + steamGame.AppId = int.Parse(GetVdfValue(l)); + if (l.Contains("\"name\"")) + steamGame.Name = GetVdfValue(l); + if (l.Contains("\"installdir\"")) + steamGame.InstallDir = Path.Combine(p, "common", GetVdfValue(l)); + }); + + games.Add(steamGame); + }); + }); + + Games = games; + } + + private static string GetVdfValue(string line) + { + var trim = line.Trim('\t').Replace("\t", ""); + string[] s = trim.Split('\"'); + return s[3].Replace("\\\\", "\\"); + } + } +} diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj index 9485a93e..dfb31465 100644 --- a/Wabbajack.Common/Wabbajack.Common.csproj +++ b/Wabbajack.Common/Wabbajack.Common.csproj @@ -98,8 +98,10 @@ <Compile Include="Extensions\TaskExt.cs" /> <Compile Include="FileExtractor.cs" /> <Compile Include="GameMetaData.cs" /> + <Compile Include="GOGHandler.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="SplittingStream.cs" /> + <Compile Include="SteamHandler.cs" /> <Compile Include="Utils.cs" /> <Compile Include="WorkQueue.cs" /> </ItemGroup> diff --git a/Wabbajack/Views/ModListGalleryView.xaml b/Wabbajack/Views/ModListGalleryView.xaml new file mode 100644 index 00000000..e27e7e25 --- /dev/null +++ b/Wabbajack/Views/ModListGalleryView.xaml @@ -0,0 +1,116 @@ +<UserControl x:Class="Wabbajack.Views.ModListGalleryView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" + xmlns:local="clr-namespace:Wabbajack" + xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" + xmlns:system="clr-namespace:System;assembly=mscorlib" + mc:Ignorable="d" + d:DesignHeight="450" d:DesignWidth="800"> + <UserControl.Resources> + <Color x:Key="TextBackgroundFill">#92000000</Color> + <SolidColorBrush x:Key="TextBackgroundFillBrush" Color="{StaticResource TextBackgroundFill}" /> + <Style x:Key="TileStyle" TargetType="{x:Type mahapps:Tile}"> + <Setter Property="Background" Value="Transparent"/> + </Style> + <Style x:Key="BackgroundBlurStyle" TargetType="{x:Type TextBlock}"> + <Setter Property="Background" Value="{StaticResource TextBackgroundFillBrush}"/> + </Style> + </UserControl.Resources> + <Grid> + <ScrollViewer HorizontalScrollBarVisibility="Disabled" Background="{x:Null}"> + <ScrollViewer.Resources> + <system:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">8</system:Double> + </ScrollViewer.Resources> + <StackPanel VerticalAlignment="Top" HorizontalAlignment="Left" Margin="8,0,8,0"> + <!--<Image Name="Banner"/> + <Button Width="60" HorizontalAlignment="Left" Command="{Binding BackCommand}" + Margin="4,0,0,8"> + <StackPanel Orientation="Horizontal"> + <iconPacks:PackIconMaterial Width="20" Height="20" Kind="ArrowLeft" /> + <TextBlock FontSize="15" Text="Back" Margin="4,0,0,0"/> + </StackPanel> + </Button>--> + <WrapPanel HorizontalAlignment="Center"> + <ItemsControl + Name="ItemsControlElement" + Visibility="{Binding ItemsControlVisibility}" + ItemsSource="{Binding ModLists}" + HorizontalAlignment="Center" + VerticalAlignment="Top"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel/> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <mahapps:Tile + Style="{StaticResource TileStyle}" + Click="Tile_OnClick" + Width="590" + Height="Auto"> + <Grid + Width="590" + Background="#222222"> + <Grid.RowDefinitions> + <RowDefinition Height="10*"/> + <RowDefinition Height="3*"/> + <RowDefinition Height="*"/> + </Grid.RowDefinitions> + <Grid + Grid.Row="0" + Width="590" + Height="Auto"> + <Grid.RowDefinitions> + <RowDefinition Height="*"/> + </Grid.RowDefinitions> + <Viewbox Grid.Row="0" Stretch="Uniform"> + <Image Source="{Binding Links.ImageUri}"/> + </Viewbox> + <TextBlock FontSize="30" TextWrapping="Wrap" Text="{Binding Title}" + Style="{StaticResource BackgroundBlurStyle}" Padding="4,0,4,0" + Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" + FontWeight="Bold"/> + </Grid> + <Grid Grid.Row="1" Margin="4,0,2,4"> + <Grid.RowDefinitions> + <RowDefinition Height="20"/> + <RowDefinition Height="*"/> + </Grid.RowDefinitions> + <TextBlock FontSize="15" FontStyle="Italic" Grid.Row="0" TextWrapping="Wrap" Text="{Binding Game}"/> + <TextBlock FontSize="15" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Description}"/> + </Grid> + <StackPanel Orientation="Horizontal" Grid.Row="2" Margin="0,0,4,4" HorizontalAlignment="Right"> + <Button Click="Info_OnClick" Margin="0,0,4,0"> + <StackPanel Orientation="Horizontal"> + <!--<TextBlock FontSize="13" TextWrapping="Wrap" Text="More Info" Margin="4,0,4,0"/>--> + <iconPacks:PackIconMaterial Width="16" Height="16" Kind="InformationOutline" Margin="0,0,4,0"/> + </StackPanel> + </Button> + <Button Click="Download_OnClick"> + <StackPanel Orientation="Horizontal"> + <!--<TextBlock FontSize="13" TextWrapping="Wrap" Text="Download" Margin="4,0,4,0"/>--> + <iconPacks:PackIconMaterial Width="16" Height="16" Kind="Download" /> + </StackPanel> + </Button> + </StackPanel> + </Grid> + </mahapps:Tile> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </WrapPanel> + <!--<Button Width="60" HorizontalAlignment="Left" Command="{Binding BackCommand}" + Margin="4,0,12,0"> + <StackPanel Orientation="Horizontal"> + <iconPacks:PackIconMaterial Width="20" Height="20" Kind="ArrowLeft" /> + <TextBlock FontSize="15" Text="Back" Margin="4,0,0,0"/> + </StackPanel> + </Button>--> + </StackPanel> + </ScrollViewer> + </Grid> +</UserControl> diff --git a/Wabbajack/Views/ModListGalleryView.xaml.cs b/Wabbajack/Views/ModListGalleryView.xaml.cs new file mode 100644 index 00000000..59a19b97 --- /dev/null +++ b/Wabbajack/Views/ModListGalleryView.xaml.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using MahApps.Metro.Controls; +using Wabbajack.Lib.ModListRegistry; + +namespace Wabbajack.Views +{ + public partial class ModListGalleryView : UserControl + { + public ModListGalleryView() + { + InitializeComponent(); + } + + public void Info_OnClick(object sender, RoutedEventArgs e) + { + if (!(sender is Button b)) return; + if (!(b.DataContext is ModlistMetadata mm)) return; + var link = mm.Links.MachineURL; + Process.Start($"https://www.wabbajack.org/modlist/{link}"); + } + + public void Download_OnClick(object sender, RoutedEventArgs routedEventArgs) + { + /* unsure about this since the downloader changed + + + + if (!(sender is Button b)) return; + if (!(b.DataContext is ModlistMetadata mm)) return; + var link = mm.Links.Download; + + if (!Directory.Exists(Consts.ModListDownloadFolder)) + Directory.CreateDirectory(Consts.ModListDownloadFolder); + var dest = Path.Combine(Consts.ModListDownloadFolder, mm.Links.MachineURL + ExtensionManager.Extension); + + var downloadWindow = new DownloadWindow(link, mm.Title, mm.,dest); + downloadWindow.ShowDialog();*/ + } + + private void Tile_OnClick(object sender, RoutedEventArgs e) + { + if (!(sender is Tile t)) return; + if (!t.IsFocused) return; + if (!t.IsMouseOver) return; + if (!(t.DataContext is ModlistMetadata mm)) return; + var link = mm.Links.MachineURL; + Process.Start($"https://www.wabbajack.org/modlist/{link}"); + } + } +} diff --git a/Wabbajack/Wabbajack.csproj b/Wabbajack/Wabbajack.csproj index 9f3b6e48..37e11ef8 100644 --- a/Wabbajack/Wabbajack.csproj +++ b/Wabbajack/Wabbajack.csproj @@ -204,6 +204,9 @@ <Compile Include="Converters\LeftMarginMultiplierConverter.cs" /> <Compile Include="Util\TreeViewItemExtensions.cs" /> <Compile Include="View Models\SlideShow.cs" /> + <Compile Include="Views\ModListGalleryView.xaml.cs"> + <DependentUpon>ModListGalleryView.xaml</DependentUpon> + </Compile> <Compile Include="Views\TextViewer.xaml.cs"> <DependentUpon>TextViewer.xaml</DependentUpon> </Compile> @@ -262,6 +265,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Views\ModListGalleryView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Views\TextViewer.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType>