ShortcutHelper.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Configuration;
  3. using ShellLink;
  4. using SixLabors.ImageSharp;
  5. using SixLabors.ImageSharp.Formats.Png;
  6. using SixLabors.ImageSharp.PixelFormats;
  7. using SixLabors.ImageSharp.Processing;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Runtime.Versioning;
  12. namespace Ryujinx.Ui.Common.Helper
  13. {
  14. public static class ShortcutHelper
  15. {
  16. [SupportedOSPlatform("windows")]
  17. private static void CreateShortcutWindows(string applicationFilePath, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath)
  18. {
  19. string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
  20. iconPath += ".ico";
  21. MemoryStream iconDataStream = new(iconData);
  22. var image = Image.Load(iconDataStream);
  23. image.Mutate(x => x.Resize(128, 128));
  24. SaveBitmapAsIcon(image, iconPath);
  25. var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath), iconPath, 0);
  26. shortcut.StringData.NameString = cleanedAppName;
  27. shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
  28. }
  29. [SupportedOSPlatform("linux")]
  30. private static void CreateShortcutLinux(string applicationFilePath, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
  31. {
  32. string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
  33. var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-template.desktop");
  34. iconPath += ".png";
  35. var image = Image.Load<Rgba32>(iconData);
  36. image.SaveAsPng(iconPath);
  37. using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
  38. outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath)}");
  39. }
  40. [SupportedOSPlatform("macos")]
  41. private static void CreateShortcutMacos(string appFilePath, byte[] iconData, string desktopPath, string cleanedAppName)
  42. {
  43. string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
  44. var plistFile = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-template.plist");
  45. var shortcutScript = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-launch-script.sh");
  46. // Macos .App folder
  47. string contentFolderPath = Path.Combine("/Applications", cleanedAppName + ".app", "Contents");
  48. string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS");
  49. if (!Directory.Exists(scriptFolderPath))
  50. {
  51. Directory.CreateDirectory(scriptFolderPath);
  52. }
  53. // Runner script
  54. const string ScriptName = "runner.sh";
  55. string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
  56. using StreamWriter scriptFile = new(scriptPath);
  57. scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath));
  58. // Set execute permission
  59. FileInfo fileInfo = new(scriptPath);
  60. fileInfo.UnixFileMode |= UnixFileMode.UserExecute;
  61. // img
  62. string resourceFolderPath = Path.Combine(contentFolderPath, "Resources");
  63. if (!Directory.Exists(resourceFolderPath))
  64. {
  65. Directory.CreateDirectory(resourceFolderPath);
  66. }
  67. const string IconName = "icon.png";
  68. var image = Image.Load<Rgba32>(iconData);
  69. image.SaveAsPng(Path.Combine(resourceFolderPath, IconName));
  70. // plist file
  71. using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist"));
  72. outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
  73. }
  74. public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData)
  75. {
  76. string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
  77. string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars()));
  78. if (OperatingSystem.IsWindows())
  79. {
  80. string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app");
  81. CreateShortcutWindows(applicationFilePath, iconData, iconPath, cleanedAppName, desktopPath);
  82. return;
  83. }
  84. if (OperatingSystem.IsLinux())
  85. {
  86. string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
  87. Directory.CreateDirectory(iconPath);
  88. CreateShortcutLinux(applicationFilePath, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName);
  89. return;
  90. }
  91. if (OperatingSystem.IsMacOS())
  92. {
  93. CreateShortcutMacos(applicationFilePath, iconData, desktopPath, cleanedAppName);
  94. return;
  95. }
  96. throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
  97. }
  98. private static string GetArgsString(string appFilePath)
  99. {
  100. // args are first defined as a list, for easier adjustments in the future
  101. var argsList = new List<string>();
  102. if (!string.IsNullOrEmpty(CommandLineState.BaseDirPathArg))
  103. {
  104. argsList.Add("--root-data-dir");
  105. argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
  106. }
  107. argsList.Add($"\"{appFilePath}\"");
  108. return String.Join(" ", argsList);
  109. }
  110. /// <summary>
  111. /// Creates a Icon (.ico) file using the source bitmap image at the specified file path.
  112. /// </summary>
  113. /// <param name="source">The source bitmap image that will be saved as an .ico file</param>
  114. /// <param name="filePath">The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).</param>
  115. [SupportedOSPlatform("windows")]
  116. private static void SaveBitmapAsIcon(Image source, string filePath)
  117. {
  118. // Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz
  119. byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 };
  120. using FileStream fs = new(filePath, FileMode.Create);
  121. fs.Write(header);
  122. // Writing actual data
  123. source.Save(fs, PngFormat.Instance);
  124. // Getting data length (file length minus header)
  125. long dataLength = fs.Length - header.Length;
  126. // Write it in the correct place
  127. fs.Seek(14, SeekOrigin.Begin);
  128. fs.WriteByte((byte)dataLength);
  129. fs.WriteByte((byte)(dataLength >> 8));
  130. }
  131. }
  132. }