using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32; using Mono.Unix; namespace Saradomin.Utilities { public static class CrossPlatform { public static void LaunchURL(string url) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Process.Start("xdg-open", url); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", url); } } public static void OpenFolder(string path) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Process.Start(new ProcessStartInfo(path) { UseShellExecute = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Process.Start("xdg-open", path); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", path); } } public static bool IsJavaExecutableValid(string location) { try { if (!File.Exists(location)) { return false; } using (var fileStream = File.OpenRead(location)) { var bytes = new byte[4]; fileStream.Read(bytes, 0, 4); if (bytes[0] == 0x7F && bytes[1] == 0x45 && bytes[2] == 0x4C && bytes[3] == 0x46) { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } if (bytes[0] == 'M' && bytes[1] == 'Z') { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } if ((bytes[0] == 0xCF && bytes[1] == 0xFA) || (bytes[0] == 0xCA && bytes[1] == 0xFE)) { return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); } } } catch { // Ignore } return false; } public static string LocateJavaExecutable() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var envPath = Environment.GetEnvironmentVariable("JAVA_HOME"); if (!string.IsNullOrEmpty(envPath)) return Path.Combine(envPath, "bin/java.exe"); using (var rk = Registry.LocalMachine.OpenSubKey("SOFTWARE\\JavaSoft\\Java Runtime Environment\\")) { if (rk == null) return null; var currentVersion = rk.GetValue("CurrentVersion")?.ToString(); if (currentVersion == null) return null; using (var key = rk.OpenSubKey(currentVersion)) { if (key == null) return null; envPath = key.GetValue("JavaHome")?.ToString(); } } if (!string.IsNullOrEmpty(envPath)) return Path.Combine(envPath, "bin/java.exe"); throw new FileNotFoundException("Failed to find Java. Make sure it's installed!"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) { var proc = new Process { StartInfo = new("/bin/which") { Arguments = "java", RedirectStandardOutput = true, UseShellExecute = false } }; proc.Start(); proc.WaitForExit(); var data = proc.StandardOutput.ReadToEnd(); if (!string.IsNullOrEmpty(data)) return UnixPath.GetCompleteRealPath(data.Trim()); throw new FileNotFoundException("Failed to find Java. Make sure it's installed!"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var proc = new Process { StartInfo = new("/usr/bin/which") { Arguments = "java", RedirectStandardOutput = true, UseShellExecute = false } }; proc.Start(); proc.WaitForExit(); var data = proc.StandardOutput.ReadToEnd(); if (!string.IsNullOrEmpty(data)) return Path.Combine(UnixPath.GetCompleteRealPath(data.Trim())); throw new FileNotFoundException("Failed to find Java. Make sure it's installed!"); } else { throw new NotSupportedException("Your platform is not supported."); } } public static string LocateUnixUserHome() { return Environment.GetEnvironmentVariable("XDG_DATA_HOME") ?? Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share" ); } public static string Get2009scapeHome() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) { return Path.Combine( LocateUnixUserHome(), "EmoScape" ); } else { var userProfile = Path.Combine ( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "EmoScape" ); var appData = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EmoScape" ); return Directory.Exists(userProfile) ? userProfile : appData; } } public static string GetSaradominHome() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) { return Path.Combine( // Get the XDG_DATA_HOME environment variable, or if it doesn't exist, use the default ~/.local/share LocateUnixUserHome(), "EmoScape", "saradomin" ); } return Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "EmoScape", "saradomin" ); } public static string GetSingleplayerBackupsHome() { return Path.Combine(Get2009scapeHome(), "singleplayer_backups"); } public static string GetSingleplayerHome() { return Path.Combine(Get2009scapeHome(), "singleplayer"); } public static string LocateSingleplayerExecutable() { return Path.Combine(GetSingleplayerHome(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "launch.bat" : "launch.sh"); } public static string Get2009scapeExecutable() { return Path.Combine(Get2009scapeHome(), "2009scape.jar"); } public static string GetServerProfilePath(string baseDirectory) { baseDirectory ??= Get2009scapeHome(); return Path.Combine(baseDirectory, "server_profiles.json"); } public static string RunCommandAndGetOutput(string command, Action onOutputReceived = null, Action onErrorReceived = null) { Process process = new Process(); StringBuilder output = new StringBuilder(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { process.StartInfo = new ProcessStartInfo("cmd.exe", "/c " + command) { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, }; } else if ( RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ) { process.StartInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"") { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, }; } process.OutputDataReceived += (_, e) => { if (e.Data == null) return; output.AppendLine(e.Data); onOutputReceived?.Invoke(e.Data); }; process.ErrorDataReceived += (_, e) => { if (e.Data == null) return; output.AppendLine(e.Data); onErrorReceived?.Invoke(e.Data); }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); return output.ToString(); } public static string GetJava11DownloadUrl() { string architecture = GetSystemArchitecture(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.20%2B8/OpenJDK11U-jre_x64_windows_hotspot_11.0.20_8.zip"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { return architecture == "x64" ? "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.20%2B8/OpenJDK11U-jre_x64_linux_hotspot_11.0.20_8.tar.gz" : "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.20%2B8/OpenJDK11U-jre_aarch64_linux_hotspot_11.0.20_8.tar.gz"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return architecture == "x64" ? "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.20%2B8/OpenJDK11U-jre_x64_mac_hotspot_11.0.20_8.tar.gz" : "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.20%2B8/OpenJDK11U-jre_aarch64_mac_hotspot_11.0.20_8.tar.gz"; } else { throw new NotSupportedException("Your platform is not supported."); } } private static string GetSystemArchitecture() { if (RuntimeInformation.OSArchitecture == Architecture.X64) { return "x64"; } else if (RuntimeInformation.OSArchitecture == Architecture.Arm64) { return "aarch64"; } else { throw new NotSupportedException("Your architecture is not supported."); } } public static bool IsDirectoryWritable(string directoryPath) { var testFilePath = Path.Combine(directoryPath, "test"); try { File.Create(testFilePath).Dispose(); File.Delete(testFilePath); return true; } catch (UnauthorizedAccessException) { return false; } } public static Process StartJavaProcess(string javaExecutable, string jarPath, string memoryAllocation, Action outputHandler, Action onExit) { Process process = new Process(); process.StartInfo = new ProcessStartInfo { FileName = javaExecutable, Arguments = $"-Xmx{memoryAllocation} -Xms{memoryAllocation} -jar \"{jarPath}\"", UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = Path.Combine(CrossPlatform.GetSingleplayerHome(), "game") }; process.OutputDataReceived += (_, args) => { if (string.IsNullOrEmpty(args.Data)) return; outputHandler?.Invoke(args.Data); }; process.ErrorDataReceived += (_, args) => { if (string.IsNullOrEmpty(args.Data)) return; outputHandler?.Invoke(args.Data); }; process.EnableRaisingEvents = true; process.Exited += (_, _) => onExit?.Invoke(); process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); return process; } } }