diff --git a/WheelWizard.Test/Features/GameBananaTests.cs b/WheelWizard.Test/Features/GameBananaTests.cs index 2e1a67d0..6b20ef20 100644 --- a/WheelWizard.Test/Features/GameBananaTests.cs +++ b/WheelWizard.Test/Features/GameBananaTests.cs @@ -213,7 +213,7 @@ private GameBananaModPreview CreateFakeModPreview(int id) IconUrl = "", }, ModelName = "Mod", - PreviewMedia = new() + PreviewMedia = new(), }; } diff --git a/WheelWizard/Features/AutoUpdating/AutoUpdatingExtensions.cs b/WheelWizard/Features/AutoUpdating/AutoUpdatingExtensions.cs index a6dde854..91715714 100644 --- a/WheelWizard/Features/AutoUpdating/AutoUpdatingExtensions.cs +++ b/WheelWizard/Features/AutoUpdating/AutoUpdatingExtensions.cs @@ -13,7 +13,7 @@ public static IServiceCollection AddAutoUpdating(this IServiceCollection service implementationType = typeof(WindowsUpdatePlatform); #elif LINUX // We can enable this again once the auto updater has been fixed and tested - // implementationType = typeof(LinuxUpdatePlatform); + implementationType = typeof(LinuxUpdatePlatform); #elif MACOS // MacOS updater #endif diff --git a/WheelWizard/Features/AutoUpdating/Platforms/.gitattributes b/WheelWizard/Features/AutoUpdating/Platforms/.gitattributes new file mode 100644 index 00000000..d2a6fb8e --- /dev/null +++ b/WheelWizard/Features/AutoUpdating/Platforms/.gitattributes @@ -0,0 +1 @@ +LinuxUpdatePlatform.cs text eol=lf diff --git a/WheelWizard/Features/AutoUpdating/Platforms/LinuxUpdatePlatform.cs b/WheelWizard/Features/AutoUpdating/Platforms/LinuxUpdatePlatform.cs index 1d767606..8041cc5b 100644 --- a/WheelWizard/Features/AutoUpdating/Platforms/LinuxUpdatePlatform.cs +++ b/WheelWizard/Features/AutoUpdating/Platforms/LinuxUpdatePlatform.cs @@ -80,15 +80,17 @@ private OperationResult CreateAndRunShellScript(string currentFilePath, string n sleep 1 echo 'Replacing old executable...' - rm -f "{fileSystem.Path.Combine(currentFolder, originalFileName)}" - mv "{fileSystem.Path.Combine(currentFolder, newFileName)}" "{fileSystem.Path.Combine(currentFolder, originalFileName)}" - chmod +x "{fileSystem.Path.Combine(currentFolder, originalFileName)}" + rm -f {EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, originalFileName))} + mv {EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, newFileName))} {EnvHelper.SingleQuotePath( + fileSystem.Path.Combine(currentFolder, originalFileName) + )} + chmod +x {EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, originalFileName))} echo 'Starting the updated application...' - nohup "{fileSystem.Path.Combine(currentFolder, originalFileName)}" > /dev/null 2>&1 & + nohup {EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, originalFileName))} > /dev/null 2>&1 & echo 'Cleaning up...' - rm -- "{scriptFilePath}" + rm -- {EnvHelper.SingleQuotePath(scriptFilePath)} echo 'Update completed successfully.' diff --git a/WheelWizard/Features/AutoUpdating/Platforms/WindowsUpdatePlatform.cs b/WheelWizard/Features/AutoUpdating/Platforms/WindowsUpdatePlatform.cs index 51b113eb..3b09b075 100644 --- a/WheelWizard/Features/AutoUpdating/Platforms/WindowsUpdatePlatform.cs +++ b/WheelWizard/Features/AutoUpdating/Platforms/WindowsUpdatePlatform.cs @@ -117,7 +117,7 @@ private OperationResult CreateAndRunPowerShellScript(string currentFilePath, str Write-Output 'Starting update process...' # Wait for the original application to exit - while (Get-Process -Name '{{fileSystem.Path.GetFileNameWithoutExtension(originalFileName)}}' -ErrorAction SilentlyContinue) { + while (Get-Process -Name {{EnvHelper.SingleQuotePath(fileSystem.Path.GetFileNameWithoutExtension(originalFileName))}} -ErrorAction SilentlyContinue) { Write-Output 'Waiting for {{originalFileName}} to exit...' Start-Sleep -Seconds 1 } @@ -129,7 +129,7 @@ private OperationResult CreateAndRunPowerShellScript(string currentFilePath, str while (-not $deleted -and $retryCount -lt $maxRetries) { try { - Remove-Item -Path '{{fileSystem.Path.Combine(currentFolder, originalFileName)}}' -Force -ErrorAction Stop + Remove-Item -Path {{EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, originalFileName))}} -Force -ErrorAction Stop $deleted = $true } catch { @@ -147,10 +147,10 @@ exit 1 Write-Output 'Renaming new executable...' try { - Rename-Item -Path '{{fileSystem.Path.Combine( + Rename-Item -Path {{EnvHelper.SingleQuotePath(fileSystem.Path.Combine( currentFolder, newFileName - )}}' -NewName '{{originalFileName}}' -ErrorAction Stop + ))}} -NewName {{EnvHelper.SingleQuotePath(originalFileName)}} -ErrorAction Stop } catch { Write-Output 'Failed to rename {{newFileName}} to {{originalFileName}}. Update aborted.' @@ -159,13 +159,13 @@ exit 1 } Write-Output 'Starting the updated application...' - Start-Process -FilePath '{{fileSystem.Path.Combine(currentFolder, originalFileName)}}' + Start-Process -FilePath {{EnvHelper.SingleQuotePath(fileSystem.Path.Combine(currentFolder, originalFileName))}} Write-Output 'Cleaning up...' - Remove-Item -Path '{{scriptFilePath}}' -Force + Remove-Item -Path {{EnvHelper.SingleQuotePath(scriptFilePath)}} -Force Write-Output 'Update completed successfully.' - + """; fileSystem.File.WriteAllText(scriptFilePath, scriptContent); @@ -173,7 +173,7 @@ exit 1 var processStartInfo = new ProcessStartInfo { FileName = "powershell.exe", - Arguments = $"-NoProfile -ExecutionPolicy Bypass -File \"{scriptFilePath}\"", + ArgumentList = { "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", scriptFilePath }, CreateNoWindow = false, UseShellExecute = false, WorkingDirectory = currentFolder, diff --git a/WheelWizard/Helpers/EnvHelper.cs b/WheelWizard/Helpers/EnvHelper.cs index 8fa4a749..b1ba056c 100644 --- a/WheelWizard/Helpers/EnvHelper.cs +++ b/WheelWizard/Helpers/EnvHelper.cs @@ -19,8 +19,8 @@ public static bool IsValidUnixCommand(string command) }; using var process = Process.Start(processInfo); - process.WaitForExit(); - return process.ExitCode == 0; + process?.WaitForExit(); + return process?.ExitCode == 0; } catch { @@ -57,4 +57,24 @@ public static bool IsRelativeLinuxPath(string path) { return IsRelativeLinuxPath(path) ? null : path; } + + public static string SingleQuotePath(string path) + { + if (OperatingSystem.IsWindows()) + { + // PowerShell expects doubled single quotes inside the quoted string + return $"'{path.Replace("'", "''")}'"; + } + return $"'{path.Replace("'", "'\\''")}'"; + } + + public static string QuotePath(string path) + { + if (OperatingSystem.IsWindows()) + { + // Use double quotes outside of PowerShell + return $"\"{path}\""; + } + return SingleQuotePath(path); + } } diff --git a/WheelWizard/Services/Launcher/Helpers/DolphinLaunchHelper.cs b/WheelWizard/Services/Launcher/Helpers/DolphinLaunchHelper.cs index 4bea4b7b..0ded58c1 100644 --- a/WheelWizard/Services/Launcher/Helpers/DolphinLaunchHelper.cs +++ b/WheelWizard/Services/Launcher/Helpers/DolphinLaunchHelper.cs @@ -91,7 +91,7 @@ void AddFilesystemPerm(string newFilesystemPerm, string mode = "") var flatpakRunCommand = "flatpak run"; fixedFlatpakDolphinLocation = fixedFlatpakDolphinLocation.Replace( flatpakRunCommand, - $"{flatpakRunCommand} --filesystem=\"{Path.GetFullPath(newFilesystemPerm)}\"{mode}" + $"{flatpakRunCommand} --filesystem={EnvHelper.SingleQuotePath(Path.GetFullPath(newFilesystemPerm))}{mode}" ); } @@ -127,7 +127,7 @@ public static void LaunchDolphin(string arguments = "", bool shellExecute = fals var startInfo = new ProcessStartInfo(); var cannotPassUserFolder = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PathManager.IsLinuxDolphinConfigSplit(); - var userFolderArgument = cannotPassUserFolder ? "" : $"-u \"{Path.GetFullPath(PathManager.UserFolderPath)}\""; + var userFolderArgument = cannotPassUserFolder ? "" : $"-u {EnvHelper.QuotePath(Path.GetFullPath(PathManager.UserFolderPath))}"; var dolphinLaunchArguments = $"{arguments} {userFolderArgument}"; var dolphinLocation = (string)SettingsManager.DOLPHIN_LOCATION.Get(); diff --git a/WheelWizard/Services/Launcher/Helpers/MiiChannelLaunchHelper.cs b/WheelWizard/Services/Launcher/Helpers/MiiChannelLaunchHelper.cs index 5e0a80c2..35d04fd7 100644 --- a/WheelWizard/Services/Launcher/Helpers/MiiChannelLaunchHelper.cs +++ b/WheelWizard/Services/Launcher/Helpers/MiiChannelLaunchHelper.cs @@ -31,6 +31,6 @@ public static async Task LaunchMiiChannel() } if (miiChannelExists) - DolphinLaunchHelper.LaunchDolphin($"-b \"{Path.GetFullPath(MiiChannelPath)}\""); + DolphinLaunchHelper.LaunchDolphin($"-b {EnvHelper.QuotePath(Path.GetFullPath(MiiChannelPath))}"); } } diff --git a/WheelWizard/Services/Launcher/RrLauncher.cs b/WheelWizard/Services/Launcher/RrLauncher.cs index a8a91dcf..26ceb75e 100644 --- a/WheelWizard/Services/Launcher/RrLauncher.cs +++ b/WheelWizard/Services/Launcher/RrLauncher.cs @@ -39,7 +39,7 @@ public async Task Launch() RetroRewindLaunchHelper.GenerateLaunchJson(); var dolphinLaunchType = (bool)SettingsManager.LAUNCH_WITH_DOLPHIN.Get() ? "" : "-b"; DolphinLaunchHelper.LaunchDolphin( - $"{dolphinLaunchType} -e \"{Path.GetFullPath(RrLaunchJsonFilePath)}\" --config=Dolphin.Core.EnableCheats=False" + $"{dolphinLaunchType} -e {EnvHelper.QuotePath(Path.GetFullPath(RrLaunchJsonFilePath))} --config=Dolphin.Core.EnableCheats=False" ); } catch (Exception ex) diff --git a/WheelWizard/Services/Storage/FilePickerHelper.cs b/WheelWizard/Services/Storage/FilePickerHelper.cs index bad143cc..f58b6e98 100644 --- a/WheelWizard/Services/Storage/FilePickerHelper.cs +++ b/WheelWizard/Services/Storage/FilePickerHelper.cs @@ -105,23 +105,30 @@ public static async Task> OpenMultipleFilesAsync(string title, IEnu public static void OpenFolderInFileManager(string folderPath) { + string? openExecutable; if (OperatingSystem.IsWindows()) { - Process.Start("explorer.exe", folderPath); + openExecutable = "explorer.exe"; } else if (OperatingSystem.IsLinux()) { - Process.Start("xdg-open", folderPath); + openExecutable = "xdg-open"; } else if (OperatingSystem.IsMacOS()) { - // Ensures the ~/Library/Application Support/ folder is escaped properly - folderPath = $"\"{folderPath}\""; - Process.Start("open", folderPath); + openExecutable = "open"; } else { throw new PlatformNotSupportedException("Unsupported operating system."); } + + var info = new ProcessStartInfo(openExecutable) + { + // Ensures the folder path is escaped properly + ArgumentList = { folderPath }, + }; + + Process.Start(info); } } diff --git a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs b/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs index 4df3c7c3..283cd8ad 100644 --- a/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs +++ b/WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs @@ -69,17 +69,9 @@ private void AutoFillPaths() DolphinUserPathInput.Text = folderPath; } - private string WrapOnWhiteSpace(string inputText) + private void AssignWrappedDolphinExeInput(string inputText) { - if (inputText.Any(character => Char.IsWhiteSpace(character))) - return $"\"{inputText}\""; - - return inputText; - } - - private async void AssignWrappedDolphinExeInput(string inputText) - { - DolphinExeInput.Text = WrapOnWhiteSpace(inputText); + DolphinExeInput.Text = EnvHelper.SingleQuotePath(inputText); } private async void DolphinExeBrowse_OnClick(object sender, RoutedEventArgs e)