diff --git a/UWPHook/GamesWindow.xaml.cs b/UWPHook/GamesWindow.xaml.cs index dd054a6..3eba4ba 100644 --- a/UWPHook/GamesWindow.xaml.cs +++ b/UWPHook/GamesWindow.xaml.cs @@ -40,7 +40,7 @@ public GamesWindow() if (args?.Length > 1) { // When length is 1, the only argument is the path where the app is installed - _ = LauncherAsync(args); // Launches the requested game + _ = LauncherAsync(args); // Launches the requested game } else @@ -210,7 +210,7 @@ await Task.Run(() => stream.Close(); client.Dispose(); } - }); + }); } /// @@ -218,16 +218,16 @@ await Task.Run(() => /// /// The user path to copy images to private void CopyTempGridImagesToSteamUser(string user) - { + { string tmpGridDirectory = Path.GetTempPath() + "UWPHook\\tmp_grid\\"; string userGridDirectory = user + "\\config\\grid\\"; - + // No images were downloaded, maybe the key is invalid or no app had an image if (!Directory.Exists(tmpGridDirectory)) { return; } - + string[] images = Directory.GetFiles(tmpGridDirectory); if (!Directory.Exists(userGridDirectory)) @@ -273,7 +273,7 @@ private async Task DownloadTempGridImages(string appName, string appTarget) { throw; } - + if (games != null) { var game = games[0]; @@ -348,7 +348,7 @@ private async Task ExportGames(bool restartSteam) { var users = SteamManager.GetUsers(steam_folder); var selected_apps = Apps.Entries.Where(app => app.Selected); - var exePath = @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @""""; + var exePath = GenerateExePath(); var exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); List gridImagesDownloadTasks = new List(); @@ -370,89 +370,8 @@ private async Task ExportGames(bool restartSteam) // Export the selected apps and the downloaded images to each user // in the steam folder by modifying it's VDF file - foreach (var user in users) - { - try - { - VDFEntry[] shortcuts = new VDFEntry[0]; - try - { - shortcuts = SteamManager.ReadShortcuts(user); - } - catch (Exception ex) - { - //If it's a short VDF, let's just overwrite it - if (ex.GetType() != typeof(VDFTooShortException)) - { - throw new Exception("Error: Program failed to load existing Steam shortcuts." + Environment.NewLine + ex.Message); - } - } - - if (shortcuts != null) - { - foreach (var app in selected_apps) - { - VDFEntry newApp = new VDFEntry() - { - AppName = app.Name, - Exe = exePath, - StartDir = exeDir, - LaunchOptions = app.Aumid, - AllowDesktopConfig = 1, - AllowOverlay = 1, - Icon = app.Icon, - Index = shortcuts.Length, - IsHidden = 0, - OpenVR = 0, - ShortcutPath = "", - Tags = tags, - Devkit = 0, - DevkitGameID = "", - LastPlayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), - }; - Boolean isFound = false; - for (int i = 0; i < shortcuts.Length; i++) - { - Debug.WriteLine(shortcuts[i].ToString()); - - - if (shortcuts[i].AppName == app.Name) - { - isFound = true; - Debug.WriteLine(app.Name + " already added to Steam. Updating existing shortcut."); - shortcuts[i] = newApp; - } - } - - if (!isFound) - { - //Resize this array so it fits the new entries - Array.Resize(ref shortcuts, shortcuts.Length + 1); - shortcuts[shortcuts.Length - 1] = newApp; - } - - } - - try - { - if (!Directory.Exists(user + @"\\config\\")) - { - Directory.CreateDirectory(user + @"\\config\\"); - } - //Write the file with all the shortcuts - File.WriteAllBytes(user + @"\\config\\shortcuts.vdf", VDFSerializer.Serialize(shortcuts)); - } - catch (Exception ex) - { - throw new Exception("Error: Program failed while trying to write your Steam shortcuts" + Environment.NewLine + ex.Message); - } - } - } - catch (Exception ex) - { - MessageBox.Show("Error: Program failed exporting your games:" + Environment.NewLine + ex.Message + ex.StackTrace); - } - } + ExportAppsToSteamShortcuts(tags, users, selected_apps, exePath, exeDir); + ExportAppsToShieldShortcuts(selected_apps, exePath, exeDir); if (gridImagesDownloadTasks.Count > 0) { @@ -463,6 +382,7 @@ await Task.Run(() => foreach (var user in users) { CopyTempGridImagesToSteamUser(user); + CopyTempGridImagesToShieldStreamingAssets(selected_apps); } RemoveTempGridImages(); @@ -470,7 +390,7 @@ await Task.Run(() => } } - if(restartSteam) + if (restartSteam) { Func getSteam = () => Process.GetProcessesByName("steam").SingleOrDefault(); @@ -517,6 +437,103 @@ await Task.Run(() => return true; } + /// + /// Extracted method to simplify the complexity of the ExportGames method. + /// + /// This method updates and existing steam vdf file to add or update shortcuts for the selected windows apps. + /// + /// + /// + /// + /// + /// + private static void ExportAppsToSteamShortcuts(string[] tags, string[] users, IEnumerable selected_apps, string exePath, string exeDir) + { + foreach (var user in users) + { + try + { + VDFEntry[] shortcuts = new VDFEntry[0]; + try + { + shortcuts = SteamManager.ReadShortcuts(user); + } + catch (Exception ex) + { + //If it's a short VDF, let's just overwrite it + if (ex.GetType() != typeof(VDFTooShortException)) + { + throw new Exception("Error: Program failed to load existing Steam shortcuts." + Environment.NewLine + ex.Message); + } + } + + if (shortcuts != null) + { + foreach (var app in selected_apps) + { + VDFEntry newApp = new VDFEntry() + { + AppName = app.Name, + Exe = exePath, + StartDir = exeDir, + LaunchOptions = app.Aumid, + AllowDesktopConfig = 1, + AllowOverlay = 1, + Icon = app.Icon, + Index = shortcuts.Length, + IsHidden = 0, + OpenVR = 0, + ShortcutPath = "", + Tags = tags, + Devkit = 0, + DevkitGameID = "", + LastPlayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), + }; + Boolean isFound = false; + for (int i = 0; i < shortcuts.Length; i++) + { + Debug.WriteLine(shortcuts[i].ToString()); + + + if (shortcuts[i].AppName == app.Name) + { + isFound = true; + Debug.WriteLine(app.Name + " already added to Steam. Updating existing shortcut."); + shortcuts[i] = newApp; + } + } + + if (!isFound) + { + //Resize this array so it fits the new entries + Array.Resize(ref shortcuts, shortcuts.Length + 1); + shortcuts[shortcuts.Length - 1] = newApp; + } + + } + + try + { + if (!Directory.Exists(user + @"\\config\\")) + { + Directory.CreateDirectory(user + @"\\config\\"); + } + //Write the file with all the shortcuts + File.WriteAllBytes(user + @"\\config\\shortcuts.vdf", VDFSerializer.Serialize(shortcuts)); + } + catch (Exception ex) + { + throw new Exception("Error: Program failed while trying to write your Steam shortcuts" + Environment.NewLine + ex.Message); + } + } + } + catch (Exception ex) + { + MessageBox.Show("Error: Program failed exporting your games:" + Environment.NewLine + ex.Message + ex.StackTrace); + } + } + } + public static void ClearAllShortcuts() { Debug.WriteLine("DBG: Clearing all elements in shortcuts.vdf"); @@ -695,5 +712,140 @@ private void Window_Loaded(object sender, RoutedEventArgs e) } } } + + /// + /// Generates the exe path used by Steam and NVIDIA Shield to call UWPHook + /// + /// + private static string GenerateExePath() + { + return @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @""""; + } + + /// + /// Iterates through the selected apps and creates windwos shortcut files for each inside the NVIDIA Shield Apps directory. + /// Will place an NVIDIA required placeholder box-art.png file inside the StreamingAssets/{app-name} dir. + /// + /// + /// + /// + private static void ExportAppsToShieldShortcuts(IEnumerable selected_apps, string exePath, string exeDir) + { + string shieldAppsDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "NVIDIA Corporation", "Shield Apps"); + string streamingAssetsDir = Path.Combine(shieldAppsDir, "StreamingAssets"); + if (!Directory.Exists(streamingAssetsDir)) + { + DirectoryInfo di = Directory.CreateDirectory(streamingAssetsDir); + di.Attributes = FileAttributes.Directory | FileAttributes.Hidden; + } + + foreach (AppEntry app in selected_apps) + { + string scrubbedAppName = app.Name; + foreach (char c in Path.GetInvalidFileNameChars()) + { + scrubbedAppName = scrubbedAppName.Replace(c, '_'); + } + + string shortcutFile = Path.Combine(shieldAppsDir, scrubbedAppName + ".lnk"); + Debug.WriteLine("Creating NVIDIA Shield shortcut: " + shortcutFile); + + IWshRuntimeLibrary.IWshShell3 wsh = new IWshRuntimeLibrary.IWshShell_Class(); + IWshRuntimeLibrary.IWshShortcut shortcut = wsh.CreateShortcut(shortcutFile); + shortcut.Arguments = app.Aumid; + shortcut.TargetPath = exePath; + // not sure about what this is for + shortcut.WindowStyle = 1; + shortcut.Description = "test link for " + app.Name; + shortcut.WorkingDirectory = exeDir; + //shortcut.IconLocation = "specify icon location"; + shortcut.Save(); + + // Copy over placeholder box-art.png (required by NVIDIA) + string boxArtDir = Path.Combine(streamingAssetsDir, scrubbedAppName); + string boxArtFile = Path.Combine(streamingAssetsDir, scrubbedAppName, "box-art.png"); + if (!Directory.Exists(boxArtDir)) + { + Debug.WriteLine("creating directory for game streaming assets: " + boxArtDir); + try + { + Directory.CreateDirectory(boxArtDir); + } + catch (Exception e) + { + Console.Error.WriteLine("unable to create game streaming assets dir: {0}", e); + } + + } + try + { + Debug.WriteLine("copying placeholder box-art to " + boxArtFile); + Properties.Resources.box_art.Save(boxArtFile); + } + catch (Exception e) + { + Console.Error.WriteLine("unable to copy placeholder box-art: {0}", e); + } + + } + + } + + /// + /// Copies all temporary images to the {drive}:/Users/{user}/Appdata/Local/NVIDIA Corporation/Shield Apps/StreamingAssets/{scrubbed game name} dirs + /// + /// Selected app entrys to copy images for + private void CopyTempGridImagesToShieldStreamingAssets(IEnumerable selectedApps) + { + string tmpGridDirectory = Path.Combine(Path.GetTempPath(), "UWPHook", "tmp_grid"); + string streamingAssetsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "NVIDIA Corporation", "Shield Apps", "StreamingAssets"); + + // No images were downloaded, maybe the key is invalid or no app had an image + if (!Directory.Exists(tmpGridDirectory)) + { + return; + } + if (!Directory.Exists(streamingAssetsDirectory)) + { + DirectoryInfo di = Directory.CreateDirectory(streamingAssetsDirectory); + di.Attributes = FileAttributes.Directory | FileAttributes.Hidden; + } + + foreach (AppEntry app in selectedApps) + { + string scrubbedAppName = app.Name; + foreach (char c in Path.GetInvalidFileNameChars()) + { + scrubbedAppName = scrubbedAppName.Replace(c, '_'); + } + + ulong gameId = GenerateSteamGridAppId(app.Name, GenerateExePath()); + string srcFile = Path.Combine(tmpGridDirectory, string.Format("{0}p.png", gameId)); + string destDir = Path.Combine(streamingAssetsDirectory, scrubbedAppName); + string destFile = Path.Combine(destDir, "box-art.png"); + if (!Directory.Exists(destDir)) + { + try + { + Debug.WriteLine("Creating directory: ", destDir); + _ = Directory.CreateDirectory(destDir); + } + catch (Exception e) + { + Console.Error.WriteLine("unable to create directory: ", e); + } + } + try + { + Debug.WriteLine("Copying box-art image from {0} to {1}", new object[] { srcFile, destFile }); + File.Copy(srcFile, destFile, true); + } + catch (Exception e) + { + Console.Error.WriteLine("unable to copy box-art: ", e); + } + + } + } } } diff --git a/UWPHook/Properties/Resources.Designer.cs b/UWPHook/Properties/Resources.Designer.cs index 236c6b6..aa2687d 100644 --- a/UWPHook/Properties/Resources.Designer.cs +++ b/UWPHook/Properties/Resources.Designer.cs @@ -60,6 +60,16 @@ internal Resources() { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + public static System.Drawing.Bitmap box_art { + get { + object obj = ResourceManager.GetObject("box_art", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// diff --git a/UWPHook/Properties/Resources.resx b/UWPHook/Properties/Resources.resx index 4285897..8898583 100644 --- a/UWPHook/Properties/Resources.resx +++ b/UWPHook/Properties/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\box-art.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\GetAUMIDScript.ps1;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/UWPHook/Resources/box-art.png b/UWPHook/Resources/box-art.png new file mode 100644 index 0000000..80b8fb7 Binary files /dev/null and b/UWPHook/Resources/box-art.png differ diff --git a/UWPHook/SettingsWindow.xaml b/UWPHook/SettingsWindow.xaml index d1fe0dc..b624cd0 100644 --- a/UWPHook/SettingsWindow.xaml +++ b/UWPHook/SettingsWindow.xaml @@ -92,7 +92,6 @@