From badc283f3c8fb224f992725af88a5a1907da3bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Sun, 14 Jan 2024 13:30:09 +0100 Subject: [PATCH 1/6] Add `--launch=` command line argument to start game executables `--launch=` option accepts both executable paths and URIs for launcher compatibility. Some games like Sackboy or Telltale Expanse Series on Epic will fail to launch correctly if .exe path is used directly. Instead they require proper launcher URI. This change will allow UEVR fronted to act as a launcher for games. Example. Creating shortcut to: ``` UEVRInjector.exe "--launch=com.epicgames.launcher://apps/da36711940d4460da57d8d6ad67236a4%3Aa1afcdb1d2344232954be97d4b86b9a8%3Acfbbc006f3ee4ba0bfff3ffa46942f90?action=launch&silent=true" --attach=CosmicShake-Win64-Shipping ``` Will allow to automatically launch and inject Sponge Bob: Cosmic Shake on EGS --- UEVR/MainWindow.xaml.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/UEVR/MainWindow.xaml.cs b/UEVR/MainWindow.xaml.cs index b8edd14..79e7244 100644 --- a/UEVR/MainWindow.xaml.cs +++ b/UEVR/MainWindow.xaml.cs @@ -172,6 +172,7 @@ public partial class MainWindow : Window { private ExecutableFilter m_executableFilter = new ExecutableFilter(); private string? m_commandLineAttachExe = null; + private string? m_commandLineLaunchExe = null; private bool m_ignoreFutureVDWarnings = false; [System.Runtime.InteropServices.DllImport("user32.dll")] @@ -187,6 +188,11 @@ public MainWindow() { foreach (string arg in args) { if (arg.StartsWith("--attach=")) { m_commandLineAttachExe = arg.Split('=')[1]; + continue; + } + if (arg.StartsWith("--launch=")) { + m_commandLineLaunchExe = arg.Substring(arg.IndexOf('=') + 1); // game exe URI may contain any characters including '=' + continue; } } } @@ -251,6 +257,7 @@ private void RestartAsAdminButton_Click(object sender, RoutedEventArgs e) { } private DateTime m_lastAutoInjectTime = DateTime.MinValue; + private bool m_launchExeDone = false; private void Update_InjectStatus() { if (m_connected) { @@ -261,6 +268,21 @@ private void Update_InjectStatus() { DateTime now = DateTime.Now; TimeSpan oneSecond = TimeSpan.FromSeconds(1); + if (m_commandLineLaunchExe != null && !m_launchExeDone) { + try { + Process.Start(new ProcessStartInfo { + FileName = m_commandLineLaunchExe, + UseShellExecute = true // for launcher compatiblity, executable might be an URI + }); + + m_launchExeDone = true; + } + catch (Exception) { + MessageBox.Show("Failed to launch: " + m_commandLineLaunchExe, "Launch error", MessageBoxButton.OK, MessageBoxImage.Error); + Application.Current.Dispatcher.Invoke(new Action(() => { Application.Current.Shutdown(1); })); + } + } + if (m_commandLineAttachExe == null) { if (m_lastSelectedProcessId == 0) { m_injectButton.Content = "Inject"; From 8b7c03413d5087a30c804b21134cbc82c238ceef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Sun, 14 Jan 2024 13:34:16 +0100 Subject: [PATCH 2/6] Add `--delay` option to wait before injection Some games don't respond well to be injected directly on boot. Intro movies, menus, loading screens can appear broken or as a black screen. This commit augments `--attach` mode with `--delay` parameter allowing UEVR injector to wait upon detecting target application. This gives user time to get into the game without needing to go back to the injector UI and pressing inject manually. Example: `UEVRInjector.exe --attach=CosmicShake-Win64-Shipping --delay=30` will wait for SpongeBob Cosmic Shake process to spawn, then wait 30s on top and inject. --- UEVR/MainWindow.xaml.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/UEVR/MainWindow.xaml.cs b/UEVR/MainWindow.xaml.cs index 79e7244..7761084 100644 --- a/UEVR/MainWindow.xaml.cs +++ b/UEVR/MainWindow.xaml.cs @@ -173,6 +173,7 @@ public partial class MainWindow : Window { private ExecutableFilter m_executableFilter = new ExecutableFilter(); private string? m_commandLineAttachExe = null; private string? m_commandLineLaunchExe = null; + private int m_commandLineDelayInjection = 0; private bool m_ignoreFutureVDWarnings = false; [System.Runtime.InteropServices.DllImport("user32.dll")] @@ -194,6 +195,16 @@ public MainWindow() { m_commandLineLaunchExe = arg.Substring(arg.IndexOf('=') + 1); // game exe URI may contain any characters including '=' continue; } + if (arg.StartsWith("--delay=")) { + try { + m_commandLineDelayInjection = int.Parse(arg.Split('=')[1]); + } + catch { + m_commandLineDelayInjection = 0; + } + + continue; + } } } @@ -258,6 +269,7 @@ private void RestartAsAdminButton_Click(object sender, RoutedEventArgs e) { private DateTime m_lastAutoInjectTime = DateTime.MinValue; private bool m_launchExeDone = false; + private int? m_delayInjectionTimer = null; private void Update_InjectStatus() { if (m_connected) { @@ -335,6 +347,16 @@ private void Update_InjectStatus() { return; } + if (m_commandLineDelayInjection > 0) { + if (m_delayInjectionTimer == null) { + m_delayInjectionTimer = m_commandLineDelayInjection; + } + + m_injectButton.Content = m_commandLineAttachExe.ToLower() + " found. Delaying " + m_delayInjectionTimer + "s"; + m_delayInjectionTimer--; + if (m_delayInjectionTimer > 0) return; + } + if (now - m_lastAutoInjectTime > oneSecond) { if (m_nullifyVRPluginsCheckbox.IsChecked == true) { IntPtr nullifierBase; @@ -377,6 +399,8 @@ private void Update_InjectStatus() { m_lastAutoInjectTime = now; m_commandLineAttachExe = null; // no need anymore. + m_delayInjectionTimer = null; + m_commandLineDelayInjection = 0; FillProcessList(); if (m_focusGameOnInjectionCheckbox.IsChecked == true) { From d1fd62db41581281e62fff2fce9d769a13d426a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Mon, 15 Jan 2024 18:46:28 +0100 Subject: [PATCH 3/6] Add ability to pass arguments to game executable via `--launch_args=` Some launchers, notably GOG Galaxy don't support launching game via URI. They require command line option `Galaxy.exe /run:gameId`. This commit adds command line parameter `--launch-args=` thant can accept space separated argument list as as single string. --- UEVR/MainWindow.xaml.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UEVR/MainWindow.xaml.cs b/UEVR/MainWindow.xaml.cs index 7761084..181a008 100644 --- a/UEVR/MainWindow.xaml.cs +++ b/UEVR/MainWindow.xaml.cs @@ -173,6 +173,7 @@ public partial class MainWindow : Window { private ExecutableFilter m_executableFilter = new ExecutableFilter(); private string? m_commandLineAttachExe = null; private string? m_commandLineLaunchExe = null; + private string? m_commandLineLaunchArgs = null; private int m_commandLineDelayInjection = 0; private bool m_ignoreFutureVDWarnings = false; @@ -195,6 +196,10 @@ public MainWindow() { m_commandLineLaunchExe = arg.Substring(arg.IndexOf('=') + 1); // game exe URI may contain any characters including '=' continue; } + if (arg.StartsWith("--launch_args=")) { + m_commandLineLaunchArgs = arg.Substring(arg.IndexOf('=') + 1); // space separated list of game exe arguments + continue; + } if (arg.StartsWith("--delay=")) { try { m_commandLineDelayInjection = int.Parse(arg.Split('=')[1]); @@ -284,7 +289,8 @@ private void Update_InjectStatus() { try { Process.Start(new ProcessStartInfo { FileName = m_commandLineLaunchExe, - UseShellExecute = true // for launcher compatiblity, executable might be an URI + UseShellExecute = true, // for launcher compatiblity, executable might be an URI + Arguments = m_commandLineLaunchArgs }); m_launchExeDone = true; From 952754b29bb6577128b912e936704498dbc5cc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Tue, 16 Jan 2024 09:42:23 +0100 Subject: [PATCH 4/6] Offer option to automatically disable unwanted plugins on launch This commit allows option to automatically rename unwanted VR plugins to 'plugin_name_bak'. All user needs to do is to click 'yes'. Additionally game is checked before launch for being UE title and warning is issued if it's not. This requires `--attach=` parameter to be full path to game executable. If only process name is passed old behavior is used instead. --- UEVR/MainWindow.xaml.cs | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/UEVR/MainWindow.xaml.cs b/UEVR/MainWindow.xaml.cs index 181a008..4ae807b 100644 --- a/UEVR/MainWindow.xaml.cs +++ b/UEVR/MainWindow.xaml.cs @@ -31,6 +31,7 @@ using static UEVR.SharedMemory; using System.Threading.Channels; using System.Security.Principal; +using Path = System.IO.Path; namespace UEVR { class GameSettingEntry : INotifyPropertyChanged { @@ -172,6 +173,7 @@ public partial class MainWindow : Window { private ExecutableFilter m_executableFilter = new ExecutableFilter(); private string? m_commandLineAttachExe = null; + private string? m_commandLineAttachExePath = null; private string? m_commandLineLaunchExe = null; private string? m_commandLineLaunchArgs = null; private int m_commandLineDelayInjection = 0; @@ -190,6 +192,11 @@ public MainWindow() { foreach (string arg in args) { if (arg.StartsWith("--attach=")) { m_commandLineAttachExe = arg.Split('=')[1]; + + if(m_commandLineAttachExe.EndsWith(".exe")) { + m_commandLineAttachExePath = Path.GetDirectoryName(m_commandLineAttachExe); + m_commandLineAttachExe = Path.GetFileNameWithoutExtension(m_commandLineAttachExe); + } continue; } if (arg.StartsWith("--launch=")) { @@ -286,6 +293,39 @@ private void Update_InjectStatus() { TimeSpan oneSecond = TimeSpan.FromSeconds(1); if (m_commandLineLaunchExe != null && !m_launchExeDone) { + if(m_commandLineAttachExePath != null && m_commandLineAttachExe != null) { + if(!IsUnrealEngineGame(m_commandLineAttachExePath, m_commandLineAttachExe)) { + var result = MessageBox.Show(m_lastSelectedProcessName + " does not appear to be an Unreal Engine title.\n" + + "Do you want to proceed?", + "Non UE game", + MessageBoxButton.YesNo, MessageBoxImage.Warning); + switch (result) { + case MessageBoxResult.Yes: + break; + case MessageBoxResult.No: + Application.Current.Dispatcher.Invoke(new Action(() => { Application.Current.Shutdown(1); })); + break; + }; + } + + var pluginsDir = AreVRPluginsPresent(m_commandLineAttachExePath); + if(pluginsDir != null) { + var result = MessageBox.Show("VR plugins have been detected in game directory.\n" + + "Do you want to automatically disable them?\n" + + "NOTE: for a handful of games disabling VR plugins will lead to game crashes", + "VR Plugins detected", + MessageBoxButton.YesNo, MessageBoxImage.Question); + + switch (result) { + case MessageBoxResult.Yes: + RenameVRPlugins(pluginsDir); + break; + case MessageBoxResult.No: + break; + }; + } + } + try { Process.Start(new ProcessStartInfo { FileName = m_commandLineLaunchExe, @@ -689,6 +729,22 @@ private void MainWindow_Closing(object sender, System.ComponentModel.CancelEvent return null; } + private void RenameVRPlugins(string? pluginsPath) { + if(pluginsPath == null) return; + + foreach (string discouragedPlugin in m_discouragedPlugins) { + string pluginPath = pluginsPath + "\\" + discouragedPlugin; + + if (Directory.Exists(pluginPath)) { + try { + Directory.Move(pluginPath, pluginPath + "_bak"); + } catch (Exception) { + MessageBox.Show("Failed to rename:" + pluginPath); + } + } + } + } + private bool IsUnrealEngineGame(string gameDirectory, string targetName) { try { if (targetName.ToLower().EndsWith("-win64-shipping")) { From f861467f17730698970c81a255bb07b9b3feee57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Tue, 16 Jan 2024 18:44:33 +0100 Subject: [PATCH 5/6] Add UI explanation and toggle for launching games Adds 'autostart game' toggle. User can turn it off and `--launch=` will be ignored. When `--launch=` option is passed explanation dialog will appear with option to launch game or not and hide the dialog on subsequent runs. This allows launchers like Rai Pal to always pass `--launch=` along with `--attach=`. --- UEVR/GameAutoStartExplanationDialog.xaml | 26 ++++++++++++++++++ UEVR/GameAutoStartExplanationDialog.xaml.cs | 29 +++++++++++++++++++++ UEVR/MainWindow.xaml | 4 +++ UEVR/MainWindow.xaml.cs | 24 +++++++++++++++++ UEVR/MainWindowSettings.cs | 14 ++++++++++ 5 files changed, 97 insertions(+) create mode 100644 UEVR/GameAutoStartExplanationDialog.xaml create mode 100644 UEVR/GameAutoStartExplanationDialog.xaml.cs diff --git a/UEVR/GameAutoStartExplanationDialog.xaml b/UEVR/GameAutoStartExplanationDialog.xaml new file mode 100644 index 0000000..e4a009b --- /dev/null +++ b/UEVR/GameAutoStartExplanationDialog.xaml @@ -0,0 +1,26 @@ + + + + + +