diff --git a/.ci/license-header.sh b/.ci/license-header.sh
index f438d59dac..784c6bac5a 100755
--- a/.ci/license-header.sh
+++ b/.ci/license-header.sh
@@ -4,7 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# specify full path if dupes may exist
-EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
+EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
# license header constants, please change when needed :))))
YEAR=2025
diff --git a/.gitignore b/.gitignore
index 0886224d8d..d070d94681 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
# Build directory
/[Bb]uild*/
doc-build/
+out/
AppDir/
uruntime
@@ -59,3 +60,4 @@ eden-windows-msvc
artifacts
*.AppImage*
/install*
+vulkansdk*.exe
diff --git a/docs/Build.md b/docs/Build.md
index 52a671ab1e..0bb70ee322 100644
--- a/docs/Build.md
+++ b/docs/Build.md
@@ -26,7 +26,7 @@ Android has a completely different build process than other platforms. See its [
If the configure phase fails, see the `Troubleshooting` section below. Usually, as long as you followed the dependencies guide, the defaults *should* successfully configure and build.
-### Qt Creator
+### Option A: Qt Creator
This is the recommended GUI method for Linux, macOS, and Windows.
@@ -46,39 +46,51 @@ Hit "Configure Project", then wait for CMake to finish configuring (may take a w
-### Command Line
-
-This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended.
+### Option B: Command Line
Click to Open
-Note that CMake must be in your PATH, and you must be in the cloned Eden directory. On Windows, you must also set up a Visual C++ development environment. This can be done by running `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat` in the same terminal.
-
-Recommended generators:
-
+> [!WARNING]
+>For all systems:
+>- *CMake* **MUST** be in your PATH (and also *ninja*, if you are using it as ``)
+>- You *MUST* be in the cloned *Eden* directory
+>
+>On Windows:
+> - It's recommended to install **[Ninja](https://ninja-build.org/)**
+> - You must load **Visual C++ development environment**, this can be done by running our convenience script:
+> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+)
+> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc)
+
+Available ``:
- MSYS2: `MSYS Makefiles`
-- MSVC: Install **[ninja](https://ninja-build.org/)** and use `Ninja`, OR use `Visual Studio 17 2022`
+- MSVC: `Ninja` (preferred) or `Visual Studio 17 2022`
- macOS: `Ninja` (preferred) or `Xcode`
- Others: `Ninja` (preferred) or `UNIX Makefiles`
-BUILD_TYPE should usually be `Release` or `RelWithDebInfo` (debug symbols--compiled executable will be large). If you are using a debugger and annoyed with stuff getting optimized out, try `Debug`.
+Available ``:
+- `Release` (default)
+- `RelWithDebInfo` (debug symbols--compiled executable will be large)
+- `Debug` (if you are using a debugger and annoyed with stuff getting optimized out)
+
+Caveat for Debug Builds:
+- If you're building with CCache, you will need to add the environment variable `CL` with the `/FS` flag ([Reference](https://learn.microsoft.com/pt-br/cpp/build/reference/fs-force-synchronous-pdb-writes?view=msvc-170))
Also see the [Options](Options.md) page for additional CMake options.
```sh
-cmake -S . -B build -G "GENERATOR" -DCMAKE_BUILD_TYPE= -DYUZU_TESTS=OFF
+cmake -S . -B build -G "" -DCMAKE_BUILD_TYPE= -DYUZU_TESTS=OFF
```
If you are on Windows and prefer to use Clang:
```sh
-cmake -S . -B build -G "GENERATOR" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
+cmake -S . -B build -G "" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
```
-### [CLion](https://www.jetbrains.com/clion/)
+### Option C: [CLion](https://www.jetbrains.com/clion/)
Click to Open
@@ -133,13 +145,13 @@ Many platforms have quirks, bugs, and other fun stuff that may cause issues when
## Building & Running
-### Qt Creator
+### On Qt Creator
Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "play" icon, or Ctrl+R.
-### Command Line
+### On Command Line
-If you are not on Windows and are using the `UNIX Makefiles` generator, you must also add `-j$(nproc)` to this command.
+If you are using the `UNIX Makefiles` or `Visual Studio 17 2022` as ``, you should also add `--parallel` for faster build times.
```
cmake --build build
@@ -157,4 +169,4 @@ Some platforms have convenience scripts provided for building.
- **[Linux](scripts/Linux.md)**
- **[Windows](scripts/Windows.md)**
-macOS scripts will come soon.
\ No newline at end of file
+macOS scripts will come soon.
diff --git a/docs/Deps.md b/docs/Deps.md
index 15ffff28a8..05764341ec 100644
--- a/docs/Deps.md
+++ b/docs/Deps.md
@@ -3,10 +3,13 @@
To build Eden, you MUST have a C++ compiler.
* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+
- GCC 12 also requires Clang 14+
-* On Windows, this is either:
- - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option),
- - clang-cl - can be downloaded from the MSVC installer,
- - or **[MSYS2](https://www.msys2.org)**
+* On Windows, we support:
+ - **[MSVC](https://visualstudio.microsoft.com/downloads/)** (default)
+ - It's STRONGLY RECOMMENDED to use the **Community** option and **Visual Studio 2022**
+ - You need to install: **[Desktop development with C++](https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170)**
+ - **[clang-cl](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-180)**
+ - You need to install: **C++ Clang tools for Windows**
+ - **[MSYS2](https://www.msys2.org)** (experimental)
* On macOS, this is Apple Clang
- This can be installed with `xcode-select --install`
@@ -15,20 +18,23 @@ The following additional tools are also required:
* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK
* **[Git](https://git-scm.com/)** for version control
- **[Windows installer](https://gitforwindows.org)**
+* **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories
* On Windows, you must install the **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** as well
- - *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`*
+ - *A convenience script to install the latest SDK is provided in:*
+ - `tools/windows/install-vulkan-sdk.ps1` (for PowerShell 5+)
+ - `tools/windows/install-vulkan-sdk.sh` (for MSYS2, Git Bash, etc)
-If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the recommended IDE for building)
+If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the **RECOMMENDED** IDE for building)
* On Linux, *BSD and macOS, this can be done by the package manager
- If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below.
-* MSVC/clang-cl users on Windows must install through the [official installer](https://www.qt.io/download-qt-installer-oss)
+* MSVC/clang-cl users on Windows must install through the official [Qt](https://www.qt.io/download-qt-installer-oss) installer
* Linux and macOS users may choose to use the installer as well.
* MSYS2 can also install Qt 6 via the package manager
If you are on Windows, a convenience script to install MSVC, MSYS2, Qt, all necessary packages for MSYS2, and set up a zsh environment with useful keybinds and aliases can be found [here](https://git.crueter.xyz/scripts/windev).
- For help setting up Qt Creator, run `./install.sh -h qtcreator`
-If you are on Windows and NOT building with MSYS2, you may go [back home](Build.md) and continue.
+If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
## Externals
The following are handled by Eden's externals:
diff --git a/.ci/windows/install-vulkan-sdk.ps1 b/tools/windows/install-vulkan-sdk.ps1
similarity index 79%
rename from .ci/windows/install-vulkan-sdk.ps1
rename to tools/windows/install-vulkan-sdk.ps1
index 4c5274d1b7..072b531201 100755
--- a/.ci/windows/install-vulkan-sdk.ps1
+++ b/tools/windows/install-vulkan-sdk.ps1
@@ -1,25 +1,36 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$ErrorActionPreference = "Stop"
# Check if running as administrator
-if (-not ([bool](net session 2>$null))) {
+try {
+ net session 1>$null 2>$null
+} catch {
Write-Host "This script must be run with administrator privileges!"
Exit 1
}
-$VulkanSDKVer = "1.4.321.1"
+$VulkanSDKVer = "1.4.328.1"
+$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
$ExeFile = "vulkansdk-windows-X64-$VulkanSDKVer.exe"
$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
$Destination = "./$ExeFile"
+# Check if Vulkan SDK is already installed
+if (Test-Path $VULKAN_SDK) {
+ Write-Host "-- Vulkan SDK already installed at $VULKAN_SDK"
+ return
+}
+
echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri"
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile($Uri, $Destination)
echo "Finished downloading $ExeFile"
-$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer"
$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install"
echo "Installing Vulkan SDK $VulkanSDKVer"
@@ -36,4 +47,4 @@ echo "Finished installing Vulkan SDK $VulkanSDKVer"
if ("$env:GITHUB_ACTIONS" -eq "true") {
echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
-}
\ No newline at end of file
+}
diff --git a/tools/windows/install-vulkan-sdk.sh b/tools/windows/install-vulkan-sdk.sh
new file mode 100644
index 0000000000..cc2bcf2c50
--- /dev/null
+++ b/tools/windows/install-vulkan-sdk.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/sh
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+: "${VULKAN_SDK_VER:=1.4.328.1}"
+: "${VULKAN_ROOT:=C:/VulkanSDK/$VULKAN_SDK_VER}"
+EXE_FILE="vulkansdk-windows-X64-$VULKAN_SDK_VER.exe"
+URI="https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VER/windows/$EXE_FILE"
+VULKAN_ROOT_UNIX=$(cygpath -u "$VULKAN_ROOT")
+
+# Check if Vulkan SDK is already installed
+if [ -d "$VULKAN_ROOT_UNIX" ]; then
+ echo "-- Vulkan SDK already installed at $VULKAN_ROOT_UNIX"
+ exit 0
+fi
+
+echo "Downloading Vulkan SDK $VULKAN_SDK_VER from $URI"
+[ ! -f "./$EXE_FILE" ] && curl -L -o "./$EXE_FILE" "$URI"
+chmod +x "./$EXE_FILE"
+echo "Finished downloading $EXE_FILE"
+
+echo "Installing Vulkan SDK $VULKAN_SDK_VER..."
+if net session > /dev/null 2>&1; then
+ ./$EXE_FILE --root "$VULKAN_ROOT" --accept-licenses --default-answer --confirm-command install
+else
+ echo "This script must be run with administrator privileges!"
+ exit 1
+fi
+
+echo "Finished installing Vulkan SDK $VULKAN_SDK_VER"
+
+# GitHub Actions integration
+if [ \"${GITHUB_ACTIONS:-false}\" = \"true\" ]; then
+ echo \"VULKAN_SDK=$VULKAN_ROOT\" >> \"$GITHUB_ENV\"
+ echo \"$VULKAN_ROOT/bin\" >> \"$GITHUB_PATH\"
+fi
diff --git a/tools/windows/load-msvc-env.ps1 b/tools/windows/load-msvc-env.ps1
new file mode 100644
index 0000000000..8b98101bdb
--- /dev/null
+++ b/tools/windows/load-msvc-env.ps1
@@ -0,0 +1,42 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+$osArch = $env:PROCESSOR_ARCHITECTURE
+
+switch ($osArch) {
+ "AMD64" { $arch = "x64" }
+ "ARM64" { $arch = "arm64" }
+ default {
+ Write-Error "load-msvc-env.ps1: Unsupported architecture: $osArch"
+ exit 1
+ }
+}
+
+$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
+
+if (!(Test-Path $vswhere)) {
+ Write-Error "load-msvc-env.ps1: vswhere not found"
+ exit 1
+}
+
+$vs = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
+
+if (-not $vs) {
+ Write-Error "load-msvc-env.ps1: Visual Studio (with Desktop development with C++) not found"
+ exit 1
+}
+
+$bat = "$vs\VC\Auxiliary\Build\vcvarsall.bat"
+
+if (!(Test-Path $bat)) {
+ Write-Error "load-msvc-env.ps1: (vcvarsall.bat) not found"
+ exit 1
+}
+
+cmd /c "`"$bat`" $arch && set" | ForEach-Object {
+ if ($_ -match "^(.*?)=(.*)$") {
+ [Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
+ }
+}
+
+Write-Host "load-msvc-env.ps1: MSVC environment loaded for $arch ($vs)"
diff --git a/tools/windows/load-msvc-env.sh b/tools/windows/load-msvc-env.sh
new file mode 100644
index 0000000000..008621f7e4
--- /dev/null
+++ b/tools/windows/load-msvc-env.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/bash
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ARCH_RAW="$PROCESSOR_ARCHITECTURE"
+
+case "$ARCH_RAW" in
+ AMD64) ARCH="x64" ;;
+ ARM64) ARCH="arm64" ;;
+ *) echo "load-msvc-env.sh: Unsupported architecture: $ARCH_RAW"; exit 1 ;;
+esac
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+VCVARS_BASH="$SCRIPT_DIR/vcvarsall.sh"
+
+if [ ! -f "$VCVARS_BASH" ]; then
+ echo "load-msvc-env.sh: vcvarsall.sh not found in $SCRIPT_DIR"
+ #exit 1
+fi
+chmod +x "$VCVARS_BASH"
+
+eval "$("$VCVARS_BASH" "$ARCH")"
+
+echo "MSVC environment loaded for $ARCH with vcvars-bash script"
\ No newline at end of file
diff --git a/tools/windows/vcvarsall.sh b/tools/windows/vcvarsall.sh
new file mode 100644
index 0000000000..e6270730d7
--- /dev/null
+++ b/tools/windows/vcvarsall.sh
@@ -0,0 +1,433 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: Copyright 2023 Nathan Poirier
+# SPDX-License-Identifier: MIT License
+#
+# Source: https://github.com/nathan818fr/vcvars-bash
+#
+set -Eeuo pipefail
+shopt -s inherit_errexit
+
+declare -r VERSION='2025-07-09.1'
+
+function detect_platform() {
+ case "${OSTYPE:-}" in
+ cygwin* | msys* | win32)
+ declare -gr bash_platform='win_cyg'
+ ;;
+ *)
+ if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
+ declare -gr bash_platform='win_wsl'
+ else
+ printf 'error: Unsupported platform (%s)\n' "${OSTYPE:-}" >&2
+ printf 'hint: This script only supports Bash on Windows (Git Bash, WSL, etc.)\n' >&2
+ return 1
+ fi
+ ;;
+ esac
+}
+
+function detect_mode() {
+ # Detect the mode depending on the name called
+ if [[ "$(basename -- "$0")" == vcvarsrun* ]]; then
+ declare -gr script_mode='run' # vcvarsrun.sh
+ else
+ declare -gr script_mode='default' # vcvarsall.sh
+ fi
+}
+
+function print_usage() {
+ case "$script_mode" in
+ default)
+ cat </dev/null || true; } | fix_crlf | sed '/^Syntax:/d; s/^ vcvarsall.bat //')"
+}
+
+function main() {
+ detect_platform
+ detect_mode
+
+ # Parse arguments
+ if [[ $# -eq 0 ]]; then
+ print_usage >&2
+ return 1
+ fi
+
+ local arg vcvarsall_args=()
+ for arg in "$@"; do
+ shift
+ if [[ "$arg" == '--' ]]; then
+ if [[ "$script_mode" == 'default' ]]; then
+ printf 'error: Unexpected argument: --\n' >&2
+ printf 'hint: Use vcvarsrun to run a command\n' >&2
+ return 1
+ fi
+ break
+ fi
+ vcvarsall_args+=("$(cmdesc "$arg")")
+ done
+
+ if [[ "$script_mode" == 'run' && $# -eq 0 ]]; then
+ printf 'error: No command specified\n' >&2
+ return 1
+ fi
+
+ # Get MSVC environment variables from vcvarsall.bat
+ local vcvarsall vcvarsall_env
+ vcvarsall=$(find_vcvarsall)
+ vcvarsall_env=$({ cmd "$(cmdesc "$vcvarsall")" "${vcvarsall_args[@]}" '&&' 'set' &2
+ continue
+ fi
+
+ case "${name^^}" in
+ LIB | LIBPATH | INCLUDE | EXTERNAL_INCLUDE | COMMANDPROMPTTYPE | DEVENVDIR | EXTENSIONSDKDIR | FRAMEWORK* | \
+ PLATFORM | PREFERREDTOOLARCHITECTURE | UCRT* | UNIVERSALCRTSDK* | VCIDE* | VCINSTALL* | VCPKG* | VCTOOLS* | \
+ VSCMD* | VSINSTALL* | VS[0-9]* | VISUALSTUDIO* | WINDOWSLIB* | WINDOWSSDK*)
+ export_env "$name" "$value"
+ ;;
+ PATH)
+ # PATH is a special case, requiring special handling
+ local new_paths
+ new_paths=$(pathlist_win_to_unix "$value") # Convert to unix-style path list
+ new_paths=$(pathlist_normalize "${PATH}:${new_paths}") # Prepend the current PATH
+ export_env 'WINDOWS_PATH' "$value"
+ export_env 'PATH' "$new_paths"
+ ;;
+ esac
+ done <<<"$vcvarsall_env"
+
+ if [[ "$initialized" == 'false' ]]; then
+ printf 'error: vcvarsall.bat failed' >&2
+ return 1
+ fi
+
+ # Execute command if needed
+ if [[ "$script_mode" == 'run' ]]; then
+ exec "$@"
+ fi
+}
+
+# Locate vcvarsall.bat
+# Inputs:
+# VSINSTALLDIR: The path to the Visual Studio installation directory (optional)
+# VSWHEREPATH: The path to the vswhere.exe executable (optional)
+# VSWHEREARGS: The arguments to pass to vswhere.exe (optional)
+# Outputs:
+# stdout: The windows-style path to vcvarsall.bat
+function find_vcvarsall() {
+ local vsinstalldir
+ if [[ -n "${VSINSTALLDIR:-}" ]]; then
+ vsinstalldir="$VSINSTALLDIR"
+ else
+ local vswhere
+ if [[ -n "${VSWHEREPATH:-}" ]]; then
+ vswhere=$(unixpath "$VSWHEREPATH")
+ else
+ vswhere=$(command -v 'vswhere' 2>/dev/null || unixpath 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
+ fi
+
+ local vswhereargs=(-latest -property installationPath)
+ if [[ -n "${VSWHEREARGS:-}" ]]; then
+ parse_args_simple "$VSWHEREARGS"
+ vswhereargs+=("${result[@]}")
+ else
+ vswhereargs+=(-products '*')
+ fi
+
+ if [[ "${VCVARSBASH_DEBUG:-}" == 1 ]]; then
+ printf 'debug: vswhere path: %s\n' "$vswhere" >&2
+ printf 'debug: vswhere args:\n' >&2
+ printf 'debug: [ %s ]\n' "${vswhereargs[@]}" >&2
+ fi
+
+ vsinstalldir=$("$vswhere" "${vswhereargs[@]}" &2
+ return 1
+ fi
+ fi
+
+ printf '%s\n' "$(winpath "$vsinstalldir")\\VC\\Auxiliary\\Build\\vcvarsall.bat"
+}
+
+# Split command arguments into an array, with a simple logic
+# - Arguments are separated by whitespace (space, tab, newline, carriage return)
+# - To include whitespace in an argument, it must be enclosed in double quotes (")
+# - When inside a double-quoted argument, quotes can be escaped by doubling them ("")
+# Inputs:
+# $1: The string to parse
+# Outputs:
+# result: An array of parsed arguments
+function parse_args_simple() {
+ declare -g result=()
+ local str="$1 " # add a trailing space to simplify the last argument handling
+ local args=()
+ local current_type=none # none = waiting for next arg, normal = inside normal arg, quoted = inside quoted arg
+ local current_value=''
+ local i=0 len=${#str}
+ while ((i < len)); do
+ local c=${str:i:1}
+ case "$current_type" in
+ none)
+ case "$c" in
+ ' ' | $'\t' | $'\n' | $'\r')
+ # Ignore whitespace
+ ;;
+ '"')
+ # Start a quoted argument
+ current_type=quoted
+ current_value=''
+ ;;
+ *)
+ # Start a normal argument
+ current_type=normal
+ current_value="$c"
+ ;;
+ esac
+ ;;
+ normal)
+ case "$c" in
+ ' ' | $'\t' | $'\n' | $'\r')
+ # End of normal argument, add it to the list
+ args+=("$current_value")
+ current_type=none
+ current_value=''
+ ;;
+ *)
+ # Continue building the normal argument
+ current_value+="$c"
+ ;;
+ esac
+ ;;
+ quoted)
+ case "$c" in
+ '"')
+ if [[ "${str:i+1:1}" != '"' ]]; then
+ # End of quoted argument, add it to the list
+ args+=("$current_value")
+ current_type=none
+ current_value=''
+ else
+ # Escaped quote, add a single quote to the current value
+ current_value+='"'
+ ((++i)) # Skip the next quote
+ fi
+ ;;
+ *)
+ # Continue building the quoted argument
+ current_value+="$c"
+ ;;
+ esac
+ ;;
+ esac
+ ((++i))
+ done
+ if [[ "$current_type" != none ]]; then
+ printf 'error: Unfinished %s argument: %s\n' "$current_type" "$current_value" >&2
+ return 1
+ fi
+ declare -g result=("${args[@]}")
+}
+
+# Run a command with cmd.exe
+# Inputs:
+# $@: The command string to run (use cmdesc to escape arguments when needed)
+# Outputs:
+# stdout: The cmd.exe standard output
+# stderr: The cmd.exe error output
+function cmd() {
+ # This seems to work fine on all supported platforms
+ # (even with all the weird path and argument conversions on MSYS-like)
+ MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' cmd.exe /s /c " ; $* "
+}
+
+# Escape a cmd.exe command argument
+# Inputs:
+# $1: The argument to escape
+# Outputs:
+# stdout: The escaped argument
+function cmdesc() {
+ # shellcheck disable=SC2001
+ sed 's/[^0-9A-Za-z]/^\0/g' <<<"$1"
+}
+
+# Convert path to an absolute unix-style path
+# Inputs:
+# $1: The path to convert
+# Outputs:
+# stdout: The converted path
+function unixpath() {
+ local path=$1
+ case "$bash_platform" in
+ win_wsl)
+ case "$path" in
+ [a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
+ # Convert windows path using wslpath (unix mode, absolute path)
+ wslpath -u -a -- "$path"
+ ;;
+ *)
+ # Convert unix path using realpath
+ realpath -m -- "$path"
+ ;;
+ esac
+ ;;
+ *)
+ cygpath -u -a -- "$path"
+ ;;
+ esac
+}
+
+# Convert path to an absolute windows-style path
+# Inputs:
+# $1: The path to convert
+# Outputs:
+# stdout: The converted path
+function winpath() {
+ local path=$1
+ case "$bash_platform" in
+ win_wsl)
+ case "$path" in
+ [a-zA-Z]:\\* | [a-zA-Z]:/* | \\\\* | //*)
+ # Already a windows path
+ printf '%s' "$path"
+ ;;
+ *)
+ # Convert using wslpath (windows mode, absolute path)
+ wslpath -w -a -- "$path"
+ ;;
+ esac
+ ;;
+ *)
+ # Convert using cygpath (windows mode, absolute path, long form)
+ cygpath -w -a -l -- "$path"
+ ;;
+ esac
+}
+
+# Convert a windows-style path list to a unix-style path list
+# Inputs:
+# $1: The windows-style path list to convert
+# Outputs:
+# stdout: The converted unix-style path list
+function pathlist_win_to_unix() {
+ local win_paths=$1
+
+ local path_dir first=true
+ while IFS= read -r -d';' path_dir; do
+ if [[ -z "$path_dir" ]]; then continue; fi
+
+ if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
+ printf '%s' "$(unixpath "$path_dir")"
+ done <<<"${win_paths};"
+}
+
+# Normalize a unix-style path list, removing duplicates and empty entries
+# Inputs:
+# $1: The list to normalize
+# Outputs:
+# stdout: The normalized path list
+function pathlist_normalize() {
+ local unix_paths=$1
+
+ declare -A seen_paths
+ local path_dir first=true
+ while IFS= read -r -d ':' path_dir; do
+ if [[ -z "$path_dir" ]]; then continue; fi
+ if [[ -n "${seen_paths[$path_dir]:-}" ]]; then continue; fi
+ seen_paths[$path_dir]=1
+
+ if [[ "$first" == 'true' ]]; then first=false; else printf ':'; fi
+ printf '%s' "$path_dir"
+ done <<<"${unix_paths}:"
+}
+
+# Convert CRLF to LF
+# Inputs:
+# stdin: The input to convert
+# Outputs:
+# stdout: The converted input
+function fix_crlf() {
+ sed 's/\r$//'
+}
+
+eval 'main "$@";exit "$?"'