initial NVIDIA Shield streaming support implementation

pull/85/head
Sectorbob 2 years ago
parent db6098b7fe
commit 57f5af399b

@ -40,7 +40,7 @@ namespace UWPHook
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 @@ namespace UWPHook
stream.Close();
client.Dispose();
}
});
});
}
/// <summary>
@ -218,16 +218,16 @@ namespace UWPHook
/// </summary>
/// <param name="user">The user path to copy images to</param>
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 @@ namespace UWPHook
{
throw;
}
if (games != null)
{
var game = games[0];
@ -348,7 +348,7 @@ namespace UWPHook
{
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<Task> gridImagesDownloadTasks = new List<Task>();
@ -370,89 +370,8 @@ namespace UWPHook
// 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 @@ namespace UWPHook
foreach (var user in users)
{
CopyTempGridImagesToSteamUser(user);
CopyTempGridImagesToShieldStreamingAssets(selected_apps);
}
RemoveTempGridImages();
@ -470,7 +390,7 @@ namespace UWPHook
}
}
if(restartSteam)
if (restartSteam)
{
Func<Process> getSteam = () => Process.GetProcessesByName("steam").SingleOrDefault();
@ -517,6 +437,103 @@ namespace UWPHook
return true;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="tags"></param>
/// <param name="users"></param>
/// <param name="selected_apps"></param>
/// <param name="exePath"></param>
/// <param name="exeDir"></param>
private static void ExportAppsToSteamShortcuts(string[] tags, string[] users, IEnumerable<AppEntry> 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 @@ namespace UWPHook
}
}
}
/// <summary>
/// Generates the exe path used by Steam and NVIDIA Shield to call UWPHook
/// </summary>
/// <returns></returns>
private static string GenerateExePath()
{
return @"""" + System.Reflection.Assembly.GetExecutingAssembly().Location + @"""";
}
/// <summary>
/// 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.
/// </summary>
/// <param name="selected_apps"></param>
/// <param name="exePath"></param>
/// <param name="exeDir"></param>
private static void ExportAppsToShieldShortcuts(IEnumerable<AppEntry> 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);
}
}
}
/// <summary>
/// Copies all temporary images to the {drive}:/Users/{user}/Appdata/Local/NVIDIA Corporation/Shield Apps/StreamingAssets/{scrubbed game name} dirs
/// </summary>
/// <param name="selectedApps">Selected app entrys to copy images for</param>
private void CopyTempGridImagesToShieldStreamingAssets(IEnumerable<AppEntry> 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);
}
}
}
}
}

Loading…
Cancel
Save