diff --git a/UWPHook.sln b/UWPHook.sln index 8a1bd6d..6a1fd6c 100644 --- a/UWPHook.sln +++ b/UWPHook.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.27703.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWPHook", "UWPHook\UWPHook.csproj", "{AFE09BCF-28A4-48EE-876B-FEF080D04D5F}" EndProject -Project("{840C416C-B8F3-42BC-B0DD-F6BB14C9F8CB}") = "Setup", "Setup\Setup.aiproj", "{F63C6051-9812-47BE-87F2-ECA8F2E89EC0}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,12 +18,6 @@ Global {AFE09BCF-28A4-48EE-876B-FEF080D04D5F}.DefaultBuild|Any CPU.Build.0 = Debug|Any CPU {AFE09BCF-28A4-48EE-876B-FEF080D04D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU {AFE09BCF-28A4-48EE-876B-FEF080D04D5F}.Release|Any CPU.Build.0 = Release|Any CPU - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.Debug|Any CPU.ActiveCfg = DefaultBuild - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.Debug|Any CPU.Build.0 = DefaultBuild - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.DefaultBuild|Any CPU.ActiveCfg = DefaultBuild - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.DefaultBuild|Any CPU.Build.0 = DefaultBuild - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.Release|Any CPU.ActiveCfg = DefaultBuild - {F63C6051-9812-47BE-87F2-ECA8F2E89EC0}.Release|Any CPU.Build.0 = DefaultBuild EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UWPHook/Crc32.cs b/UWPHook/Crc32.cs new file mode 100644 index 0000000..7cf768f --- /dev/null +++ b/UWPHook/Crc32.cs @@ -0,0 +1,48 @@ +using System; + +namespace UWPHook +{ + public class Crc32 + { + uint[] table; + + public uint ComputeChecksum(byte[] bytes) + { + uint crc = 0xffffffff; + for (int i = 0; i < bytes.Length; ++i) + { + byte index = (byte)(((crc) & 0xff) ^ bytes[i]); + crc = (uint)((crc >> 8) ^ table[index]); + } + return ~crc; + } + + public byte[] ComputeChecksumBytes(byte[] bytes) + { + return BitConverter.GetBytes(ComputeChecksum(bytes)); + } + + public Crc32() + { + uint poly = 0xedb88320; + table = new uint[256]; + uint temp = 0; + for (uint i = 0; i < table.Length; ++i) + { + temp = i; + for (int j = 8; j > 0; --j) + { + if ((temp & 1) == 1) + { + temp = (uint)((temp >> 1) ^ poly); + } + else + { + temp >>= 1; + } + } + table[i] = temp; + } + } + } +} diff --git a/UWPHook/GamesWindow.xaml.cs b/UWPHook/GamesWindow.xaml.cs index 1d41e4d..d3d3672 100644 --- a/UWPHook/GamesWindow.xaml.cs +++ b/UWPHook/GamesWindow.xaml.cs @@ -2,12 +2,17 @@ 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; @@ -96,7 +101,7 @@ namespace UWPHook { if (Properties.Settings.Default.ChangeLanguage && !String.IsNullOrEmpty(Properties.Settings.Default.TargetLanguage)) { - ScriptManager.RunScript("Set - WinUILanguageOverride " + currentLanguage); + ScriptManager.RunScript("Set-WinUILanguageOverride " + currentLanguage); } //The user has probably finished using the app, so let's close UWPHook to keep the experience clean @@ -104,25 +109,97 @@ 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 = new Crc32().ComputeChecksum(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 void SaveImage(string imageUrl, string destinationFilename, ImageFormat format) + { + 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 async Task DownloadGridImages(string userId, string appName, string appTarget) + { + // Generate app id for grid images + SteamGridDbApi api = new SteamGridDbApi("0973373b2cec3120ce673d06747d506c"); + string gridDirectory = userId + @"\\config\\grid\\"; + + var games = await api.SearchGame(appName); + + if (games != null) + { + var game = games[0]; + UInt64 gameId = GenerateSteamGridAppId(appName, appTarget); + + if (!Directory.Exists(gridDirectory)) + { + Directory.CreateDirectory(gridDirectory); + } + + var gameGrids = api.GetGameGrids(game.Id, "600x900", "static"); + var gameHeroes = api.GetGameHeroes(game.Id, "static"); + var gameLogos = api.GetGameLogos(game.Id, "static"); + + await Task.WhenAll( + gameGrids, + gameHeroes, + gameLogos + ); + + var grids = await gameGrids; + var heroes = await gameHeroes; + var logos = await gameLogos; + + if (grids != null) + { + var grid = grids[0]; + SaveImage(grid.Url, $"{gridDirectory}\\{gameId}p.png", ImageFormat.Png); + } + + if (heroes != null) + { + var hero = heroes[0]; + SaveImage(hero.Url, $"{gridDirectory}\\{gameId}_hero.png", ImageFormat.Png); + } + + if (logos != null) + { + var logo = logos[0]; + SaveImage(logo.Url, $"{gridDirectory}\\{gameId}_logo.png", ImageFormat.Png); + } + + } + } + + private async Task ExportGames() { string steam_folder = SteamManager.GetSteamFolder(); if (Directory.Exists(steam_folder)) @@ -158,10 +235,12 @@ namespace UWPHook { foreach (var app in selected_apps) { + string appTarget = @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @""" " + app.Aumid; + VDFEntry newApp = new VDFEntry() { AppName = app.Name, - Exe = @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @""" " + app.Aumid, + Exe = appTarget, StartDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), AllowDesktopConfig = 1, Icon = app.Icon, @@ -178,6 +257,9 @@ namespace UWPHook //Resize this array so it fits the new entries Array.Resize(ref shortcuts, shortcuts.Length + 1); shortcuts[shortcuts.Length - 1] = newApp; + + // TODO: Check if api key setting exist + await DownloadGridImages(user, app.Name, appTarget); } try 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..715e634 --- /dev/null +++ b/UWPHook/SteamGridDb/SteamGridDbApi.cs @@ -0,0 +1,111 @@ +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}"; + + 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}"; + + 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}"; + + 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..7ac201b 100644 --- a/UWPHook/UWPHook.csproj +++ b/UWPHook/UWPHook.csproj @@ -65,6 +65,9 @@ ..\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 +77,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 + @@ -97,6 +103,7 @@ + FullScreenLauncher.xaml @@ -108,6 +115,11 @@ SettingsWindow.xaml + + + + + MSBuild:Compile diff --git a/UWPHook/packages.config b/UWPHook/packages.config index a0c4728..b43367d 100644 --- a/UWPHook/packages.config +++ b/UWPHook/packages.config @@ -2,6 +2,8 @@ + + \ No newline at end of file