diff --git a/UWPHook/App.config b/UWPHook/App.config
index c80186b..f9ab9ff 100644
--- a/UWPHook/App.config
+++ b/UWPHook/App.config
@@ -14,7 +14,7 @@
False
-
+
5
@@ -22,6 +22,9 @@
False
+
+
+
diff --git a/UWPHook/GamesWindow.xaml.cs b/UWPHook/GamesWindow.xaml.cs
index c35f409..1baa8df 100644
--- a/UWPHook/GamesWindow.xaml.cs
+++ b/UWPHook/GamesWindow.xaml.cs
@@ -1,13 +1,19 @@
-using SharpSteam;
+using Force.Crc32;
+using SharpSteam;
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
+using UWPHook.SteamGridDb;
using VDFParser;
using VDFParser.Models;
@@ -104,36 +110,157 @@ namespace UWPHook
}
}
- private void ExportButton_Click(object sender, RoutedEventArgs e)
+ private UInt64 GenerateSteamGridAppId(string appName, string appTarget)
{
- bwrSave = new BackgroundWorker();
- bwrSave.DoWork += BwrSave_DoWork;
- bwrSave.RunWorkerCompleted += BwrSave_RunWorkerCompleted;
- grid.IsEnabled = false;
- progressBar.Visibility = Visibility.Visible;
+ byte[] nameTargetBytes = Encoding.UTF8.GetBytes(appTarget + appName + "");
+ UInt64 crc = Crc32Algorithm.Compute(nameTargetBytes);
+ UInt64 gameId = crc | 0x80000000;
- bwrSave.RunWorkerAsync();
+ return gameId;
}
- private void BwrSave_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+ private async void ExportButton_Click(object sender, RoutedEventArgs e)
{
+ grid.IsEnabled = false;
+ progressBar.Visibility = Visibility.Visible;
+
+ await ExportGames();
+
grid.IsEnabled = true;
progressBar.Visibility = Visibility.Collapsed;
MessageBox.Show("Your apps were successfuly exported, please restart Steam in order to see your apps.", "UWPHook", MessageBoxButton.OK, MessageBoxImage.Information);
}
- private void BwrSave_DoWork(object sender, DoWorkEventArgs e)
+ private async Task SaveImage(string imageUrl, string destinationFilename, ImageFormat format)
+ {
+ await Task.Run(() =>
+ {
+ WebClient client = new WebClient();
+ Stream stream = client.OpenRead(imageUrl);
+ Bitmap bitmap; bitmap = new Bitmap(stream);
+
+ if (bitmap != null)
+ {
+ bitmap.Save(destinationFilename, format);
+ }
+
+ stream.Flush();
+ stream.Close();
+ client.Dispose();
+ });
+ }
+
+ private void CopyTempGridImagesToSteamUser(string user)
+ {
+ string tmpGridDirectory = Path.GetTempPath() + "UWPHook\\tmp_grid\\";
+ string userGridDirectory = user + "\\config\\grid\\";
+ string[] images = Directory.GetFiles(tmpGridDirectory);
+
+ if (!Directory.Exists(userGridDirectory))
+ {
+ Directory.CreateDirectory(userGridDirectory);
+ }
+
+ foreach (string image in images)
+ {
+ string destFile = userGridDirectory + Path.GetFileName(image);
+ File.Copy(image, destFile, true);
+ }
+ }
+
+ private void RemoveTempGridImages()
+ {
+ string tmpGridDirectory = Path.GetTempPath() + "UWPHook\\tmp_grid\\";
+ Directory.Delete(tmpGridDirectory, true);
+ }
+
+ private async Task DownloadTempGridImages(string appName, string appTarget)
+ {
+ SteamGridDbApi api = new SteamGridDbApi(Properties.Settings.Default.SteamGridDbApiKey);
+ string tmpGridDirectory = Path.GetTempPath() + "UWPHook\\tmp_grid\\";
+
+ var games = await api.SearchGame(appName);
+
+ if (games != null)
+ {
+ var game = games[0];
+ UInt64 gameId = GenerateSteamGridAppId(appName, appTarget);
+
+ if (!Directory.Exists(tmpGridDirectory))
+ {
+ Directory.CreateDirectory(tmpGridDirectory);
+ }
+
+ var gameGridsVertical = api.GetGameGrids(game.Id, "600x900", "static");
+ var gameGridsHorizontal = api.GetGameGrids(game.Id, "460x215", "static");
+ var gameHeroes = api.GetGameHeroes(game.Id, "static");
+ var gameLogos = api.GetGameLogos(game.Id, "static");
+
+ await Task.WhenAll(
+ gameGridsVertical,
+ gameGridsHorizontal,
+ gameHeroes,
+ gameLogos
+ );
+
+ var gridsVertical = await gameGridsVertical;
+ var gridsHorizontal = await gameGridsHorizontal;
+ var heroes = await gameHeroes;
+ var logos = await gameLogos;
+
+ List saveImagesTasks = new List();
+
+ if (gridsHorizontal != null && gridsHorizontal.Length > 0)
+ {
+ var grid = gridsHorizontal[0];
+ saveImagesTasks.Add(SaveImage(grid.Url, $"{tmpGridDirectory}\\{gameId}.png", ImageFormat.Png));
+ }
+
+ if (gridsVertical != null && gridsVertical.Length > 0)
+ {
+ var grid = gridsVertical[0];
+ saveImagesTasks.Add(SaveImage(grid.Url, $"{tmpGridDirectory}\\{gameId}p.png", ImageFormat.Png));
+ }
+
+ if (heroes != null && heroes.Length > 0)
+ {
+ var hero = heroes[0];
+ saveImagesTasks.Add(SaveImage(hero.Url, $"{tmpGridDirectory}\\{gameId}_hero.png", ImageFormat.Png));
+ }
+
+ if (logos != null && logos.Length > 0)
+ {
+ var logo = logos[0];
+ saveImagesTasks.Add(SaveImage(logo.Url, $"{tmpGridDirectory}\\{gameId}_logo.png", ImageFormat.Png));
+ }
+
+ await Task.WhenAll(saveImagesTasks);
+
+ }
+ }
+
+ private async Task ExportGames()
{
string steam_folder = SteamManager.GetSteamFolder();
if (Directory.Exists(steam_folder))
{
var users = SteamManager.GetUsers(steam_folder);
var selected_apps = Apps.Entries.Where(app => app.Selected);
+ var exePath = @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @"""";
+ var exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
+
+ List gridImagesDownloadTasks = new List();
+ bool downloadGridImages = !String.IsNullOrEmpty(Properties.Settings.Default.SteamGridDbApiKey);
- //To make things faster, decide icons before looping users
+ //To make things faster, decide icons and download grid images before looping users
foreach (var app in selected_apps)
{
app.Icon = app.widestSquareIcon();
+
+ if (downloadGridImages)
+ {
+ gridImagesDownloadTasks.Add(DownloadTempGridImages(app.Name, exePath));
+ }
}
foreach (var user in users)
@@ -156,8 +283,7 @@ namespace UWPHook
if (shortcuts != null)
{
- var exePath = @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @"""";
- var exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
+
foreach (var app in selected_apps)
{
VDFEntry newApp = new VDFEntry()
@@ -204,6 +330,21 @@ namespace UWPHook
MessageBox.Show("Error: Program failed exporting your games:" + Environment.NewLine + ex.Message + ex.StackTrace);
}
}
+
+ if (gridImagesDownloadTasks.Count > 0)
+ {
+ await Task.WhenAll(gridImagesDownloadTasks);
+
+ await Task.Run(() =>
+ {
+ foreach (var user in users)
+ {
+ CopyTempGridImagesToSteamUser(user);
+ }
+
+ RemoveTempGridImages();
+ });
+ }
}
}
diff --git a/UWPHook/Properties/Settings.Designer.cs b/UWPHook/Properties/Settings.Designer.cs
index 7a982ca..fc330f9 100644
--- a/UWPHook/Properties/Settings.Designer.cs
+++ b/UWPHook/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace UWPHook.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -70,5 +70,17 @@ namespace UWPHook.Properties {
this["StreamMode"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string SteamGridDbApiKey {
+ get {
+ return ((string)(this["SteamGridDbApiKey"]));
+ }
+ set {
+ this["SteamGridDbApiKey"] = value;
+ }
+ }
}
}
diff --git a/UWPHook/Properties/Settings.settings b/UWPHook/Properties/Settings.settings
index 6aed1ad..339d33b 100644
--- a/UWPHook/Properties/Settings.settings
+++ b/UWPHook/Properties/Settings.settings
@@ -14,5 +14,8 @@
False
+
+
+
\ No newline at end of file
diff --git a/UWPHook/SettingsWindow.xaml b/UWPHook/SettingsWindow.xaml
index 582b481..a5124ce 100644
--- a/UWPHook/SettingsWindow.xaml
+++ b/UWPHook/SettingsWindow.xaml
@@ -26,9 +26,12 @@
-
-
-
+
+
+
+
+
+
@@ -39,16 +42,19 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UWPHook/SettingsWindow.xaml.cs b/UWPHook/SettingsWindow.xaml.cs
index fabd401..74faae5 100644
--- a/UWPHook/SettingsWindow.xaml.cs
+++ b/UWPHook/SettingsWindow.xaml.cs
@@ -32,6 +32,7 @@ namespace UWPHook
cultures_comboBox.SelectedItem = Properties.Settings.Default.TargetLanguage;
language_toggle.IsChecked = Properties.Settings.Default.ChangeLanguage;
streaming_toggle.IsChecked = Properties.Settings.Default.StreamMode;
+ steamgriddb_api_key.Text = Properties.Settings.Default.SteamGridDbApiKey;
}
private void saveButton_Click(object sender, RoutedEventArgs e)
@@ -40,6 +41,7 @@ namespace UWPHook
Properties.Settings.Default.TargetLanguage = cultures_comboBox.SelectedItem.ToString();
Properties.Settings.Default.Seconds = Int32.Parse(seconds_comboBox.SelectedItem.ToString().Substring(0, 1));
Properties.Settings.Default.StreamMode = (bool)streaming_toggle.IsChecked;
+ Properties.Settings.Default.SteamGridDbApiKey = steamgriddb_api_key.Text;
Properties.Settings.Default.Save();
this.Close();
}
diff --git a/UWPHook/SteamGridDb/GameResponse.cs b/UWPHook/SteamGridDb/GameResponse.cs
new file mode 100644
index 0000000..daafef5
--- /dev/null
+++ b/UWPHook/SteamGridDb/GameResponse.cs
@@ -0,0 +1,10 @@
+namespace UWPHook.SteamGridDb
+{
+ class GameResponse
+ {
+
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ }
+}
diff --git a/UWPHook/SteamGridDb/GridResponse.cs b/UWPHook/SteamGridDb/GridResponse.cs
new file mode 100644
index 0000000..0f39b6e
--- /dev/null
+++ b/UWPHook/SteamGridDb/GridResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace UWPHook.SteamGridDb
+{
+ class GridResponse
+ {
+ public string Url { get; set; }
+ }
+}
diff --git a/UWPHook/SteamGridDb/HeroResponse.cs b/UWPHook/SteamGridDb/HeroResponse.cs
new file mode 100644
index 0000000..80c38ad
--- /dev/null
+++ b/UWPHook/SteamGridDb/HeroResponse.cs
@@ -0,0 +1,7 @@
+namespace UWPHook.SteamGridDb
+{
+ public class HeroResponse
+ {
+ public string Url { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/UWPHook/SteamGridDb/LogoResponse.cs b/UWPHook/SteamGridDb/LogoResponse.cs
new file mode 100644
index 0000000..258f072
--- /dev/null
+++ b/UWPHook/SteamGridDb/LogoResponse.cs
@@ -0,0 +1,7 @@
+namespace UWPHook.SteamGridDb
+{
+ public class LogoResponse
+ {
+ public string Url { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/UWPHook/SteamGridDb/SteamGridDbApi.cs b/UWPHook/SteamGridDb/SteamGridDbApi.cs
new file mode 100644
index 0000000..632cda5
--- /dev/null
+++ b/UWPHook/SteamGridDb/SteamGridDbApi.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace UWPHook.SteamGridDb
+{
+ class SteamGridDbApi
+ {
+ private const string BASE_URL = "https://www.steamgriddb.com/api/v2/";
+
+ private HttpClient httpClient;
+
+ public SteamGridDbApi(string apiKey)
+ {
+ httpClient = new HttpClient();
+ httpClient.BaseAddress = new Uri(BASE_URL);
+ httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
+ }
+
+ public async Task SearchGame(string gameName)
+ {
+ string path = $"search/autocomplete/{gameName}";
+
+ GameResponse[] games = null;
+ HttpResponseMessage response = await httpClient.GetAsync(path);
+
+ if(response.IsSuccessStatusCode)
+ {
+ var parsedResponse = await response.Content.ReadAsAsync>();
+ games = parsedResponse.Data;
+ }
+
+ return games;
+ }
+
+ public async Task GetGameGrids(
+ int gameId,
+ string dimensions = null,
+ string types = null,
+ string styles = null,
+ string nsfw = "any",
+ string humor = "any")
+ {
+ string path = $"grids/game/{gameId}?";
+
+ if (!String.IsNullOrEmpty(dimensions))
+ path += $"dimensions={dimensions}&";
+
+ if (!String.IsNullOrEmpty(types))
+ path += $"types={types}&";
+
+ if (!String.IsNullOrEmpty(styles))
+ path += $"styles={styles}&";
+
+ if (!String.IsNullOrEmpty(nsfw))
+ path += $"nsfw={nsfw}&";
+
+ if (!String.IsNullOrEmpty(humor))
+ path += $"humor={humor}&";
+
+ GridResponse[] grids = null;
+ HttpResponseMessage response = await httpClient.GetAsync(path);
+
+ if (response.IsSuccessStatusCode)
+ {
+ var parsedResponse = await response.Content.ReadAsAsync>();
+ grids = parsedResponse.Data;
+ }
+
+ return grids;
+ }
+
+ public async Task GetGameHeroes(
+ int gameId,
+ string types = null,
+ string dimensions = null,
+ string styles = null,
+ string nsfw = "any",
+ string humor = "any")
+ {
+ string path = $"heroes/game/{gameId}?";
+
+ if (!String.IsNullOrEmpty(dimensions))
+ path += $"dimensions={dimensions}&";
+
+ if (!String.IsNullOrEmpty(types))
+ path += $"types={types}&";
+
+ if (!String.IsNullOrEmpty(styles))
+ path += $"styles={styles}&";
+
+ if (!String.IsNullOrEmpty(nsfw))
+ path += $"nsfw={nsfw}&";
+
+ if (!String.IsNullOrEmpty(humor))
+ path += $"humor={humor}&";
+
+ HeroResponse[] heroes = null;
+ HttpResponseMessage response = await httpClient.GetAsync(path);
+
+ if (response.IsSuccessStatusCode)
+ {
+ var parsedResponse = await response.Content.ReadAsAsync>();
+ heroes = parsedResponse.Data;
+ }
+
+ return heroes;
+ }
+
+ public async Task GetGameLogos(
+ int gameId,
+ string types = null,
+ string styles = null,
+ string nsfw = "any",
+ string humor = "any")
+ {
+ string path = $"logos/game/{gameId}?";
+
+ if (!String.IsNullOrEmpty(types))
+ path += $"types={types}&";
+
+ if (!String.IsNullOrEmpty(styles))
+ path += $"styles={styles}&";
+
+ if (!String.IsNullOrEmpty(nsfw))
+ path += $"nsfw={nsfw}&";
+
+ if (!String.IsNullOrEmpty(humor))
+ path += $"humor={humor}&";
+
+ LogoResponse[] logos = null;
+ HttpResponseMessage response = await httpClient.GetAsync(path);
+
+ if (response.IsSuccessStatusCode)
+ {
+ var parsedResponse = await response.Content.ReadAsAsync>();
+ logos = parsedResponse.Data;
+ }
+
+ return logos;
+ }
+
+ private class ResponseWrapper
+ {
+ public bool Success { get; set; }
+ public T[] Data { get; set; }
+ }
+ }
+}
diff --git a/UWPHook/UWPHook.csproj b/UWPHook/UWPHook.csproj
index 65a6e5e..66d1eee 100644
--- a/UWPHook/UWPHook.csproj
+++ b/UWPHook/UWPHook.csproj
@@ -59,12 +59,18 @@
UWPHook.App
+
+ ..\packages\Crc32.NET.1.2.0\lib\net20\Crc32.NET.dll
+
..\packages\MaterialDesignColors.1.2.6\lib\net45\MaterialDesignColors.dll
..\packages\MaterialDesignThemes.3.1.3\lib\net45\MaterialDesignThemes.Wpf.dll
+
+ ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll
+
..\..\SharpSteam\SharpSteam\bin\Release\SharpSteam.dll
@@ -74,6 +80,9 @@
..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll
+
@@ -108,6 +117,11 @@
SettingsWindow.xaml
+
+
+
+
+
MSBuild:Compile
diff --git a/UWPHook/packages.config b/UWPHook/packages.config
index a0c4728..804c406 100644
--- a/UWPHook/packages.config
+++ b/UWPHook/packages.config
@@ -1,7 +1,10 @@
+
+
+
\ No newline at end of file