using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using MelonLoader; namespace ADOFAIRenderer.Util; internal static class FolderDialogUtil { public static void OpenFolderBrowser(string initialDirectory, Action onSelected) { var syncContext = SynchronizationContext.Current; Task.Run(() => { string? selected = null; try { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { selected = OpenWindowsFolderBrowser(initialDirectory); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { selected = OpenMacFolderBrowser(initialDirectory); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { selected = OpenLinuxFolderBrowser(initialDirectory); } } catch (Exception ex) { MelonLogger.Warning($"[FolderDialogUtil] Failed to open folder browser: {ex.Message}"); } if (syncContext != null) { syncContext.Post(_ => onSelected(selected), null); } else { onSelected(selected); } }); } private static string? OpenWindowsFolderBrowser(string initialDirectory) { try { var escapedDir = initialDirectory.Replace("'", "''"); var script = $@" [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null $f = New-Object System.Windows.Forms.FolderBrowserDialog $f.Description = 'Select Output Directory' if ([System.IO.Directory]::Exists('{escapedDir}')) {{ $f.SelectedPath = '{escapedDir}' }} $f.ShowNewFolderButton = $true if ($f.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {{ [Console]::Write($f.SelectedPath) }}"; var bytes = System.Text.Encoding.Unicode.GetBytes(script); var base64 = Convert.ToBase64String(bytes); var startInfo = new ProcessStartInfo { FileName = "powershell.exe", Arguments = $"-NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand {base64}", CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, WindowStyle = ProcessWindowStyle.Hidden }; using var process = Process.Start(startInfo); if (process == null) return null; var output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return !string.IsNullOrEmpty(output) && Directory.Exists(output) ? output : null; } catch (Exception ex) { MelonLogger.Warning($"[FolderDialogUtil] Windows folder dialog error: {ex.Message}"); return null; } } private static string? OpenMacFolderBrowser(string initialDirectory) { try { var escaped = initialDirectory.Replace("\"", "\\\""); var script = $"POSIX path of (choose folder with prompt \"Select Output Directory\" default location \"{escaped}\")"; var startInfo = new ProcessStartInfo { FileName = "osascript", CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, WindowStyle = ProcessWindowStyle.Hidden }; startInfo.ArgumentList.Add("-e"); startInfo.ArgumentList.Add(script); using var process = Process.Start(startInfo); if (process == null) return null; var output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return !string.IsNullOrEmpty(output) && Directory.Exists(output) ? output : null; } catch { return null; } } private static string? OpenLinuxFolderBrowser(string initialDirectory) { try { var startInfo = new ProcessStartInfo { FileName = "zenity", CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, WindowStyle = ProcessWindowStyle.Hidden }; startInfo.ArgumentList.Add("--file-selection"); startInfo.ArgumentList.Add("--directory"); startInfo.ArgumentList.Add("--title=Select Output Directory"); if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory)) { startInfo.ArgumentList.Add($"--filename={initialDirectory}"); } using var process = Process.Start(startInfo); if (process == null) return null; var output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return !string.IsNullOrEmpty(output) && Directory.Exists(output) ? output : null; } catch { return null; } } }