ShortcutHelper.cs 7.3 KB

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