[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[quickemu]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\n\n[quickget]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".envrc",
    "content": "use flake"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [flexiondotorg, philclifford, lj3954]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: 'bug: description of the bug you encountered'\nlabels: ''\nassignees: ''\n\n---\n\n**I confirm this bug has not already been reported**\n- [ ] I have searched the issues and this bug has not been reported previously\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behaviour:\n1. Run `quickemu` with arguments '...'\n2. See error\n\n**Expected behaviour**\nA clear and concise description of what you expected to happen.\n\n**Quickemu output**\nRun `quickemu` or `quickemu` and include the output of the failure below:\n\n<details>\n  <summary>Quickemu output</summary>\n\n  ```text\n  quickemu/quickget output here\n  ```\n</details>\n\n**System information**\nRun `quickreport` and include the output here; if you can't run `quickreport`,\nplease provide the output of the following:\n\n<details>\n  <summary>Quickreport output</summary>\n\n  ```text\n  quickreport output here\n  ```\n</details>\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: 'feat: describe the feature you are requesting'\nlabels: ''\nassignees: ''\n\n---\n\n**I confirm this feature has not been previously requested**\n- [ ] I have searched the issues and this feature has not previously been requested\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      # Check for updates to GitHub Actions every week\n      interval: \"weekly\""
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "# Description\n\nPlease include a summary of the changes along with any relevant motivation and context.\n\n<!-- Close any related issues. Delete if not relevant -->\n\n- Closes #\n- Fixes #\n- Resolves #\n\n## Type of change\n\n<!-- Delete any that are not relevant -->\n\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] Packaging (updates the packaging)\n- [ ] Documentation (updates the documentation)\n\n# Checklist:\n\n- [ ] I have performed a self-review of my code\n- [ ] I have tested my code in common scenarios and confirmed there are no regressions\n- [ ] I have added comments to my code, particularly in hard-to-understand sections\n- [ ] I have made corresponding changes to the documentation (*remove if no documentation changes were required*)\n"
  },
  {
    "path": ".github/workflows/flake-checker.yml",
    "content": "name: Flake ❄️ Checker ✅\n\non:\n  push:\n    branches:\n      - master\n  schedule:\n    - cron: '42 0 * * 6'\n  workflow_dispatch:\n\njobs:\n  flake-checker:\n    name: Flake Checker\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: DeterminateSystems/nix-installer-action@v21\n      - uses: DeterminateSystems/magic-nix-cache-action@v9\n      - uses: DeterminateSystems/flake-checker-action@v12\n"
  },
  {
    "path": ".github/workflows/flake-updater.yml",
    "content": "name: Flake ❄️ Lock 🔒️ Updater ✨\n\non:\n  schedule:\n    - cron: '37 13 14,28 * *'\n  workflow_dispatch:\n\njobs:\n  lock-updater:\n    name: Flake Lock Updater\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: DeterminateSystems/nix-installer-action@v21\n      - uses: DeterminateSystems/magic-nix-cache-action@v9\n      - uses: DeterminateSystems/update-flake-lock@v28\n        with:\n          pr-title: \"chore: update flake.lock\"\n"
  },
  {
    "path": ".github/workflows/lint-pr.yml",
    "content": "name: \"Lint Pull Request 🐙\"\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n\npermissions:\n  pull-requests: read\n\njobs:\n  main:\n    name: Validate pull request title\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: amannn/action-semantic-pull-request@v6\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          # If the PR only contains a single commit, the action will validate that\n          # it matches the configured pattern.\n          validateSingleCommit: true\n          # Related to `validateSingleCommit` you can opt-in to validate that the PR\n          # title matches a single commit to avoid confusion.\n          validateSingleCommitMatchesPrTitle: true\n"
  },
  {
    "path": ".github/workflows/lint-shellcheck.yml",
    "content": "name: \"Lint Shellcheck 🐚\"\n\non:\n  pull_request:\n    branches: '**'\n  workflow_dispatch:\n\njobs:\n  shellcheck:\n    name: Shellcheck\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v6\n      - name: Run ShellCheck\n        uses: ludeeus/action-shellcheck@master\n        with:\n          format: gcc\n          severity: warning\n"
  },
  {
    "path": ".github/workflows/publish-release.yml",
    "content": "name: Publish Release 🏷️\n\non:\n  push:\n    tags:\n      - \"v?[0-9]+.[0-9]+.[0-9]+*\"\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: \"The existing tag to publish\"\n        type: \"string\"\n        required: true\n\njobs:\n  version-check:\n    name: \"Check versions ⚖️\"\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: \"Compare App and Git versions 🟰\"\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          APP_VERSION=$(grep \"^readonly VERSION\" quickemu | cut -d'\"' -f2)\n          GIT_VERSION=$(git describe --tags | cut -d'-' -f1)\n          echo \"App version: ${REL_VERSION}\"\n          echo \"Git version: ${GIT_VERSION}\"\n          if [ \"${APP_VERSION}\" != \"${GIT_VERSION}\" ]; then\n              echo \"ERROR! Version mismatch.\";\n              exit 1\n          fi\n\n  draft-release:\n    needs: [version-check]\n    name: \"Draft Release 📥️\"\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v6\n    - name: Create release ${{ github.ref }} as a draft\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        gh release create \"${{ github.ref }}\" --draft --generate-notes\n\n  build-release:\n    needs: [draft-release]\n    name: \"Build Release 👨‍🔧\"\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v6\n    - name: Build and Upload .deb\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        sudo apt-get -y update\n        sudo apt-get -y install debhelper devscripts\n        REL_VER=$(grep \"^readonly VERSION\" quickemu | cut -d'\"' -f2)\n        rm debian/changelog\n        dch --package quickemu --newversion=\"${REL_VER}-1\" --distribution=unstable \"New upstream release.\" --create\n        dpkg-buildpackage --build=binary --no-check-builddeps --compression=gzip\n        gh release upload \"${{ github.ref }}\" \"../quickemu_${REL_VER}-1_all.deb\" --clobber\n\n  publish-release:\n    needs: [build-release]\n    name: \"Publish Release 📤️\"\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v6\n    - name: Publish release ${{ github.ref }}\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        if [ \"$(gh release view \"${{ github.ref }}\" --json assets --template '{{len .assets}}')\" -lt 0 ]; then\n          exit 1\n        fi\n        gh release edit \"${{ github.ref }}\" --draft=false\n\n  publish-flakehub:\n    needs: [version-check]\n    name: \"Publish FlakeHub ❄️\"\n    runs-on: \"ubuntu-22.04\"\n    permissions:\n      id-token: \"write\"\n      contents: \"read\"\n    steps:\n      - uses: \"actions/checkout@v6\"\n        with:\n          ref: \"${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}\"\n      - uses: \"DeterminateSystems/nix-installer-action@main\"\n      - uses: \"DeterminateSystems/magic-nix-cache-action@main\"\n      - uses: \"DeterminateSystems/flakehub-push@main\"\n        with:\n          visibility: \"public\"\n          name: \"quickemu-project/quickemu\"\n          tag: \"${{ inputs.tag }}\"\n\n  publish-ppa:\n    needs: [version-check]\n    name: \"Publish PPA 📦️\"\n    runs-on: ubuntu-22.04\n    steps:\n    - name: \"Checkout 🥡\"\n      uses: actions/checkout@v6\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    - name: \"Import gpg key 🔑\"\n      uses: crazy-max/ghaction-import-gpg@v7\n      with:\n        gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}\n        passphrase: ${{ secrets.PPA_GPG_PASSPHRASE }}\n    - name: \"Install dependencies 💾\"\n      run: |\n        sudo apt-get -y update\n        sudo apt-get -y install debhelper-compat distro-info dput devscripts\n    - name: \"Upload to PPA ⤴️\"\n      env:\n        DEBEMAIL: ${{ secrets.DEBEMAIL }}\n        DEBFULLNAME: ${{ secrets.DEBFULLNAME }}\n      run: |\n        REL_VER=$(grep \"^readonly VERSION\" quickemu | cut -d'\"' -f2)\n        STAMP=$(date +%y%j.%H%M)\n        for CODENAME in $(distro-info --supported); do\n          rm debian/changelog\n          dch --package quickemu --newversion=\"${REL_VER}-1~${CODENAME}${STAMP}\" --distribution=${CODENAME} \"New upstream release.\" --create\n          dpkg-buildpackage -d -S -sa\n          dput ppa:flexiondotorg/quickemu ../quickemu_${REL_VER}-1~${CODENAME}${STAMP}_source.changes\n        done\n"
  },
  {
    "path": ".github/workflows/test-build-quickemu.yml",
    "content": "name: Test build quickemu 🚧\n\non:\n  pull_request:\n    branches:\n      - master\n    paths:\n      - quickemu\n      - quickget\n      - debian/**\n      - flake.nix\n      - package.nix\n  push:\n    branches:\n      - master\n    paths:\n      - quickemu\n      - quickget\n      - debian/**\n      - flake.nix\n      - package.nix\n  workflow_dispatch:\n\n# TODO: arm64 runner\n# https://github.blog/changelog/2024-06-03-actions-arm-based-linux-and-windows-runners-are-now-in-public-beta/\n\njobs:\n  test-deb-build:\n    runs-on: ubuntu-22.04\n    steps:\n    - name: \"Checkout 🥡\"\n      uses: actions/checkout@v6\n    - name: \"Build & Test .deb 🍥\"\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: |\n        sudo apt-get -y update\n        sudo apt-get -y install debhelper devscripts\n        REL_VER=$(grep \"^readonly VERSION\" quickemu | cut -d'\"' -f2)\n        rm debian/changelog\n        dch --package quickemu --newversion=\"${REL_VER}-1\" --distribution=unstable \"New upstream release.\" --create\n        dpkg-buildpackage --build=binary --no-check-builddeps --compression=gzip\n        sudo apt-get -y install ../quickemu_${REL_VER}-1_all.deb\n        quickemu --help\n        quickget --help\n\n  test-nix-build:\n    runs-on: ubuntu-22.04\n    permissions:\n      id-token: \"write\"\n      contents: \"read\"\n    steps:\n      - name: \"Checkout 🥡\"\n        uses: \"actions/checkout@v6\"\n      - name: \"Install Nix ❄️\"\n        uses: \"DeterminateSystems/nix-installer-action@v21\"\n      - name: \"Enable Magic Nix Cache 🪄\"\n        uses: \"DeterminateSystems/magic-nix-cache-action@v9\"\n      - name: \"Build & Test .nix ❄️\"\n        run: |\n          nix build .#quickemu\n          tree ./result\n          ./result/bin/quickemu --help\n          ./result/bin/quickget --help\n          ./result/bin/quickreport\n          # Text a VM if the Nix Installer successfully enabled KVM\n          if [ $DETERMINATE_NIX_KVM -eq 1 ]; then\n            ./result/bin/quickget alpine v3.20\n            ./result/bin/quickemu --vm alpine-v3.20.conf --display none\n            sleep 5\n            if pgrep -F ./alpine-v3.20/alpine-v3.20.pid; then\n              echo \"Test VM is running\"\n            else\n              echo \"Test VM is not running\"\n              exit 1\n            fi\n            # Test a few more quickemu commands to clean up\n            ./result/bin/quickemu --vm alpine-v3.20.conf --kill\n            ./result/bin/quickemu --vm alpine-v3.20.conf --delete-disk\n            ./result/bin/quickemu --vm alpine-v3.20.conf --delete-vm\n          fi\n\n"
  },
  {
    "path": ".github/workflows/test-quickget.yml",
    "content": "name: \"Test quickget 🧪\"\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n    paths:\n      - quickget\n  pull_request:\n    branches:\n      - '**'\n    paths:\n      - quickget\n\njobs:\n  detect-distros:\n    name: \"Detect distros 🔍\"\n    runs-on: ubuntu-22.04\n    outputs:\n      distros: ${{ steps.get-distros.outputs.distros }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: \"Extract unique distros 📋\"\n        id: get-distros\n        run: |\n          # Get unique distros, excluding windows variants\n          DISTROS=$(./quickget --list | tail -n +2 | cut -d' ' -f1 | sort -u | grep -v '^windows$' | grep -v '^windows-server$')\n          # Convert to JSON array\n          JSON=$(echo \"$DISTROS\" | jq -R -s -c 'split(\"\\n\") | map(select(length > 0))')\n          echo \"distros=${JSON}\" >> \"$GITHUB_OUTPUT\"\n          echo \"Found $(echo \"$DISTROS\" | wc -l | tr -d ' ') distros to test\"\n\n  test-distro:\n    name: \"Test ${{ matrix.distro }} 🧪\"\n    needs: detect-distros\n    runs-on: ubuntu-22.04\n    continue-on-error: true\n    strategy:\n      fail-fast: false\n      matrix:\n        distro: ${{ fromJson(needs.detect-distros.outputs.distros) }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: \"Install dependencies 📦️\"\n        run: |\n          sudo apt-get -y update\n          sudo apt-get -y install curl qemu-utils\n      - name: \"Check ${{ matrix.distro }} downloads 💿️\"\n        id: check\n        run: |\n          mkdir -p results\n          ./quickget --check-all-arch \"${{ matrix.distro }}\" | tee results/check.txt\n\n          # Count results (use wc -l to avoid grep exit code issues)\n          PASSED=$(grep '^PASS' results/check.txt | wc -l | tr -d ' ')\n          FAILED=$(grep '^FAIL' results/check.txt | wc -l | tr -d ' ')\n          SKIPPED=$(grep '^SKIP' results/check.txt | wc -l | tr -d ' ')\n\n          echo \"passed=${PASSED}\" >> \"$GITHUB_OUTPUT\"\n          echo \"failed=${FAILED}\" >> \"$GITHUB_OUTPUT\"\n          echo \"skipped=${SKIPPED}\" >> \"$GITHUB_OUTPUT\"\n\n          # Extract failed URLs for reporting\n          grep '^FAIL' results/check.txt > results/failed.txt || true\n\n          echo \"Results for ${{ matrix.distro }}: PASS=${PASSED} FAIL=${FAILED} SKIP=${SKIPPED}\"\n      - name: \"Create result JSON 📝\"\n        run: |\n          mkdir -p result\n          jq -n \\\n            --arg distro \"${{ matrix.distro }}\" \\\n            --argjson passed \"${{ steps.check.outputs.passed }}\" \\\n            --argjson failed \"${{ steps.check.outputs.failed }}\" \\\n            --argjson skipped \"${{ steps.check.outputs.skipped }}\" \\\n            '{distro: $distro, passed: $passed, failed: $failed, skipped: $skipped}' \\\n            > result/result.json\n\n          # Include failed URLs if any\n          if [ -s results/failed.txt ]; then\n            cp results/failed.txt result/failed.txt\n          fi\n\n          cat result/result.json\n      - name: \"Upload result artifact 📤\"\n        uses: actions/upload-artifact@v7\n        with:\n          name: result-${{ matrix.distro }}\n          path: result/\n          retention-days: 1\n      - name: \"Fail if any failures ❌\"\n        if: steps.check.outputs.failed != '0'\n        run: |\n          echo \"::error::${{ matrix.distro }} has ${{ steps.check.outputs.failed }} failed downloads\"\n          exit 1\n\n  report-results:\n    name: \"Report results 📊\"\n    needs: test-distro\n    runs-on: ubuntu-22.04\n    if: always()\n    steps:\n      - name: \"Download all result artifacts 📥\"\n        uses: actions/download-artifact@v8\n        with:\n          pattern: result-*\n          path: results\n      - name: \"Generate summary report 📈\"\n        run: |\n          # Aggregate all results\n          TOTAL_PASSED=0\n          TOTAL_FAILED=0\n          TOTAL_SKIPPED=0\n          HAS_FAILURES=false\n\n          # Temp file for collecting per-distro table rows\n          TEMP_ROWS=$(mktemp)\n\n          for json_file in results/result-*/result.json; do\n            [ -f \"$json_file\" ] || continue\n\n            DISTRO=$(jq -r '.distro' \"$json_file\")\n            PASSED=$(jq -r '.passed' \"$json_file\")\n            FAILED=$(jq -r '.failed' \"$json_file\")\n            SKIPPED=$(jq -r '.skipped' \"$json_file\")\n\n            TOTAL_PASSED=$((TOTAL_PASSED + PASSED))\n            TOTAL_FAILED=$((TOTAL_FAILED + FAILED))\n            TOTAL_SKIPPED=$((TOTAL_SKIPPED + SKIPPED))\n\n            if [ \"$FAILED\" -gt 0 ]; then\n              HAS_FAILURES=true\n              if [ \"$PASSED\" -eq 0 ]; then\n                STATUS=\"❌\"\n              else\n                STATUS=\"⚠️\"\n              fi\n            else\n              STATUS=\"✅\"\n            fi\n\n            echo \"| ${DISTRO} | ${PASSED} | ${FAILED} | ${SKIPPED} | ${STATUS} |\" >> \"$TEMP_ROWS\"\n          done\n\n          # Write summary header\n          echo \"## Test Results 📊\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"| Metric | Count |\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"|--------|-------|\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"| ✅ Passed | ${TOTAL_PASSED} |\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"| ❌ Failed | ${TOTAL_FAILED} |\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"| ⏭️ Skipped | ${TOTAL_SKIPPED} |\" >> \"$GITHUB_STEP_SUMMARY\"\n\n          # Write per-distro breakdown\n          echo \"\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"### Per-distro breakdown\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"| Distro | Passed | Failed | Skipped | Status |\" >> \"$GITHUB_STEP_SUMMARY\"\n          echo \"|--------|--------|--------|---------|--------|\" >> \"$GITHUB_STEP_SUMMARY\"\n          sort \"$TEMP_ROWS\" >> \"$GITHUB_STEP_SUMMARY\"\n\n          # Collect and display failed URLs if any\n          if [ \"$HAS_FAILURES\" = true ]; then\n            echo \"\" >> \"$GITHUB_STEP_SUMMARY\"\n            echo \"### ❌ Failed URLs\" >> \"$GITHUB_STEP_SUMMARY\"\n            echo \"\" >> \"$GITHUB_STEP_SUMMARY\"\n            echo '```' >> \"$GITHUB_STEP_SUMMARY\"\n\n            for failed_file in results/result-*/failed.txt; do\n              [ -f \"$failed_file\" ] && cat \"$failed_file\" >> \"$GITHUB_STEP_SUMMARY\"\n            done\n\n            echo '```' >> \"$GITHUB_STEP_SUMMARY\"\n          fi\n\n          # Print summary to log as well\n          echo \"===== SUMMARY =====\"\n          echo \"Total Passed:  ${TOTAL_PASSED}\"\n          echo \"Total Failed:  ${TOTAL_FAILED}\"\n          echo \"Total Skipped: ${TOTAL_SKIPPED}\"\n          echo \"==================\"\n\n          # Clean up temp file\n          rm -f \"$TEMP_ROWS\"\n\n          # Exit with failure if any distro had failures\n          if [ \"$HAS_FAILURES\" = true ]; then\n            echo \"::error::Some distros have failed downloads. Check the summary for details.\"\n            exit 1\n          fi\n"
  },
  {
    "path": ".gitignore",
    "content": "*.chunklist\n*.conf\n*.dmg\n*.fd\n*.fixed\n*.img\n*.iso\n*.ISO\n*.lock\n!flake.lock\n*.log\n*.markdownlint.jsonc\n*.markdownlint.yaml\n*.msi\n*.part\n*.permall\n*.pid\n*.ports\n*.qcow2\n*.sh\n*.sock\n*.xml\n*.zs-old\n.direnv/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"build-docs\"]\n\tpath = build-docs\n\turl = https://github.com/philclifford/quickemu-docs.git\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# AGENTS.md\n\nQuickemu is a Bash wrapper around QEMU that automates VM creation for ~1000 operating systems (Linux, macOS, Windows, BSDs). Two main tools: `quickget` downloads ISOs and creates configs; `quickemu` launches VMs with optimised hardware detection.\n\n## Project structure\n\n```\nquickemu        # Main VM launcher script (Bash)\nquickget        # OS downloader and config generator (Bash)\nquickreport     # System diagnostics tool (Bash)\nchunkcheck      # Download verification utility (Bash)\nflake.nix       # Nix flake for packaging\npackage.nix     # Nix package definition\ndevshell.nix    # Development environment\n```\n\n## Development environment\n\n```shell\nnix develop                    # Enter devshell with all dependencies\ndirenv reload                  # Update .direnv/bin/quickemu for testing\n```\n\nThe devshell patches `quickemu` to use Nix store paths for OVMF and Samba, writing to `.direnv/bin/quickemu`.\n\n## Build and test commands\n\n- Lint scripts: `shellcheck quickemu quickget quickreport chunkcheck`\n- Test quickget URLs: `./quickget --check <os> [release] [edition]`\n- Test ARM64 downloads: `./quickget --arch arm64 --check <os> [release] [edition]`\n- Test all architectures: `./quickget --check-all-arch <os> [release] [edition]`\n- List all supported OSes: `./quickget --list`\n- Run VM: `./quickemu --vm <name>.conf`\n\n## Code style\n\nAll scripts are Bash 4.0+ with ShellCheck compliance.\n\n| File | Indent | Notes |\n|------|--------|-------|\n| quickemu | 2 spaces | Main VM launcher |\n| quickget | 4 spaces | OS definitions and download logic |\n\n**General conventions:**\n\n- UTF-8, LF line endings, final newline\n- Trim trailing whitespace (except Markdown)\n- Functions use `function name() {` syntax\n- Many functions are invoked indirectly via dynamic dispatch - `SC2317` is disabled globally in quickget\n\n## Adding a new OS to quickget\n\nFollow the [guide in the wiki](https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features#adding-a-new-os-to-quickget). Each OS requires:\n\n1. Entry in `os_info()` case statement\n2. `releases_<os>()` function returning available versions\n3. `editions_<os>()` function if multiple editions exist\n4. `arch_<os>()` function if ARM64 is supported (defaults to amd64 only if omitted)\n5. Download URL construction logic\n\n## Commit message format\n\nCommits must follow [Conventional Commits](https://www.conventionalcommits.org/). PR titles are validated against this format.\n\n**Common prefixes:**\n\n- `fix(quickget):` - Fix distro downloads, update editions/releases\n- `fix(quickemu):` - Fix VM configuration or runtime issues\n- `feat(quickget):` - Add new OS support\n- `feat(quickemu):` - Add new VM features\n- `ci:` - CI/workflow changes\n- `docs:` - Documentation updates\n\n**Examples from recent history:**\n\n```\nfix(quickget): update Zorin OS download method and editions\nfix(quickget): handle Solus Xfce beta naming for older releases\nfix(quickget): remove Athena OS (no longer getting updates)\n```\n\n## Pull request guidelines\n\n- PR titles must match Conventional Commits format (enforced by CI)\n- Single-commit PRs: commit message must match PR title\n- Run `shellcheck` before submitting\n- Test affected OS downloads with `./quickget --check <os> <release>`\n\n## CI workflows\n\n| Workflow | Trigger | Purpose |\n|----------|---------|---------|\n| lint-shellcheck | PR | ShellCheck with severity: warning |\n| lint-pr | PR | Validates PR title matches Conventional Commits |\n| test-quickget | PR | Tests quickget functionality |\n| test-build-quickemu | PR | Build verification |\n| flake-checker | PR | Nix flake validation |\n\n## Platform support\n\n- Host: Linux (x86_64, aarch64), macOS (x86_64, aarch64)\n- Guest: x86_64 (default), aarch64 (set `arch=\"aarch64\"` in VM config)\n- ARM64 guests use AAVMF firmware and `virt` machine type\n- Cross-arch emulation uses TCG (no KVM acceleration)\n- OVMF/UEFI firmware: Linux only\n- Bash 4.0+ required (explicit version check at runtime)\n\n## Key dependencies\n\nRuntime: qemu, cdrtools, curl, jq, spice-gtk, swtpm, samba, zsync\nLinux-specific: OVMF (x86_64 guests), AAVMF (aarch64 guests), usbutils, mesa-demos\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behaviour that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behaviour include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behaviour and will take appropriate and fair corrective action in\nresponse to any behaviour that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behaviour may be\nreported to the community leaders responsible for enforcement at\n#quickemu channel on Discord.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behaviour deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehaviour was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behaviour. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behaviour.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behaviour,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe welcome contributions to Quickemu.\n\n- Help other Quickemu users by answering questions in the [Quickemu Discussions](https://github.com/quickemu-project/quickemu/discussions) 🛟\n- Improve the documentation in [this README](https://github.com/quickemu-project/quickemu/edit/master/README.md) and the [Quickemu Wiki](https://github.com/quickemu-project/quickemu/wiki) 📖\n- File bug reports and feature requests in the [Quickemu Issues](https://github.com/quickemu-project/quickemu/issues) 📁\n- Submit [Quickemu Pull requests](https://github.com/quickemu-project/quickemu/pulls) to fix bugs 🐞 or add new features ✨\n  - Follow our [guide to adding a new OS to quickget](https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features#adding-a-new-os-to-quickget)\n  - Commit messages must [conform to the Conventional Commits specification](https://www.conventionalcommits.org/).\n- [Sponsor the project](https://github.com/sponsors/flexiondotorg) 💖\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Wimpy's World\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n<img src=\".github/logo.png\" alt=\"Quickemu\" width=\"256\" />\n\n# Quickemu\n\n**Quickly create and run optimised Windows, macOS and Linux virtual machines:**\n\n**Made with 💝 for <img src=\".github/tux.png\" align=\"top\" width=\"24\" alt=\"Tux (Linux)\"/> & <img src=\".github/apple.png\" align=\"top\" width=\"24\" alt=\"Apple (macOS)\"/>**\n</div>\n\n<p align=\"center\">\n  &nbsp;<a href=\"https://wimpysworld.io/discord\" target=\"_blank\"><img alt=\"Discord\" src=\"https://img.shields.io/discord/712850672223125565?style=for-the-badge&logo=discord&logoColor=%23ffffff&label=Discord&labelColor=%234253e8&color=%23e4e2e2\"></a>&nbsp;\n  &nbsp;<a href=\"https://fosstodon.org/@wimpy\" target=\"_blank\"><img alt=\"Mastodon\" src=\"https://img.shields.io/badge/Mastodon-6468fa?style=for-the-badge&logo=mastodon&logoColor=%23ffffff\"></a>&nbsp;\n  &nbsp;<a href=\"https://twitter.com/m_wimpress\" target=\"_blank\"><img alt=\"Twitter\" src=\"https://img.shields.io/badge/Twitter-303030?style=for-the-badge&logo=x&logoColor=%23ffffff\"></a>&nbsp;\n  &nbsp;<a href=\"https://linkedin.com/in/martinwimpress\" target=\"_blank\"><img alt=\"LinkedIn\" src=\"https://img.shields.io/badge/LinkedIn-1667be?style=for-the-badge&logo=linkedin&logoColor=%23ffffff\"></a>&nbsp;\n</p>\n\n\n# Introduction\n\n**Quickemu** is a wrapper for the excellent [QEMU](https://www.qemu.org/) that\nautomatically *\"does the right thing\"* when creating virtual machines. No\nrequirement for exhaustive configuration options. You decide what operating\nsystem you want to run and Quickemu takes care of the rest 🤖\n\n- `quickget` **automatically downloads the upstream OS** and creates the configuration 📀\n- `quickemu` enumerates your hardware and launches the virtual machine with the **optimum configuration best suited to your computer** ⚡️\n\nThe original objective of the project was to [enable quick testing of Linux\ndistributions](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines)\nwhere the virtual machines and their configuration can be stored anywhere (such\nas external USB storage or your home directory) and no elevated permissions are\nrequired to run the virtual machines.\n\n**Today, Quickemu includes comprehensive support for [macOS](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines),\n[Windows](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines)**, most of the BSDs, novel non-Linux operating systems such as FreeDOS, Haiku, KolibriOS, OpenIndiana, ReactOS, and more.\n\n# Features\n\n- Host support for **Linux and macOS**\n- **macOS** Sequoia, Sonoma, Ventura, Monterey, Big Sur, Catalina & Mojave\n- **Windows** 10 and 11 including TPM 2.0\n- **Windows Server** 2022 2019 2016\n- **ARM64 guest support** for running aarch64 VMs (native on ARM hosts, emulated on x86_64)\n- [Ubuntu](https://ubuntu.com/desktop) and all the **[official Ubuntu\n  flavours](https://ubuntu.com/download/flavours)**\n- **Nearly 1000 operating system editions are supported!**\n- Full SPICE support including host/guest clipboard sharing\n- VirtIO-webdavd file sharing for Linux and Windows guests\n- VirtIO-9p file sharing for Linux and macOS guests\n- [QEMU Guest Agent\n  support](https://wiki.qemu.org/Features/GuestAgent); provides access\n  to a system-level agent via standard QMP commands\n- Samba file sharing for Linux, macOS and Windows guests (*if `smbd`\n  is installed on the host*)\n- VirGL acceleration\n- USB device pass-through\n- Smartcard pass-through\n- Automatic SSH port forwarding to guests\n- Network port forwarding\n- Full duplex audio\n- Braille support\n- EFI (with or without SecureBoot) and Legacy BIOS boot\n\n## As featured on [Linux Matters](https://linuxmatters.sh) podcast!\n\nThe presenters of Linux Matters 🐧🎙️ are the creators of each of the principal Quickemu projects. We discussed Quickemu's 2024 reboot in [Episode 30 - Quickemu Rising From the Bashes](https://linuxmatters.sh/30). <!-- and in [Episode 32 - Quick, quicker, quickest](https://linuxmatters.sh/32) [Martin](https://github.com/flexiondotorg) unveils macOS host support for [**Quickemu**](https://github.com/quickemu-project/quickemu), [Mark](https://github.com/marxjohnson) explains the origins of the [**Quickgui**](https://github.com/quickemu-project/quickgui) desktop app and upcoming improvements, and [Alan](https://github.com/popey) debuts [**Quicktest**](https://github.com/quickemu-project/quicktest); a framework for automatically testing operating systems via Quickemu -->\n\n<div align=\"center\">\n\n<a href=\"https://linuxmatters.sh\" target=\"_blank\"><img src=\"https://github.com/wimpysworld/nix-config/raw/main/.github/screenshots/linuxmatters.png\" alt=\"Linux Matters Podcast\"/></a>\n<br />\n<em>Linux Matters Podcast</em>\n\n</div>\n\n# Quick start\n\n[Once Quickemu is installed](https://github.com/quickemu-project/quickemu/wiki/01-Installation), there are two simple steps to create and run a virtual machine:\n\n- `quickget` automatically downloads the ISO image for the operating system you want to run and creates a configuration file for the virtual machine.\n\n``` shell\nquickget nixos unstable minimal\n```\n\n- `quickemu` starts the virtual machine using the configuration file created by `quickget`.\n\n``` shell\nquickemu --vm nixos-unstable-minimal.conf\n```\n\nExecute `quickget` (with no arguments) to see a list of all the supported operating systems.\n\n## Demo\n\n<div align=\"center\">\n\n<a href=\"https://asciinema.org/a/658148?autoplay=1\" target=\"_blank\"><img src=\"https://asciinema.org/a/658148.svg\" /></a>\n\n</div>\n\n# Documentation\n\nThe wiki describes how to get up and running with Quickemu and also covers more advanced configuration and usage.\n\n- [**Installation**](https://github.com/quickemu-project/quickemu/wiki/01-Installation) 💾\n- [**Create Linux virtual machines**](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines) 🐧\n- [**Create macOS virtual machines**](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines) 🍏\n- [**Create Windows virtual machines**](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines) 🪟\n- [**Advanced quickemu configuration**](https://github.com/quickemu-project/quickemu/wiki/05-Advanced-quickemu-configuration) 🔧\n- [**Advanced quickget features**](https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features) 🤓\n- [**Alternative frontends**](https://github.com/quickemu-project/quickemu/wiki/07-Alternative-frontends) 🧑‍💻\n- [**References**](https://github.com/quickemu-project/quickemu/wiki/08-References) 📚️\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nHere are the versions of Quickemu currently being supported with security updates.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 4.9.x   | :white_check_mark: |\n| < 4.8   | :x:                |\n\n## Reporting a Vulnerability\n\nIf you discover a vulnerability in Quickemu then [file an issue](https://github.com/quickemu-project/quickemu/issues/new) and click *Report a vulnerability*.\n\n- Quickemu is a spare-time hobby project.\n- We do not have SLAs for responding to security issues.\n- It is a best-efforts basis.\n"
  },
  {
    "path": "chunkcheck",
    "content": "#!/usr/bin/env python3\n\nfrom pathlib import Path\nimport struct\nimport hashlib\nimport argparse\nv1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF\n# v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883\n# v2_dev_pubkey  = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3\n\nChunkListHeader = struct.Struct('<4sIBBBxQQQ')\nassert ChunkListHeader.size == 0x24\n\nChunk = struct.Struct('<I32s')\nassert Chunk.size == 0x24\n\ndef parse_chunklist(path):\n    with open(path, 'rb') as f:\n        hash_ctx = hashlib.sha256()\n        data = f.read(ChunkListHeader.size)\n        hash_ctx.update(data)\n        magic, header_size, file_version, chunk_method, signature_method, chunk_count, chunk_offset, signature_offset = ChunkListHeader.unpack(data)\n        assert magic == b'CNKL'\n        assert header_size == ChunkListHeader.size\n        assert file_version == 1\n        assert chunk_method == 1\n        assert signature_method in [1, 2]\n        assert chunk_count > 0\n        assert chunk_offset == 0x24\n        assert signature_offset == chunk_offset + Chunk.size * chunk_count\n        for i in range(chunk_count):\n            data = f.read(Chunk.size)\n            hash_ctx.update(data)\n            chunk_size, chunk_sha256 = Chunk.unpack(data)\n            yield chunk_size, chunk_sha256\n        digest = hash_ctx.digest()\n        if signature_method == 1:\n            data = f.read(256)\n            assert len(data) == 256\n            signature = int.from_bytes(data, 'little')\n            plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big')\n            assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext\n        elif signature_method == 2:\n            data = f.read(32)\n            assert data == digest\n        else:\n            raise NotImplementedError\n        assert f.read(1) == b''\n\ndef check_chunklist(path, chunklist_path):\n    with open(path, 'rb') as f:\n        for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path):\n            chunk = f.read(chunk_size)\n            assert len(chunk) == chunk_size\n            assert hashlib.sha256(chunk).digest() == chunk_sha256\n        assert f.read(1) == b''\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('vmdir', type=Path)\n    args = parser.parse_args()\n    vmdir = args.vmdir\n    check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist')\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "debian/changelog",
    "content": "quickemu (4.9.6-1) unstable; urgency=medium\n\n  * New upstream release.\n\n -- Martin Wimpress <code@wimpress.io>  Fri, 10 Nov 2023 14:49:08 +0000\n"
  },
  {
    "path": "debian/control",
    "content": "Source: quickemu\nSection: utils\nPriority: optional\nMaintainer: Martin Wimpress <code@wimpress.io>\nBuild-Depends:\n debhelper-compat (= 12),\nStandards-Version: 4.5.1\nHomepage: https://github.com/quickemu-project/quickemu\nVcs-Browser: https://github.com/quickemu-project/quickemu\nVcs-Git: https://github.com/quickemu-project/quickemu.git\nRules-Requires-Root: no\n\nPackage: quickemu\nArchitecture: all\nDepends:\n coreutils,\n curl,\n genisoimage,\n jq,\n mesa-utils,\n pciutils,\n procps,\n python3-minimal,\n qemu-system (>= 6.0),\n base-files (<< 13~) | qemu-system-modules-spice,\n socat,\n spice-client-gtk,\n swtpm,\n usbutils,\n util-linux,\n uuid-runtime,\n x11-xserver-utils,\n xdg-user-dirs,\n zsync,\n ${misc:Depends},\n ${shlibs:Depends},\nSuggests:\n virt-viewer,\nDescription: Quickemu creates and runs optimised virtual machines.\n Simple script to \"manage\" Qemu virtual machines. Each virtual machine\n configuration is a few lines long requiring minimal setup. The main objective\n of the project is to enable quick testing of desktop Linux distributions where\n the virtual machines configuration and disk images can be stored anywhere,\n such as external USB storage or your home directory. Windows and macOS guests\n are also supported.\n .\n"
  },
  {
    "path": "debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: quickemu\nUpstream-Contact: Martin Wimpress <code@wimpress.io>\nSource: https://github.com/quickemu-project/quickemu\n\nFiles: *\nCopyright: 2020-2024 Martin Wimpress <code@wimpress.io>\nLicense: MIT\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n .\n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n .\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n\n# If you want to use GPL v2 or later for the /debian/* files use\n# the following clauses, or change it to suit. Delete these two lines\nFiles: debian/*\nCopyright: 2021 - 2024 Martin Wimpress <code@wimpress.io>\nLicense: GPL-2+\n This package is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n .\n This package is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n .\n You should have received a copy of the GNU General Public License\n along with this program. If not, see <https://www.gnu.org/licenses/>\n .\n On Debian systems, the complete text of the GNU General\n Public License version 2 can be found in \"/usr/share/common-licenses/GPL-2\".\n"
  },
  {
    "path": "debian/install",
    "content": "chunkcheck usr/bin\nquickemu usr/bin\nquickget usr/bin\nquickreport usr/bin\n"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n\n%:\n\tdh $@\n\noverride_dh_builddeb:\n\tdh_builddeb -- -Zgzip\n"
  },
  {
    "path": "debian/source/format",
    "content": "3.0 (native)\n"
  },
  {
    "path": "devshell.nix",
    "content": "{\n  lib,\n  mkShell,\n  pkgs,\n  stdenv,\n  OVMF ? null,\n  OVMFFull ? null,\n}:\nmkShell {\n  packages =\n    with pkgs;\n    (\n      [\n        cdrtools\n        curl\n        gawk\n        gnugrep\n        gnused\n        jq\n        pciutils\n        procps\n        python3\n        qemu_full\n        samba\n        socat\n        spice-gtk\n        swtpm\n        unzip\n        util-linux\n        xorg.xrandr\n        zsync\n      ]\n      ++ lib.optionals stdenv.hostPlatform.isLinux [\n        mesa-demos\n        usbutils\n        xdg-user-dirs\n      ]\n      ++ lib.optionals (OVMF != null && OVMFFull != null) [\n        OVMF\n        OVMFFull\n      ]\n    );\n\n  inputsFrom = with pkgs; [\n    git\n  ];\n\n  shellHook = ''\n    echo \"**********************************************************************\"\n    echo \"* 'direnv reload' to update '.direnv/bin/quickemu' for testing  *\"\n    echo \"**********************************************************************\"\n    sed \\\n      ${\n        lib.optionalString (OVMF != null && OVMFFull != null) ''\n          -e '/OVMF_CODE_4M.secboot.fd/s|ovmfs=(|ovmfs=(\"${OVMFFull.firmware}\",\"${OVMFFull.variablesMs}\" |' \\\n          -e '/OVMF_CODE_4M.fd/s|ovmfs=(|ovmfs=(\"${OVMF.firmware}\",\"${OVMF.variables}\" |' \\\n        ''\n      }${lib.optionalString stdenv.hostPlatform.isDarwin ''\n        -e 's|ovmfs=(\"[$][{]SHARE_PATH}/OVMF/OVMF_CODE_4M.secboot.fd\"|ovmfs=(\"${pkgs.qemu_full}/share/qemu/edk2-x86_64-secure-code.fd\",\"${pkgs.qemu_full}/share/qemu/edk2-i386-vars.fd\" \"''${SHARE_PATH}/OVMF/OVMF_CODE_4M.secboot.fd\"|' \\\n        -e 's|ovmfs=(\"[$][{]SHARE_PATH}/OVMF/OVMF_CODE_4M.fd\"|ovmfs=(\"${pkgs.qemu_full}/share/qemu/edk2-x86_64-code.fd\",\"${pkgs.qemu_full}/share/qemu/edk2-i386-vars.fd\" \"''${SHARE_PATH}/OVMF/OVMF_CODE_4M.fd\"|' \\\n        -e 's|ovmfs=(\"/usr/share/AAVMF/AAVMF_CODE.fd\"|ovmfs=(\"${pkgs.qemu_full}/share/qemu/edk2-aarch64-code.fd\",\"${pkgs.qemu_full}/share/qemu/edk2-arm-vars.fd\" \"/usr/share/AAVMF/AAVMF_CODE.fd\"|' \\\n      ''} \\\n      -e '/cp \"''${VARS_IN}\" \"''${VARS_OUT}\"/a chmod +w \"''${VARS_OUT}\"' \\\n      -e 's,\\$(command -v smbd),${pkgs.samba}/bin/smbd,' \\\n      quickemu > $PWD/.direnv/bin/quickemu\n    chmod +x $PWD/.direnv/bin/quickemu\n  '';\n}\n"
  },
  {
    "path": "docs/Makefile",
    "content": "include pandoc-man.mk\n\nifeq ($(PREFIX),)\n    PREFIX := /usr/local\nendif\n\ndatarootdir := $(PREFIX)/share\ndatadir := $(datarootdir)\nmandir := $(datarootdir)/man\nbindir :=  $(PREFIX)/bin\n\nall: quickget.1 quickemu.1 quickemu_conf.5\n\nclean:\n\trm *.1 *.5\n\ninstall_docs: all\n\tinstall -d $(DESTDIR)$(mandir)/man1\n\tinstall -d $(DESTDIR)$(mandir)/man5\n\tinstall -m 644 quickget.1 $(DESTDIR)$(mandir)/man1\n\tinstall -m 644 quickemu.1 $(DESTDIR)$(mandir)/man1\n\tinstall -m 644 quickemu_conf.5 $(DESTDIR)$(mandir)/man5\n\trm -f $(DESTDIR)$(mandir)/man1/quickemu_conf.1\n\n#   install -m 644 quickgui.1 $(DESTDIR)$(mandir)/man1\n\ninstall_bins:\n\tinstall -d $(DESTDIR)$(bindir)\n\tinstall -m 755 ../quickget $(DESTDIR)$(bindir)\n\tinstall -m 755 ../quickemu $(DESTDIR)$(bindir)\n\tinstall -m 755 ../quickreport $(DESTDIR)$(bindir)\n\tinstall -m 755 ../chunkcheck $(DESTDIR)$(bindir)\n\ninstall: install_bins  install_docs\n\nuninstall::\n\trm -f $(DESTDIR)$(mandir)/man1/quickget.1\n\trm -f $(DESTDIR)$(mandir)/man1/quickemu.1\n\trm -f $(DESTDIR)$(mandir)/man5/quickemu_conf.5\n\trm -f $(DESTDIR)$(bindir)/quickget\n\trm -f $(DESTDIR)$(bindir)/quickemu\n\trm -f $(DESTDIR)$(bindir)/quickreport\n\trm -f $(DESTDIR)$(bindir)/macrecovery\n\trm -f $(DESTDIR)$(bindir)/chunkcheck\n\n\n.PHONY: all\n"
  },
  {
    "path": "docs/pandoc-man.mk",
    "content": "PANDOC ?= pandoc\nPANDOC_OPTIONS = -f gfm+definition_lists\n\n.SUFFIXES: .1.md .1 .5.md .5\n\n.1.md.1 .5.md.5:\n\t$(PANDOC) --standalone $(PANDOC_OPTIONS) --to man -o $@ $<\n"
  },
  {
    "path": "docs/quickemu.1",
    "content": ".\\\" Automatically generated by Pandoc 3.8.3\n.\\\"\n.TH \"QUICKEMU\" \"1\" \"February 2, 2026\" \"quickemu\" \"Quickemu User Manual\"\n.SH NAME\nquickemu \\- A quick VM builder and manager\n.SH SYNOPSIS\n\\f[B]quickemu\\f[R] [\\f[I]OPTION\\f[R]]...\n.SH DESCRIPTION\n\\f[B]quickemu\\f[R] will create and run highly optimised desktop virtual\nmachines for Linux, macOS and Windows\n.SH OPTIONS\n.TP\n\\f[B]\\-\\-vm\\f[R]\nvm configuration file\n.PP\nYou can also pass optional parameters\n.TP\n\\f[B]\\-\\-access\\f[R]\nEnable remote spice access support.\n\\(aqlocal\\(aq (default), \\(aqremote\\(aq, \\(aqclientipaddress\\(aq\n.TP\n\\f[B]\\-\\-braille\\f[R]\nEnable braille support.\nRequires SDL.\n.TP\n\\f[B]\\-\\-delete\\-disk\\f[R]\nDelete the disk image and EFI variables\n.TP\n\\f[B]\\-\\-delete\\-vm\\f[R]\nDelete the entire VM and its configuration\n.TP\n\\f[B]\\-\\-display\\f[R]\nSelect display backend.\n\\(aqgtk\\(aq (default), \\(aqsdl\\(aq, \\(aqcocoa\\(aq, \\(aqnone\\(aq,\n\\(aqspice\\(aq or \\(aqspice\\-app\\(aq\n.TP\n\\f[B]\\-\\-fullscreen\\f[R]\nStarts VM in full screen mode (Ctl+Alt+f to exit)\n.TP\n\\f[B]\\-\\-ignore\\-msrs\\-always\\f[R]\nConfigure KVM to always ignore unhandled machine\\-specific registers\n.TP\n\\f[B]\\-\\-kill\\f[R]\nKill the VM process if it is running\n.TP\n\\f[B]\\-\\-offline\\f[R]\nOverride all network settings and start the VM offline\n.TP\n\\f[B]\\-\\-shortcut\\f[R]\nCreate a desktop shortcut\n.TP\n\\f[B]\\-\\-snapshot apply <tag>\\f[R]\nApply/restore a snapshot.\n.TP\n\\f[B]\\-\\-snapshot create <tag>\\f[R]\nCreate a snapshot.\n.TP\n\\f[B]\\-\\-snapshot delete <tag>\\f[R]\nDelete a snapshot.\n.TP\n\\f[B]\\-\\-snapshot info\\f[R]\nShow disk/snapshot info.\n.TP\n\\f[B]\\-\\-status\\-quo\\f[R]\nDo not commit any changes to disk/snapshot.\n.TP\n\\f[B]\\-\\-viewer <viewer>\\f[R]\nChoose an alternative viewer.\n\\(atOptions: \\(aqspicy\\(aq (default), \\(aqremote\\-viewer\\(aq,\n\\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-width <width>\\f[R]\nSet VM screen width; requires \\(aq\\-\\-height\\(aq\n.TP\n\\f[B]\\-\\-height <height>\\f[R]\nSet VM screen height; requires \\(aq\\-\\-width\\(aq\n.TP\n\\f[B]\\-\\-ssh\\-port <port>\\f[R]\nSet SSH port manually\n.TP\n\\f[B]\\-\\-spice\\-port <port>\\f[R]\nSet SPICE port manually\n.TP\n\\f[B]\\-\\-public\\-dir <path>\\f[R]\nExpose share directory.\n\\(atOptions: \\(aq\\(aq (default: xdg\\-user\\-dir PUBLICSHARE), \\(aq\\(aq,\n\\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-monitor <type>\\f[R]\nSet monitor connection type.\n\\(atOptions: \\(aqsocket\\(aq (default), \\(aqtelnet\\(aq, \\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-monitor\\-telnet\\-host <ip/host>\\f[R]\nSet telnet host for monitor.\n(default: \\(aqlocalhost\\(aq)\n.TP\n\\f[B]\\-\\-monitor\\-telnet\\-port <port>\\f[R]\nSet telnet port for monitor.\n(default: \\(aq4440\\(aq)\n.TP\n\\f[B]\\-\\-monitor\\-cmd <cmd>\\f[R]\nSend command to monitor if available.\n(Example: system_powerdown)\n.TP\n\\f[B]\\-\\-serial <type>\\f[R]\nSet serial connection type.\n\\(atOptions: \\(aqsocket\\(aq (default), \\(aqtelnet\\(aq, \\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-serial\\-telnet\\-host <ip/host>\\f[R]\nSet telnet host for serial.\n(default: \\(aqlocalhost\\(aq)\n.TP\n\\f[B]\\-\\-serial\\-telnet\\-port <port>\\f[R]\nSet telnet port for serial.\n(default: \\(aq6660\\(aq)\n.TP\n\\f[B]\\-\\-keyboard <type>\\f[R]\nSet keyboard.\n\\(atOptions: \\(aqusb\\(aq (default), \\(aqps2\\(aq, \\(aqvirtio\\(aq\n.TP\n\\f[B]\\-\\-keyboard_layout <layout>\\f[R]\nSet keyboard layout: \\(aqen\\-us\\(aq (default)\n.TP\n\\f[B]\\-\\-mouse <type>\\f[R]\nSet mouse.\n\\(atOptions: \\(aqtablet\\(aq (default), \\(aqps2\\(aq, \\(aqusb\\(aq,\n\\(aqvirtio\\(aq\n.TP\n\\f[B]\\-\\-usb\\-controller <type>\\f[R]\nSet usb\\-controller.\n\\(atOptions: \\(aqehci\\(aq (default), \\(aqxhci\\(aq, \\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-sound\\-card <type>\\f[R]\nSet sound card.\n\\(atOptions: \\(aqintel\\-hda\\(aq (default), \\(aqac97\\(aq, \\(aqes1370\\(aq,\n\\(aqsb16\\(aq, \\(aqnone\\(aq\n.TP\n\\f[B]\\-\\-extra_args <arguments>\\f[R]\nPass additional arguments to qemu\n.TP\n\\f[B]\\-\\-version\\f[R]\nPrint version\n.SH EXAMPLES\n.TP\n\\f[B]quickemu \\-\\-vm ubuntu\\-mate\\-22.04.conf\\f[R]\nLaunches the VM specified in the file \\f[I]ubuntu\\-mate\\-22.04.conf\\f[R]\n.SH Introduction\n\\f[B]Quickemu\\f[R] is a wrapper for the excellent \\c\n.UR https://www.qemu.org/\nQEMU\n.UE \\c\n\\ that automatically \\f[I]\\(dqdoes the right thing\\(dq\\f[R] when\ncreating virtual machines.\nNo requirement for exhaustive configuration options.\nYou decide what operating system you want to run and Quickemu takes care\nof the rest 🤖\n.IP \\(bu 2\n\\f[CR]quickget\\f[R] \\f[B]automatically downloads the upstream OS\\f[R]\nand creates the configuration 📀\n.IP \\(bu 2\n\\f[CR]quickemu\\f[R] enumerates your hardware and launches the virtual\nmachine with the \\f[B]optimum configuration best suited to your\ncomputer\\f[R] ⚡️\n.PP\nThe original objective of the project was to \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines\nenable quick testing of Linux distributions\n.UE \\c\n\\ where the virtual machines and their configuration can be stored\nanywhere (such as external USB storage or your home directory) and no\nelevated permissions are required to run the virtual machines.\n.PP\n\\f[B]Today, Quickemu includes comprehensive support for \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines\nmacOS\n.UE \\c\n, \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines\nWindows\n.UE \\c\n\\f[R], most of the BSDs, novel non\\-Linux operating systems such as\nFreeDOS, Haiku, KolibriOS, OpenIndiana, ReactOS, and more.\n.SH Features\n.IP \\(bu 2\nHost support for \\f[B]Linux and macOS\\f[R]\n.IP \\(bu 2\n\\f[B]macOS\\f[R] Sequoia, Sonoma, Ventura, Monterey, Big Sur, Catalina &\nMojave\n.IP \\(bu 2\n\\f[B]Windows\\f[R] 10 and 11 including TPM 2.0\n.IP \\(bu 2\n\\f[B]Windows Server\\f[R] 2022 2019 2016\n.IP \\(bu 2\n\\f[B]ARM64 guest support\\f[R] for running aarch64 VMs (native on ARM\nhosts, emulated on x86_64)\n.IP \\(bu 2\n\\c\n.UR https://ubuntu.com/desktop\nUbuntu\n.UE \\c\n\\ and all the \\f[B]\\c\n.UR https://ubuntu.com/download/flavours\nofficial Ubuntu flavours\n.UE \\c\n\\f[R]\n.IP \\(bu 2\n\\f[B]Nearly 1000 operating system editions are supported!\\f[R]\n.IP \\(bu 2\nFull SPICE support including host/guest clipboard sharing\n.IP \\(bu 2\nVirtIO\\-webdavd file sharing for Linux and Windows guests\n.IP \\(bu 2\nVirtIO\\-9p file sharing for Linux and macOS guests\n.IP \\(bu 2\n\\c\n.UR https://wiki.qemu.org/Features/GuestAgent\nQEMU Guest Agent support\n.UE \\c\n; provides access to a system\\-level agent via standard QMP commands\n.IP \\(bu 2\nSamba file sharing for Linux, macOS and Windows guests (\\f[I]if\n\\f[CI]smbd\\f[I] is installed on the host\\f[R])\n.IP \\(bu 2\nVirGL acceleration\n.IP \\(bu 2\nUSB device pass\\-through\n.IP \\(bu 2\nSmartcard pass\\-through\n.IP \\(bu 2\nAutomatic SSH port forwarding to guests\n.IP \\(bu 2\nNetwork port forwarding\n.IP \\(bu 2\nFull duplex audio\n.IP \\(bu 2\nBraille support\n.IP \\(bu 2\nEFI (with or without SecureBoot) and Legacy BIOS boot\n.SS As featured on \\c\n.UR https://linuxmatters.sh\nLinux Matters\n.UE \\c\n\\ podcast!\nThe presenters of Linux Matters 🐧🎙️ are the creators of each of the\nprincipal Quickemu projects.\nWe discussed Quickemu\\(aqs 2024 reboot in \\c\n.UR https://linuxmatters.sh/30\nEpisode 30 \\- Quickemu Rising From the Bashes\n.UE \\c\n\\&.\n.PP\n\\  Linux Matters Podcast\n.PP\nWhen installing from source, you will need to install the following\nrequirements manually:\n.IP \\(bu 2\n\\c\n.UR https://www.qemu.org/\nQEMU\n.UE \\c\n\\ (\\f[I]6.0.0 or newer\\f[R]) \\f[B]with GTK, SDL, SPICE & VirtFS\nsupport\\f[R]\n.IP \\(bu 2\n\\c\n.UR https://www.gnu.org/software/bash/\nbash\n.UE \\c\n\\ (\\f[I]4.0 or newer\\f[R])\n.IP \\(bu 2\n\\c\n.UR https://www.gnu.org/software/coreutils/\nCoreutils\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://curl.se/\ncurl\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/tianocore/edk2\nEDK II\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.gnu.org/software/gawk/\ngawk\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.gnu.org/software/grep/\ngrep\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://gitlab.freedesktop.org/mesa/demos\nglxinfo\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://stedolan.github.io/jq/\njq\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://wiki.linuxfoundation.org/lsb/start\nLSB\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/pciutils/pciutils\npciutils\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://gitlab.com/procps-ng/procps\nprocps\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.python.org/\npython3\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR http://cdrtools.sourceforge.net/private/cdrecord.html\nmkisofs\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/gregkh/usbutils\nusbutils\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/karelzak/util-linux\nutil\\-linux\n.UE \\c\n; including \\f[CR]uuidgen\\f[R]\n.IP \\(bu 2\n\\c\n.UR https://www.gnu.org/software/sed/\nsed\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR http://www.dest-unreach.org/socat/\nsocat\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://gitlab.freedesktop.org/spice/spice-gtk\nspicy\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/stefanberger/swtpm\nswtpm\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.freedesktop.org/wiki/Software/xdg-user-dirs/\nxdg\\-user\\-dirs\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://gitlab.freedesktop.org/xorg/app/xrandr\nxrandr\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR http://zsync.moria.org.uk/\nzsync\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR http://www.info-zip.org/UnZip.html\nunzip\n.UE \\c\n.PP\nFor Ubuntu, Debian, Fedora, Arch and NixOS hosts the native packaging or\n\\c\n.UR https://launchpad.net/~flexiondotorg/+archive/ubuntu/quickemu\nppa\n.UE \\c\n, \\c\n.UR https://aur.archlinux.org/packages/quickemu\nAUR\n.UE \\c\n\\ or \\c\n.UR https://github.com/NixOS/nixpkgs/tree/master/pkgs/development/quickemu\nnix\n.UE \\c\n\\ packaging will take care of the dependencies.\nFor other host distributions or operating systems it will be necessary\nto install the above requirements or their equivalents.\n.PP\nThese examples may save a little typing:\n.SS Install requirements on Debian hosts\nThese should be handled by dependencies in Trixie and later.\nFor earlier versions (and their derivatives):\n.IP\n.EX\nsudo apt\\-get install bash coreutils curl genisoimage grep jq mesa\\-utils ovmf pciutils procps python3 qemu sed socat spice\\-client\\-gtk swtpm\\-tools unzip usbutils util\\-linux xdg\\-user\\-dirs xrandr zsync \n.EE\n.SS Install requirements on Fedora hosts\nThese are handled natively for Fedora 41 on.\nFor earlier versions:\n.IP\n.EX\nsudo dnf install bash coreutils curl edk2\\-tools genisoimage grep jq mesa\\-demos pciutils procps python3 qemu sed socat spice\\-gtk\\-tools swtpm unzip usbutils util\\-linux uuidgen\\-runtime xdg\\-user\\-dirs xrandr zsync\n.EE\n.SS Install requirements on Gentoo\nPlease note that you may have to use \\f[CR]sys\\-firmware/edk2\\-ovmf\\f[R]\ninstead of \\f[CR]sys\\-firmware/edk2\\-ovmf\\-bin\\f[R] \\- depending on how\nyour system is configured.\n.IP\n.EX\nsudo emerge \\-\\-ask \\-\\-noreplace app\\-emulation/qemu \\(rs\n app\\-shells/bash \\(rs\n sys\\-apps/coreutils \\(rs\n net\\-misc/curl \\(rs\n sys\\-firmware/edk2\\-ovmf\\-bin \\(rs\n sys\\-apps/gawk \\(rs\n sys\\-apps/grep \\(rs\n x11\\-apps/mesa\\-progs \\(rs\n app\\-misc/jq \\(rs\n sys\\-apps/pciutils \\(rs\n sys\\-process/procps \\(rs\n app\\-cdr/cdrtools \\(rs\n sys\\-apps/usbutils \\(rs\n sys\\-apps/util\\-linux \\(rs\n sys\\-apps/sed \\(rs\n net\\-misc/socat \\(rs\n app\\-emulation/spice \\(rs\n app\\-crypt/swtpm \\(rs\n x11\\-misc/xdg\\-user\\-dirs \\(rs\n x11\\-apps/xrandr \\(rs\n net\\-misc/zsync \\(rs\n app\\-arch/unzip\n.EE\n.SS Install requirements on macOS hosts\nInstall the Quickemu requirements using brew:\n.IP\n.EX\nbrew install bash cdrtools coreutils jq python3 qemu usbutils samba socat swtpm zsync\n.EE\n.PP\nNow clone the project:\n.IP\n.EX\ngit clone https://github.com/quickemu\\-project/quickemu\ncd quickemu\n.EE\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/07-Alternative-frontends\nAlternative Frontends\n.UE \\c\n.SS Quickgui\nWhile \\f[CR]quickemu\\f[R] and \\f[CR]quickget\\f[R] are designed for the\nterminal, a graphical user interface is also available:\n.IP \\(bu 2\n\\f[B]\\c\n.UR https://github.com/quickemu-project/quickgui\nQuickgui\n.UE \\c\n\\f[R] by \\c\n.UR https://github.com/marxjohnson\nMark Johnson\n.UE \\c\n\\ and \\c\n.UR https://github.com/ymauray\nYannick Mauray\n.UE \\c\n\\&.\n.PP\nTo install Quickgui on Ubuntu:\n.IP\n.EX\nsudo add\\-apt\\-repository ppa:yannick\\-mauray/quickgui\nsudo apt update\nsudo apt install quickgui\n.EE\n.PP\nMany thanks to \\c\n.UR https://github.com/Lukewh\nLuke Wesley\\-Holley\n.UE \\c\n\\ and \\c\n.UR https://github.com/daPhipz\nPhilipp Kiemle\n.UE \\c\n\\ for creating the \\f[B]\\c\n.UR https://github.com/Lukewh/quickemu-icons\nQuickemu icons\n.UE \\c\n\\f[R] 🎨\n.SS Creating Linux guests 🐧\n.SS Ubuntu\n\\f[CR]quickget\\f[R] will automatically download an Ubuntu release and\ncreate the virtual machine configuration.\n.IP\n.EX\nquickget ubuntu 22.04\nquickemu \\-\\-vm ubuntu\\-22.04.conf\n.EE\n.IP \\(bu 2\nComplete the installation as normal.\n.IP \\(bu 2\nPost\\-install:\n.RS 2\n.IP \\(bu 2\nInstall the SPICE agent (\\f[CR]spice\\-vdagent\\f[R]) in the guest to\nenable copy/paste and USB redirection\n.RS 2\n.IP \\(bu 2\n\\f[CR]sudo apt install spice\\-vdagent\\f[R]\n.RE\n.IP \\(bu 2\nInstall the SPICE WebDAV agent (\\f[CR]spice\\-webdavd\\f[R]) in the guest\nto enable file sharing.\n.RS 2\n.IP \\(bu 2\n\\f[CR]sudo apt install spice\\-webdavd\\f[R]\n.RE\n.RE\n.SS Ubuntu daily\\-live images\n\\f[CR]quickget\\f[R] can also download/refresh daily\\-live images via\n\\f[CR]zsync\\f[R] for Ubuntu developers and testers.\n.IP\n.EX\nquickget ubuntu daily\\-live\nquickemu \\-\\-vm ubuntu\\-daily\\-live.conf\n.EE\n.PP\nYou can run \\f[CR]quickget ubuntu daily\\-live\\f[R] to refresh your daily\ndevelopment image as often as you like, it will even automatically\nswitch to a new series.\n.SS Ubuntu Flavours\nAll the official Ubuntu flavours are supported, just replace\n\\f[CR]ubuntu\\f[R] with your preferred flavour.\n.PP\nThe project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines\nwiki\n.UE \\c\n\\ may have further information.\n.IP \\(bu 2\n\\f[CR]edubuntu\\f[R] (Edubuntu)\n.IP \\(bu 2\n\\f[CR]kubuntu\\f[R] (Kubuntu)\n.IP \\(bu 2\n\\f[CR]lubuntu\\f[R] (Lubuntu)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-budgie\\f[R] (Ubuntu Budgie)\n.IP \\(bu 2\n\\f[CR]ubuntucinnamon\\f[R] (Ubuntu Cinnamon)\n.IP \\(bu 2\n\\f[CR]ubuntukylin\\f[R] (Ubuntu Kylin)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-mate\\f[R] (Ubuntu MATE)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-server\\f[R] (Ubuntu Server)\n.IP \\(bu 2\n\\f[CR]ubuntustudio\\f[R] (Ubuntu Studio)\n.IP \\(bu 2\n\\f[CR]ubuntu\\f[R] (Ubuntu)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-unity\\f[R] (Ubuntu Unity)\n.IP \\(bu 2\n\\f[CR]xubuntu\\f[R] (Xubuntu)\n.PP\nYou can also use \\f[CR]quickget\\f[R] with advanced options :\n.IP\n.EX\n   \\-\\-arch           <arch>                    : Set architecture (arm64, aarch64, amd64, x86_64)\n   \\-\\-download       <os> <release> [edition]  : Download image; no VM configuration\n   \\-\\-create\\-config  <os> [path/url] [flags]   : Create VM config for an OS image\n   \\-\\-open\\-homepage  <os>                      : Open homepage for the OS\n   \\-\\-show           [os]                      : Show OS information\n   \\-\\-version                                  : Show version\n   \\-\\-help                                     : Show this help message\n\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\- Flags \\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\n\\-\\-create\\-config:\n  \\-\\-disable\\-unattended                        : Force quickget not to set up an unattended installation\n\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\- For testing & development \\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\n   \\-\\-url            [os] [release] [edition]  : Show image URL(s)\n   \\-\\-check          [os] [release] [edition]  : Check image URL(s)\n   \\-\\-check\\-all\\-arch [os] [release] [edition]  : Check downloads for all architectures (amd64 and arm64)\n   \\-\\-list                                     : List all supported systems\n   \\-\\-list\\-csv                                 : List everything in csv format\n   \\-\\-list\\-json                                : List everything in json format\n.EE\n.PP\nHere are some typical uses\n.IP\n.EX\n    # show an OS ISO download URL for {os} {release} [edition]\n    quickget \\-\\-url fedora 38 Silverblue\n    # test if an OS ISO is available for {os} {release} [edition]\n    quickget \\-\\-check nixos unstable plasma5\n    # open an OS distribution homepage in a browser\n    quickget \\-\\-open\\-homepage  ubuntu\\-mate\n    # Only download image file into current directory, without creating VM\n    quickget \\-\\-download elementary 7.1\n.EE\n.PP\nThe \\f[CR]\\-\\-url\\f[R], \\f[CR]\\-\\-check\\f[R], and\n\\f[CR]\\-\\-download\\f[R] options are fully functional for all operating\nsystems, including Windows and macOS.\n.PP\nFurther information is available from the project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features\nwiki\n.UE \\c\n.SS Other Operating Systems\n\\f[CR]quickget\\f[R] also supports:\n.IP \\(bu 2\n\\f[CR]alma\\f[R] (AlmaLinux)\n.IP \\(bu 2\n\\f[CR]alpine\\f[R] (Alpine Linux)\n.IP \\(bu 2\n\\f[CR]android\\f[R] (Android x86)\n.IP \\(bu 2\n\\f[CR]antix\\f[R] (Antix)\n.IP \\(bu 2\n\\f[CR]archcraft\\f[R] (Archcraft)\n.IP \\(bu 2\n\\f[CR]archlinux\\f[R] (Arch Linux)\n.IP \\(bu 2\n\\f[CR]artixlinux\\f[R] (Artix Linux)\n.IP \\(bu 2\n\\f[CR]azurelinux\\f[R] (Azure Linux)\n.IP \\(bu 2\n\\f[CR]batocera\\f[R] (Batocera)\n.IP \\(bu 2\n\\f[CR]bazzite\\f[R] (Bazzite)\n.IP \\(bu 2\n\\f[CR]biglinux\\f[R] (BigLinux)\n.IP \\(bu 2\n\\f[CR]blendos\\f[R] (BlendOS)\n.IP \\(bu 2\n\\f[CR]bodhi\\f[R] (Bodhi)\n.IP \\(bu 2\n\\f[CR]bunsenlabs\\f[R] (BunsenLabs)\n.IP \\(bu 2\n\\f[CR]cachyos\\f[R] (CachyOS)\n.IP \\(bu 2\n\\f[CR]centos\\-stream\\f[R] (CentOS Stream)\n.IP \\(bu 2\n\\f[CR]chimeralinux\\f[R] (Chimera Linux)\n.IP \\(bu 2\n\\f[CR]crunchbang++\\f[R] (Crunchbangplusplus)\n.IP \\(bu 2\n\\f[CR]debian\\f[R] (Debian)\n.IP \\(bu 2\n\\f[CR]deepin\\f[R] (Deepin)\n.IP \\(bu 2\n\\f[CR]devuan\\f[R] (Devuan)\n.IP \\(bu 2\n\\f[CR]dragonflybsd\\f[R] (DragonFlyBSD)\n.IP \\(bu 2\n\\f[CR]easyos\\f[R] (EasyOS)\n.IP \\(bu 2\n\\f[CR]elementary\\f[R] (elementary OS)\n.IP \\(bu 2\n\\f[CR]endeavouros\\f[R] (EndeavourOS)\n.IP \\(bu 2\n\\f[CR]endless\\f[R] (Endless OS)\n.IP \\(bu 2\n\\f[CR]fedora\\f[R] (Fedora)\n.IP \\(bu 2\n\\f[CR]freebsd\\f[R] (FreeBSD)\n.IP \\(bu 2\n\\f[CR]freedos\\f[R] (FreeDOS)\n.IP \\(bu 2\n\\f[CR]garuda\\f[R] (Garuda Linux)\n.IP \\(bu 2\n\\f[CR]gentoo\\f[R] (Gentoo)\n.IP \\(bu 2\n\\f[CR]ghostbsd\\f[R] (GhostBSD)\n.IP \\(bu 2\n\\f[CR]gnomeos\\f[R] (GNOME OS)\n.IP \\(bu 2\n\\f[CR]guix\\f[R] (Guix)\n.IP \\(bu 2\n\\f[CR]haiku\\f[R] (Haiku)\n.IP \\(bu 2\n\\f[CR]kali\\f[R] (Kali)\n.IP \\(bu 2\n\\f[CR]kdeneon\\f[R] (KDE Neon)\n.IP \\(bu 2\n\\f[CR]kolibrios\\f[R] (KolibriOS)\n.IP \\(bu 2\n\\f[CR]linuxlite\\f[R] (Linux Lite)\n.IP \\(bu 2\n\\f[CR]linuxmint\\f[R] (Linux Mint)\n.IP \\(bu 2\n\\f[CR]lmde\\f[R] (Linux Mint Debian Edition)\n.IP \\(bu 2\n\\f[CR]maboxlinux\\f[R] (Mabox Linux)\n.IP \\(bu 2\n\\f[CR]mageia\\f[R] (Mageia)\n.IP \\(bu 2\n\\f[CR]manjaro\\f[R] (Manjaro)\n.IP \\(bu 2\n\\f[CR]mxlinux\\f[R] (MX Linux)\n.IP \\(bu 2\n\\f[CR]netboot\\f[R] (netboot.xyz)\n.IP \\(bu 2\n\\f[CR]netbsd\\f[R] (NetBSD)\n.IP \\(bu 2\n\\f[CR]nitrux\\f[R] (Nitrux)\n.IP \\(bu 2\n\\f[CR]nixos\\f[R] (NixOS)\n.IP \\(bu 2\n\\f[CR]nwg\\-shell\\f[R] (nwg\\-shell)\n.IP \\(bu 2\n\\f[CR]openbsd\\f[R] (OpenBSD)\n.IP \\(bu 2\n\\f[CR]openindiana\\f[R] (OpenIndiana)\n.IP \\(bu 2\n\\f[CR]opensuse\\f[R] (openSUSE)\n.IP \\(bu 2\n\\f[CR]oraclelinux\\f[R] (Oracle Linux)\n.IP \\(bu 2\n\\f[CR]parrotsec\\f[R] (Parrot Security)\n.IP \\(bu 2\n\\f[CR]pclinuxos\\f[R] (PCLinuxOS)\n.IP \\(bu 2\n\\f[CR]peppermint\\f[R] (PeppermintOS)\n.IP \\(bu 2\n\\f[CR]popos\\f[R] (Pop!_OS)\n.IP \\(bu 2\n\\f[CR]porteus\\f[R] (Porteus)\n.IP \\(bu 2\n\\f[CR]primtux\\f[R] (PrimTux)\n.IP \\(bu 2\n\\f[CR]proxmox\\-ve\\f[R] (Proxmox VE)\n.IP \\(bu 2\n\\f[CR]pureos\\f[R] (PureOS)\n.IP \\(bu 2\n\\f[CR]reactos\\f[R] (ReactOS)\n.IP \\(bu 2\n\\f[CR]rebornos\\f[R] (RebornOS)\n.IP \\(bu 2\n\\f[CR]rockylinux\\f[R] (Rocky Linux)\n.IP \\(bu 2\n\\f[CR]siduction\\f[R] (Siduction)\n.IP \\(bu 2\n\\f[CR]slackware\\f[R] (Slackware)\n.IP \\(bu 2\n\\f[CR]slax\\f[R] (Slax)\n.IP \\(bu 2\n\\f[CR]slint\\f[R] (Slint)\n.IP \\(bu 2\n\\f[CR]slitaz\\f[R] (SliTaz)\n.IP \\(bu 2\n\\f[CR]solus\\f[R] (Solus)\n.IP \\(bu 2\n\\f[CR]sparkylinux\\f[R] (SparkyLinux)\n.IP \\(bu 2\n\\f[CR]spirallinux\\f[R] (SpiralLinux)\n.IP \\(bu 2\n\\f[CR]tails\\f[R] (Tails)\n.IP \\(bu 2\n\\f[CR]tinycore\\f[R] (Tiny Core Linux)\n.IP \\(bu 2\n\\f[CR]trisquel\\f[R] (Trisquel)\n.IP \\(bu 2\n\\f[CR]tuxedo\\-os\\f[R] (Tuxedo OS)\n.IP \\(bu 2\n\\f[CR]vanillaos\\f[R] (Vanilla OS)\n.IP \\(bu 2\n\\f[CR]void\\f[R] (Void Linux)\n.IP \\(bu 2\n\\f[CR]zorin\\f[R] (Zorin OS)\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines#manually-create-linux-guests\nCustom Linux guests\n.UE \\c\nOr you can download a Linux image and manually create a VM\nconfiguration.\n.IP \\(bu 2\nDownload a .iso image of a Linux distribution\n.IP \\(bu 2\nCreate a VM configuration file; for example\n\\f[CR]debian\\-bullseye.conf\\f[R]\n.IP\n.EX\nguest_os=\\(dqlinux\\(dq\ndisk_img=\\(dqdebian\\-bullseye/disk.qcow2\\(dq\niso=\\(dqdebian\\-bullseye/firmware\\-11.0.0\\-amd64\\-DVD\\-1.iso\\(dq\n.EE\n.IP \\(bu 2\nUse \\f[CR]quickemu\\f[R] to start the virtual machine:\n.IP\n.EX\nquickemu \\-\\-vm debian\\-bullseye.conf\n.EE\n.IP \\(bu 2\nComplete the installation as normal.\n.IP \\(bu 2\nPost\\-install:\n.RS 2\n.IP \\(bu 2\nInstall the SPICE agent (\\f[CR]spice\\-vdagent\\f[R]) in the guest to\nenable copy/paste and USB redirection.\n.IP \\(bu 2\nInstall the SPICE WebDAV agent (\\f[CR]spice\\-webdavd\\f[R]) in the guest\nto enable file sharing.\n.RE\n.SS Supporting old Linux distros\nIf you want to run an old Linux , from 2016 or earlier, change the\n\\f[CR]guest_os\\f[R] to \\f[CR]linux_old\\f[R].\nThis will enable the \\f[CR]vmware\\-svga\\f[R] graphics driver which is\nbetter supported on older distros.\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests\nCreating macOS Guests\n.UE \\c\n\\ 🍏\n\\f[B]Installing macOS in a VM can be a bit finicky, if you encounter\nproblems, \\c\n.UR https://github.com/quickemu-project/quickemu/discussions\ncheck the Discussions\n.UE \\c\n\\ for solutions or ask for help there\\f[R] 🛟\n.PP\n\\f[CR]quickget\\f[R] automatically downloads a macOS recovery image and\ncreates a virtual machine configuration.\n.PP\nNote: Some VPN users may need to \\c\n.UR https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-3506845235\nturn off their VPN\n.UE \\c\n\\ in order to download a recovery image.\nSome other users may find \\c\n.UR https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-2429146013\nusing a VPN\n.UE \\c\n\\ necessary in order to download a recovery image.\n.IP\n.EX\nquickget macos big\\-sur\nquickemu \\-\\-vm macos\\-big\\-sur.conf\n.EE\n.PP\nmacOS \\f[CR]mojave\\f[R], \\f[CR]catalina\\f[R], \\f[CR]big\\-sur\\f[R],\n\\f[CR]monterey\\f[R], \\f[CR]ventura\\f[R] and \\f[CR]sonoma\\f[R] are\nsupported.\n.IP \\(bu 2\nUse cursor keys and enter key to select the \\f[B]macOS Base System\\f[R]\n.IP \\(bu 2\nFrom \\f[B]macOS Utilities\\f[R]\n.RS 2\n.IP \\(bu 2\nClick \\f[B]Disk Utility\\f[R] and \\f[B]Continue\\f[R]\n.RS 2\n.IP \\(bu 2\nSelect \\f[CR]QEMU HARDDISK Media\\f[R] (\\(ti103.08GB) from the list (on\nBig Sur and above use \\f[CR]Apple Inc. VirtIO Block Device\\f[R]) and\nclick \\f[B]Erase\\f[R].\n.IP \\(bu 2\nEnter a \\f[CR]Name:\\f[R] for the disk\n.IP \\(bu 2\nIf you are installing macOS Mojave or later (Catalina, Big Sur,\nMonterey, Ventura and Sonoma), choose any of the APFS options as the\nfilesystem.\nMacOS Extended may not work.\n.RE\n.IP \\(bu 2\nClick \\f[B]Erase\\f[R].\n.IP \\(bu 2\nClick \\f[B]Done\\f[R].\n.IP \\(bu 2\nClose Disk Utility\n.RE\n.IP \\(bu 2\nFrom \\f[B]macOS Utilities\\f[R]\n.RS 2\n.IP \\(bu 2\nClick \\f[B]Reinstall macOS\\f[R] and \\f[B]Continue\\f[R]\n.RE\n.IP \\(bu 2\nComplete the installation as you normally would.\n.RS 2\n.IP \\(bu 2\nOn the first reboot use cursor keys and enter key to select \\f[B]macOS\nInstaller\\f[R]\n.IP \\(bu 2\nOn the subsequent reboots use cursor keys and enter key to select the\ndisk you named\n.RE\n.IP \\(bu 2\nOnce you have finished installing macOS you will be presented with an\nthe out\\-of\\-the\\-box first\\-start wizard to configure various options\nand set up your username and password\n.IP \\(bu 2\nOPTIONAL: After you have concluded the out\\-of\\-the\\-box wizard, you may\nwant to enable the TRIM feature that the computer industry created for\nSSD disks.\nThis feature in our macOS installation will allow QuickEmu to compact\n(shrink) your macOS disk image whenever you delete files inside the\nVirtual Machine.\nWithout this step your macOS disk image will only ever get larger and\nwill not shrink even when you delete lots of data inside macOS.\n.RS 2\n.IP \\(bu 2\nTo enable TRIM, open the Terminal application and type the following\ncommand followed by pressing enter to tell macos to use the TRIM command\non the hard disk when files are deleted:\n.RE\n.IP\n.EX\nsudo trimforce enable\n.EE\n.PP\nYou will be prompted to enter your account\\(aqs password to gain the\nprivilege needed.\nOnce you\\(aqve entered your password and pressed enter the command will\nrequest confirmation in the form of two questions that require you to\ntype y (for a \\(dqyes\\(dq response) followed by enter to confirm.\n.PP\nIf you press enter without first typing y the system will consider that\na negative response as though you said \\(dqno\\(dq:\n.IP\n.EX\nIMPORTANT NOTICE: This tool force\\-enables TRIM for all relevant attached devices, even though such devices may not have been validated for data integrity while using TRIM. Use of this tool to enable TRIM may result in unintended data loss or data corruption. It should not be used in a commercial operating environment or with important data. Before using this tool, you should back up all of your data and regularly back up data while TRIM is enabled. This tool is provided on an \\(dqas is\\(dq basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON\\-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS TOOL OR ITS USE ALONE OR IN COMBINATION WITH YOUR DEVICES, SYSTEMS, OR SERVICES. BY USING THIS TOOL TO ENABLE TRIM, YOU AGREE THAT, TO THE EXTENT PERMITTED BY APPLICABLE LAW, USE OF THE TOOL IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU.\nAre you sure you with to proceed (y/N)?\n.EE\n.PP\nAnd a second confirmation once you\\(aqve confirmed the previous one:\n.IP\n.EX\nYour system will immediately reboot when this is complete.\nIs this OK (y/N)?\n.EE\n.PP\nAs the last message states, your system will automatically reboot as\nsoon as the command completes.\n.PP\nThe default macOS configuration looks like this:\n.IP\n.EX\nguest_os=\\(dqmacos\\(dq\nimg=\\(dqmacos\\-big\\-sur/RecoveryImage.img\\(dq\ndisk_img=\\(dqmacos\\-big\\-sur/disk.qcow2\\(dq\nmacos_release=\\(dqbig\\-sur\\(dq\n.EE\n.IP \\(bu 2\n\\f[CR]guest_os=\\(dqmacos\\(dq\\f[R] instructs Quickemu to optimise for\nmacOS.\n.IP \\(bu 2\n\\f[CR]macos_release=\\(dqbig\\-sur\\(dq\\f[R] instructs Quickemu to optimise\nfor a particular macOS release.\n.RS 2\n.IP \\(bu 2\nFor example VirtIO Network and Memory Ballooning are available in Big\nSur and newer, but not previous releases.\n.IP \\(bu 2\nAnd VirtIO Block Media (disks) are supported/stable in Catalina and\nnewer.\n.RE\n.SH macOS compatibility\nThere are some considerations when running macOS via Quickemu.\n.IP \\(bu 2\nSupported macOS releases:\n.RS 2\n.IP \\(bu 2\nMojave\n.IP \\(bu 2\nCatalina\n.IP \\(bu 2\nBig Sur\n.IP \\(bu 2\nMonterey\n.IP \\(bu 2\nVentura\n.IP \\(bu 2\nSonoma\n.RE\n.IP \\(bu 2\n\\f[CR]quickemu\\f[R] will automatically download the required \\c\n.UR https://github.com/acidanthera/OpenCorePkg\nOpenCore\n.UE \\c\n\\ bootloader and OVMF firmware from \\c\n.UR https://github.com/kholia/OSX-KVM\nOSX\\-KVM\n.UE \\c\n\\&.\n.IP \\(bu 2\nOptimised by default, but no GPU acceleration is available.\n.RS 2\n.IP \\(bu 2\nHost CPU vendor is detected and guest CPU is optimised accordingly.\n.IP \\(bu 2\n\\c\n.UR https://www.kraxel.org/blog/2019/06/macos-qemu-guest/\nVirtIO Block Media\n.UE \\c\n\\ is used for the system disk where supported.\n.IP \\(bu 2\n\\c\n.UR http://philjordan.eu/osx-virt/\nVirtIO \\f[CR]usb\\-tablet\\f[R]\n.UE \\c\n\\ is used for the mouse.\n.IP \\(bu 2\nVirtIO Network (\\f[CR]virtio\\-net\\f[R]) is supported and enabled on\nmacOS Big Sur and newer, but earlier releases use \\f[CR]vmxnet3\\f[R].\n.IP \\(bu 2\nVirtIO Memory Ballooning is supported and enabled on macOS Big Sur and\nnewer but disabled for other support macOS releases.\n.RE\n.IP \\(bu 2\nUSB host and SPICE pass\\-through is:\n.RS 2\n.IP \\(bu 2\nUHCI (USB 2.0) on macOS Catalina and earlier.\n.IP \\(bu 2\nXHCI (USB 3.0) on macOS Big Sur and newer.\n.RE\n.IP \\(bu 2\nDisplay resolution can be changed via \\f[CR]quickemu\\f[R] using\n\\f[CR]\\-\\-width\\f[R] and \\f[CR]\\-\\-height\\f[R] command line arguments.\n.IP \\(bu 2\n\\f[B]Full Duplex audio requires \\c\n.UR https://github.com/chris1111/VoodooHDA-OC\nVoodooHDA OC\n.UE \\c\n\\ or pass\\-through a USB audio\\-device to the macOS guest VM\\f[R].\n.RS 2\n.IP \\(bu 2\nNOTE!\n\\c\n.UR https://disable-gatekeeper.github.io/\nGatekeeper\n.UE \\c\n\\ and \\c\n.UR https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection\nSystem Integrity Protection (SIP)\n.UE \\c\n\\ need to be disabled to install VoodooHDA OC\n.RE\n.IP \\(bu 2\nFile sharing between guest and host is available via \\c\n.UR https://wiki.qemu.org/Documentation/9psetup\nvirtio\\-9p\n.UE \\c\n\\ and \\c\n.UR https://gitlab.gnome.org/GNOME/phodav/-/merge_requests/24\nSPICE webdavd\n.UE \\c\n\\&.\n.IP \\(bu 2\nCopy/paste via SPICE agent is \\f[B]not available on macOS\\f[R].\n.SH macOS App Store\nIf you see \\f[I]\\(dqYour device or computer could not be\nverified\\(dq\\f[R] when you try to login to the App Store, make sure that\nyour wired ethernet device is \\f[CR]en0\\f[R].\nUse \\f[CR]ifconfig\\f[R] in a terminal to verify this.\n.PP\nIf the wired ethernet device is not \\f[CR]en0\\f[R], then then go to\n\\f[I]System Preferences\\f[R] \\-> \\f[I]Network\\f[R], delete all the\nnetwork devices and apply the changes.\nNext, open a terminal and run the following:\n.IP\n.EX\nsudo rm /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist\n.EE\n.PP\nNow reboot, and the App Store should work.\n.PP\nThere may be further advice and information about macOS guests in the\nproject \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests\nwiki\n.UE \\c\n\\&.\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines\nCreating Windows guests\n.UE \\c\n\\ 🪟\n\\f[CR]quickget\\f[R] can download \\c\n.UR https://www.microsoft.com/software-download/windows10\n\\f[B]Windows 10\\f[R]\n.UE \\c\n\\ and \\c\n.UR https://www.microsoft.com/software-download/windows11\n\\f[B]Windows 11\\f[R]\n.UE \\c\n\\ automatically and create an optimised virtual machine configuration.\nThis configuration also includes the \\c\n.UR https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/\nVirtIO drivers for Windows\n.UE \\c\n\\&.\n.PP\n\\f[B]Windows 8.1\\f[R] is also supported but doesn\\(aqt feature any\nautomated installation or driver optimisation.\n.PP\n\\f[CR]quickget\\f[R] can also download \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise\nWindows 10 LTSC\n.UE \\c\n\\ and Windows Server \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2\n2012\\-r2\n.UE \\c\n, \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016\n2016\n.UE \\c\n, \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019\n2019\n.UE \\c\n, and \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022\n2022\n.UE \\c\n\\&.\nNo automated installation is supported for these releases.\n.IP\n.EX\nquickget windows 11\nquickemu \\-\\-vm windows\\-11.conf\n.EE\n.IP \\(bu 2\nComplete the installation as you normally would.\n.IP \\(bu 2\nAll relevant drivers and services should be installed automatically.\n.IP \\(bu 2\nA local administrator user account is automatically created, with these\ncredentials:\n.RS 2\n.IP \\(bu 2\nUsername: \\f[CR]Quickemu\\f[R]\n.IP \\(bu 2\nPassword: \\f[CR]quickemu\\f[R]\n.RE\n.PP\nFurther information is available from the project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines\nwiki\n.UE \\c\n.SS Configuration\nHere are the usage instructions:\n.IP\n.EX\nUsage\n  quickemu \\-\\-vm ubuntu.conf <arguments>\n\nArguments\n  \\-\\-access                          : Enable remote spice access support. \\(aqlocal\\(aq (default), \\(aqremote\\(aq, \\(aqclientipaddress\\(aq\n  \\-\\-braille                         : Enable braille support. Requires SDL.\n  \\-\\-cpu\\-pinning                     : Choose which host cores correspond to which guest cores.\n  \\-\\-delete\\-disk                     : Delete the disk image and EFI variables\n  \\-\\-delete\\-vm                       : Delete the entire VM and its configuration\n  \\-\\-display                         : Select display backend. \\(aqgtk\\(aq (default), \\(aqsdl\\(aq, \\(aqcocoa\\(aq, \\(aqnone\\(aq, \\(aqspice\\(aq or \\(aqspice\\-app\\(aq\n  \\-\\-fullscreen                      : Starts VM in full screen mode (Ctl+Alt+f to exit)\n  \\-\\-ignore\\-msrs\\-always              : Configure KVM to always ignore unhandled machine\\-specific registers\n  \\-\\-ignore\\-tsc\\-warning              : Skip TSC stability warning for macOS VMs on AMD\n  \\-\\-kill                            : Kill the VM process if it is running\n  \\-\\-offline                         : Override all network settings and start the VM offline\n  \\-\\-shortcut                        : Create a desktop shortcut\n  \\-\\-snapshot apply <tag>            : Apply/restore a snapshot.\n  \\-\\-snapshot create <tag>           : Create a snapshot.\n  \\-\\-snapshot delete <tag>           : Delete a snapshot.\n  \\-\\-snapshot info                   : Show disk/snapshot info.\n  \\-\\-status\\-quo                      : Do not commit any changes to disk/snapshot.\n  \\-\\-viewer <viewer>                 : Choose an alternative viewer. \\(atOptions: \\(aqspicy\\(aq (default), \\(aqremote\\-viewer\\(aq, \\(aqnone\\(aq\n  \\-\\-width <width>                   : Set VM screen width; requires \\(aq\\-\\-height\\(aq\n  \\-\\-height <height>                 : Set VM screen height; requires \\(aq\\-\\-width\\(aq\n  \\-\\-ssh\\-port <port>                 : Set SSH port manually\n  \\-\\-spice\\-port <port>               : Set SPICE port manually\n  \\-\\-public\\-dir <path>               : Expose share directory. \\(atOptions: \\(aq\\(aq (default: xdg\\-user\\-dir PUBLICSHARE), \\(aq<directory>\\(aq, \\(aqnone\\(aq\n  \\-\\-monitor <type>                  : Set monitor connection type. \\(atOptions: \\(aqsocket\\(aq (default), \\(aqtelnet\\(aq, \\(aqnone\\(aq\n  \\-\\-monitor\\-telnet\\-host <ip/host>   : Set telnet host for monitor. (default: \\(aqlocalhost\\(aq)\n  \\-\\-monitor\\-telnet\\-port <port>      : Set telnet port for monitor. (default: \\(aq4440\\(aq)\n  \\-\\-monitor\\-cmd <cmd>               : Send command to monitor if available. (Example: system_powerdown)\n  \\-\\-serial <type>                   : Set serial connection type. \\(atOptions: \\(aqsocket\\(aq (default), \\(aqtelnet\\(aq, \\(aqnone\\(aq\n  \\-\\-serial\\-telnet\\-host <ip/host>    : Set telnet host for serial. (default: \\(aqlocalhost\\(aq)\n  \\-\\-serial\\-telnet\\-port <port>       : Set telnet port for serial. (default: \\(aq6660\\(aq)\n  \\-\\-keyboard <type>                 : Set keyboard. \\(atOptions: \\(aqusb\\(aq (default), \\(aqps2\\(aq, \\(aqvirtio\\(aq\n  \\-\\-keyboard_layout <layout>        : Set keyboard layout: \\(aqen\\-us\\(aq (default)\n  \\-\\-mouse <type>                    : Set mouse. \\(atOptions: \\(aqtablet\\(aq (default), \\(aqps2\\(aq, \\(aqusb\\(aq, \\(aqvirtio\\(aq\n  \\-\\-usb\\-controller <type>           : Set usb\\-controller. \\(atOptions: \\(aqehci\\(aq (default), \\(aqxhci\\(aq, \\(aqnone\\(aq\n  \\-\\-sound\\-card <type>               : Set sound card. \\(atOptions: \\(aqintel\\-hda\\(aq (default), \\(aqac97\\(aq, \\(aqes1370\\(aq, \\(aqsb16\\(aq, \\(aqusb\\-audio\\(aq, \\(aqvirtio\\-sound\\-pci\\(aq, \\(aqnone\\(aq\n  \\-\\-sound\\-duplex <type>             : Set sound card duplex. \\(atOptions: \\(aqhda\\-micro\\(aq (default: speaker/mic), \\(aqhda\\-duplex\\(aq (line\\-in/line\\-out), \\(aqhda\\-output\\(aq (output\\-only)\n  \\-\\-extra_args <arguments>          : Pass additional arguments to qemu\n  \\-\\-version                         : Print version\n.EE\n.SS Desktop shortcuts\nDesktop shortcuts can be created for a VM, the shortcuts are saved in\n\\f[CR]\\(ti/.local/share/applications\\f[R].\nHere is an example of how to create a shortcut.\n.IP\n.EX\nquickemu \\-\\-vm ubuntu\\-22.04\\-desktop.conf \\-\\-shortcut\n.EE\n.SS References\nUseful reference that assisted the development of Quickemu.\n.IP \\(bu 2\nGeneral\n.RS 2\n.IP \\(bu 2\n\\c\n.UR https://qemu.readthedocs.io/en/latest/\nQEMU\\(aqs documentation!\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://pve.proxmox.com/wiki/Qemu/KVM_Virtual_Machines\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.kraxel.org/blog/2020/01/qemu-sound-audiodev/\n.UE \\c\n.RE\n.IP \\(bu 2\nmacOS\n.RS 2\n.IP \\(bu 2\n\\c\n.UR https://www.nicksherlock.com/2020/06/installing-macos-big-sur-on-proxmox/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://passthroughpo.st/mac-os-adds-early-support-for-virtio-qemu/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/kholia/OSX-KVM\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/thenickdude/KVM-Opencore\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/acidanthera/OpenCorePkg/tree/master/Utilities/macrecovery\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.kraxel.org/blog/2017/09/running-macos-as-guest-in-kvm/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.nicksherlock.com/2017/10/passthrough-of-advanced-cpu-features-for-macos-high-sierra-guests/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR http://philjordan.eu/osx-virt/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/Dids/clover-builder\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://mackie100projects.altervista.org\nOpenCore Configurator\n.UE \\c\n.RE\n.IP \\(bu 2\nWindows\n.RS 2\n.IP \\(bu 2\n\\c\n.UR https://www.heiko-sieger.info/running-windows-10-on-linux-using-kvm-with-vga-passthrough/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://leduccc.medium.com/improving-the-performance-of-a-windows-10-guest-on-qemu-a5b3f54d9cf5\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://frontpagelinux.com/tutorials/how-to-use-linux-kvm-to-optimize-your-windows-10-virtual-machine/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://turlucode.com/qemu-command-line-args/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://github.com/pbatard/Fido\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.catapultsystems.com/blogs/create-zero-touch-windows-10-iso/\n.UE \\c\n.RE\n.IP \\(bu 2\nTPM\n.RS 2\n.IP \\(bu 2\n\\c\n.UR https://qemu-project.gitlab.io/qemu/specs/tpm.html\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.tecklyfe.com/how-to-create-a-windows-11-virtual-machine-in-qemu/\n.UE \\c\n.RE\n.IP \\(bu 2\n9p & virtiofs\n.RS 2\n.IP \\(bu 2\n\\c\n.UR https://wiki.qemu.org/Documentation/9p\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://wiki.qemu.org/Documentation/9psetup\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://www.kraxel.org/blog/2019/06/macos-qemu-guest/\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://superuser.com/questions/628169/how-to-share-a-directory-with-the-host-without-networking-in-qemu\n.UE \\c\n.IP \\(bu 2\n\\c\n.UR https://virtio-fs.gitlab.io/\n.UE \\c\n.RE\n.SH AUTHORS\nWritten by Martin Wimpress.\n.SH BUGS\nSubmit bug reports online at: \\c\n.UR https://github.com/quickemu-project/quickemu/issues\n.UE \\c\n.SH SEE ALSO\nFull sources at: \\c\n.UR https://github.com/quickemu-project/quickemu\n.UE \\c\n.PP\nquickemu_conf(5), quickget(1), quickgui(1)\n.SH AUTHORS\nMartin Wimpress.\n"
  },
  {
    "path": "docs/quickemu.1.md",
    "content": "---\nauthor: Martin Wimpress\ndate: February 2, 2026\nfooter: quickemu\nheader: Quickemu User Manual\nsection: 1\ntitle: QUICKEMU\n---\n\n# NAME\n\nquickemu - A quick VM builder and manager\n\n# SYNOPSIS\n\n**quickemu** \\[*OPTION*\\]...\n\n# DESCRIPTION\n\n**quickemu** will create and run highly optimised desktop virtual\nmachines for Linux, macOS and Windows\n\n# OPTIONS\n\n**--vm**\n:   vm configuration file\n\nYou can also pass optional parameters\n\n**--access**\n:   Enable remote spice access support. 'local' (default), 'remote',\n    'clientipaddress'\n\n**--braille**\n:   Enable braille support. Requires SDL.\n\n**--delete-disk**\n:   Delete the disk image and EFI variables\n\n**--delete-vm**\n:   Delete the entire VM and its configuration\n\n**--display**\n:   Select display backend. 'gtk' (default), 'sdl', 'cocoa', 'none',\n    'spice' or 'spice-app'\n\n**--fullscreen**\n:   Starts VM in full screen mode (Ctl+Alt+f to exit)\n\n**--ignore-msrs-always**\n:   Configure KVM to always ignore unhandled machine-specific registers\n\n**--kill**\n:   Kill the VM process if it is running\n\n**--offline**\n:   Override all network settings and start the VM offline\n\n**--shortcut**\n:   Create a desktop shortcut\n\n**--snapshot apply \\<tag\\>**\n:   Apply/restore a snapshot.\n\n**--snapshot create \\<tag\\>**\n:   Create a snapshot.\n\n**--snapshot delete \\<tag\\>**\n:   Delete a snapshot.\n\n**--snapshot info**\n:   Show disk/snapshot info.\n\n**--status-quo**\n:   Do not commit any changes to disk/snapshot.\n\n**--viewer \\<viewer\\>**\n:   Choose an alternative viewer. @Options: 'spicy' (default),\n    'remote-viewer', 'none'\n\n**--width \\<width\\>**\n:   Set VM screen width; requires '--height'\n\n**--height \\<height\\>**\n:   Set VM screen height; requires '--width'\n\n**--ssh-port \\<port\\>**\n:   Set SSH port manually\n\n**--spice-port \\<port\\>**\n:   Set SPICE port manually\n\n**--public-dir \\<path\\>**\n:   Expose share directory. @Options: '' (default: xdg-user-dir\n    PUBLICSHARE), '<directory>', 'none'\n\n**--monitor \\<type\\>**\n:   Set monitor connection type. @Options: 'socket' (default), 'telnet',\n    'none'\n\n**--monitor-telnet-host \\<ip/host\\>**\n:   Set telnet host for monitor. (default: 'localhost')\n\n**--monitor-telnet-port \\<port\\>**\n:   Set telnet port for monitor. (default: '4440')\n\n**--monitor-cmd \\<cmd\\>**\n:   Send command to monitor if available. (Example: system_powerdown)\n\n**--serial \\<type\\>**\n:   Set serial connection type. @Options: 'socket' (default), 'telnet',\n    'none'\n\n**--serial-telnet-host \\<ip/host\\>**\n:   Set telnet host for serial. (default: 'localhost')\n\n**--serial-telnet-port \\<port\\>**\n:   Set telnet port for serial. (default: '6660')\n\n**--keyboard \\<type\\>**\n:   Set keyboard. @Options: 'usb' (default), 'ps2', 'virtio'\n\n**--keyboard_layout \\<layout\\>**\n:   Set keyboard layout: 'en-us' (default)\n\n**--mouse \\<type\\>**\n:   Set mouse. @Options: 'tablet' (default), 'ps2', 'usb', 'virtio'\n\n**--usb-controller \\<type\\>**\n:   Set usb-controller. @Options: 'ehci' (default), 'xhci', 'none'\n\n**--sound-card \\<type\\>**\n:   Set sound card. @Options: 'intel-hda' (default), 'ac97', 'es1370',\n    'sb16', 'none'\n\n**--extra_args \\<arguments\\>**\n:   Pass additional arguments to qemu\n\n**--version**\n:   Print version\n\n# EXAMPLES\n\n**quickemu --vm ubuntu-mate-22.04.conf**\n:   Launches the VM specified in the file *ubuntu-mate-22.04.conf*\n\n# Introduction\n\n**Quickemu** is a wrapper for the excellent\n[QEMU](https://www.qemu.org/) that automatically *\"does the right\nthing\"* when creating virtual machines. No requirement for exhaustive\nconfiguration options. You decide what operating system you want to run\nand Quickemu takes care of the rest 🤖\n\n- `quickget` **automatically downloads the upstream OS** and creates the\n  configuration 📀\n- `quickemu` enumerates your hardware and launches the virtual machine\n  with the **optimum configuration best suited to your computer** ⚡️\n\nThe original objective of the project was to [enable quick testing of\nLinux\ndistributions](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines)\nwhere the virtual machines and their configuration can be stored\nanywhere (such as external USB storage or your home directory) and no\nelevated permissions are required to run the virtual machines.\n\n**Today, Quickemu includes comprehensive support for\n[macOS](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines),\n[Windows](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines)**,\nmost of the BSDs, novel non-Linux operating systems such as FreeDOS,\nHaiku, KolibriOS, OpenIndiana, ReactOS, and more.\n\n# Features\n\n- Host support for **Linux and macOS**\n- **macOS** Sequoia, Sonoma, Ventura, Monterey, Big Sur, Catalina &\n  Mojave\n- **Windows** 10 and 11 including TPM 2.0\n- **Windows Server** 2022 2019 2016\n- **ARM64 guest support** for running aarch64 VMs (native on ARM hosts,\n  emulated on x86_64)\n- [Ubuntu](https://ubuntu.com/desktop) and all the **[official Ubuntu\n  flavours](https://ubuntu.com/download/flavours)**\n- **Nearly 1000 operating system editions are supported!**\n- Full SPICE support including host/guest clipboard sharing\n- VirtIO-webdavd file sharing for Linux and Windows guests\n- VirtIO-9p file sharing for Linux and macOS guests\n- [QEMU Guest Agent support](https://wiki.qemu.org/Features/GuestAgent);\n  provides access to a system-level agent via standard QMP commands\n- Samba file sharing for Linux, macOS and Windows guests (*if `smbd` is\n  installed on the host*)\n- VirGL acceleration\n- USB device pass-through\n- Smartcard pass-through\n- Automatic SSH port forwarding to guests\n- Network port forwarding\n- Full duplex audio\n- Braille support\n- EFI (with or without SecureBoot) and Legacy BIOS boot\n\n## As featured on [Linux Matters](https://linuxmatters.sh) podcast!\n\nThe presenters of Linux Matters 🐧🎙️ are the creators of each of the\nprincipal Quickemu projects. We discussed Quickemu's 2024 reboot in\n[Episode 30 - Quickemu Rising From the\nBashes](https://linuxmatters.sh/30).\n<!-- and in [Episode 32 - Quick, quicker, quickest](https://linuxmatters.sh/32) [Martin](https://github.com/flexiondotorg) unveils macOS host support for [**Quickemu**](https://github.com/quickemu-project/quickemu), [Mark](https://github.com/marxjohnson) explains the origins of the [**Quickgui**](https://github.com/quickemu-project/quickgui) desktop app and upcoming improvements, and [Alan](https://github.com/popey) debuts [**Quicktest**](https://github.com/quickemu-project/quicktest); a framework for automatically testing operating systems via Quickemu -->\n\n<div align=\"center\">\n\n<a href=\"https://linuxmatters.sh\" target=\"_blank\"><img src=\"https://github.com/wimpysworld/nix-config/raw/main/.github/screenshots/linuxmatters.png\" alt=\"Linux Matters Podcast\"/></a>\n<br /> <em>Linux Matters Podcast</em>\n\n</div>\n\nWhen installing from source, you will need to install the following\nrequirements manually:\n\n- [QEMU](https://www.qemu.org/) (*6.0.0 or newer*) **with GTK, SDL,\n  SPICE & VirtFS support**\n- [bash](https://www.gnu.org/software/bash/) (*4.0 or newer*)\n- [Coreutils](https://www.gnu.org/software/coreutils/)\n- [curl](https://curl.se/)\n- [EDK II](https://github.com/tianocore/edk2)\n- [gawk](https://www.gnu.org/software/gawk/)\n- [grep](https://www.gnu.org/software/grep/)\n- [glxinfo](https://gitlab.freedesktop.org/mesa/demos)\n- [jq](https://stedolan.github.io/jq/)\n- [LSB](https://wiki.linuxfoundation.org/lsb/start)\n- [pciutils](https://github.com/pciutils/pciutils)\n- [procps](https://gitlab.com/procps-ng/procps)\n- [python3](https://www.python.org/)\n- [mkisofs](http://cdrtools.sourceforge.net/private/cdrecord.html)\n- [usbutils](https://github.com/gregkh/usbutils)\n- [util-linux](https://github.com/karelzak/util-linux); including\n  `uuidgen`\n- [sed](https://www.gnu.org/software/sed/)\n- [socat](http://www.dest-unreach.org/socat/)\n- [spicy](https://gitlab.freedesktop.org/spice/spice-gtk)\n- [swtpm](https://github.com/stefanberger/swtpm)\n- [xdg-user-dirs](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)\n- [xrandr](https://gitlab.freedesktop.org/xorg/app/xrandr)\n- [zsync](http://zsync.moria.org.uk/)\n- [unzip](http://www.info-zip.org/UnZip.html)\n\nFor Ubuntu, Debian, Fedora, Arch and NixOS hosts the native packaging or\n[ppa](https://launchpad.net/~flexiondotorg/+archive/ubuntu/quickemu),\n[AUR](https://aur.archlinux.org/packages/quickemu) or\n[nix](https://github.com/NixOS/nixpkgs/tree/master/pkgs/development/quickemu)\npackaging will take care of the dependencies. For other host\ndistributions or operating systems it will be necessary to install the\nabove requirements or their equivalents.\n\nThese examples may save a little typing:\n\n#### Install requirements on Debian hosts\n\nThese should be handled by dependencies in Trixie and later. For earlier\nversions (and their derivatives):\n\n``` shell\nsudo apt-get install bash coreutils curl genisoimage grep jq mesa-utils ovmf pciutils procps python3 qemu sed socat spice-client-gtk swtpm-tools unzip usbutils util-linux xdg-user-dirs xrandr zsync \n```\n\n#### Install requirements on Fedora hosts\n\nThese are handled natively for Fedora 41 on. For earlier versions:\n\n``` shell\nsudo dnf install bash coreutils curl edk2-tools genisoimage grep jq mesa-demos pciutils procps python3 qemu sed socat spice-gtk-tools swtpm unzip usbutils util-linux uuidgen-runtime xdg-user-dirs xrandr zsync\n```\n\n### Install requirements on Gentoo\n\nPlease note that you may have to use `sys-firmware/edk2-ovmf` instead of\n`sys-firmware/edk2-ovmf-bin` - depending on how your system is\nconfigured.\n\n``` shell\nsudo emerge --ask --noreplace app-emulation/qemu \\\n app-shells/bash \\\n sys-apps/coreutils \\\n net-misc/curl \\\n sys-firmware/edk2-ovmf-bin \\\n sys-apps/gawk \\\n sys-apps/grep \\\n x11-apps/mesa-progs \\\n app-misc/jq \\\n sys-apps/pciutils \\\n sys-process/procps \\\n app-cdr/cdrtools \\\n sys-apps/usbutils \\\n sys-apps/util-linux \\\n sys-apps/sed \\\n net-misc/socat \\\n app-emulation/spice \\\n app-crypt/swtpm \\\n x11-misc/xdg-user-dirs \\\n x11-apps/xrandr \\\n net-misc/zsync \\\n app-arch/unzip\n```\n\n#### Install requirements on macOS hosts\n\nInstall the Quickemu requirements using brew:\n\n``` shell\nbrew install bash cdrtools coreutils jq python3 qemu usbutils samba socat swtpm zsync\n```\n\nNow clone the project:\n\n``` shell\ngit clone https://github.com/quickemu-project/quickemu\ncd quickemu\n```\n\n## [Alternative Frontends](https://github.com/quickemu-project/quickemu/wiki/07-Alternative-frontends)\n\n### Quickgui\n\nWhile `quickemu` and `quickget` are designed for the terminal, a\ngraphical user interface is also available:\n\n- **[Quickgui](https://github.com/quickemu-project/quickgui)** by [Mark\n  Johnson](https://github.com/marxjohnson) and [Yannick\n  Mauray](https://github.com/ymauray).\n\nTo install Quickgui on Ubuntu:\n\n``` shell\nsudo add-apt-repository ppa:yannick-mauray/quickgui\nsudo apt update\nsudo apt install quickgui\n```\n\nMany thanks to [Luke Wesley-Holley](https://github.com/Lukewh) and\n[Philipp Kiemle](https://github.com/daPhipz) for creating the\n**[Quickemu icons](https://github.com/Lukewh/quickemu-icons)** 🎨\n\n## Creating Linux guests 🐧\n\n### Ubuntu\n\n`quickget` will automatically download an Ubuntu release and create the\nvirtual machine configuration.\n\n``` shell\nquickget ubuntu 22.04\nquickemu --vm ubuntu-22.04.conf\n```\n\n- Complete the installation as normal.\n- Post-install:\n  - Install the SPICE agent (`spice-vdagent`) in the guest to enable\n    copy/paste and USB redirection\n    - `sudo apt install spice-vdagent`\n  - Install the SPICE WebDAV agent (`spice-webdavd`) in the guest to\n    enable file sharing.\n    - `sudo apt install spice-webdavd`\n\n### Ubuntu daily-live images\n\n`quickget` can also download/refresh daily-live images via `zsync` for\nUbuntu developers and testers.\n\n``` shell\nquickget ubuntu daily-live\nquickemu --vm ubuntu-daily-live.conf\n```\n\nYou can run `quickget ubuntu daily-live` to refresh your daily\ndevelopment image as often as you like, it will even automatically\nswitch to a new series.\n\n### Ubuntu Flavours\n\nAll the official Ubuntu flavours are supported, just replace `ubuntu`\nwith your preferred flavour.\n\nThe project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines)\nmay have further information.\n\n- `edubuntu` (Edubuntu)\n- `kubuntu` (Kubuntu)\n- `lubuntu` (Lubuntu)\n- `ubuntu-budgie` (Ubuntu Budgie)\n- `ubuntucinnamon` (Ubuntu Cinnamon)\n- `ubuntukylin` (Ubuntu Kylin)\n- `ubuntu-mate` (Ubuntu MATE)\n- `ubuntu-server` (Ubuntu Server)\n- `ubuntustudio` (Ubuntu Studio)\n- `ubuntu` (Ubuntu)\n- `ubuntu-unity` (Ubuntu Unity)\n- `xubuntu` (Xubuntu)\n\nYou can also use `quickget` with advanced options :\n\n``` text\n   --arch           <arch>                    : Set architecture (arm64, aarch64, amd64, x86_64)\n   --download       <os> <release> [edition]  : Download image; no VM configuration\n   --create-config  <os> [path/url] [flags]   : Create VM config for an OS image\n   --open-homepage  <os>                      : Open homepage for the OS\n   --show           [os]                      : Show OS information\n   --version                                  : Show version\n   --help                                     : Show this help message\n------------------------------------ Flags -------------------------------------\n--create-config:\n  --disable-unattended                        : Force quickget not to set up an unattended installation\n-------------------------- For testing & development ---------------------------\n   --url            [os] [release] [edition]  : Show image URL(s)\n   --check          [os] [release] [edition]  : Check image URL(s)\n   --check-all-arch [os] [release] [edition]  : Check downloads for all architectures (amd64 and arm64)\n   --list                                     : List all supported systems\n   --list-csv                                 : List everything in csv format\n   --list-json                                : List everything in json format\n```\n\nHere are some typical uses\n\n``` shell\n    # show an OS ISO download URL for {os} {release} [edition]\n    quickget --url fedora 38 Silverblue\n    # test if an OS ISO is available for {os} {release} [edition]\n    quickget --check nixos unstable plasma5\n    # open an OS distribution homepage in a browser\n    quickget --open-homepage  ubuntu-mate\n    # Only download image file into current directory, without creating VM\n    quickget --download elementary 7.1\n```\n\nThe `--url`, `--check`, and `--download` options are fully functional\nfor all operating systems, including Windows and macOS.\n\nFurther information is available from the project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features)\n\n### Other Operating Systems\n\n`quickget` also supports:\n\n- `alma` (AlmaLinux)\n- `alpine` (Alpine Linux)\n- `android` (Android x86)\n- `antix` (Antix)\n- `archcraft` (Archcraft)\n- `archlinux` (Arch Linux)\n- `artixlinux` (Artix Linux)\n- `azurelinux` (Azure Linux)\n- `batocera` (Batocera)\n- `bazzite` (Bazzite)\n- `biglinux` (BigLinux)\n- `blendos` (BlendOS)\n- `bodhi` (Bodhi)\n- `bunsenlabs` (BunsenLabs)\n- `cachyos` (CachyOS)\n- `centos-stream` (CentOS Stream)\n- `chimeralinux` (Chimera Linux)\n- `crunchbang++` (Crunchbangplusplus)\n- `debian` (Debian)\n- `deepin` (Deepin)\n- `devuan` (Devuan)\n- `dragonflybsd` (DragonFlyBSD)\n- `easyos` (EasyOS)\n- `elementary` (elementary OS)\n- `endeavouros` (EndeavourOS)\n- `endless` (Endless OS)\n- `fedora` (Fedora)\n- `freebsd` (FreeBSD)\n- `freedos` (FreeDOS)\n- `garuda` (Garuda Linux)\n- `gentoo` (Gentoo)\n- `ghostbsd` (GhostBSD)\n- `gnomeos` (GNOME OS)\n- `guix` (Guix)\n- `haiku` (Haiku)\n- `kali` (Kali)\n- `kdeneon` (KDE Neon)\n- `kolibrios` (KolibriOS)\n- `linuxlite` (Linux Lite)\n- `linuxmint` (Linux Mint)\n- `lmde` (Linux Mint Debian Edition)\n- `maboxlinux` (Mabox Linux)\n- `mageia` (Mageia)\n- `manjaro` (Manjaro)\n- `mxlinux` (MX Linux)\n- `netboot` (netboot.xyz)\n- `netbsd` (NetBSD)\n- `nitrux` (Nitrux)\n- `nixos` (NixOS)\n- `nwg-shell` (nwg-shell)\n- `openbsd` (OpenBSD)\n- `openindiana` (OpenIndiana)\n- `opensuse` (openSUSE)\n- `oraclelinux` (Oracle Linux)\n- `parrotsec` (Parrot Security)\n- `pclinuxos` (PCLinuxOS)\n- `peppermint` (PeppermintOS)\n- `popos` (Pop!\\_OS)\n- `porteus` (Porteus)\n- `primtux` (PrimTux)\n- `proxmox-ve` (Proxmox VE)\n- `pureos` (PureOS)\n- `reactos` (ReactOS)\n- `rebornos` (RebornOS)\n- `rockylinux` (Rocky Linux)\n- `siduction` (Siduction)\n- `slackware` (Slackware)\n- `slax` (Slax)\n- `slint` (Slint)\n- `slitaz` (SliTaz)\n- `solus` (Solus)\n- `sparkylinux` (SparkyLinux)\n- `spirallinux` (SpiralLinux)\n- `tails` (Tails)\n- `tinycore` (Tiny Core Linux)\n- `trisquel` (Trisquel)\n- `tuxedo-os` (Tuxedo OS)\n- `vanillaos` (Vanilla OS)\n- `void` (Void Linux)\n- `zorin` (Zorin OS)\n\n### [Custom Linux guests](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines#manually-create-linux-guests)\n\nOr you can download a Linux image and manually create a VM\nconfiguration.\n\n- Download a .iso image of a Linux distribution\n- Create a VM configuration file; for example `debian-bullseye.conf`\n\n``` shell\nguest_os=\"linux\"\ndisk_img=\"debian-bullseye/disk.qcow2\"\niso=\"debian-bullseye/firmware-11.0.0-amd64-DVD-1.iso\"\n```\n\n- Use `quickemu` to start the virtual machine:\n\n``` shell\nquickemu --vm debian-bullseye.conf\n```\n\n- Complete the installation as normal.\n- Post-install:\n  - Install the SPICE agent (`spice-vdagent`) in the guest to enable\n    copy/paste and USB redirection.\n  - Install the SPICE WebDAV agent (`spice-webdavd`) in the guest to\n    enable file sharing.\n\n## Supporting old Linux distros\n\nIf you want to run an old Linux , from 2016 or earlier, change the\n`guest_os` to `linux_old`. This will enable the `vmware-svga` graphics\ndriver which is better supported on older distros.\n\n## [Creating macOS Guests](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests) 🍏\n\n**Installing macOS in a VM can be a bit finicky, if you encounter\nproblems, [check the\nDiscussions](https://github.com/quickemu-project/quickemu/discussions)\nfor solutions or ask for help there** 🛟\n\n`quickget` automatically downloads a macOS recovery image and creates a\nvirtual machine configuration.\n\nNote: Some VPN users may need to [turn off their\nVPN](https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-3506845235)\nin order to download a recovery image. Some other users may find [using\na\nVPN](https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-2429146013)\nnecessary in order to download a recovery image.\n\n``` shell\nquickget macos big-sur\nquickemu --vm macos-big-sur.conf\n```\n\nmacOS `mojave`, `catalina`, `big-sur`, `monterey`, `ventura` and\n`sonoma` are supported.\n\n- Use cursor keys and enter key to select the **macOS Base System**\n- From **macOS Utilities**\n  - Click **Disk Utility** and **Continue**\n    - Select `QEMU HARDDISK Media` (~103.08GB) from the list (on Big Sur\n      and above use `Apple Inc. VirtIO Block Device`) and click\n      **Erase**.\n    - Enter a `Name:` for the disk\n    - If you are installing macOS Mojave or later (Catalina, Big Sur,\n      Monterey, Ventura and Sonoma), choose any of the APFS options as\n      the filesystem. MacOS Extended may not work.\n  - Click **Erase**.\n  - Click **Done**.\n  - Close Disk Utility\n- From **macOS Utilities**\n  - Click **Reinstall macOS** and **Continue**\n- Complete the installation as you normally would.\n  - On the first reboot use cursor keys and enter key to select **macOS\n    Installer**\n  - On the subsequent reboots use cursor keys and enter key to select\n    the disk you named\n- Once you have finished installing macOS you will be presented with an\n  the out-of-the-box first-start wizard to configure various options and\n  set up your username and password\n- OPTIONAL: After you have concluded the out-of-the-box wizard, you may\n  want to enable the TRIM feature that the computer industry created for\n  SSD disks. This feature in our macOS installation will allow QuickEmu\n  to compact (shrink) your macOS disk image whenever you delete files\n  inside the Virtual Machine. Without this step your macOS disk image\n  will only ever get larger and will not shrink even when you delete\n  lots of data inside macOS.\n  - To enable TRIM, open the Terminal application and type the following\n    command followed by pressing <kbd>enter</kbd> to tell macos to use\n    the TRIM command on the hard disk when files are deleted:\n\n``` shell\nsudo trimforce enable\n```\n\nYou will be prompted to enter your account's password to gain the\nprivilege needed. Once you've entered your password and pressed\n<kbd>enter</kbd> the command will request confirmation in the form of\ntwo questions that require you to type <kbd>y</kbd> (for a \"yes\"\nresponse) followed by <kbd>enter</kbd> to confirm.\n\nIf you press <kbd>enter</kbd> without first typing <kbd>y</kbd> the\nsystem will consider that a negative response as though you said \"no\":\n\n``` plain\nIMPORTANT NOTICE: This tool force-enables TRIM for all relevant attached devices, even though such devices may not have been validated for data integrity while using TRIM. Use of this tool to enable TRIM may result in unintended data loss or data corruption. It should not be used in a commercial operating environment or with important data. Before using this tool, you should back up all of your data and regularly back up data while TRIM is enabled. This tool is provided on an \"as is\" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS TOOL OR ITS USE ALONE OR IN COMBINATION WITH YOUR DEVICES, SYSTEMS, OR SERVICES. BY USING THIS TOOL TO ENABLE TRIM, YOU AGREE THAT, TO THE EXTENT PERMITTED BY APPLICABLE LAW, USE OF THE TOOL IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU.\nAre you sure you with to proceed (y/N)?\n```\n\nAnd a second confirmation once you've confirmed the previous one:\n\n``` plain\nYour system will immediately reboot when this is complete.\nIs this OK (y/N)?\n```\n\nAs the last message states, your system will automatically reboot as\nsoon as the command completes.\n\nThe default macOS configuration looks like this:\n\n``` shell\nguest_os=\"macos\"\nimg=\"macos-big-sur/RecoveryImage.img\"\ndisk_img=\"macos-big-sur/disk.qcow2\"\nmacos_release=\"big-sur\"\n```\n\n- `guest_os=\"macos\"` instructs Quickemu to optimise for macOS.\n- `macos_release=\"big-sur\"` instructs Quickemu to optimise for a\n  particular macOS release.\n  - For example VirtIO Network and Memory Ballooning are available in\n    Big Sur and newer, but not previous releases.\n  - And VirtIO Block Media (disks) are supported/stable in Catalina and\n    newer.\n\n# macOS compatibility\n\nThere are some considerations when running macOS via Quickemu.\n\n- Supported macOS releases:\n  - Mojave\n  - Catalina\n  - Big Sur\n  - Monterey\n  - Ventura\n  - Sonoma\n- `quickemu` will automatically download the required\n  [OpenCore](https://github.com/acidanthera/OpenCorePkg) bootloader and\n  OVMF firmware from [OSX-KVM](https://github.com/kholia/OSX-KVM).\n- Optimised by default, but no GPU acceleration is available.\n  - Host CPU vendor is detected and guest CPU is optimised accordingly.\n  - [VirtIO Block\n    Media](https://www.kraxel.org/blog/2019/06/macos-qemu-guest/) is\n    used for the system disk where supported.\n  - [VirtIO `usb-tablet`](http://philjordan.eu/osx-virt/) is used for\n    the mouse.\n  - VirtIO Network (`virtio-net`) is supported and enabled on macOS Big\n    Sur and newer, but earlier releases use `vmxnet3`.\n  - VirtIO Memory Ballooning is supported and enabled on macOS Big Sur\n    and newer but disabled for other support macOS releases.\n- USB host and SPICE pass-through is:\n  - UHCI (USB 2.0) on macOS Catalina and earlier.\n  - XHCI (USB 3.0) on macOS Big Sur and newer.\n- Display resolution can be changed via `quickemu` using `--width` and\n  `--height` command line arguments.\n- **Full Duplex audio requires [VoodooHDA\n  OC](https://github.com/chris1111/VoodooHDA-OC) or pass-through a USB\n  audio-device to the macOS guest VM**.\n  - NOTE! [Gatekeeper](https://disable-gatekeeper.github.io/) and\n    [System Integrity Protection\n    (SIP)](https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection)\n    need to be disabled to install VoodooHDA OC\n- File sharing between guest and host is available via\n  [virtio-9p](https://wiki.qemu.org/Documentation/9psetup) and [SPICE\n  webdavd](https://gitlab.gnome.org/GNOME/phodav/-/merge_requests/24).\n- Copy/paste via SPICE agent is **not available on macOS**.\n\n# macOS App Store\n\nIf you see *\"Your device or computer could not be verified\"* when you\ntry to login to the App Store, make sure that your wired ethernet device\nis `en0`. Use `ifconfig` in a terminal to verify this.\n\nIf the wired ethernet device is not `en0`, then then go to *System\nPreferences* -\\> *Network*, delete all the network devices and apply the\nchanges. Next, open a terminal and run the following:\n\n``` shell\nsudo rm /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist\n```\n\nNow reboot, and the App Store should work.\n\nThere may be further advice and information about macOS guests in the\nproject\n[wiki](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests).\n\n## [Creating Windows guests](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines) 🪟\n\n`quickget` can download [**Windows\n10**](https://www.microsoft.com/software-download/windows10) and\n[**Windows 11**](https://www.microsoft.com/software-download/windows11)\nautomatically and create an optimised virtual machine configuration.\nThis configuration also includes the [VirtIO drivers for\nWindows](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/).\n\n**Windows 8.1** is also supported but doesn't feature any automated\ninstallation or driver optimisation.\n\n`quickget` can also download [Windows 10\nLTSC](https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise)\nand Windows Server\n[2012-r2](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2),\n[2016](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016),\n[2019](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019),\nand\n[2022](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022).\nNo automated installation is supported for these releases.\n\n``` shell\nquickget windows 11\nquickemu --vm windows-11.conf\n```\n\n- Complete the installation as you normally would.\n- All relevant drivers and services should be installed automatically.\n- A local administrator user account is automatically created, with\n  these credentials:\n  - Username: `Quickemu`\n  - Password: `quickemu`\n\nFurther information is available from the project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines)\n\n## Configuration\n\nHere are the usage instructions:\n\n``` text\nUsage\n  quickemu --vm ubuntu.conf <arguments>\n\nArguments\n  --access                          : Enable remote spice access support. 'local' (default), 'remote', 'clientipaddress'\n  --braille                         : Enable braille support. Requires SDL.\n  --cpu-pinning                     : Choose which host cores correspond to which guest cores.\n  --delete-disk                     : Delete the disk image and EFI variables\n  --delete-vm                       : Delete the entire VM and its configuration\n  --display                         : Select display backend. 'gtk' (default), 'sdl', 'cocoa', 'none', 'spice' or 'spice-app'\n  --fullscreen                      : Starts VM in full screen mode (Ctl+Alt+f to exit)\n  --ignore-msrs-always              : Configure KVM to always ignore unhandled machine-specific registers\n  --ignore-tsc-warning              : Skip TSC stability warning for macOS VMs on AMD\n  --kill                            : Kill the VM process if it is running\n  --offline                         : Override all network settings and start the VM offline\n  --shortcut                        : Create a desktop shortcut\n  --snapshot apply <tag>            : Apply/restore a snapshot.\n  --snapshot create <tag>           : Create a snapshot.\n  --snapshot delete <tag>           : Delete a snapshot.\n  --snapshot info                   : Show disk/snapshot info.\n  --status-quo                      : Do not commit any changes to disk/snapshot.\n  --viewer <viewer>                 : Choose an alternative viewer. @Options: 'spicy' (default), 'remote-viewer', 'none'\n  --width <width>                   : Set VM screen width; requires '--height'\n  --height <height>                 : Set VM screen height; requires '--width'\n  --ssh-port <port>                 : Set SSH port manually\n  --spice-port <port>               : Set SPICE port manually\n  --public-dir <path>               : Expose share directory. @Options: '' (default: xdg-user-dir PUBLICSHARE), '<directory>', 'none'\n  --monitor <type>                  : Set monitor connection type. @Options: 'socket' (default), 'telnet', 'none'\n  --monitor-telnet-host <ip/host>   : Set telnet host for monitor. (default: 'localhost')\n  --monitor-telnet-port <port>      : Set telnet port for monitor. (default: '4440')\n  --monitor-cmd <cmd>               : Send command to monitor if available. (Example: system_powerdown)\n  --serial <type>                   : Set serial connection type. @Options: 'socket' (default), 'telnet', 'none'\n  --serial-telnet-host <ip/host>    : Set telnet host for serial. (default: 'localhost')\n  --serial-telnet-port <port>       : Set telnet port for serial. (default: '6660')\n  --keyboard <type>                 : Set keyboard. @Options: 'usb' (default), 'ps2', 'virtio'\n  --keyboard_layout <layout>        : Set keyboard layout: 'en-us' (default)\n  --mouse <type>                    : Set mouse. @Options: 'tablet' (default), 'ps2', 'usb', 'virtio'\n  --usb-controller <type>           : Set usb-controller. @Options: 'ehci' (default), 'xhci', 'none'\n  --sound-card <type>               : Set sound card. @Options: 'intel-hda' (default), 'ac97', 'es1370', 'sb16', 'usb-audio', 'virtio-sound-pci', 'none'\n  --sound-duplex <type>             : Set sound card duplex. @Options: 'hda-micro' (default: speaker/mic), 'hda-duplex' (line-in/line-out), 'hda-output' (output-only)\n  --extra_args <arguments>          : Pass additional arguments to qemu\n  --version                         : Print version\n```\n\n## Desktop shortcuts\n\nDesktop shortcuts can be created for a VM, the shortcuts are saved in\n`~/.local/share/applications`. Here is an example of how to create a\nshortcut.\n\n``` shell\nquickemu --vm ubuntu-22.04-desktop.conf --shortcut\n```\n\n## References\n\nUseful reference that assisted the development of Quickemu.\n\n- General\n  - [QEMU's documentation!](https://qemu.readthedocs.io/en/latest/)\n  - <https://pve.proxmox.com/wiki/Qemu/KVM_Virtual_Machines>\n  - <https://www.kraxel.org/blog/2020/01/qemu-sound-audiodev/>\n- macOS\n  - <https://www.nicksherlock.com/2020/06/installing-macos-big-sur-on-proxmox/>\n  - <https://passthroughpo.st/mac-os-adds-early-support-for-virtio-qemu/>\n  - <https://github.com/kholia/OSX-KVM>\n  - <https://github.com/thenickdude/KVM-Opencore>\n  - <https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7>\n  - <https://github.com/acidanthera/OpenCorePkg/tree/master/Utilities/macrecovery>\n  - <https://www.kraxel.org/blog/2017/09/running-macos-as-guest-in-kvm/>\n  - <https://www.nicksherlock.com/2017/10/passthrough-of-advanced-cpu-features-for-macos-high-sierra-guests/>\n  - <http://philjordan.eu/osx-virt/>\n  - <https://github.com/Dids/clover-builder>\n  - [OpenCore Configurator](https://mackie100projects.altervista.org)\n- Windows\n  - <https://www.heiko-sieger.info/running-windows-10-on-linux-using-kvm-with-vga-passthrough/>\n  - <https://leduccc.medium.com/improving-the-performance-of-a-windows-10-guest-on-qemu-a5b3f54d9cf5>\n  - <https://frontpagelinux.com/tutorials/how-to-use-linux-kvm-to-optimize-your-windows-10-virtual-machine/>\n  - <https://turlucode.com/qemu-command-line-args/>\n  - <https://github.com/pbatard/Fido>\n  - <https://www.catapultsystems.com/blogs/create-zero-touch-windows-10-iso/>\n- TPM\n  - <https://qemu-project.gitlab.io/qemu/specs/tpm.html>\n  - <https://www.tecklyfe.com/how-to-create-a-windows-11-virtual-machine-in-qemu/>\n- 9p & virtiofs\n  - <https://wiki.qemu.org/Documentation/9p>\n  - <https://wiki.qemu.org/Documentation/9psetup>\n  - <https://www.kraxel.org/blog/2019/06/macos-qemu-guest/>\n  - <https://superuser.com/questions/628169/how-to-share-a-directory-with-the-host-without-networking-in-qemu>\n  - <https://virtio-fs.gitlab.io/>\n\n# AUTHORS\n\nWritten by Martin Wimpress.\n\n# BUGS\n\nSubmit bug reports online at:\n<https://github.com/quickemu-project/quickemu/issues>\n\n# SEE ALSO\n\nFull sources at: <https://github.com/quickemu-project/quickemu>\n\nquickemu_conf(5), quickget(1), quickgui(1)\n"
  },
  {
    "path": "docs/quickemu_conf.5",
    "content": ".\\\" Automatically generated by Pandoc 3.8.3\n.\\\"\n.TH \"QUICKEMU_CONF\" \"5\" \"February 2, 2026\" \"quickemu_conf\" \"Quickemu Configuration Manual\"\n.SH NAME\nquickemu_conf \\- Options and parameters in the quickemu <vm>.conf\n.SH DESCRIPTION\n\\f[B]quickemu\\f[R] will create and run highly optimised desktop virtual\nmachines for Linux, macOS and Windows.\nIt uses sensible defaults, but many configuration options can be\noverridden in the required configuration file, which will as a minimum\nspecify the path to the installation ISO and QEMU disk for the installed\nVM\n.SH OPTIONS\nThese are the options and defaults for the <vm>.conf file\n.IP\n.EX\n# Lowercase variables are used in the VM config file only\nboot=\\(dqefi\\(dq\ncpu_cores=\\(dq\\(dq\ndisk_img=\\(dq\\(dq\ndisk_size=\\(dq\\(dq\ndisplay=\\(dq\\(dq\nextra_args=\\(dq\\(dq\nfixed_iso=\\(dq\\(dq\nfloppy=\\(dq\\(dq\nguest_os=\\(dqlinux\\(dq\nimg=\\(dq\\(dq\niso=\\(dq\\(dq\nmacaddr=\\(dq\\(dq\nmacos_release=\\(dq\\(dq\nnetwork=\\(dq\\(dq\nport_forwards=()\npreallocation=\\(dqoff\\(dq\nram=\\(dq\\(dq\nsecureboot=\\(dqoff\\(dq\ntpm=\\(dqoff\\(dq\nusb_devices=()\nviewer=\\(dqspicy\\(dq\nssh_port=\\(dq\\(dq\nspice_port=\\(dq\\(dq\npublic_dir=\\(dq\\(dq\nmonitor=\\(dqsocket\\(dq\nmonitor_telnet_port=\\(dq4440\\(dq\nmonitor_telnet_host=\\(dqlocalhost\\(dq\nmonitor_cmd=\\(dq\\(dq\nserial=\\(dqsocket\\(dq\nserial_telnet_port=\\(dq6660\\(dq\nserial_telnet_host=\\(dqlocalhost\\(dq\n# options: ehci(USB2.0), xhci(USB3.0)\nusb_controller=\\(dqehci\\(dq\n# options: ps2, usb, virtio\nkeyboard=\\(dqusb\\(dq\nkeyboard_layout=\\(dqen\\-us\\(dq\n# options: ps2, usb, tablet, virtio\nmouse=\\(dqtablet\\(dq\n.EE\n.SH EXAMPLES\n.IP\n.EX\nguest_os=\\(dqlinux\\(dq\ndisk_img=\\(dqdebian\\-bullseye/disk.qcow2\\(dq\niso=\\(dqdebian\\-bullseye/firmware\\-11.0.0\\-amd64\\-DVD\\-1.iso\\(dq\n.EE\n.PP\nThe default macOS configuration looks like this:\n.IP\n.EX\nguest_os=\\(dqmacos\\(dq\nimg=\\(dqmacos\\-catalina/RecoveryImage.img\\(dq\ndisk_img=\\(dqmacos\\-catalina/disk.qcow2\\(dq\nmacos_release=\\(dqcatalina\\(dq\n.EE\n.IP \\(bu 2\n\\f[CR]guest_os=\\(dqmacos\\(dq\\f[R] instructs Quickemu to optimise for\nmacOS.\n.IP \\(bu 2\n\\f[CR]macos_release=\\(dqcatalina\\(dq\\f[R] instructs Quickemu to optimise\nfor a particular macOS release.\n.RS 2\n.IP \\(bu 2\nFor example VirtIO Network and Memory Ballooning are available in Big\nSur and newer, but not previous releases.\n.IP \\(bu 2\nAnd VirtIO Block Media (disks) are supported/stable in Catalina and\nnewer.\n.RE\n.PP\nThe default Windows 11 configuration looks like this:\n.IP\n.EX\nguest_os=\\(dqwindows\\(dq\ndisk_img=\\(dqwindows\\-11/disk.qcow2\\(dq\niso=\\(dqwindows\\-11/Win11_EnglishInternational_x64.iso\\(dq\nfixed_iso=\\(dqwindows\\-11/virtio\\-win.iso\\(dq\ntpm=\\(dqon\\(dq\nsecureboot=\\(dqon\\(dq\n.EE\n.IP \\(bu 2\n\\f[CR]guest_os=\\(dqwindows\\(dq\\f[R] instructs \\f[CR]quickemu\\f[R] to\noptimise for Windows.\n.IP \\(bu 2\n\\f[CR]fixed_iso=\\f[R] specifies the ISO image that provides VirtIO\ndrivers.\n.IP \\(bu 2\n\\f[CR]tpm=\\(dqon\\(dq\\f[R] instructs \\f[CR]quickemu\\f[R] to create a\nsoftware emulated TPM device using \\f[CR]swtpm\\f[R].\n.SS BIOS and EFI\nSince Quickemu 2.1.0 \\f[CR]efi\\f[R] is the default boot option.\nIf you want to override this behaviour then add the following line to\nyou VM configuration to enable legacy BIOS.\n.IP \\(bu 2\n\\f[CR]boot=\\(dqlegacy\\(dq\\f[R] \\- Enable Legacy BIOS boot\n.SS Tuning CPU cores, RAM & disks\nBy default, Quickemu will calculate the number of CPUs cores and RAM to\nallocate to a VM based on the specifications of your host computer.\nYou can override this default behaviour and tune the VM configuration to\nyour liking.\n.PP\nAdd additional lines to your virtual machine configuration:\n.IP \\(bu 2\n\\f[CR]cpu_cores=\\(dq4\\(dq\\f[R] \\- Specify the number of CPU cores\nallocated to the VM\n.IP \\(bu 2\n\\f[CR]ram=\\(dq4G\\(dq\\f[R] \\- Specify the amount of RAM to allocate to\nthe VM\n.IP \\(bu 2\n\\f[CR]disk_size=\\(dq16G\\(dq\\f[R] \\- Specify the size of the virtual disk\nallocated to the VM\n.SS Disk preallocation\nPreallocation mode (allowed values: \\f[CR]off\\f[R] (default),\n\\f[CR]metadata\\f[R], \\f[CR]falloc\\f[R], \\f[CR]full\\f[R]).\nAn image with preallocated metadata is initially larger but can improve\nperformance when the image needs to grow.\n.PP\nSpecify what disk preallocation should be used, if any, when creating\nthe system disk image by adding a line like this to your VM\nconfiguration.\n.IP \\(bu 2\n\\f[CR]preallocation=\\(dqmetadata\\(dq\\f[R]\n.SS CD\\-ROM disks\nIf you want to expose an ISO image from the host to guest add the\nfollowing line to the VM configuration:\n.IP \\(bu 2\n\\f[CR]fixed_iso=\\(dq/path/to/image.iso\\(dq\\f[R]\n.SS Floppy disks\nIf you\\(aqre like \\c\n.UR https://popey.com\nAlan Pope\n.UE \\c\n\\ you\\(aqll probably want to mount a floppy disk image in the guest.\nTo do so add the following line to the VM configuration:\n.IP \\(bu 2\n\\f[CR]floppy=\\(dq/path/to/floppy.img\\(dq\\f[R]\n.SS File Sharing\nAll File Sharing options will only expose \\f[CR]\\(ti/Public\\f[R] (or\nlocalised variations) for the current user to the guest VMs.\n.SS Samba 🐧 🍏 🪟\nIf \\f[CR]smbd\\f[R] is available on the host, Quickemu will automatically\nenable the built\\-in QEMU support for exposing a Samba share from the\nhost to the guest.\n.PP\nYou can install the minimal Samba components on Ubuntu using:\n.IP\n.EX\nsudo apt install \\-\\-no\\-install\\-recommends samba\n.EE\n.PP\nIf everything is set up correctly, the \\f[CR]smbd\\f[R] address will be\nprinted when the virtual machine is started.\nFor example:\n.IP\n.EX\n \\- smbd:     On guest: smb://10.0.2.4/qemu\n.EE\n.PP\nIf using a Windows guest, right\\-click on \\(dqThis PC\\(dq, click \\(dqAdd\na network location\\(dq, and paste this address, removing \\f[CR]smb:\\f[R]\nand replacing forward slashes with backslashes (in this example\n\\f[CR]\\(rs\\(rs10.0.2.4\\(rsqemu\\f[R]).\n.SS SPICE WebDAV 🐧 🪟\n.IP \\(bu 2\nTBD\n.SS VirtIO\\-9P 🐧 🍏\n.IP \\(bu 2\nTBD\n.SS Networking\n.SS Port forwarding\nAdd an additional line to your virtual machine configuration.\nFor example:\n.IP \\(bu 2\n\\f[CR]port_forwards=(\\(dq8123:8123\\(dq \\(dq8888:80\\(dq)\\f[R]\n.PP\nIn the example above:\n.IP \\(bu 2\nPort 8123 on the host is forwarded to port 8123 on the guest.\n.IP \\(bu 2\nPort 8888 on the host is forwarded to port 80 on the guest.\n.SS Disable networking\nTo completely disable all network interfaces in a guest VM add this\nadditional line to your virtual machine configuration:\n.IP \\(bu 2\n\\f[CR]network=\\(dqnone\\(dq\\f[R]\n.SS Restricted networking\nYou can isolate the guest from the host (and broader network) using the\nrestrict option, which will restrict networking to just the guest and\nany virtual devices.\n.PP\nThis can be used to prevent software running inside the guest from\nphoning home while still providing a network inside the guest.\nAdd this additional line to your virtual machine configuration:\n.IP \\(bu 2\n\\f[CR]network=\\(dqrestrict\\(dq\\f[R]\n.SS Bridged networking\nConnect your virtual machine to a preconfigured network bridge.\nAdd an additional line to your virtual machine configuration:\n.IP \\(bu 2\n\\f[CR]network=\\(dqbr0\\(dq\\f[R]\n.PP\nIf you want to have a persistent MAC address for your bridged network\ninterface in the guest VM you can add \\f[CR]macaddr\\f[R] to the virtual\nmachine configuration.\nQEMU requires that the MAC address is in the range:\n\\f[B]52:54:00:AB:00:00 \\- 52:54:00:AB:FF:FF\\f[R]\n.PP\nSo you can generate your own MAC addresses with:\n.IP \\(bu 2\n\\f[CR]macaddr=\\(dq52:54:00:AB:51:AE\\(dq\\f[R]\n.SS USB redirection\nQuickemu supports USB redirection via SPICE pass\\-through and host\npass\\-through.\nQuickemu supports USB redirection via SPICE pass\\-through and host\npass\\-through.\n.PP\n\\f[B]NOTE!\\f[R] When a USB device is redirected from the host, it will\nnot be usable by host operating system until the guest redirection is\nstopped.\nTherefore, do not redirect the input devices, such as the keyboard and\nmouse, as it will be difficult (or impossible) to revert the situation.\n.SS SPICE redirection (recommended)\nUsing SPICE for USB pass\\-through is easiest as it doesn\\(aqt require\nany elevated permission:\n.PP\nBoth \\f[CR]spicy\\f[R] from \\c\n.UR https://www.spice-space.org/spice-gtk.html\nspice\\-gtk\n.UE \\c\n\\ (\\f[I]Input \\-> Select USB Devices for redirection\\f[R]) and\n\\f[CR]remote\\-viewer\\f[R] from \\c\n.UR https://gitlab.com/virt-viewer/virt-viewer\nvirt\\-viewer\n.UE \\c\n\\ (\\f[I]File \\-> USB device selection\\f[R]) support this feature.\n.IP \\(bu 2\nStart Quickemu with \\f[CR]\\-\\-display spice\\f[R] and then\n.IP \\(bu 2\nSelect \\f[CR]Input\\f[R] \\-> \\f[CR]Select USB Device for redirection\\f[R]\nfrom the menu to choose which device(s) you want to attach to the guest.\n.IP \\(bu 2\n**\\f[CR]spicy\\f[R] (default)\n.RS 2\n.IP \\(bu 2\n**Select \\f[CR]Input\\f[R] \\->\n\\f[CR]Select USB Device for redirection\\f[R] from the menu to choose\nwhich device(s) you want to attach to the guest.\n.RE\n.IP \\(bu 2\n**\\f[CR]remote\\-viewer\\f[R]\n.RS 2\n.IP \\(bu 2\n**Select \\f[CR]File\\f[R] \\-> \\f[CR]USB device selection\\f[R] from the\nmenu to choose which device(s) you want to attach to the guest.\n.RE\n.PP\nTo ensure that this functionality works as expected, make sure that you\nhave installed the necessary SPICE Guest Tools on the virtual machine.\n.SS Enabling SPICE redirection on NixOS\nOn NixOS, if you encounter this error:\n.IP\n.EX\nError setting facl: Operation not permitted\n.EE\n.PP\nTry setting \\c\n.UR https://search.nixos.org/options?channel=23.11&show=virtualisation.spiceUSBRedirection.enable&from=0&size=50&sort=relevance&type=packages&query=spiceusbredirec\nthe following option\n.UE \\c\n:\n.IP\n.EX\nvirtualisation.spiceUSBRedirection.enable = true;\n.EE\n.SS Host redirection (\\f[B]NOT Recommended\\f[R])\n\\f[B]USB host redirection is not recommended\\f[R], it is provided purely\nfor backwards compatibility to older versions of Quickemu.\nUsing SPICE is preferred, see above.\n.PP\nAdd an additional line to your virtual machine configuration.\nFor example:\n.IP \\(bu 2\n\\f[CR]usb_devices=(\\(dq046d:082d\\(dq \\(dq046d:085e\\(dq)\\f[R]\n.PP\nIn the example above:\n.IP \\(bu 2\nThe USB device with vendor_id 046d and product_id 082d will be exposed\nto the guest.\n.IP \\(bu 2\nThe USB device with vendor_id 046d and product_id 085e will be exposed\nto the guest.\n.PP\nIf the USB devices are not writable, \\f[CR]quickemu\\f[R] will display\nthe appropriate commands to modify the USB device(s) access permissions,\nlike this:\n.IP\n.EX\n \\- USB:      Host pass\\-through requested:\n              \\- Sennheiser Communications EPOS GTW 270 on bus 001 device 005 needs permission changes:\n                sudo chown \\-v root:user /dev/bus/usb/001/005\n                ERROR! USB permission changes are required 👆\n.EE\n.SS TPM\nSince Quickemu 2.2.0 a software emulated TPM device can be added to\nguest virtual machines.\nJust add \\f[CR]tpm=\\(dqon\\(dq\\f[R] to your VM configuration.\n\\f[CR]quickget\\f[R] will automatically add this line to Windows 11\nvirtual machines.\n.SH AUTHORS\nWritten by Martin Wimpress.\n.SH BUGS\nSubmit bug reports online at: \\c\n.UR https://github.com/quickemu-project/quickemu/issues\n.UE \\c\n.SH SEE ALSO\nFull sources at: \\c\n.UR https://github.com/quickemu-project/quickemu\n.UE \\c\n.PP\nquickget(1), quickemu(1), quickgui(1)\n.SH AUTHORS\nMartin Wimpress.\n"
  },
  {
    "path": "docs/quickemu_conf.5.md",
    "content": "---\nauthor: Martin Wimpress\ndate: February 2, 2026\nfooter: quickemu_conf\nheader: Quickemu Configuration Manual\nsection: 5\ntitle: QUICKEMU_CONF\n---\n\n# NAME\n\nquickemu_conf - Options and parameters in the quickemu \\<vm\\>.conf\n\n# DESCRIPTION\n\n**quickemu** will create and run highly optimised desktop virtual\nmachines for Linux, macOS and Windows. It uses sensible defaults, but\nmany configuration options can be overridden in the required\nconfiguration file, which will as a minimum specify the path to the\ninstallation ISO and QEMU disk for the installed VM\n\n# OPTIONS\n\nThese are the options and defaults for the \\<vm\\>.conf file\n\n``` shell\n# Lowercase variables are used in the VM config file only\nboot=\"efi\"\ncpu_cores=\"\"\ndisk_img=\"\"\ndisk_size=\"\"\ndisplay=\"\"\nextra_args=\"\"\nfixed_iso=\"\"\nfloppy=\"\"\nguest_os=\"linux\"\nimg=\"\"\niso=\"\"\nmacaddr=\"\"\nmacos_release=\"\"\nnetwork=\"\"\nport_forwards=()\npreallocation=\"off\"\nram=\"\"\nsecureboot=\"off\"\ntpm=\"off\"\nusb_devices=()\nviewer=\"spicy\"\nssh_port=\"\"\nspice_port=\"\"\npublic_dir=\"\"\nmonitor=\"socket\"\nmonitor_telnet_port=\"4440\"\nmonitor_telnet_host=\"localhost\"\nmonitor_cmd=\"\"\nserial=\"socket\"\nserial_telnet_port=\"6660\"\nserial_telnet_host=\"localhost\"\n# options: ehci(USB2.0), xhci(USB3.0)\nusb_controller=\"ehci\"\n# options: ps2, usb, virtio\nkeyboard=\"usb\"\nkeyboard_layout=\"en-us\"\n# options: ps2, usb, tablet, virtio\nmouse=\"tablet\"\n```\n\n# EXAMPLES\n\n``` shell\nguest_os=\"linux\"\ndisk_img=\"debian-bullseye/disk.qcow2\"\niso=\"debian-bullseye/firmware-11.0.0-amd64-DVD-1.iso\"\n```\n\nThe default macOS configuration looks like this:\n\n``` shell\nguest_os=\"macos\"\nimg=\"macos-catalina/RecoveryImage.img\"\ndisk_img=\"macos-catalina/disk.qcow2\"\nmacos_release=\"catalina\"\n```\n\n- `guest_os=\"macos\"` instructs Quickemu to optimise for macOS.\n- `macos_release=\"catalina\"` instructs Quickemu to optimise for a\n  particular macOS release.\n  - For example VirtIO Network and Memory Ballooning are available in\n    Big Sur and newer, but not previous releases.\n  - And VirtIO Block Media (disks) are supported/stable in Catalina and\n    newer.\n\nThe default Windows 11 configuration looks like this:\n\n``` shell\nguest_os=\"windows\"\ndisk_img=\"windows-11/disk.qcow2\"\niso=\"windows-11/Win11_EnglishInternational_x64.iso\"\nfixed_iso=\"windows-11/virtio-win.iso\"\ntpm=\"on\"\nsecureboot=\"on\"\n```\n\n- `guest_os=\"windows\"` instructs `quickemu` to optimise for Windows.\n- `fixed_iso=` specifies the ISO image that provides VirtIO drivers.\n- `tpm=\"on\"` instructs `quickemu` to create a software emulated TPM\n  device using `swtpm`.\n\n### BIOS and EFI\n\nSince Quickemu 2.1.0 `efi` is the default boot option. If you want to\noverride this behaviour then add the following line to you VM\nconfiguration to enable legacy BIOS.\n\n- `boot=\"legacy\"` - Enable Legacy BIOS boot\n\n### Tuning CPU cores, RAM & disks\n\nBy default, Quickemu will calculate the number of CPUs cores and RAM to\nallocate to a VM based on the specifications of your host computer. You\ncan override this default behaviour and tune the VM configuration to\nyour liking.\n\nAdd additional lines to your virtual machine configuration:\n\n- `cpu_cores=\"4\"` - Specify the number of CPU cores allocated to the VM\n- `ram=\"4G\"` - Specify the amount of RAM to allocate to the VM\n- `disk_size=\"16G\"` - Specify the size of the virtual disk allocated to\n  the VM\n\n### Disk preallocation\n\nPreallocation mode (allowed values: `off` (default), `metadata`,\n`falloc`, `full`). An image with preallocated metadata is initially\nlarger but can improve performance when the image needs to grow.\n\nSpecify what disk preallocation should be used, if any, when creating\nthe system disk image by adding a line like this to your VM\nconfiguration.\n\n- `preallocation=\"metadata\"`\n\n### CD-ROM disks\n\nIf you want to expose an ISO image from the host to guest add the\nfollowing line to the VM configuration:\n\n- `fixed_iso=\"/path/to/image.iso\"`\n\n### Floppy disks\n\nIf you're like [Alan Pope](https://popey.com) you'll probably want to\nmount a floppy disk image in the guest. To do so add the following line\nto the VM configuration:\n\n- `floppy=\"/path/to/floppy.img\"`\n\n### File Sharing\n\nAll File Sharing options will only expose `~/Public` (or localised\nvariations) for the current user to the guest VMs.\n\n#### Samba 🐧 🍏 🪟\n\nIf `smbd` is available on the host, Quickemu will automatically enable\nthe built-in QEMU support for exposing a Samba share from the host to\nthe guest.\n\nYou can install the minimal Samba components on Ubuntu using:\n\n``` shell\nsudo apt install --no-install-recommends samba\n```\n\nIf everything is set up correctly, the `smbd` address will be printed\nwhen the virtual machine is started. For example:\n\n     - smbd:     On guest: smb://10.0.2.4/qemu\n\nIf using a Windows guest, right-click on \"This PC\", click \"Add a network\nlocation\", and paste this address, removing `smb:` and replacing forward\nslashes with backslashes (in this example `\\\\10.0.2.4\\qemu`).\n\n#### SPICE WebDAV 🐧 🪟\n\n- TBD\n\n#### VirtIO-9P 🐧 🍏\n\n- TBD\n\n### Networking\n\n#### Port forwarding\n\nAdd an additional line to your virtual machine configuration. For\nexample:\n\n- `port_forwards=(\"8123:8123\" \"8888:80\")`\n\nIn the example above:\n\n- Port 8123 on the host is forwarded to port 8123 on the guest.\n- Port 8888 on the host is forwarded to port 80 on the guest.\n\n#### Disable networking\n\nTo completely disable all network interfaces in a guest VM add this\nadditional line to your virtual machine configuration:\n\n- `network=\"none\"`\n\n#### Restricted networking\n\nYou can isolate the guest from the host (and broader network) using the\nrestrict option, which will restrict networking to just the guest and\nany virtual devices.\n\nThis can be used to prevent software running inside the guest from\nphoning home while still providing a network inside the guest. Add this\nadditional line to your virtual machine configuration:\n\n- `network=\"restrict\"`\n\n#### Bridged networking\n\nConnect your virtual machine to a preconfigured network bridge. Add an\nadditional line to your virtual machine configuration:\n\n- `network=\"br0\"`\n\nIf you want to have a persistent MAC address for your bridged network\ninterface in the guest VM you can add `macaddr` to the virtual machine\nconfiguration. QEMU requires that the MAC address is in the range:\n**52:54:00:AB:00:00 - 52:54:00:AB:FF:FF**\n\nSo you can generate your own MAC addresses with:\n\n- `macaddr=\"52:54:00:AB:51:AE\"`\n\n### USB redirection\n\nQuickemu supports USB redirection via SPICE pass-through and host\npass-through. Quickemu supports USB redirection via SPICE pass-through\nand host pass-through.\n\n**NOTE!** When a USB device is redirected from the host, it will not be\nusable by host operating system until the guest redirection is stopped.\nTherefore, do not redirect the input devices, such as the keyboard and\nmouse, as it will be difficult (or impossible) to revert the situation.\n\n#### SPICE redirection (recommended)\n\nUsing SPICE for USB pass-through is easiest as it doesn't require any\nelevated permission:\n\nBoth `spicy` from\n[spice-gtk](https://www.spice-space.org/spice-gtk.html) (*Input -\\>\nSelect USB Devices for redirection*) and `remote-viewer` from\n[virt-viewer](https://gitlab.com/virt-viewer/virt-viewer) (*File -\\> USB\ndevice selection*) support this feature.\n\n- Start Quickemu with `--display spice` and then\n- Select `Input` -\\> `Select USB Device for redirection` from the menu\n  to choose which device(s) you want to attach to the guest.\n- \\*\\*`spicy` (default)\n  - \\*\\*Select `Input` -\\> `Select USB Device for redirection` from the\n    menu to choose which device(s) you want to attach to the guest.\n- \\*\\*`remote-viewer`\n  - \\*\\*Select `File` -\\> `USB device selection` from the menu to choose\n    which device(s) you want to attach to the guest.\n\nTo ensure that this functionality works as expected, make sure that you\nhave installed the necessary SPICE Guest Tools on the virtual machine.\n\n##### Enabling SPICE redirection on NixOS\n\nOn NixOS, if you encounter this error:\n\n    Error setting facl: Operation not permitted\n\nTry setting [the following\noption](https://search.nixos.org/options?channel=23.11&show=virtualisation.spiceUSBRedirection.enable&from=0&size=50&sort=relevance&type=packages&query=spiceusbredirec):\n\n``` nix\nvirtualisation.spiceUSBRedirection.enable = true;\n```\n\n#### Host redirection (**NOT Recommended**)\n\n**USB host redirection is not recommended**, it is provided purely for\nbackwards compatibility to older versions of Quickemu. Using SPICE is\npreferred, see above.\n\nAdd an additional line to your virtual machine configuration. For\nexample:\n\n- `usb_devices=(\"046d:082d\" \"046d:085e\")`\n\nIn the example above:\n\n- The USB device with vendor_id 046d and product_id 082d will be exposed\n  to the guest.\n- The USB device with vendor_id 046d and product_id 085e will be exposed\n  to the guest.\n\nIf the USB devices are not writable, `quickemu` will display the\nappropriate commands to modify the USB device(s) access permissions,\nlike this:\n\n     - USB:      Host pass-through requested:\n                  - Sennheiser Communications EPOS GTW 270 on bus 001 device 005 needs permission changes:\n                    sudo chown -v root:user /dev/bus/usb/001/005\n                    ERROR! USB permission changes are required 👆\n\n### TPM\n\nSince Quickemu 2.2.0 a software emulated TPM device can be added to\nguest virtual machines. Just add `tpm=\"on\"` to your VM configuration.\n`quickget` will automatically add this line to Windows 11 virtual\nmachines.\n\n# AUTHORS\n\nWritten by Martin Wimpress.\n\n# BUGS\n\nSubmit bug reports online at:\n<https://github.com/quickemu-project/quickemu/issues>\n\n# SEE ALSO\n\nFull sources at: <https://github.com/quickemu-project/quickemu>\n\nquickget(1), quickemu(1), quickgui(1)\n"
  },
  {
    "path": "docs/quickget.1",
    "content": ".\\\" Automatically generated by Pandoc 3.8.3\n.\\\"\n.TH \"QUICKGET\" \"1\" \"February 2, 2026\" \"quickget\" \"Quickget User Manual\"\n.SH NAME\nquickget \\- download and prepare materials for building a quickemu VM\n.SH SYNOPSIS\n\\f[B]quickget\\f[R] [\\f[I]os\\f[R]] [\\f[I]release\\f[R]]\n[\\f[I]edition\\f[R]] | [\\f[I]OPTION\\f[R]]*\n.SH DESCRIPTION\n\\f[B]quickget\\f[R] will download the requisite materials and prepare a\nconfiguration for \\f[CR]quickemu\\f[R] to use to build and run\n.SH OPTIONS\n.TP\n\\f[B][OS] [Release] [Edition]\\f[R]\nspecify the OS and release (and optional edition) if insufficient input\nis provided a list of missing options will be reported and the script\nwill exit.\nEditions may not apply and will be defaulted if not provided.\n.TP\n\\f[B]\\-\\-download\\f[R]   [edition]\nDownload image; no VM configuration\n.TP\n\\f[B]\\-\\-create\\-config\\f[R]  [path/url]\nCreate VM config for a OS image\n.TP\n\\f[B]\\-\\-open\\-homepage\\f[R] \nOpen homepage for the OS\n.TP\n\\f[B]\\-\\-show\\f[R] [os]\nShow OS information\n.TP\n\\f[B]\\-\\-url\\f[R] [os] [release] [edition]\nShow image URL(s)\n.TP\n\\f[B]\\-\\-check\\f[R] [os] [release] [edition]\nCheck image URL(s)\n.TP\n\\f[B]\\-\\-list\\f[R]\nList all supported systems\n.TP\n\\f[B]\\-\\-list\\-csv\\f[R]\nList everything in csv format\n.TP\n\\f[B]\\-\\-list\\-json\\f[R]\nList everything in json format\n.TP\n\\f[B]\\-\\-version\\f[R]\nShow version\n.TP\n\\f[B]\\-\\-help\\f[R]\nShow this help message\n.SH NOTES\n.SS Creating Linux guests 🐧\n.SS Ubuntu\n\\f[CR]quickget\\f[R] will automatically download an Ubuntu release and\ncreate the virtual machine configuration.\n.IP\n.EX\nquickget ubuntu 22.04\nquickemu \\-\\-vm ubuntu\\-22.04.conf\n.EE\n.IP \\(bu 2\nComplete the installation as normal.\n.IP \\(bu 2\nPost\\-install:\n.RS 2\n.IP \\(bu 2\nInstall the SPICE agent (\\f[CR]spice\\-vdagent\\f[R]) in the guest to\nenable copy/paste and USB redirection\n.RS 2\n.IP \\(bu 2\n\\f[CR]sudo apt install spice\\-vdagent\\f[R]\n.RE\n.IP \\(bu 2\nInstall the SPICE WebDAV agent (\\f[CR]spice\\-webdavd\\f[R]) in the guest\nto enable file sharing.\n.RS 2\n.IP \\(bu 2\n\\f[CR]sudo apt install spice\\-webdavd\\f[R]\n.RE\n.RE\n.SS Ubuntu daily\\-live images\n\\f[CR]quickget\\f[R] can also download/refresh daily\\-live images via\n\\f[CR]zsync\\f[R] for Ubuntu developers and testers.\n.IP\n.EX\nquickget ubuntu daily\\-live\nquickemu \\-\\-vm ubuntu\\-daily\\-live.conf\n.EE\n.PP\nYou can run \\f[CR]quickget ubuntu daily\\-live\\f[R] to refresh your daily\ndevelopment image as often as you like, it will even automatically\nswitch to a new series.\n.SS Ubuntu Flavours\nAll the official Ubuntu flavours are supported, just replace\n\\f[CR]ubuntu\\f[R] with your preferred flavour.\n.PP\nThe project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines\nwiki\n.UE \\c\n\\ may have further information.\n.IP \\(bu 2\n\\f[CR]edubuntu\\f[R] (Edubuntu)\n.IP \\(bu 2\n\\f[CR]kubuntu\\f[R] (Kubuntu)\n.IP \\(bu 2\n\\f[CR]lubuntu\\f[R] (Lubuntu)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-budgie\\f[R] (Ubuntu Budgie)\n.IP \\(bu 2\n\\f[CR]ubuntucinnamon\\f[R] (Ubuntu Cinnamon)\n.IP \\(bu 2\n\\f[CR]ubuntukylin\\f[R] (Ubuntu Kylin)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-mate\\f[R] (Ubuntu MATE)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-server\\f[R] (Ubuntu Server)\n.IP \\(bu 2\n\\f[CR]ubuntustudio\\f[R] (Ubuntu Studio)\n.IP \\(bu 2\n\\f[CR]ubuntu\\f[R] (Ubuntu)\n.IP \\(bu 2\n\\f[CR]ubuntu\\-unity\\f[R] (Ubuntu Unity)\n.IP \\(bu 2\n\\f[CR]xubuntu\\f[R] (Xubuntu)\n.PP\nYou can also use \\f[CR]quickget\\f[R] with advanced options :\n.IP\n.EX\n   \\-\\-arch           <arch>                    : Set architecture (arm64, aarch64, amd64, x86_64)\n   \\-\\-download       <os> <release> [edition]  : Download image; no VM configuration\n   \\-\\-create\\-config  <os> [path/url] [flags]   : Create VM config for an OS image\n   \\-\\-open\\-homepage  <os>                      : Open homepage for the OS\n   \\-\\-show           [os]                      : Show OS information\n   \\-\\-version                                  : Show version\n   \\-\\-help                                     : Show this help message\n\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\- Flags \\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\n\\-\\-create\\-config:\n  \\-\\-disable\\-unattended                        : Force quickget not to set up an unattended installation\n\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\- For testing & development \\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-\n   \\-\\-url            [os] [release] [edition]  : Show image URL(s)\n   \\-\\-check          [os] [release] [edition]  : Check image URL(s)\n   \\-\\-check\\-all\\-arch [os] [release] [edition]  : Check downloads for all architectures (amd64 and arm64)\n   \\-\\-list                                     : List all supported systems\n   \\-\\-list\\-csv                                 : List everything in csv format\n   \\-\\-list\\-json                                : List everything in json format\n.EE\n.PP\nHere are some typical uses\n.IP\n.EX\n    # show an OS ISO download URL for {os} {release} [edition]\n    quickget \\-\\-url fedora 38 Silverblue\n    # test if an OS ISO is available for {os} {release} [edition]\n    quickget \\-\\-check nixos unstable plasma5\n    # open an OS distribution homepage in a browser\n    quickget \\-\\-open\\-homepage  ubuntu\\-mate\n    # Only download image file into current directory, without creating VM\n    quickget \\-\\-download elementary 7.1\n.EE\n.PP\nThe \\f[CR]\\-\\-url\\f[R], \\f[CR]\\-\\-check\\f[R], and\n\\f[CR]\\-\\-download\\f[R] options are fully functional for all operating\nsystems, including Windows and macOS.\n.PP\nFurther information is available from the project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features\nwiki\n.UE \\c\n.SS Other Operating Systems\n\\f[CR]quickget\\f[R] also supports:\n.IP \\(bu 2\n\\f[CR]alma\\f[R] (AlmaLinux)\n.IP \\(bu 2\n\\f[CR]alpine\\f[R] (Alpine Linux)\n.IP \\(bu 2\n\\f[CR]android\\f[R] (Android x86)\n.IP \\(bu 2\n\\f[CR]antix\\f[R] (Antix)\n.IP \\(bu 2\n\\f[CR]archcraft\\f[R] (Archcraft)\n.IP \\(bu 2\n\\f[CR]archlinux\\f[R] (Arch Linux)\n.IP \\(bu 2\n\\f[CR]artixlinux\\f[R] (Artix Linux)\n.IP \\(bu 2\n\\f[CR]azurelinux\\f[R] (Azure Linux)\n.IP \\(bu 2\n\\f[CR]batocera\\f[R] (Batocera)\n.IP \\(bu 2\n\\f[CR]bazzite\\f[R] (Bazzite)\n.IP \\(bu 2\n\\f[CR]biglinux\\f[R] (BigLinux)\n.IP \\(bu 2\n\\f[CR]blendos\\f[R] (BlendOS)\n.IP \\(bu 2\n\\f[CR]bodhi\\f[R] (Bodhi)\n.IP \\(bu 2\n\\f[CR]bunsenlabs\\f[R] (BunsenLabs)\n.IP \\(bu 2\n\\f[CR]cachyos\\f[R] (CachyOS)\n.IP \\(bu 2\n\\f[CR]centos\\-stream\\f[R] (CentOS Stream)\n.IP \\(bu 2\n\\f[CR]chimeralinux\\f[R] (Chimera Linux)\n.IP \\(bu 2\n\\f[CR]crunchbang++\\f[R] (Crunchbangplusplus)\n.IP \\(bu 2\n\\f[CR]debian\\f[R] (Debian)\n.IP \\(bu 2\n\\f[CR]deepin\\f[R] (Deepin)\n.IP \\(bu 2\n\\f[CR]devuan\\f[R] (Devuan)\n.IP \\(bu 2\n\\f[CR]dragonflybsd\\f[R] (DragonFlyBSD)\n.IP \\(bu 2\n\\f[CR]easyos\\f[R] (EasyOS)\n.IP \\(bu 2\n\\f[CR]elementary\\f[R] (elementary OS)\n.IP \\(bu 2\n\\f[CR]endeavouros\\f[R] (EndeavourOS)\n.IP \\(bu 2\n\\f[CR]endless\\f[R] (Endless OS)\n.IP \\(bu 2\n\\f[CR]fedora\\f[R] (Fedora)\n.IP \\(bu 2\n\\f[CR]freebsd\\f[R] (FreeBSD)\n.IP \\(bu 2\n\\f[CR]freedos\\f[R] (FreeDOS)\n.IP \\(bu 2\n\\f[CR]garuda\\f[R] (Garuda Linux)\n.IP \\(bu 2\n\\f[CR]gentoo\\f[R] (Gentoo)\n.IP \\(bu 2\n\\f[CR]ghostbsd\\f[R] (GhostBSD)\n.IP \\(bu 2\n\\f[CR]gnomeos\\f[R] (GNOME OS)\n.IP \\(bu 2\n\\f[CR]guix\\f[R] (Guix)\n.IP \\(bu 2\n\\f[CR]haiku\\f[R] (Haiku)\n.IP \\(bu 2\n\\f[CR]kali\\f[R] (Kali)\n.IP \\(bu 2\n\\f[CR]kdeneon\\f[R] (KDE Neon)\n.IP \\(bu 2\n\\f[CR]kolibrios\\f[R] (KolibriOS)\n.IP \\(bu 2\n\\f[CR]linuxlite\\f[R] (Linux Lite)\n.IP \\(bu 2\n\\f[CR]linuxmint\\f[R] (Linux Mint)\n.IP \\(bu 2\n\\f[CR]lmde\\f[R] (Linux Mint Debian Edition)\n.IP \\(bu 2\n\\f[CR]maboxlinux\\f[R] (Mabox Linux)\n.IP \\(bu 2\n\\f[CR]mageia\\f[R] (Mageia)\n.IP \\(bu 2\n\\f[CR]manjaro\\f[R] (Manjaro)\n.IP \\(bu 2\n\\f[CR]mxlinux\\f[R] (MX Linux)\n.IP \\(bu 2\n\\f[CR]netboot\\f[R] (netboot.xyz)\n.IP \\(bu 2\n\\f[CR]netbsd\\f[R] (NetBSD)\n.IP \\(bu 2\n\\f[CR]nitrux\\f[R] (Nitrux)\n.IP \\(bu 2\n\\f[CR]nixos\\f[R] (NixOS)\n.IP \\(bu 2\n\\f[CR]nwg\\-shell\\f[R] (nwg\\-shell)\n.IP \\(bu 2\n\\f[CR]openbsd\\f[R] (OpenBSD)\n.IP \\(bu 2\n\\f[CR]openindiana\\f[R] (OpenIndiana)\n.IP \\(bu 2\n\\f[CR]opensuse\\f[R] (openSUSE)\n.IP \\(bu 2\n\\f[CR]oraclelinux\\f[R] (Oracle Linux)\n.IP \\(bu 2\n\\f[CR]parrotsec\\f[R] (Parrot Security)\n.IP \\(bu 2\n\\f[CR]pclinuxos\\f[R] (PCLinuxOS)\n.IP \\(bu 2\n\\f[CR]peppermint\\f[R] (PeppermintOS)\n.IP \\(bu 2\n\\f[CR]popos\\f[R] (Pop!_OS)\n.IP \\(bu 2\n\\f[CR]porteus\\f[R] (Porteus)\n.IP \\(bu 2\n\\f[CR]primtux\\f[R] (PrimTux)\n.IP \\(bu 2\n\\f[CR]proxmox\\-ve\\f[R] (Proxmox VE)\n.IP \\(bu 2\n\\f[CR]pureos\\f[R] (PureOS)\n.IP \\(bu 2\n\\f[CR]reactos\\f[R] (ReactOS)\n.IP \\(bu 2\n\\f[CR]rebornos\\f[R] (RebornOS)\n.IP \\(bu 2\n\\f[CR]rockylinux\\f[R] (Rocky Linux)\n.IP \\(bu 2\n\\f[CR]siduction\\f[R] (Siduction)\n.IP \\(bu 2\n\\f[CR]slackware\\f[R] (Slackware)\n.IP \\(bu 2\n\\f[CR]slax\\f[R] (Slax)\n.IP \\(bu 2\n\\f[CR]slint\\f[R] (Slint)\n.IP \\(bu 2\n\\f[CR]slitaz\\f[R] (SliTaz)\n.IP \\(bu 2\n\\f[CR]solus\\f[R] (Solus)\n.IP \\(bu 2\n\\f[CR]sparkylinux\\f[R] (SparkyLinux)\n.IP \\(bu 2\n\\f[CR]spirallinux\\f[R] (SpiralLinux)\n.IP \\(bu 2\n\\f[CR]tails\\f[R] (Tails)\n.IP \\(bu 2\n\\f[CR]tinycore\\f[R] (Tiny Core Linux)\n.IP \\(bu 2\n\\f[CR]trisquel\\f[R] (Trisquel)\n.IP \\(bu 2\n\\f[CR]tuxedo\\-os\\f[R] (Tuxedo OS)\n.IP \\(bu 2\n\\f[CR]vanillaos\\f[R] (Vanilla OS)\n.IP \\(bu 2\n\\f[CR]void\\f[R] (Void Linux)\n.IP \\(bu 2\n\\f[CR]zorin\\f[R] (Zorin OS)\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines#manually-create-linux-guests\nCustom Linux guests\n.UE \\c\nOr you can download a Linux image and manually create a VM\nconfiguration.\n.IP \\(bu 2\nDownload a .iso image of a Linux distribution\n.IP \\(bu 2\nCreate a VM configuration file; for example\n\\f[CR]debian\\-bullseye.conf\\f[R]\n.IP\n.EX\nguest_os=\\(dqlinux\\(dq\ndisk_img=\\(dqdebian\\-bullseye/disk.qcow2\\(dq\niso=\\(dqdebian\\-bullseye/firmware\\-11.0.0\\-amd64\\-DVD\\-1.iso\\(dq\n.EE\n.IP \\(bu 2\nUse \\f[CR]quickemu\\f[R] to start the virtual machine:\n.IP\n.EX\nquickemu \\-\\-vm debian\\-bullseye.conf\n.EE\n.IP \\(bu 2\nComplete the installation as normal.\n.IP \\(bu 2\nPost\\-install:\n.RS 2\n.IP \\(bu 2\nInstall the SPICE agent (\\f[CR]spice\\-vdagent\\f[R]) in the guest to\nenable copy/paste and USB redirection.\n.IP \\(bu 2\nInstall the SPICE WebDAV agent (\\f[CR]spice\\-webdavd\\f[R]) in the guest\nto enable file sharing.\n.RE\n.SS Supporting old Linux distros\nIf you want to run an old Linux , from 2016 or earlier, change the\n\\f[CR]guest_os\\f[R] to \\f[CR]linux_old\\f[R].\nThis will enable the \\f[CR]vmware\\-svga\\f[R] graphics driver which is\nbetter supported on older distros.\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests\nCreating macOS Guests\n.UE \\c\n\\ 🍏\n\\f[B]Installing macOS in a VM can be a bit finicky, if you encounter\nproblems, \\c\n.UR https://github.com/quickemu-project/quickemu/discussions\ncheck the Discussions\n.UE \\c\n\\ for solutions or ask for help there\\f[R] 🛟\n.PP\n\\f[CR]quickget\\f[R] automatically downloads a macOS recovery image and\ncreates a virtual machine configuration.\n.PP\nNote: Some VPN users may need to \\c\n.UR https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-3506845235\nturn off their VPN\n.UE \\c\n\\ in order to download a recovery image.\nSome other users may find \\c\n.UR https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-2429146013\nusing a VPN\n.UE \\c\n\\ necessary in order to download a recovery image.\n.IP\n.EX\nquickget macos big\\-sur\nquickemu \\-\\-vm macos\\-big\\-sur.conf\n.EE\n.PP\nmacOS \\f[CR]mojave\\f[R], \\f[CR]catalina\\f[R], \\f[CR]big\\-sur\\f[R],\n\\f[CR]monterey\\f[R], \\f[CR]ventura\\f[R] and \\f[CR]sonoma\\f[R] are\nsupported.\n.IP \\(bu 2\nUse cursor keys and enter key to select the \\f[B]macOS Base System\\f[R]\n.IP \\(bu 2\nFrom \\f[B]macOS Utilities\\f[R]\n.RS 2\n.IP \\(bu 2\nClick \\f[B]Disk Utility\\f[R] and \\f[B]Continue\\f[R]\n.RS 2\n.IP \\(bu 2\nSelect \\f[CR]QEMU HARDDISK Media\\f[R] (\\(ti103.08GB) from the list (on\nBig Sur and above use \\f[CR]Apple Inc. VirtIO Block Device\\f[R]) and\nclick \\f[B]Erase\\f[R].\n.IP \\(bu 2\nEnter a \\f[CR]Name:\\f[R] for the disk\n.IP \\(bu 2\nIf you are installing macOS Mojave or later (Catalina, Big Sur,\nMonterey, Ventura and Sonoma), choose any of the APFS options as the\nfilesystem.\nMacOS Extended may not work.\n.RE\n.IP \\(bu 2\nClick \\f[B]Erase\\f[R].\n.IP \\(bu 2\nClick \\f[B]Done\\f[R].\n.IP \\(bu 2\nClose Disk Utility\n.RE\n.IP \\(bu 2\nFrom \\f[B]macOS Utilities\\f[R]\n.RS 2\n.IP \\(bu 2\nClick \\f[B]Reinstall macOS\\f[R] and \\f[B]Continue\\f[R]\n.RE\n.IP \\(bu 2\nComplete the installation as you normally would.\n.RS 2\n.IP \\(bu 2\nOn the first reboot use cursor keys and enter key to select \\f[B]macOS\nInstaller\\f[R]\n.IP \\(bu 2\nOn the subsequent reboots use cursor keys and enter key to select the\ndisk you named\n.RE\n.IP \\(bu 2\nOnce you have finished installing macOS you will be presented with an\nthe out\\-of\\-the\\-box first\\-start wizard to configure various options\nand set up your username and password\n.IP \\(bu 2\nOPTIONAL: After you have concluded the out\\-of\\-the\\-box wizard, you may\nwant to enable the TRIM feature that the computer industry created for\nSSD disks.\nThis feature in our macOS installation will allow QuickEmu to compact\n(shrink) your macOS disk image whenever you delete files inside the\nVirtual Machine.\nWithout this step your macOS disk image will only ever get larger and\nwill not shrink even when you delete lots of data inside macOS.\n.RS 2\n.IP \\(bu 2\nTo enable TRIM, open the Terminal application and type the following\ncommand followed by pressing enter to tell macos to use the TRIM command\non the hard disk when files are deleted:\n.RE\n.IP\n.EX\nsudo trimforce enable\n.EE\n.PP\nYou will be prompted to enter your account\\(aqs password to gain the\nprivilege needed.\nOnce you\\(aqve entered your password and pressed enter the command will\nrequest confirmation in the form of two questions that require you to\ntype y (for a \\(dqyes\\(dq response) followed by enter to confirm.\n.PP\nIf you press enter without first typing y the system will consider that\na negative response as though you said \\(dqno\\(dq:\n.IP\n.EX\nIMPORTANT NOTICE: This tool force\\-enables TRIM for all relevant attached devices, even though such devices may not have been validated for data integrity while using TRIM. Use of this tool to enable TRIM may result in unintended data loss or data corruption. It should not be used in a commercial operating environment or with important data. Before using this tool, you should back up all of your data and regularly back up data while TRIM is enabled. This tool is provided on an \\(dqas is\\(dq basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON\\-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS TOOL OR ITS USE ALONE OR IN COMBINATION WITH YOUR DEVICES, SYSTEMS, OR SERVICES. BY USING THIS TOOL TO ENABLE TRIM, YOU AGREE THAT, TO THE EXTENT PERMITTED BY APPLICABLE LAW, USE OF THE TOOL IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU.\nAre you sure you with to proceed (y/N)?\n.EE\n.PP\nAnd a second confirmation once you\\(aqve confirmed the previous one:\n.IP\n.EX\nYour system will immediately reboot when this is complete.\nIs this OK (y/N)?\n.EE\n.PP\nAs the last message states, your system will automatically reboot as\nsoon as the command completes.\n.PP\nThe default macOS configuration looks like this:\n.IP\n.EX\nguest_os=\\(dqmacos\\(dq\nimg=\\(dqmacos\\-big\\-sur/RecoveryImage.img\\(dq\ndisk_img=\\(dqmacos\\-big\\-sur/disk.qcow2\\(dq\nmacos_release=\\(dqbig\\-sur\\(dq\n.EE\n.IP \\(bu 2\n\\f[CR]guest_os=\\(dqmacos\\(dq\\f[R] instructs Quickemu to optimise for\nmacOS.\n.IP \\(bu 2\n\\f[CR]macos_release=\\(dqbig\\-sur\\(dq\\f[R] instructs Quickemu to optimise\nfor a particular macOS release.\n.RS 2\n.IP \\(bu 2\nFor example VirtIO Network and Memory Ballooning are available in Big\nSur and newer, but not previous releases.\n.IP \\(bu 2\nAnd VirtIO Block Media (disks) are supported/stable in Catalina and\nnewer.\n.RE\n.SH macOS compatibility\nThere are some considerations when running macOS via Quickemu.\n.IP \\(bu 2\nSupported macOS releases:\n.RS 2\n.IP \\(bu 2\nMojave\n.IP \\(bu 2\nCatalina\n.IP \\(bu 2\nBig Sur\n.IP \\(bu 2\nMonterey\n.IP \\(bu 2\nVentura\n.IP \\(bu 2\nSonoma\n.RE\n.IP \\(bu 2\n\\f[CR]quickemu\\f[R] will automatically download the required \\c\n.UR https://github.com/acidanthera/OpenCorePkg\nOpenCore\n.UE \\c\n\\ bootloader and OVMF firmware from \\c\n.UR https://github.com/kholia/OSX-KVM\nOSX\\-KVM\n.UE \\c\n\\&.\n.IP \\(bu 2\nOptimised by default, but no GPU acceleration is available.\n.RS 2\n.IP \\(bu 2\nHost CPU vendor is detected and guest CPU is optimised accordingly.\n.IP \\(bu 2\n\\c\n.UR https://www.kraxel.org/blog/2019/06/macos-qemu-guest/\nVirtIO Block Media\n.UE \\c\n\\ is used for the system disk where supported.\n.IP \\(bu 2\n\\c\n.UR http://philjordan.eu/osx-virt/\nVirtIO \\f[CR]usb\\-tablet\\f[R]\n.UE \\c\n\\ is used for the mouse.\n.IP \\(bu 2\nVirtIO Network (\\f[CR]virtio\\-net\\f[R]) is supported and enabled on\nmacOS Big Sur and newer, but earlier releases use \\f[CR]vmxnet3\\f[R].\n.IP \\(bu 2\nVirtIO Memory Ballooning is supported and enabled on macOS Big Sur and\nnewer but disabled for other support macOS releases.\n.RE\n.IP \\(bu 2\nUSB host and SPICE pass\\-through is:\n.RS 2\n.IP \\(bu 2\nUHCI (USB 2.0) on macOS Catalina and earlier.\n.IP \\(bu 2\nXHCI (USB 3.0) on macOS Big Sur and newer.\n.RE\n.IP \\(bu 2\nDisplay resolution can be changed via \\f[CR]quickemu\\f[R] using\n\\f[CR]\\-\\-width\\f[R] and \\f[CR]\\-\\-height\\f[R] command line arguments.\n.IP \\(bu 2\n\\f[B]Full Duplex audio requires \\c\n.UR https://github.com/chris1111/VoodooHDA-OC\nVoodooHDA OC\n.UE \\c\n\\ or pass\\-through a USB audio\\-device to the macOS guest VM\\f[R].\n.RS 2\n.IP \\(bu 2\nNOTE!\n\\c\n.UR https://disable-gatekeeper.github.io/\nGatekeeper\n.UE \\c\n\\ and \\c\n.UR https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection\nSystem Integrity Protection (SIP)\n.UE \\c\n\\ need to be disabled to install VoodooHDA OC\n.RE\n.IP \\(bu 2\nFile sharing between guest and host is available via \\c\n.UR https://wiki.qemu.org/Documentation/9psetup\nvirtio\\-9p\n.UE \\c\n\\ and \\c\n.UR https://gitlab.gnome.org/GNOME/phodav/-/merge_requests/24\nSPICE webdavd\n.UE \\c\n\\&.\n.IP \\(bu 2\nCopy/paste via SPICE agent is \\f[B]not available on macOS\\f[R].\n.SH macOS App Store\nIf you see \\f[I]\\(dqYour device or computer could not be\nverified\\(dq\\f[R] when you try to login to the App Store, make sure that\nyour wired ethernet device is \\f[CR]en0\\f[R].\nUse \\f[CR]ifconfig\\f[R] in a terminal to verify this.\n.PP\nIf the wired ethernet device is not \\f[CR]en0\\f[R], then then go to\n\\f[I]System Preferences\\f[R] \\-> \\f[I]Network\\f[R], delete all the\nnetwork devices and apply the changes.\nNext, open a terminal and run the following:\n.IP\n.EX\nsudo rm /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist\n.EE\n.PP\nNow reboot, and the App Store should work.\n.PP\nThere may be further advice and information about macOS guests in the\nproject \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests\nwiki\n.UE \\c\n\\&.\n.SS \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines\nCreating Windows guests\n.UE \\c\n\\ 🪟\n\\f[CR]quickget\\f[R] can download \\c\n.UR https://www.microsoft.com/software-download/windows10\n\\f[B]Windows 10\\f[R]\n.UE \\c\n\\ and \\c\n.UR https://www.microsoft.com/software-download/windows11\n\\f[B]Windows 11\\f[R]\n.UE \\c\n\\ automatically and create an optimised virtual machine configuration.\nThis configuration also includes the \\c\n.UR https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/\nVirtIO drivers for Windows\n.UE \\c\n\\&.\n.PP\n\\f[B]Windows 8.1\\f[R] is also supported but doesn\\(aqt feature any\nautomated installation or driver optimisation.\n.PP\n\\f[CR]quickget\\f[R] can also download \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise\nWindows 10 LTSC\n.UE \\c\n\\ and Windows Server \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2\n2012\\-r2\n.UE \\c\n, \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016\n2016\n.UE \\c\n, \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019\n2019\n.UE \\c\n, and \\c\n.UR https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022\n2022\n.UE \\c\n\\&.\nNo automated installation is supported for these releases.\n.IP\n.EX\nquickget windows 11\nquickemu \\-\\-vm windows\\-11.conf\n.EE\n.IP \\(bu 2\nComplete the installation as you normally would.\n.IP \\(bu 2\nAll relevant drivers and services should be installed automatically.\n.IP \\(bu 2\nA local administrator user account is automatically created, with these\ncredentials:\n.RS 2\n.IP \\(bu 2\nUsername: \\f[CR]Quickemu\\f[R]\n.IP \\(bu 2\nPassword: \\f[CR]quickemu\\f[R]\n.RE\n.PP\nFurther information is available from the project \\c\n.UR https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines\nwiki\n.UE \\c\n.SH AUTHORS\nWritten by Martin Wimpress.\n.SH BUGS\nSubmit bug reports online at: \\c\n.UR https://github.com/quickemu-project/quickemu/issues\n.UE \\c\n.SH SEE ALSO\nFull sources at: \\c\n.UR https://github.com/quickemu-project/quickemu\n.UE \\c\n.PP\nquickemu(1), quickemu_conf(5), quickgui(1)\n.SH AUTHORS\nMartin Wimpress.\n"
  },
  {
    "path": "docs/quickget.1.md",
    "content": "---\nauthor: Martin Wimpress\ndate: February 2, 2026\nfooter: quickget\nheader: Quickget User Manual\nsection: 1\ntitle: QUICKGET\n---\n\n# NAME\n\nquickget - download and prepare materials for building a quickemu VM\n\n# SYNOPSIS\n\n**quickget** \\[*os*\\] \\[*release*\\] \\[*edition*\\] \\| \\[*OPTION*\\]\\*\n\n# DESCRIPTION\n\n**quickget** will download the requisite materials and prepare a\nconfiguration for `quickemu` to use to build and run\n\n# OPTIONS\n\n**\\[OS\\] \\[Release\\] \\[Edition\\]**\n:   specify the OS and release (and optional edition) if insufficient\n    input is provided a list of missing options will be reported and the\n    script will exit. Editions may not apply and will be defaulted if\n    not provided.\n\n**--download** <os> <release> \\[edition\\]\n:   Download image; no VM configuration\n\n**--create-config** <os> \\[path/url\\]\n:   Create VM config for a OS image\n\n**--open-homepage** <os>\n:   Open homepage for the OS\n\n**--show** \\[os\\]\n:   Show OS information\n\n**--url** \\[os\\] \\[release\\] \\[edition\\]\n:   Show image URL(s)\n\n**--check** \\[os\\] \\[release\\] \\[edition\\]\n:   Check image URL(s)\n\n**--list**\n:   List all supported systems\n\n**--list-csv**\n:   List everything in csv format\n\n**--list-json**\n:   List everything in json format\n\n**--version**\n:   Show version\n\n**--help**\n:   Show this help message\n\n# NOTES\n\n## Creating Linux guests 🐧\n\n### Ubuntu\n\n`quickget` will automatically download an Ubuntu release and create the\nvirtual machine configuration.\n\n``` shell\nquickget ubuntu 22.04\nquickemu --vm ubuntu-22.04.conf\n```\n\n- Complete the installation as normal.\n- Post-install:\n  - Install the SPICE agent (`spice-vdagent`) in the guest to enable\n    copy/paste and USB redirection\n    - `sudo apt install spice-vdagent`\n  - Install the SPICE WebDAV agent (`spice-webdavd`) in the guest to\n    enable file sharing.\n    - `sudo apt install spice-webdavd`\n\n### Ubuntu daily-live images\n\n`quickget` can also download/refresh daily-live images via `zsync` for\nUbuntu developers and testers.\n\n``` shell\nquickget ubuntu daily-live\nquickemu --vm ubuntu-daily-live.conf\n```\n\nYou can run `quickget ubuntu daily-live` to refresh your daily\ndevelopment image as often as you like, it will even automatically\nswitch to a new series.\n\n### Ubuntu Flavours\n\nAll the official Ubuntu flavours are supported, just replace `ubuntu`\nwith your preferred flavour.\n\nThe project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines)\nmay have further information.\n\n- `edubuntu` (Edubuntu)\n- `kubuntu` (Kubuntu)\n- `lubuntu` (Lubuntu)\n- `ubuntu-budgie` (Ubuntu Budgie)\n- `ubuntucinnamon` (Ubuntu Cinnamon)\n- `ubuntukylin` (Ubuntu Kylin)\n- `ubuntu-mate` (Ubuntu MATE)\n- `ubuntu-server` (Ubuntu Server)\n- `ubuntustudio` (Ubuntu Studio)\n- `ubuntu` (Ubuntu)\n- `ubuntu-unity` (Ubuntu Unity)\n- `xubuntu` (Xubuntu)\n\nYou can also use `quickget` with advanced options :\n\n``` text\n   --arch           <arch>                    : Set architecture (arm64, aarch64, amd64, x86_64)\n   --download       <os> <release> [edition]  : Download image; no VM configuration\n   --create-config  <os> [path/url] [flags]   : Create VM config for an OS image\n   --open-homepage  <os>                      : Open homepage for the OS\n   --show           [os]                      : Show OS information\n   --version                                  : Show version\n   --help                                     : Show this help message\n------------------------------------ Flags -------------------------------------\n--create-config:\n  --disable-unattended                        : Force quickget not to set up an unattended installation\n-------------------------- For testing & development ---------------------------\n   --url            [os] [release] [edition]  : Show image URL(s)\n   --check          [os] [release] [edition]  : Check image URL(s)\n   --check-all-arch [os] [release] [edition]  : Check downloads for all architectures (amd64 and arm64)\n   --list                                     : List all supported systems\n   --list-csv                                 : List everything in csv format\n   --list-json                                : List everything in json format\n```\n\nHere are some typical uses\n\n``` shell\n    # show an OS ISO download URL for {os} {release} [edition]\n    quickget --url fedora 38 Silverblue\n    # test if an OS ISO is available for {os} {release} [edition]\n    quickget --check nixos unstable plasma5\n    # open an OS distribution homepage in a browser\n    quickget --open-homepage  ubuntu-mate\n    # Only download image file into current directory, without creating VM\n    quickget --download elementary 7.1\n```\n\nThe `--url`, `--check`, and `--download` options are fully functional\nfor all operating systems, including Windows and macOS.\n\nFurther information is available from the project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/06-Advanced-quickget-features)\n\n### Other Operating Systems\n\n`quickget` also supports:\n\n- `alma` (AlmaLinux)\n- `alpine` (Alpine Linux)\n- `android` (Android x86)\n- `antix` (Antix)\n- `archcraft` (Archcraft)\n- `archlinux` (Arch Linux)\n- `artixlinux` (Artix Linux)\n- `azurelinux` (Azure Linux)\n- `batocera` (Batocera)\n- `bazzite` (Bazzite)\n- `biglinux` (BigLinux)\n- `blendos` (BlendOS)\n- `bodhi` (Bodhi)\n- `bunsenlabs` (BunsenLabs)\n- `cachyos` (CachyOS)\n- `centos-stream` (CentOS Stream)\n- `chimeralinux` (Chimera Linux)\n- `crunchbang++` (Crunchbangplusplus)\n- `debian` (Debian)\n- `deepin` (Deepin)\n- `devuan` (Devuan)\n- `dragonflybsd` (DragonFlyBSD)\n- `easyos` (EasyOS)\n- `elementary` (elementary OS)\n- `endeavouros` (EndeavourOS)\n- `endless` (Endless OS)\n- `fedora` (Fedora)\n- `freebsd` (FreeBSD)\n- `freedos` (FreeDOS)\n- `garuda` (Garuda Linux)\n- `gentoo` (Gentoo)\n- `ghostbsd` (GhostBSD)\n- `gnomeos` (GNOME OS)\n- `guix` (Guix)\n- `haiku` (Haiku)\n- `kali` (Kali)\n- `kdeneon` (KDE Neon)\n- `kolibrios` (KolibriOS)\n- `linuxlite` (Linux Lite)\n- `linuxmint` (Linux Mint)\n- `lmde` (Linux Mint Debian Edition)\n- `maboxlinux` (Mabox Linux)\n- `mageia` (Mageia)\n- `manjaro` (Manjaro)\n- `mxlinux` (MX Linux)\n- `netboot` (netboot.xyz)\n- `netbsd` (NetBSD)\n- `nitrux` (Nitrux)\n- `nixos` (NixOS)\n- `nwg-shell` (nwg-shell)\n- `openbsd` (OpenBSD)\n- `openindiana` (OpenIndiana)\n- `opensuse` (openSUSE)\n- `oraclelinux` (Oracle Linux)\n- `parrotsec` (Parrot Security)\n- `pclinuxos` (PCLinuxOS)\n- `peppermint` (PeppermintOS)\n- `popos` (Pop!\\_OS)\n- `porteus` (Porteus)\n- `primtux` (PrimTux)\n- `proxmox-ve` (Proxmox VE)\n- `pureos` (PureOS)\n- `reactos` (ReactOS)\n- `rebornos` (RebornOS)\n- `rockylinux` (Rocky Linux)\n- `siduction` (Siduction)\n- `slackware` (Slackware)\n- `slax` (Slax)\n- `slint` (Slint)\n- `slitaz` (SliTaz)\n- `solus` (Solus)\n- `sparkylinux` (SparkyLinux)\n- `spirallinux` (SpiralLinux)\n- `tails` (Tails)\n- `tinycore` (Tiny Core Linux)\n- `trisquel` (Trisquel)\n- `tuxedo-os` (Tuxedo OS)\n- `vanillaos` (Vanilla OS)\n- `void` (Void Linux)\n- `zorin` (Zorin OS)\n\n### [Custom Linux guests](https://github.com/quickemu-project/quickemu/wiki/02-Create-Linux-virtual-machines#manually-create-linux-guests)\n\nOr you can download a Linux image and manually create a VM\nconfiguration.\n\n- Download a .iso image of a Linux distribution\n- Create a VM configuration file; for example `debian-bullseye.conf`\n\n``` shell\nguest_os=\"linux\"\ndisk_img=\"debian-bullseye/disk.qcow2\"\niso=\"debian-bullseye/firmware-11.0.0-amd64-DVD-1.iso\"\n```\n\n- Use `quickemu` to start the virtual machine:\n\n``` shell\nquickemu --vm debian-bullseye.conf\n```\n\n- Complete the installation as normal.\n- Post-install:\n  - Install the SPICE agent (`spice-vdagent`) in the guest to enable\n    copy/paste and USB redirection.\n  - Install the SPICE WebDAV agent (`spice-webdavd`) in the guest to\n    enable file sharing.\n\n## Supporting old Linux distros\n\nIf you want to run an old Linux , from 2016 or earlier, change the\n`guest_os` to `linux_old`. This will enable the `vmware-svga` graphics\ndriver which is better supported on older distros.\n\n## [Creating macOS Guests](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests) 🍏\n\n**Installing macOS in a VM can be a bit finicky, if you encounter\nproblems, [check the\nDiscussions](https://github.com/quickemu-project/quickemu/discussions)\nfor solutions or ask for help there** 🛟\n\n`quickget` automatically downloads a macOS recovery image and creates a\nvirtual machine configuration.\n\nNote: Some VPN users may need to [turn off their\nVPN](https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-3506845235)\nin order to download a recovery image. Some other users may find [using\na\nVPN](https://github.com/quickemu-project/quickemu/issues/1391#issuecomment-2429146013)\nnecessary in order to download a recovery image.\n\n``` shell\nquickget macos big-sur\nquickemu --vm macos-big-sur.conf\n```\n\nmacOS `mojave`, `catalina`, `big-sur`, `monterey`, `ventura` and\n`sonoma` are supported.\n\n- Use cursor keys and enter key to select the **macOS Base System**\n- From **macOS Utilities**\n  - Click **Disk Utility** and **Continue**\n    - Select `QEMU HARDDISK Media` (~103.08GB) from the list (on Big Sur\n      and above use `Apple Inc. VirtIO Block Device`) and click\n      **Erase**.\n    - Enter a `Name:` for the disk\n    - If you are installing macOS Mojave or later (Catalina, Big Sur,\n      Monterey, Ventura and Sonoma), choose any of the APFS options as\n      the filesystem. MacOS Extended may not work.\n  - Click **Erase**.\n  - Click **Done**.\n  - Close Disk Utility\n- From **macOS Utilities**\n  - Click **Reinstall macOS** and **Continue**\n- Complete the installation as you normally would.\n  - On the first reboot use cursor keys and enter key to select **macOS\n    Installer**\n  - On the subsequent reboots use cursor keys and enter key to select\n    the disk you named\n- Once you have finished installing macOS you will be presented with an\n  the out-of-the-box first-start wizard to configure various options and\n  set up your username and password\n- OPTIONAL: After you have concluded the out-of-the-box wizard, you may\n  want to enable the TRIM feature that the computer industry created for\n  SSD disks. This feature in our macOS installation will allow QuickEmu\n  to compact (shrink) your macOS disk image whenever you delete files\n  inside the Virtual Machine. Without this step your macOS disk image\n  will only ever get larger and will not shrink even when you delete\n  lots of data inside macOS.\n  - To enable TRIM, open the Terminal application and type the following\n    command followed by pressing <kbd>enter</kbd> to tell macos to use\n    the TRIM command on the hard disk when files are deleted:\n\n``` shell\nsudo trimforce enable\n```\n\nYou will be prompted to enter your account's password to gain the\nprivilege needed. Once you've entered your password and pressed\n<kbd>enter</kbd> the command will request confirmation in the form of\ntwo questions that require you to type <kbd>y</kbd> (for a \"yes\"\nresponse) followed by <kbd>enter</kbd> to confirm.\n\nIf you press <kbd>enter</kbd> without first typing <kbd>y</kbd> the\nsystem will consider that a negative response as though you said \"no\":\n\n``` plain\nIMPORTANT NOTICE: This tool force-enables TRIM for all relevant attached devices, even though such devices may not have been validated for data integrity while using TRIM. Use of this tool to enable TRIM may result in unintended data loss or data corruption. It should not be used in a commercial operating environment or with important data. Before using this tool, you should back up all of your data and regularly back up data while TRIM is enabled. This tool is provided on an \"as is\" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS TOOL OR ITS USE ALONE OR IN COMBINATION WITH YOUR DEVICES, SYSTEMS, OR SERVICES. BY USING THIS TOOL TO ENABLE TRIM, YOU AGREE THAT, TO THE EXTENT PERMITTED BY APPLICABLE LAW, USE OF THE TOOL IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU.\nAre you sure you with to proceed (y/N)?\n```\n\nAnd a second confirmation once you've confirmed the previous one:\n\n``` plain\nYour system will immediately reboot when this is complete.\nIs this OK (y/N)?\n```\n\nAs the last message states, your system will automatically reboot as\nsoon as the command completes.\n\nThe default macOS configuration looks like this:\n\n``` shell\nguest_os=\"macos\"\nimg=\"macos-big-sur/RecoveryImage.img\"\ndisk_img=\"macos-big-sur/disk.qcow2\"\nmacos_release=\"big-sur\"\n```\n\n- `guest_os=\"macos\"` instructs Quickemu to optimise for macOS.\n- `macos_release=\"big-sur\"` instructs Quickemu to optimise for a\n  particular macOS release.\n  - For example VirtIO Network and Memory Ballooning are available in\n    Big Sur and newer, but not previous releases.\n  - And VirtIO Block Media (disks) are supported/stable in Catalina and\n    newer.\n\n# macOS compatibility\n\nThere are some considerations when running macOS via Quickemu.\n\n- Supported macOS releases:\n  - Mojave\n  - Catalina\n  - Big Sur\n  - Monterey\n  - Ventura\n  - Sonoma\n- `quickemu` will automatically download the required\n  [OpenCore](https://github.com/acidanthera/OpenCorePkg) bootloader and\n  OVMF firmware from [OSX-KVM](https://github.com/kholia/OSX-KVM).\n- Optimised by default, but no GPU acceleration is available.\n  - Host CPU vendor is detected and guest CPU is optimised accordingly.\n  - [VirtIO Block\n    Media](https://www.kraxel.org/blog/2019/06/macos-qemu-guest/) is\n    used for the system disk where supported.\n  - [VirtIO `usb-tablet`](http://philjordan.eu/osx-virt/) is used for\n    the mouse.\n  - VirtIO Network (`virtio-net`) is supported and enabled on macOS Big\n    Sur and newer, but earlier releases use `vmxnet3`.\n  - VirtIO Memory Ballooning is supported and enabled on macOS Big Sur\n    and newer but disabled for other support macOS releases.\n- USB host and SPICE pass-through is:\n  - UHCI (USB 2.0) on macOS Catalina and earlier.\n  - XHCI (USB 3.0) on macOS Big Sur and newer.\n- Display resolution can be changed via `quickemu` using `--width` and\n  `--height` command line arguments.\n- **Full Duplex audio requires [VoodooHDA\n  OC](https://github.com/chris1111/VoodooHDA-OC) or pass-through a USB\n  audio-device to the macOS guest VM**.\n  - NOTE! [Gatekeeper](https://disable-gatekeeper.github.io/) and\n    [System Integrity Protection\n    (SIP)](https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection)\n    need to be disabled to install VoodooHDA OC\n- File sharing between guest and host is available via\n  [virtio-9p](https://wiki.qemu.org/Documentation/9psetup) and [SPICE\n  webdavd](https://gitlab.gnome.org/GNOME/phodav/-/merge_requests/24).\n- Copy/paste via SPICE agent is **not available on macOS**.\n\n# macOS App Store\n\nIf you see *\"Your device or computer could not be verified\"* when you\ntry to login to the App Store, make sure that your wired ethernet device\nis `en0`. Use `ifconfig` in a terminal to verify this.\n\nIf the wired ethernet device is not `en0`, then then go to *System\nPreferences* -\\> *Network*, delete all the network devices and apply the\nchanges. Next, open a terminal and run the following:\n\n``` shell\nsudo rm /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist\n```\n\nNow reboot, and the App Store should work.\n\nThere may be further advice and information about macOS guests in the\nproject\n[wiki](https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#automatically-create-macos-guests).\n\n## [Creating Windows guests](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines) 🪟\n\n`quickget` can download [**Windows\n10**](https://www.microsoft.com/software-download/windows10) and\n[**Windows 11**](https://www.microsoft.com/software-download/windows11)\nautomatically and create an optimised virtual machine configuration.\nThis configuration also includes the [VirtIO drivers for\nWindows](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/).\n\n**Windows 8.1** is also supported but doesn't feature any automated\ninstallation or driver optimisation.\n\n`quickget` can also download [Windows 10\nLTSC](https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise)\nand Windows Server\n[2012-r2](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2),\n[2016](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016),\n[2019](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019),\nand\n[2022](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022).\nNo automated installation is supported for these releases.\n\n``` shell\nquickget windows 11\nquickemu --vm windows-11.conf\n```\n\n- Complete the installation as you normally would.\n- All relevant drivers and services should be installed automatically.\n- A local administrator user account is automatically created, with\n  these credentials:\n  - Username: `Quickemu`\n  - Password: `quickemu`\n\nFurther information is available from the project\n[wiki](https://github.com/quickemu-project/quickemu/wiki/04-Create-Windows-virtual-machines)\n\n# AUTHORS\n\nWritten by Martin Wimpress.\n\n# BUGS\n\nSubmit bug reports online at:\n<https://github.com/quickemu-project/quickemu/issues>\n\n# SEE ALSO\n\nFull sources at: <https://github.com/quickemu-project/quickemu>\n\nquickemu(1), quickemu_conf(5), quickgui(1)\n"
  },
  {
    "path": "flake.lock",
    "content": "{\n  \"nodes\": {\n    \"flake-schemas\": {\n      \"locked\": {\n        \"lastModified\": 1772200446,\n        \"narHash\": \"sha256-hcUPpu25+VLvQsf961cu4zTeA//Ab35MaMjqSS/Ojqc=\",\n        \"rev\": \"d6a6b7cfa25bea552c197c9e227cd293ff801dbb\",\n        \"revCount\": 115,\n        \"type\": \"tarball\",\n        \"url\": \"https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.3.0/019c9f61-e746-760e-a1fe-53f05b10d026/source.tar.gz\"\n      },\n      \"original\": {\n        \"type\": \"tarball\",\n        \"url\": \"https://flakehub.com/f/DeterminateSystems/flake-schemas/%2A.tar.gz\"\n      }\n    },\n    \"nixpkgs\": {\n      \"locked\": {\n        \"lastModified\": 1771848320,\n        \"narHash\": \"sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=\",\n        \"owner\": \"nixos\",\n        \"repo\": \"nixpkgs\",\n        \"rev\": \"2fc6539b481e1d2569f25f8799236694180c0993\",\n        \"type\": \"github\"\n      },\n      \"original\": {\n        \"owner\": \"nixos\",\n        \"ref\": \"nixos-unstable\",\n        \"repo\": \"nixpkgs\",\n        \"type\": \"github\"\n      }\n    },\n    \"root\": {\n      \"inputs\": {\n        \"flake-schemas\": \"flake-schemas\",\n        \"nixpkgs\": \"nixpkgs\"\n      }\n    }\n  },\n  \"root\": \"root\",\n  \"version\": 7\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"Quickemu flake\";\n  inputs = {\n    flake-schemas.url = \"https://flakehub.com/f/DeterminateSystems/flake-schemas/*.tar.gz\";\n    nixpkgs.url = \"github:nixos/nixpkgs/nixos-unstable\";\n  };\n\n  outputs =\n    {\n      self,\n      flake-schemas,\n      nixpkgs,\n    }:\n    let\n      # Define supported systems and a helper function for generating system-specific outputs\n      supportedSystems = [\n        \"x86_64-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-darwin\"\n        \"aarch64-linux\"\n      ];\n\n      forEachSupportedSystem =\n        f:\n        nixpkgs.lib.genAttrs supportedSystems (\n          system:\n          f {\n            system = system;\n            pkgs = import nixpkgs { inherit system; };\n          }\n        );\n    in\n    {\n      # Define schemas for the flake's outputs\n      schemas = flake-schemas.schemas;\n\n      # Define overlays for each supported system\n      overlays = {\n        default =\n          final: prev:\n          let\n            # OVMF is only available/needed on Linux\n            ovmfArgs =\n              if final.stdenv.hostPlatform.isLinux then\n                { }\n              else\n                {\n                  OVMF = null;\n                  OVMFFull = null;\n                };\n          in\n          {\n            quickemu = final.callPackage ./package.nix ovmfArgs;\n          };\n      };\n\n      # Define packages for each supported system\n      packages = forEachSupportedSystem (\n        { pkgs, system, ... }:\n        let\n          # OVMF is only available/needed on Linux\n          ovmfArgs =\n            if pkgs.stdenv.hostPlatform.isLinux then\n              { }\n            else\n              {\n                OVMF = null;\n                OVMFFull = null;\n              };\n        in\n        rec {\n          quickemu = pkgs.callPackage ./package.nix ovmfArgs;\n          default = quickemu;\n        }\n      );\n\n      # Define devShells for each supported system\n      devShells = forEachSupportedSystem (\n        { pkgs, system, ... }:\n        let\n          # OVMF is only available/needed on Linux\n          ovmfArgs =\n            if pkgs.stdenv.hostPlatform.isLinux then\n              { }\n            else\n              {\n                OVMF = null;\n                OVMFFull = null;\n              };\n        in\n        {\n          default = pkgs.callPackage ./devshell.nix ovmfArgs;\n        }\n      );\n    };\n}\n"
  },
  {
    "path": "package.nix",
    "content": "{\n  lib,\n  fetchFromGitHub,\n  installShellFiles,\n  makeWrapper,\n  stdenv,\n  testers,\n  cdrtools,\n  curl,\n  gawk,\n  gnugrep,\n  gnused,\n  jq,\n  mesa-demos,\n  pciutils,\n  procps,\n  python3,\n  qemu_full,\n  samba,\n  socat,\n  spice-gtk,\n  swtpm,\n  unzip,\n  usbutils,\n  util-linux,\n  xdg-user-dirs,\n  xrandr,\n  zsync,\n  OVMF ? null,\n  OVMFFull ? null,\n}:\nlet\n  runtimePaths = [\n    cdrtools\n    curl\n    gawk\n    gnugrep\n    gnused\n    jq\n    pciutils\n    procps\n    python3\n    qemu_full\n    samba\n    socat\n    swtpm\n    unzip\n    util-linux\n    xrandr\n    zsync\n  ]\n  ++ lib.optionals stdenv.hostPlatform.isLinux [\n    mesa-demos\n    OVMF\n    OVMFFull\n    usbutils\n    xdg-user-dirs\n  ];\n  # Extract version using builtins.split to avoid regex backtracking on large files.\n  # builtins.match with .* patterns on multi-kilobyte files can cause stack overflow.\n  versionParts = builtins.split \"readonly VERSION=\\\"([0-9]+\\\\.[0-9]+\\\\.[0-9]+)\\\"\" (\n    builtins.readFile ./quickemu\n  );\n  version = builtins.elemAt (builtins.elemAt versionParts 1) 0;\nin\nstdenv.mkDerivation (finalAttrs: {\n  pname = \"quickemu\";\n  version = version;\n  src = lib.cleanSource ./.;\n\n  postPatch = ''\n    sed -i \\\n      ${\n        lib.optionalString (OVMF != null && OVMFFull != null) ''\n          -e '/OVMF_CODE_4M.secboot.fd/s|ovmfs=(|ovmfs=(\"${OVMFFull.firmware}\",\"${OVMFFull.variablesMs}\" |' \\\n          -e '/OVMF_CODE_4M.fd/s|ovmfs=(|ovmfs=(\"${OVMF.firmware}\",\"${OVMF.variables}\" |' \\\n        ''\n      } \\\n      -e '/cp \"''${VARS_IN}\" \"''${VARS_OUT}\"/a chmod +w \"''${VARS_OUT}\"' \\\n      -e 's/Icon=.*qemu.svg/Icon=qemu/' \\\n      -e 's,\\$(command -v smbd),${samba}/bin/smbd,' \\\n      quickemu\n  '';\n\n  nativeBuildInputs = [\n    makeWrapper\n    installShellFiles\n  ];\n\n  installPhase = ''\n    runHook preInstall\n\n    installManPage docs/quickget.1 docs/quickemu.1 docs/quickemu_conf.5\n    install -Dm755 -t \"$out/bin\" chunkcheck quickemu quickget quickreport\n\n    # spice-gtk needs to be put in suffix so that when virtualisation.spiceUSBRedirection\n    # is enabled, the wrapped spice-client-glib-usb-acl-helper is used\n    for f in chunkcheck quickget quickemu quickreport; do\n      wrapProgram $out/bin/$f \\\n        --prefix PATH : \"${lib.makeBinPath runtimePaths}\" \\\n        --suffix PATH : \"${lib.makeBinPath [ spice-gtk ]}\"\n    done\n\n    runHook postInstall\n  '';\n\n  passthru.tests = testers.testVersion { package = finalAttrs.finalPackage; };\n\n  meta = {\n    description = \"Quickly create and run optimised Windows, macOS and Linux virtual machines\";\n    homepage = \"https://github.com/quickemu-project/quickemu\";\n    changelog = \"https://github.com/quickemu-project/quickemu/releases/tag/${finalAttrs.version}\";\n    mainProgram = \"quickemu\";\n    license = lib.licenses.mit;\n    maintainers = with lib.maintainers; [\n      fedx-sudo\n      flexiondotorg\n    ];\n  };\n})\n"
  },
  {
    "path": "quickemu",
    "content": "#!/usr/bin/env bash\nexport LC_ALL=C\n\nif ((BASH_VERSINFO[0] < 4)); then\n    echo \"Sorry, you need bash 4.0 or newer to run this script.\"\n    exit 1\nfi\n\nfunction ignore_msrs_always() {\n    # Make sure the host has /etc/modprobe.d\n    if [ -d /etc/modprobe.d ]; then\n        # Skip if ignore_msrs is already enabled, assumes initramfs has been rebuilt\n        if ! grep -lq 'ignore_msrs=Y' /etc/modprobe.d/kvm-quickemu.conf >/dev/null 2>&1; then\n            echo \"options kvm ignore_msrs=Y\" | sudo tee /etc/modprobe.d/kvm-quickemu.conf\n            sudo update-initramfs -k all -u\n        fi\n    else\n        echo \"ERROR! /etc/modprobe.d was not found, I don't know how to configure this system.\"\n        exit 1\n    fi\n}\n\nfunction ignore_msrs_alert() {\n    local ignore_msrs=\"\"\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        return\n    elif [ -e /sys/module/kvm/parameters/ignore_msrs ]; then\n        ignore_msrs=$(cat /sys/module/kvm/parameters/ignore_msrs)\n        if [ \"${ignore_msrs}\" == \"N\" ]; then\n            echo \" - MSR:      WARNING! Ignoring unhandled Model-Specific Registers is disabled.\"\n            echo\n            echo \"             echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs\"\n            echo\n            echo \"             If you are unable to run macOS or Windows VMs then run the above 👆\"\n            echo \"             This will enable ignoring of unhandled MSRs until you reboot the host.\"\n            echo \"             You can make this change permanent by running: 'quickemu --ignore-msrs-always'\"\n        fi\n    fi\n}\n\n# Check for TSC instability that can cause macOS Ventura+ to freeze on AMD Ryzen mobile CPUs.\n# Returns 0 if check passes or user acknowledges warning, exits with 1 if user aborts.\n# Reference: https://github.com/quickemu-project/quickemu/issues/1273\nfunction check_macos_tsc_stability() {\n  # Gate 1: Only on Linux hosts\n  if [ \"${OS_KERNEL}\" != \"Linux\" ]; then\n    return 0\n  fi\n\n  # Gate 2: Only for AMD CPUs\n  if [ \"${HOST_CPU_VENDOR}\" != \"AuthenticAMD\" ]; then\n    return 0\n  fi\n\n  # Gate 3: Only for macOS guests\n  if [ \"${guest_os}\" != \"macos\" ]; then\n    return 0\n  fi\n\n  # Gate 4: Only for macOS Ventura (13) and newer\n  case ${macos_release} in\n    ventura|sonoma|sequoia|tahoe) ;;\n    *) return 0 ;;\n  esac\n\n  # Gate 5: Skip if user has already set tsc=reliable in kernel cmdline\n  local cmdline=\"\"\n  if [ -r /proc/cmdline ]; then\n    cmdline=$(cat /proc/cmdline)\n    if [[ \"${cmdline}\" == *\"tsc=reliable\"* ]]; then\n      return 0\n    fi\n  fi\n\n  # Gate 6: Check if TSC is the current clocksource (indicates stable TSC)\n  local clocksource_path=\"/sys/devices/system/clocksource/clocksource0/current_clocksource\"\n  local current_clocksource=\"\"\n  if [ -r \"${clocksource_path}\" ]; then\n    current_clocksource=$(cat \"${clocksource_path}\")\n    if [ \"${current_clocksource}\" == \"tsc\" ]; then\n      return 0\n    fi\n  else\n    # Cannot determine clocksource - assume OK and let user discover issues\n    return 0\n  fi\n\n  # All gates failed - this system is at risk\n  # Check if warning should be skipped\n  if [ \"${IGNORE_TSC_WARNING}\" == \"1\" ]; then\n    echo \" - TSC:      WARNING! Unstable TSC detected (clocksource: ${current_clocksource})\"\n    echo \"             Proceeding anyway due to --ignore-tsc-warning flag.\"\n    return 0\n  fi\n\n  # Display warning and prompt user\n  echo \" - TSC:      WARNING! Unstable TSC detected (clocksource: ${current_clocksource})\"\n  echo \"             macOS ${macos_release^} may freeze on AMD Ryzen mobile CPUs.\"\n  echo\n  echo \"             Fix: Add 'tsc=reliable' to kernel boot parameters and reboot.\"\n  echo \"             Or:  Use macOS Big Sur (11) or Monterey (12) instead.\"\n  echo \"             See: https://github.com/quickemu-project/quickemu/wiki/03-Create-macOS-virtual-machines#tsc-instability-on-amd-ryzen-mobile-cpus\"\n  echo\n\n  # Log the warning\n  echo \"TSC_WARNING: clocksource=${current_clocksource} macos_release=${macos_release} cpu_vendor=${HOST_CPU_VENDOR}\" >> \"${VMDIR}/${VMNAME}.log\"\n\n  # Interactive prompt - check if stdin is a terminal\n  if [ -t 0 ]; then\n    echo -n \"Do you want to continue anyway? [y/N] \"\n    read -r response\n    case \"${response}\" in\n      [yY]|[yY][eE][sS])\n        echo \" - TSC:      Proceeding despite unstable TSC warning.\"\n        return 0\n        ;;\n      *)\n        echo \" - TSC:      Aborting. Please apply one of the solutions above.\"\n        exit 1\n        ;;\n    esac\n  else\n    # Non-interactive mode - abort by default for safety\n    echo \"ERROR! Non-interactive mode detected. Use --ignore-tsc-warning to bypass this check.\"\n    exit 1\n  fi\n}\n\nfunction delete_shortcut() {\n    local SHORTCUT_DIR=\"${HOME}/.local/share/applications\"\n    if [ -e \"${SHORTCUT_DIR}/${VMNAME}.desktop\" ]; then\n        rm \"${SHORTCUT_DIR}/${VMNAME}.desktop\"\n        echo \" - Deleted ${SHORTCUT_DIR}/${VMNAME}.desktop\"\n    fi\n}\n\nfunction delete_disk() {\n    echo \"Deleting ${VMNAME} virtual hard disk\"\n    if [ -e \"${disk_img}\" ]; then\n        rm \"${disk_img}\" >/dev/null 2>&1\n        # Remove any EFI vars, but not for macOS\n        rm \"${VMDIR}\"/OVMF_VARS*.fd >/dev/null 2>&1\n        rm \"${VMDIR}/${VMNAME}-vars.fd\" >/dev/null 2>&1\n        echo \" - Deleted ${disk_img}\"\n        delete_shortcut\n    else\n        echo \" - ${disk_img} not found. Doing nothing.\"\n    fi\n}\n\nfunction delete_vm() {\n    echo \"Deleting ${VMNAME} completely\"\n    if [ -d \"${VMDIR}\" ]; then\n        rm -rf \"${VMDIR}\"\n        rm \"${VM}\"\n        echo \" - Deleted ${VM} and ${VMDIR}/\"\n        delete_shortcut\n    else\n        echo \" - ${VMDIR} not found. Doing nothing.\"\n    fi\n}\n\nfunction kill_vm() {\n    echo \"Killing ${VMNAME}\"\n    if [ -z \"${VM_PID}\" ]; then\n        echo \" - ${VMNAME} is not running.\"\n        rm -f \"${VMDIR}/${VMNAME}.pid\"\n        rm -f \"${VMDIR}/${VMNAME}.spice\"\n        rm -f \"${VMDIR}/${VMNAME}.sock\"\n    elif [ -n \"${VM_PID}\" ]; then\n        if kill -9 \"${VM_PID}\" > /dev/null 2>&1; then\n            echo \" - ${VMNAME} (${VM_PID}) killed.\"\n            rm -f \"${VMDIR}/${VMNAME}.pid\"\n            rm -f \"${VMDIR}/${VMNAME}.spice\"\n            rm -f \"${VMDIR}/${VMNAME}.sock\"\n        else\n            echo \" - ${VMNAME} (${VM_PID}) was not killed.\"\n        fi\n    elif [ ! -r \"${VMDIR}/${VMNAME}.pid\" ]; then\n        echo \" - ${VMNAME} has no ${VMDIR}/${VMNAME}.pid\"\n    fi\n}\n\nfunction snapshot_apply() {\n    echo \"Snapshot apply to ${disk_img}\"\n    local TAG=\"${1}\"\n    if [ -z \"${TAG}\" ]; then\n        echo \" - ERROR! No snapshot tag provided.\"\n        exit\n    fi\n\n    if [ -e \"${disk_img}\" ]; then\n        if ${QEMU_IMG} snapshot -q -a \"${TAG}\" \"${disk_img}\"; then\n            echo \" - Applied snapshot '${TAG}' to ${disk_img}\"\n        else\n            echo \" - ERROR! Failed to apply snapshot '${TAG}' to ${disk_img}\"\n        fi\n    else\n        echo \" - NOTE! ${disk_img} not found. Doing nothing.\"\n    fi\n}\n\nfunction snapshot_create() {\n    echo \"Snapshotting ${disk_img}\"\n    local TAG=\"${1}\"\n    if [ -z \"${TAG}\" ]; then\n        echo \"- ERROR! No snapshot tag provided.\"\n        exit\n    fi\n\n    if [ -e \"${disk_img}\" ]; then\n        if ${QEMU_IMG} snapshot -q -c \"${TAG}\" \"${disk_img}\"; then\n            echo \" - Created snapshot '${TAG}' for ${disk_img}\"\n        else\n            echo \" - ERROR! Failed to create snapshot '${TAG}' for ${disk_img}\"\n        fi\n    else\n        echo \" - NOTE! ${disk_img} not found. Doing nothing.\"\n    fi\n}\n\nfunction snapshot_delete() {\n    echo \"Snapshot removal ${disk_img}\"\n    local TAG=\"${1}\"\n    if [ -z \"${TAG}\" ]; then\n        echo \" - ERROR! No snapshot tag provided.\"\n        exit\n    fi\n\n    if [ -e \"${disk_img}\" ]; then\n        if ${QEMU_IMG} snapshot -q -d \"${TAG}\" \"${disk_img}\"; then\n            echo \" - Deleted snapshot '${TAG}' from ${disk_img}\"\n        else\n            echo \" - ERROR! Failed to delete snapshot '${TAG}' from ${disk_img}\"\n        fi\n    else\n        echo \" - NOTE! ${disk_img} not found. Doing nothing.\"\n    fi\n}\n\nfunction snapshot_info() {\n    echo\n    if [ -e \"${disk_img}\" ]; then\n        ${QEMU_IMG} info \"${disk_img}\"\n    fi\n}\n\nfunction get_port() {\n    local PORT_START=$1\n    local PORT_RANGE=$((PORT_START+$2))\n    local PORT\n    for ((PORT = PORT_START; PORT <= PORT_RANGE; PORT++)); do\n        # Make sure port scans do not block too long.\n        timeout 0.1s bash -c \"echo >/dev/tcp/127.0.0.1/${PORT}\" >/dev/null 2>&1\n        if [ ${?} -eq 1 ]; then\n            echo \"${PORT}\"\n            break\n        fi\n    done\n}\n\nfunction configure_usb() {\n    local DEVICE=\"\"\n    local USB_BUS=\"\"\n    local USB_DEV=\"\"\n    local USB_NAME=\"\"\n    local VENDOR_ID=\"\"\n    local PRODUCT_ID=\"\"\n    local USB_NOT_READY=0\n\n    # Have any USB devices been requested for pass-through?\n    if (( ${#usb_devices[@]} )); then\n        echo \" - USB:      Host pass-through requested:\"\n        for DEVICE in \"${usb_devices[@]}\"; do\n            VENDOR_ID=$(echo \"${DEVICE}\" | cut -d':' -f1)\n            PRODUCT_ID=$(echo \"${DEVICE}\" | cut -d':' -f2)\n            USB_BUS=$(lsusb -d \"${VENDOR_ID}:${PRODUCT_ID}\" | cut -d' ' -f2)\n            USB_DEV=$(lsusb -d \"${VENDOR_ID}:${PRODUCT_ID}\" | cut -d' ' -f4 | cut -d':' -f1)\n            USB_NAME=$(lsusb -d \"${VENDOR_ID}:${PRODUCT_ID}\" | cut -d' ' -f7-)\n            if [ -z \"${USB_NAME}\" ]; then\n                echo \"             ! USB device ${VENDOR_ID}:${PRODUCT_ID} not found. Check your configuration\"\n                continue\n            elif [ -w \"/dev/bus/usb/${USB_BUS}/${USB_DEV}\" ]; then\n                echo \"             o ${USB_NAME} on bus ${USB_BUS} device ${USB_DEV} is accessible.\"\n            else\n                echo \"             x ${USB_NAME} on bus ${USB_BUS} device ${USB_DEV} needs permission changes:\"\n                echo \"               sudo chown -v root:${USER} /dev/bus/usb/${USB_BUS}/${USB_DEV}\"\n                USB_NOT_READY=1\n            fi\n            USB_PASSTHROUGH=\"${USB_PASSTHROUGH} -device usb-host,bus=hostpass.0,vendorid=0x${VENDOR_ID},productid=0x${PRODUCT_ID}\"\n        done\n\n        if [ \"${USB_NOT_READY}\" -eq 1 ]; then\n            echo \"               ERROR! USB permission changes are required 👆\"\n            exit 1\n        fi\n    fi\n}\n\n# get the number of processing units\nfunction get_nproc() {\n    if command -v nproc &>/dev/null; then\n        nproc\n    elif command -v sysctl &>/dev/null; then\n        sysctl -n hw.ncpu\n    else\n        echo \"ERROR! Unable to determine the number of processing units.\"\n        exit 1\n    fi\n}\n\n# macOS and Linux compatible get_cpu_info function\nfunction get_cpu_info() {\n    local INFO_NAME=\"${1}\"\n\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        if [ \"^Model name:\" == \"${INFO_NAME}\" ]; then\n            sysctl -n machdep.cpu.brand_string\n        elif [ \"Socket\" == \"${INFO_NAME}\" ]; then\n            sysctl -n hw.packages\n        elif [ \"^Vendor ID\" == \"${INFO_NAME}\" ]; then\n            if [ \"${ARCH_HOST}\" == \"arm64\" ]; then\n                sysctl -n machdep.cpu.brand_string | cut -d' ' -f1\n            else\n                sysctl -n machdep.cpu.vendor | sed 's/ //g'\n            fi\n        else\n            echo \"ERROR! Could not find macOS translation for ${INFO_NAME}\"\n            exit 1\n        fi\n    else\n        if [ \"^Model name:\" == \"${INFO_NAME}\" ]; then\n            for MODEL_NAME in $(IFS=$'\\n' lscpu | grep \"${INFO_NAME}\" | cut -d':' -f2 | sed -e 's/^[[:space:]]*//'); do\n                echo -n \"${MODEL_NAME} \"\n            done\n        else\n            lscpu | grep -E \"${INFO_NAME}\" | cut -d':' -f2 | sed 's/ //g' | sort -u\n        fi\n    fi\n}\n\n# returns an enabled or disable CPU flag for QEMU, based on the host CPU\n# capabilities, or nothing if the flag is not supported\n# converts the flags appropriately from macOS and Linux to QEMU\nfunction configure_cpu_flag() {\n    local HOST_CPU_FLAG=\"${1}\"\n    # Convert the flag to lowercase for QEMU\n    local QEMU_CPU_FLAG=${HOST_CPU_FLAG,,}\n    if check_cpu_flag \"${HOST_CPU_FLAG}\"; then\n        # Replace _ with - to make it compatible with QEMU\n        QEMU_CPU_FLAG=\"${HOST_CPU_FLAG//_/-}\"\n        QEMU_CPU_FLAG=\"${QEMU_CPU_FLAG//4_/4\\.}\"\n        # macOS uses different flag names\n        if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n            case \"${HOST_CPU_FLAG}\" in\n                avx) QEMU_CPU_FLAG=\"AVX1.0\";;\n            esac\n        fi\n        echo \",+${QEMU_CPU_FLAG}\"\n    else\n        # Fully disable any QEMU flags that are not supported by the host CPU\n        if [ \"${HOST_CPU_VENDOR}\" == \"AuthenticAMD\" ]; then\n            case ${HOST_CPU_FLAG} in\n                pcid) echo \",-${QEMU_CPU_FLAG}\";;\n            esac\n        fi\n    fi\n}\n\n# checks if a CPU flag is supported by the host CPU on Linux and macOS\nfunction check_cpu_flag() {\n    local HOST_CPU_FLAG=\"\"\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        # Make the macOS compatible: uppercase, replace _ with . and replace X2APIC with x2APIC\n        HOST_CPU_FLAG=\"${1^^}\"\n        HOST_CPU_FLAG=\"${HOST_CPU_FLAG//_/.}\"\n        HOST_CPU_FLAG=\"${HOST_CPU_FLAG//X2APIC/x2APIC}\"\n        if [ \"${HOST_CPU_FLAG}\" == \"AVX\" ]; then\n            HOST_CPU_FLAG=\"AVX1.0\"\n        fi\n        if sysctl -n machdep.cpu.features | grep -o \"${HOST_CPU_FLAG}\" > /dev/null; then\n            return 0\n        else\n            return 1\n        fi\n    else\n      HOST_CPU_FLAG=\"${1}\"\n      if lscpu | grep -o \"^Flags\\b.*: .*\\b${HOST_CPU_FLAG}\\b\" > /dev/null; then\n          return 0\n      else\n          # AMD CPUs report invtsc as constant_tsc; check for equivalence\n          if [ \"${HOST_CPU_FLAG}\" == \"invtsc\" ] && [ \"${HOST_CPU_VENDOR}\" == \"AuthenticAMD\" ]; then\n              if lscpu | grep -o \"^Flags\\b.*: .*\\bconstant_tsc\\b\" > /dev/null; then\n                  return 0\n              fi\n          fi\n          return 1\n      fi\n    fi\n}\n\nfunction efi_vars() {\n    local VARS_IN=\"\"\n    local VARS_OUT=\"\"\n    VARS_IN=\"${1}\"\n    VARS_OUT=\"${2}\"\n\n    if [ ! -e \"${VARS_OUT}\" ]; then\n        if [ -e \"${VARS_IN}\" ]; then\n            cp \"${VARS_IN}\" \"${VARS_OUT}\"\n        else\n            echo \"ERROR! ${VARS_IN} was not found. Please install edk2.\"\n            exit 1\n        fi\n    fi\n}\n\n# Reset CPU flags tracking (call when initialising new CPU string)\nfunction reset_cpu_flags() {\n  CPU_FLAG_MAP=()\n}\n\n# Add a CPU flag with deduplication and conflict detection\n# Usage: add_cpu_flag \"+vmx\" or add_cpu_flag \",+vmx\"\nfunction add_cpu_flag() {\n  local flag=\"${1#,}\"  # Strip leading comma if present\n\n  # Skip empty flags\n  [[ -z \"${flag}\" ]] && return 0\n\n  # Validate flag format: must be [+-]?name or name=value\n  if [[ ! \"${flag}\" =~ ^[+-]?[a-zA-Z][a-zA-Z0-9._-]*(=.+)?$ ]]; then\n    echo \"WARNING: Invalid CPU flag format: '${flag}' - skipping\"\n    return 1\n  fi\n\n  # Extract base flag name (without +/- prefix and =value suffix)\n  local prefix=\"\"\n  local base=\"${flag}\"\n  if [[ \"${flag}\" == [+-]* ]]; then\n    prefix=\"${flag:0:1}\"\n    base=\"${flag:1}\"\n  fi\n  base=\"${base%%=*}\"\n\n  # Check for exact duplicate\n  if [[ -n \"${CPU_FLAG_MAP[${flag}]:-}\" ]]; then\n    return 0  # Silently skip duplicates\n  fi\n\n  # Check for conflicts (opposite prefix)\n  local opposite=\"\"\n  if [[ \"${prefix}\" == \"+\" ]]; then\n    opposite=\"-${base}\"\n  elif [[ \"${prefix}\" == \"-\" ]]; then\n    opposite=\"+${base}\"\n  fi\n\n  if [[ -n \"${opposite}\" ]] && [[ -n \"${CPU_FLAG_MAP[${opposite}]:-}\" ]]; then\n    echo \"WARNING: Conflicting CPU flag '${flag}' ignored (${opposite} already set)\"\n    return 1\n  fi\n\n  # Check for value conflicts (e.g., flag=on vs flag=off)\n  if [[ \"${flag}\" == *=* ]]; then\n    for existing in \"${!CPU_FLAG_MAP[@]}\"; do\n      if [[ \"${existing%%=*}\" == \"${base}\" ]] && [[ \"${existing}\" == *=* ]]; then\n        echo \"WARNING: Conflicting CPU flag '${flag}' ignored (${existing} already set)\"\n        return 1\n      fi\n    done\n  fi\n\n  # Add to tracking map and append to CPU string\n  CPU_FLAG_MAP[\"${flag}\"]=1\n  CPU+=\",${flag}\"\n  return 0\n}\n\nfunction configure_cpu() {\n    HOST_CPU_CORES=$(get_nproc)\n    HOST_CPU_MODEL=$(get_cpu_info '^Model name:')\n    HOST_CPU_SOCKETS=$(get_cpu_info 'Socket')\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        HOST_CPU_VENDOR=$(get_cpu_info 'Vendor')\n    else\n        HOST_CPU_VENDOR=$(get_cpu_info '^Vendor ID')\n    fi\n\n    if [ \"${HOST_CPU_SOCKETS}\" = \"-\" ]; then\n        HOST_CPU_SOCKETS=1\n    fi\n\n    CPU_MODEL=\"host\"\n    QEMU_ACCEL=\"tcg\"\n    # Configure appropriately for the host platform\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        MANUFACTURER=$(ioreg -l | grep -e Manufacturer | grep -v iMan | cut -d'\"' -f4 | sort -u)\n        CPU_KVM_UNHALT=\"\"\n        QEMU_ACCEL=\"hvf\"\n        # QEMU for macOS from Homebrew does not support SMM\n        SMM=\"off\"\n    else\n        if [ -r /sys/class/dmi/id/sys_vendor ]; then\n            MANUFACTURER=$(head -n 1 /sys/class/dmi/id/sys_vendor)\n        fi\n        CPU_KVM_UNHALT=\",kvm_pv_unhalt\"\n        GUEST_TWEAKS+=\" -global kvm-pit.lost_tick_policy=discard\"\n        QEMU_ACCEL=\"kvm\"\n    fi\n\n    if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n        # ARM64 guest support\n        # https://qemu-project.gitlab.io/qemu/system/arm/virt.html\n        # highmem=on allows aarch64 guests on the \"virt\" machine type to access guest RAM\n        # above the 4GB boundary. This is required for VMs configured with >3GB RAM and is\n        # generally more compatible on modern aarch64 systems.\n        # pflash0/pflash1 reference the blockdev nodes for AAVMF firmware\n        MACHINE_TYPE=\"virt,highmem=on,pflash0=rom,pflash1=efivars\"\n        case ${ARCH_HOST} in\n            arm64|aarch64)\n                # Native ARM64 host running ARM64 guest - use hardware acceleration\n                CPU_MODEL=\"max\";;\n            *)\n                # Cross-architecture emulation (e.g., x86_64 host running ARM64 guest)\n                CPU_MODEL=\"max\"\n                QEMU_ACCEL=\"tcg\";;\n        esac\n    elif [ \"${ARCH_VM}\" != \"${ARCH_HOST}\" ]; then\n        # If the architecture of the VM is different from the host, disable acceleration\n        # and use TCG (Tiny Code Generator) software emulation. TCG emulates the target\n        # architecture in software, allowing cross-architecture virtualisation (e.g.,\n        # running x86_64 VMs on ARM hosts).\n        #\n        # Users can manually force TCG mode by adding this to their VM .conf file:\n        #   cpu_model=\"qemu64\"\n        #   extra_args=\"-accel tcg\"\n        # or by exporting QEMU_ACCEL in the shell environment:\n        #   export QEMU_ACCEL=\"tcg\"\n        #\n        # TCG is useful for:\n        # - Cross-architecture virtualisation (x86 on ARM, ARM on x86)\n        # - Testing VMs on hosts without hardware virtualisation support\n        # - Emulating CPU features in software (e.g., running macOS x86_64 VMs on ARM Macs)\n        CPU_MODEL=\"qemu64\"\n        CPU_KVM_UNHALT=\"\"\n        QEMU_ACCEL=\"tcg\"\n    fi\n\n    # Detect if running inside a VM based on manufacturer detection\n    # Note: Checking CPU flags (vmx/svm) indicates hardware virtualisation SUPPORT,\n    # not whether we're inside a VM. Nested virtualisation may expose these flags.\n    case ${MANUFACTURER,,} in\n        qemu|virtualbox) CPU_MODEL=\"qemu64\"\n                         QEMU_ACCEL=\"tcg\"\n                         HYPERVISOR=\"${MANUFACTURER,,}\";;\n        *) HYPERVISOR=\"\";;\n    esac\n\n    if [ -z \"${HYPERVISOR}\" ]; then\n        # A CPU with Intel VT-x / AMD SVM support is required\n        if [ \"${HOST_CPU_VENDOR}\" == \"GenuineIntel\" ]; then\n            if ! check_cpu_flag vmx; then\n                echo \"ERROR! Intel VT-x support is required.\"\n                exit 1\n            fi\n        elif [ \"${HOST_CPU_VENDOR}\" == \"AuthenticAMD\" ] || [ \"${HOST_CPU_VENDOR}\" == \"HygonGenuine\" ]; then\n            # HygonGenuine is Chinese AMD-compatible CPUs (Hygon Dhyana)\n            if ! check_cpu_flag svm; then\n                echo \"ERROR! AMD SVM support is required.\"\n                exit 1\n            fi\n        elif [ \"${ARCH_HOST}\" == \"aarch64\" ] || [ \"${ARCH_HOST}\" == \"arm64\" ]; then\n            # ARM hosts running native ARM guests with hardware acceleration (HVF on macOS, KVM on Linux)\n            # ARM processors don't have x86-specific virtualisation flags (VT-x/SVM) to check.\n            # We use architecture detection here instead of vendor detection (used for x86)\n            # because ARM CPUs don't have standardised vendor strings like x86 (GenuineIntel/AuthenticAMD).\n            # Cross-architecture guests (x86 on ARM) use TCG and skip this validation block entirely.\n            # No validation needed here - ARM virtualisation support is handled by the hypervisor\n            true\n        else\n            # Unknown CPU vendor - could be future/custom CPUs\n            echo \"WARNING! Unknown CPU vendor '${HOST_CPU_VENDOR}' - cannot verify virtualisation support.\"\n            echo \"         If virtualisation fails, check your CPU supports hardware virtualisation and it's enabled in firmware.\"\n        fi\n    fi\n\n    CPU=\"-cpu ${CPU_MODEL}\"\n    reset_cpu_flags\n\n    # Make any OS specific adjustments\n    if [ \"${guest_os}\" == \"freedos\" ] || [ \"${guest_os}\" == \"windows\" ] || [ \"${guest_os}\" == \"windows-server\" ]; then\n        # SMM is not available on QEMU for macOS via Homebrew\n        if [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n            SMM=\"on\"\n        fi\n    fi\n\n    # SMM is also required for Linux guests when Secure Boot is enabled\n    if [ \"${secureboot}\" == \"on\" ]; then\n        if [ \"${guest_os}\" == \"linux\" ]; then\n        # SMM is not available on QEMU for macOS via Homebrew\n            if [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n                SMM=\"on\"\n            fi\n        fi\n    fi\n\n    case ${guest_os} in\n        batocera|freedos|haiku|solaris) MACHINE_TYPE=\"pc\";;\n        kolibrios|reactos)\n            CPU=\"-cpu qemu32\"\n            reset_cpu_flags\n            MACHINE_TYPE=\"pc\";;\n        macos)\n            # If the host has an Intel CPU, passes the host CPU model features, model, stepping, exactly to the guest.\n            # Disable huge pages (,-pdpe1gb) on macOS to prevent crashes\n            # - https://stackoverflow.com/questions/60231203/qemu-qcow2-mmu-gva-to-gpa-crash-in-mac-os-x\n            # vmware-cpuid-freq=on enables VMware TSC frequency reporting for macOS timing\n            if [ \"${HOST_CPU_VENDOR}\" == \"GenuineIntel\" ] && [ -z \"${HYPERVISOR}\" ]; then\n                CPU_MODEL=\"host\"\n                CPU=\"-cpu ${CPU_MODEL},-pdpe1gb,+hypervisor,vmware-cpuid-freq=on\"\n                reset_cpu_flags\n            else\n                CPU_MODEL=\"Haswell-v2\"\n                CPU=\"-cpu ${CPU_MODEL},vendor=GenuineIntel,-pdpe1gb,+avx,+sse,+sse2,+ssse3,vmware-cpuid-freq=on\"\n                reset_cpu_flags\n            fi\n            # A CPU with fma is required for Metal support\n            # A CPU with invtsc is required for macOS to boot\n            # Skip CPU feature checks when using TCG emulation (cross-architecture)\n            # as TCG will emulate the required x86 features in software. This enables\n            # running macOS x86_64 VMs on ARM Macs through software emulation.\n            if [ \"${QEMU_ACCEL}\" != \"tcg\" ]; then\n                case ${macos_release} in\n                    ventura|sonoma|sequoia|tahoe)\n                        # A CPU with AVX2 support is required for >= macOS Ventura\n                        if check_cpu_flag sse4_2 && check_cpu_flag avx2; then\n                            if [ \"${HOST_CPU_VENDOR}\" != \"GenuineIntel\" ] && [ -z \"${HYPERVISOR}\" ]; then\n                                add_cpu_flag \"+avx2\"\n                                add_cpu_flag \"+sse4.2\"\n                            fi\n                        else\n                            echo \"ERROR! macOS ${macos_release} requires a CPU with SSE 4.2 and AVX2 support.\"\n                            echo \"       Try macOS Monterey or Big Sur.\"\n                            exit 1\n                        fi;;\n                    catalina|big-sur|monterey)\n                        # A CPU with SSE4.2 support is required for >= macOS Catalina\n                        if check_cpu_flag sse4_2; then\n                            if [ \"${HOST_CPU_VENDOR}\" != \"GenuineIntel\" ] && [ -z \"${HYPERVISOR}\" ]; then\n                                add_cpu_flag \"+sse4.2\"\n                            fi\n                        else\n                            echo \"ERROR! macOS ${macos_release} requires a CPU with SSE 4.2 support.\"\n                            exit 1\n                        fi;;\n                    *)\n                        # A CPU with SSE4.1 support is required for >= macOS Sierra\n                        if check_cpu_flag sse4_1; then\n                            if [ \"${HOST_CPU_VENDOR}\" != \"GenuineIntel\" ] && [ -z \"${HYPERVISOR}\" ]; then\n                                add_cpu_flag \"+sse4.1\"\n                            fi\n                        else\n                            echo \"ERROR! macOS ${macos_release} requires a CPU with SSE 4.1 support.\"\n                            exit 1\n                        fi;;\n                esac\n\n                if [ \"${HOST_CPU_VENDOR}\" != \"GenuineIntel\" ] && [ -z \"${HYPERVISOR}\" ]; then\n                    for FLAG in abm adx aes amd-ssbd apic arat bmi1 bmi2 clflush cmov cx8 cx16 de \\\n                                eist erms f16c fma fp87 fsgsbase fxsr invpcid invtsc lahf_lm lm \\\n                                mca mce mmx movbe mpx msr mtrr nx pae pat pcid pge pse popcnt pse36 \\\n                                rdrand rdtscp sep smep syscall tsc tsc_adjust vaes vbmi2 vmx vpclmulqdq \\\n                                x2apic xgetbv1 xsave xsaveopt; do\n                        local cpu_flag\n                        cpu_flag=$(configure_cpu_flag \"${FLAG}\")\n                        [[ -n \"${cpu_flag}\" ]] && add_cpu_flag \"${cpu_flag#,}\"\n                    done\n                    # AMD CPUs with constant_tsc need explicit TSC flags for macOS stability\n                    # constant_tsc is AMD's equivalent of Intel's invtsc\n                    if [ \"${HOST_CPU_VENDOR}\" == \"AuthenticAMD\" ] && check_cpu_flag invtsc; then\n                        add_cpu_flag \"+tsc\"\n                        add_cpu_flag \"+tsc-deadline\"\n                    fi\n                fi\n            fi\n\n            # Disable S3 support in the VM to prevent macOS suspending during install\n            # Disable ACPI PCI hotplug to prevent issues with macOS (required for QEMU 6.1+)\n            GUEST_TWEAKS+=\" -global ICH9-LPC.disable_s3=1 -global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off -device isa-applesmc,osk=$(echo \"bheuneqjbexolgurfrjbeqfthneqrqcyrnfrqbagfgrny(p)NccyrPbzchgreVap\" | tr 'A-Za-z' 'N-ZA-Mn-za-m')\"\n\n            # Disable High Precision Timer\n            if [ \"${QEMU_VER_SHORT}\" -ge 70 ]; then\n                MACHINE_TYPE+=\",hpet=off\"\n            else\n                GUEST_TWEAKS+=\" -no-hpet\"\n            fi\n            ;;\n        windows|windows-server)\n            # Base CPU flags that work with all accelerators (KVM, HVF, TCG)\n            CPU=\"-cpu ${CPU_MODEL},+hypervisor,+invtsc,l3-cache=on\"\n            reset_cpu_flags\n            # KVM-specific flags: migratable and Hyper-V enlightenments\n            if [ \"${QEMU_ACCEL}\" == \"kvm\" ]; then\n                if [ \"${QEMU_VER_SHORT}\" -gt 60 ]; then\n                    add_cpu_flag \"migratable=no\"\n                    add_cpu_flag \"hv_passthrough\"\n                else\n                    add_cpu_flag \"migratable=no\"\n                    add_cpu_flag \"hv_frequencies\"\n                    [[ -n \"${CPU_KVM_UNHALT}\" ]] && add_cpu_flag \"${CPU_KVM_UNHALT#,}\"\n                    add_cpu_flag \"hv_reenlightenment\"\n                    add_cpu_flag \"hv_relaxed\"\n                    add_cpu_flag \"hv_spinlocks=8191\"\n                    add_cpu_flag \"hv_stimer\"\n                    add_cpu_flag \"hv_synic\"\n                    add_cpu_flag \"hv_time\"\n                    add_cpu_flag \"hv_vapic\"\n                    add_cpu_flag \"hv_vendor_id=1234567890ab\"\n                    add_cpu_flag \"hv_vpindex\"\n                fi\n            fi\n            # Disable S3 support in the VM to ensure Windows can boot with SecureBoot enabled\n            #  - https://wiki.archlinux.org/title/QEMU/Troubleshooting#Virtual_machine_not_booting_when_using_a_Secure_Boot_enabled_OVMF\n            GUEST_TWEAKS+=\" -global ICH9-LPC.disable_s3=1\"\n\n            # Disable High Precision Timer\n            if [ \"${QEMU_VER_SHORT}\" -ge 70 ]; then\n              MACHINE_TYPE+=\",hpet=off\"\n            else\n              GUEST_TWEAKS+=\" -no-hpet\"\n            fi\n            ;;\n    esac\n\n    if [ \"${HOST_CPU_VENDOR}\" == \"AuthenticAMD\" ] && [ \"${guest_os}\" != \"macos\" ] && [ \"${ARCH_VM}\" == \"x86_64\" ]; then\n        add_cpu_flag \"topoext\"\n    fi\n\n    if [ -z \"${cpu_cores}\" ]; then\n        if [ \"${HOST_CPU_CORES}\" -ge 32 ]; then\n            GUEST_CPU_CORES=\"16\"\n        elif [ \"${HOST_CPU_CORES}\" -ge 16 ]; then\n            GUEST_CPU_CORES=\"8\"\n        elif [ \"${HOST_CPU_CORES}\" -ge 8 ]; then\n            GUEST_CPU_CORES=\"4\"\n        elif [ \"${HOST_CPU_CORES}\" -ge 4 ]; then\n            GUEST_CPU_CORES=\"2\"\n        else\n            GUEST_CPU_CORES=\"1\"\n        fi\n    else\n        GUEST_CPU_CORES=\"${cpu_cores}\"\n    fi\n\n    # Windows 11 requires a minimum of 2 CPU cores to install\n    # https://github.com/quickemu-project/quickemu/issues/1423\n    if [ \"${guest_os}\" == \"windows\" ] && [[ \"${VMNAME}\" == *\"windows-11\"* || \"${VMNAME}\" == *\"win11\"* ]]; then\n        if [ \"${GUEST_CPU_CORES}\" -lt 2 ]; then\n            echo \" - CPU:      Adjusting CPU cores from ${GUEST_CPU_CORES} to 2 (Windows 11 minimum requirement)\"\n            GUEST_CPU_CORES=\"2\"\n        fi\n    fi\n\n    # macOS guests cannot boot with most core counts not powers of 2.\n    # Find the nearest but lowest power of 2 using a predefined table\n    if [ \"${guest_os}\" == \"macos\" ]; then\n        local POWERS=(1 2 4 8 16 32 64 128 256 512 1024)\n        for (( i=${#POWERS[@]}-1; i>=0; i-- )); do\n            if [ \"${POWERS[i]}\" -le \"${GUEST_CPU_CORES}\" ]; then\n                GUEST_CPU_CORES=\"${POWERS[i]}\"\n                break\n            fi\n        done\n    fi\n\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        # Get the number of physical cores\n        physicalcpu=$(sysctl -n hw.physicalcpu)\n        # Get the number of logical processors\n        logicalcpu=$(sysctl -n hw.logicalcpu)\n        # Check if Hyper-Threading is enabled\n        if [ \"${logicalcpu}\" -gt \"${physicalcpu}\" ]; then\n            HOST_CPU_SMT=\"on\"\n        else\n            HOST_CPU_SMT=\"off\"\n        fi\n    elif [ -e /sys/devices/system/cpu/smt/control ]; then\n        HOST_CPU_SMT=$(cat /sys/devices/system/cpu/smt/control)\n    fi\n\n    # Account for Hyperthreading/SMT.\n    if [ \"${GUEST_CPU_CORES}\" -ge 2 ]; then\n        case ${HOST_CPU_SMT} in\n            on) GUEST_CPU_THREADS=2\n                GUEST_CPU_LOGICAL_CORES=$(( GUEST_CPU_CORES / GUEST_CPU_THREADS ));;\n            *)  GUEST_CPU_THREADS=1\n                GUEST_CPU_LOGICAL_CORES=${GUEST_CPU_CORES};;\n        esac\n    else\n        GUEST_CPU_THREADS=1\n        GUEST_CPU_LOGICAL_CORES=${GUEST_CPU_CORES}\n    fi\n\n    SMP=\"-smp cores=${GUEST_CPU_LOGICAL_CORES},threads=${GUEST_CPU_THREADS},sockets=${HOST_CPU_SOCKETS}\"\n    echo \" - CPU:      ${HOST_CPU_MODEL}\"\n    echo \" - CPU VM:   ${CPU_MODEL%%,*}, ${HOST_CPU_SOCKETS} Socket(s), ${GUEST_CPU_LOGICAL_CORES} Core(s), ${GUEST_CPU_THREADS} Thread(s)\"\n\n    if [ \"${guest_os}\" == \"macos\" ] || [ \"${guest_os}\" == \"windows\" ] || [ \"${guest_os}\" == \"windows-server\" ]; then\n        # Display MSRs alert if the guest is macOS or windows\n        ignore_msrs_alert\n    fi\n}\n\nfunction configure_ram() {\n    local OS_PRETTY_NAME=\"\"\n    RAM_VM=\"2G\"\n    if [ -z \"${ram}\" ]; then\n        local RAM_HOST=\"\"\n        if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n            RAM_HOST=$(($(sysctl -n hw.memsize) / (1048576*1024)))\n        else\n            # Determine the number of gigabytes of RAM in the host by extracting the first numerical value from the output.\n            RAM_HOST=$(free --giga | tr ' ' '\\n' | grep -m 1 \"[0-9]\" )\n        fi\n\n        if [ \"${RAM_HOST}\" -ge 128 ]; then\n            RAM_VM=\"32G\"\n        elif [ \"${RAM_HOST}\" -ge 64 ]; then\n            RAM_VM=\"16G\"\n        elif [ \"${RAM_HOST}\" -ge 16 ]; then\n            RAM_VM=\"8G\"\n        elif [ \"${RAM_HOST}\" -ge 8 ]; then\n            RAM_VM=\"4G\"\n        fi\n    else\n        RAM_VM=\"${ram}\"\n    fi\n    echo \" - RAM VM:   ${RAM_VM} RAM\"\n\n    case \"${guest_os}\" in\n        windows|windows-server)\n            OS_PRETTY_NAME=\"Windows\"\n            min_ram=\"4\"\n            ;;\n        macos)\n            OS_PRETTY_NAME=\"macOS\"\n            min_ram=\"8\"\n            ;;\n    esac\n\n    if [ -n \"${min_ram}\" ] && [ \"${RAM_VM//G/}\" -lt \"${min_ram}\" ]; then\n        if [ -z \"${ram}\" ]; then\n            echo \"             ERROR! The guest virtual machine has been allocated insufficient RAM to run ${OS_PRETTY_NAME}.\"\n            echo \"             You can override the guest RAM allocation by adding 'ram=${min_ram}G' to ${VM}\"\n            exit 1\n        else\n            echo \"             WARNING! You have allocated less than the recommended amount of RAM to run ${OS_PRETTY_NAME}.\"\n        fi\n    fi\n}\n\nfunction is_firmware_qcow2() {\n    # Check for the magic bytes that indicate the firmware is in qcow2 format,\n    # otherwise default to assuming firmware files are in raw format.\n    # Use od to read bytes as hex to avoid null byte warnings in command substitution.\n    local magic\n    magic=$(od -An -tx1 -N4 \"$1\" 2>/dev/null | tr -d ' ')\n    [ \"$magic\" = \"514649fb\" ] && echo \"true\" || echo \"false\"\n}\n\n# Derive QEMU share path from binary location\n# Handles Nix, Homebrew, MacPorts, system packages, custom builds\nfunction get_qemu_share_path() {\n  local qemu_bin=\"${QEMU}\"\n  local qemu_real qemu_prefix share_path\n\n  # Resolve the actual binary path (handles symlinks, Nix store paths)\n  if command -v realpath &>/dev/null; then\n    qemu_real=$(realpath \"${qemu_bin}\" 2>/dev/null) || qemu_real=\"${qemu_bin}\"\n  else\n    # macOS fallback: follow symlink chain manually\n    qemu_real=\"${qemu_bin}\"\n    while [ -L \"${qemu_real}\" ]; do\n      local link_target\n      link_target=$(readlink \"${qemu_real}\")\n      # Handle relative symlinks\n      if [[ \"${link_target}\" != /* ]]; then\n        qemu_real=\"$(dirname \"${qemu_real}\")/${link_target}\"\n      else\n        qemu_real=\"${link_target}\"\n      fi\n    done\n  fi\n\n  # /path/to/bin/qemu-system-x86_64 -> /path/to/share\n  qemu_prefix=\"$(dirname \"$(dirname \"${qemu_real}\")\")\"\n  share_path=\"${qemu_prefix}/share\"\n\n  # Validate: must contain qemu firmware directory\n  if [ -d \"${share_path}/qemu\" ]; then\n    echo \"${share_path}\"\n    return 0\n  fi\n\n  # Fallback for system installations\n  echo \"/usr/share\"\n}\n\nfunction configure_bios() {\n    # Always Boot macOS using EFI\n    if [ \"${guest_os}\" == \"macos\" ]; then\n        boot=\"efi\"\n        if [ -e \"${VMDIR}/OVMF_CODE.fd\" ] && [ -e \"${VMDIR}/OVMF_VARS-1024x768.fd\" ]; then\n            EFI_CODE=\"${VMDIR}/OVMF_CODE.fd\"\n            EFI_VARS=\"${VMDIR}/OVMF_VARS-1024x768.fd\"\n        elif [ -e \"${VMDIR}/OVMF_CODE.fd\" ] && [ -e \"${VMDIR}/OVMF_VARS-1920x1080.fd\" ]; then\n            EFI_CODE=\"${VMDIR}/OVMF_CODE.fd\"\n            EFI_VARS=\"${VMDIR}/OVMF_VARS-1920x1080.fd\"\n        else\n            MAC_MISSING=\"Firmware\"\n        fi\n\n        # Check for OpenCore bootloader\n        if [ -e \"${VMDIR}/OpenCore.qcow2\" ]; then\n            MAC_BOOTLOADER=\"${VMDIR}/OpenCore.qcow2\"\n        elif [ -e \"${VMDIR}/ESP.qcow2\" ]; then\n            # Backwards compatibility for Clover\n            MAC_BOOTLOADER=\"${VMDIR}/ESP.qcow2\"\n        else\n            MAC_MISSING=\"Bootloader\"\n        fi\n\n        if [ -n \"${MAC_MISSING}\" ]; then\n            echo \"ERROR! macOS ${MAC_MISSING} was not found.\"\n            echo \"       Use 'quickget' to download the required files.\"\n            exit 1\n        fi\n        BOOT_STATUS=\"EFI (macOS), OVMF ($(basename \"${EFI_CODE}\")), SecureBoot (${secureboot}).\"\n    elif [[ \"${boot}\" == *\"efi\"* ]]; then\n        EFI_VARS=\"${VMDIR}/OVMF_VARS.fd\"\n\n        # Preserve backward compatibility\n        if [ -e \"${VMDIR}/${VMNAME}-vars.fd\" ]; then\n            mv \"${VMDIR}/${VMNAME}-vars.fd\" \"${EFI_VARS}\"\n        elif [ -e \"${VMDIR}/OVMF_VARS_4M.fd\" ]; then\n            mv \"${VMDIR}/OVMF_VARS_4M.fd\" \"${EFI_VARS}\"\n        fi\n\n        # OVMF_CODE_4M.fd is for booting guests in non-Secure Boot mode.\n        # While this image technically supports Secure Boot, it does so\n        # without requiring SMM support from QEMU\n\n        # OVMF_CODE.secboot.fd is like OVMF_CODE_4M.fd, but will abort if QEMU\n        # does not support SMM.\n\n        local SHARE_PATH\n        SHARE_PATH=$(get_qemu_share_path)\n\n        # https://bugzilla.redhat.com/show_bug.cgi?id=1929357#c5\n        # TODO: Check if macOS should use 'edk2-i386-vars.fd'\n        # Search for firmware if EFI_CODE is not set or the specified file doesn't exist\n        if [ -z \"${EFI_CODE}\" ] || [ ! -e \"${EFI_CODE}\" ]; then\n            if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n                # AAVMF firmware paths for ARM64 guests\n                # SecureBoot is not commonly supported on ARM64, use standard firmware\n                # shellcheck disable=SC2054,SC2140\n                ovmfs=(\"/usr/share/AAVMF/AAVMF_CODE.fd\",\"/usr/share/AAVMF/AAVMF_VARS.fd\" \\\n                    \"${SHARE_PATH}/edk2/aarch64/QEMU_CODE.fd\",\"${SHARE_PATH}/edk2/aarch64/QEMU_VARS.fd\" \\\n                    \"${SHARE_PATH}/edk2/aarch64/QEMU_EFI-pflash.raw\",\"${SHARE_PATH}/edk2/aarch64/vars-template-pflash.raw\" \\\n                    \"${SHARE_PATH}/qemu/edk2-aarch64-code.fd\",\"${SHARE_PATH}/qemu/edk2-arm-vars.fd\" \\\n                    \"${SHARE_PATH}/AAVMF/AAVMF_CODE.fd\",\"${SHARE_PATH}/AAVMF/AAVMF_VARS.fd\"\n                )\n            else\n                # x86_64 OVMF firmware paths\n                case ${secureboot} in\n                    on) # shellcheck disable=SC2054,SC2140\n                        ovmfs=(\"${SHARE_PATH}/OVMF/OVMF_CODE_4M.secboot.fd\",\"${SHARE_PATH}/OVMF/OVMF_VARS_4M.ms.fd\" \\\n                            \"${SHARE_PATH}/edk2/ovmf/OVMF_CODE.secboot.fd\",\"${SHARE_PATH}/edk2/ovmf/OVMF_VARS.secboot.fd\" \\\n                            \"${SHARE_PATH}/OVMF/x64/OVMF_CODE.secboot.fd\",\"${SHARE_PATH}/OVMF/x64/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/edk2-ovmf/OVMF_CODE.secboot.fd\",\"${SHARE_PATH}/edk2-ovmf/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/qemu/ovmf-x86_64-smm-ms-code.bin\",\"${SHARE_PATH}/qemu/ovmf-x86_64-smm-ms-vars.bin\" \\\n                            \"${SHARE_PATH}/qemu/edk2-x86_64-secure-code.fd\",\"${SHARE_PATH}/qemu/edk2-x86_64-code.fd\" \\\n                            \"${SHARE_PATH}/edk2-ovmf/x64/OVMF_CODE.secboot.fd\",\"${SHARE_PATH}/edk2-ovmf/x64/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/edk2/x64/OVMF_CODE.secboot.4m.fd\",\"${SHARE_PATH}/edk2/x64/OVMF_VARS.4m.fd\" \\\n                            \"${SHARE_PATH}/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2\",\"${SHARE_PATH}/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2\"\n                        );;\n                    *)  # shellcheck disable=SC2054,SC2140\n                        ovmfs=(\"${SHARE_PATH}/OVMF/OVMF_CODE_4M.fd\",\"${SHARE_PATH}/OVMF/OVMF_VARS_4M.fd\" \\\n                            \"${SHARE_PATH}/edk2/ovmf/OVMF_CODE.fd\",\"${SHARE_PATH}/edk2/ovmf/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/OVMF/OVMF_CODE.fd\",\"${SHARE_PATH}/OVMF/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/OVMF/x64/OVMF_CODE.fd\",\"${SHARE_PATH}/OVMF/x64/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/edk2-ovmf/OVMF_CODE.fd\",\"${SHARE_PATH}/edk2-ovmf/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/qemu/ovmf-x86_64-4m-code.bin\",\"${SHARE_PATH}/qemu/ovmf-x86_64-4m-vars.bin\" \\\n                            \"${SHARE_PATH}/qemu/edk2-x86_64-code.fd\",\"${SHARE_PATH}/qemu/edk2-x86_64-code.fd\" \\\n                            \"${SHARE_PATH}/edk2-ovmf/x64/OVMF_CODE.fd\",\"${SHARE_PATH}/edk2-ovmf/x64/OVMF_VARS.fd\" \\\n                            \"${SHARE_PATH}/edk2/x64/OVMF_CODE.4m.fd\",\"${SHARE_PATH}/edk2/x64/OVMF_VARS.4m.fd\" \\\n                            \"${SHARE_PATH}/edk2/ovmf/OVMF_CODE_4M.qcow2\",\"${SHARE_PATH}/edk2/ovmf/OVMF_VARS_4M.qcow2\"\n                        );;\n                esac\n            fi\n            # Attempt each EFI_CODE file one by one, selecting the corresponding code and vars\n            # when an existing file is found.\n            _IFS=$IFS\n            IFS=\",\"\n            for f in \"${ovmfs[@]}\"; do\n                # shellcheck disable=SC2086\n                set -- ${f};\n                if [ -e \"${1}\" ]; then\n                    EFI_CODE=\"${1}\"\n                    EFI_EXTRA_VARS=\"${2}\"\n                fi\n            done\n            IFS=$_IFS\n        fi\n        if [ -z \"${EFI_CODE}\" ] || [ ! -e \"${EFI_CODE}\" ]; then\n            if [ \"${secureboot}\" == \"on\" ]; then\n                echo \"ERROR! SecureBoot was requested but no SecureBoot capable firmware was found.\"\n            else\n                echo \"ERROR! EFI boot requested but no EFI firmware found.\"\n            fi\n            echo \"       Please install OVMF firmware.\"\n            exit 1\n        fi\n        if [ -n \"${EFI_EXTRA_VARS}\" ]; then\n            if [ ! -e \"${EFI_EXTRA_VARS}\" ]; then\n                echo \" - EFI:      ERROR! EFI_EXTRA_VARS file ${EFI_EXTRA_VARS} does not exist.\"\n                exit 1\n            fi\n            # Write destination vars file with correct extension\n            # based on source format of the EFI_EXTRA_VARS file\n            QCOW2VARS=$(is_firmware_qcow2 \"${EFI_EXTRA_VARS}\")\n            if [ \"${QCOW2VARS}\" = \"true\" ]; then\n                EFI_VARS=\"${VMDIR}/OVMF_VARS.qcow2\"\n            else\n                EFI_VARS=\"${VMDIR}/OVMF_VARS.fd\"\n            fi\n            efi_vars \"${EFI_EXTRA_VARS}\" \"${EFI_VARS}\"\n        fi\n\n        # Make sure EFI_VARS references an actual, writeable, file\n        if [ ! -f \"${EFI_VARS}\" ] || [ ! -w \"${EFI_VARS}\" ]; then\n            echo \" - EFI:      ERROR! ${EFI_VARS} is not a regular file or not writeable.\"\n            echo \"             Deleting ${EFI_VARS}. Please re-run quickemu.\"\n            rm -f \"${EFI_VARS}\"\n            exit 1\n        fi\n\n        # If EFI_CODE references a symlink, resolve it to the real file.\n        if [ -L \"${EFI_CODE}\" ]; then\n            echo \" - EFI:      WARNING! ${EFI_CODE} is a symlink.\"\n            echo -n \"             Resolving to... \"\n            EFI_CODE=$(realpath \"${EFI_CODE}\")\n            echo \"${EFI_CODE}\"\n        fi\n        BOOT_STATUS=\"EFI (${guest_os^}), OVMF (${EFI_CODE}), EFI Vars (${EFI_VARS}), SecureBoot (${secureboot}).\"\n    else\n        BOOT_STATUS=\"Legacy BIOS (${guest_os^})\"\n        boot=\"legacy\"\n        secureboot=\"off\"\n        # Legacy BIOS boot requires the i440fx/PIIX3 chipset (pc), not Q35\n        MACHINE_TYPE=\"pc\"\n    fi\n\n    echo \" - BOOT:     ${BOOT_STATUS}\"\n}\n\nfunction configure_os_quirks() {\n\n    if [ \"${guest_os}\" == \"batocera\" ] || [ \"${guest_os}\" == \"haiku\" ] || [ \"${guest_os}\" == \"kolibrios\" ]; then\n        NET_DEVICE=\"rtl8139\"\n    fi\n\n    if [ \"${guest_os}\" == \"freebsd\" ] || [ \"${guest_os}\" == \"ghostbsd\" ]; then\n        mouse=\"usb\"\n    fi\n\n    case ${guest_os} in\n        windows-server) NET_DEVICE=\"e1000\";;\n        *bsd|linux*|windows) NET_DEVICE=\"virtio-net-pci\";;\n        freedos) sound_card=\"sb16\"\n                 NET_DEVICE=\"pcnet\";;\n        *solaris) usb_controller=\"xhci\"\n                  sound_card=\"ac97\";;\n        reactos) NET_DEVICE=\"e1000\"\n                 keyboard=\"ps2\";;\n        macos)\n            # Tune QEMU optimisations based on the macOS release, or fallback to lowest\n            # common supported options if none is specified.\n            #   * VirtIO Block Media doesn't work in High Sierra (at all) or the Mojave (Recovery Image)\n            #   * VirtIO Network is supported since Big Sur\n            #   * VirtIO Memory Balloning is supported since Big Sur (https://pmhahn.github.io/virtio-balloon/)\n            #   * VirtIO RNG is supported since Big Sur, but exposed to all guests by default.\n            case ${macos_release} in\n                monterey|ventura|sonoma|sequoia|tahoe)\n                    # macOS 12+ (Monterey onwards) supports virtio-sound-pci natively\n                    BALLOON=\"-device virtio-balloon\"\n                    MAC_DISK_DEV=\"virtio-blk-pci\"\n                    NET_DEVICE=\"virtio-net-pci\"\n                    USB_HOST_PASSTHROUGH_CONTROLLER=\"nec-usb-xhci\"\n                    GUEST_TWEAKS+=\" -global nec-usb-xhci.msi=off\"\n                    sound_card=\"virtio-sound-pci\"\n                    usb_controller=\"xhci\";;\n                big-sur)\n                    # Big Sur uses VirtIO but usb-audio/VoodooHDA don't work reliably\n                    # Fall back to ich9-intel-hda which may work with VoodooHDA\n                    BALLOON=\"-device virtio-balloon\"\n                    MAC_DISK_DEV=\"virtio-blk-pci\"\n                    NET_DEVICE=\"virtio-net-pci\"\n                    USB_HOST_PASSTHROUGH_CONTROLLER=\"nec-usb-xhci\"\n                    GUEST_TWEAKS+=\" -global nec-usb-xhci.msi=off\"\n                    sound_card=\"ich9-intel-hda\"\n                    usb_controller=\"xhci\";;\n                *)\n                    # Backwards compatibility if no macos_release is specified.\n                    # Also safe catch all for High Sierra and Mojave\n                    BALLOON=\"\"\n                    if [ \"${macos_release}\" == \"catalina\" ]; then\n                        MAC_DISK_DEV=\"virtio-blk-pci\"\n                    else\n                        MAC_DISK_DEV=\"ide-hd,bus=ahci.2\"\n                    fi\n                    NET_DEVICE=\"vmxnet3\"\n                    USB_HOST_PASSTHROUGH_CONTROLLER=\"usb-ehci\";;\n            esac\n            ;;\n        *) NET_DEVICE=\"rtl8139\";;\n    esac\n}\n\nfunction configure_storage() {\n    local create_options=\"\"\n    echo \" - Disk:     ${disk_img} (${disk_size})\"\n    if [ ! -f \"${disk_img}\" ]; then\n        # If there is no disk image, create a new image.\n        mkdir -p \"${VMDIR}\" 2>/dev/null\n        case ${preallocation} in\n            off|metadata|falloc|full) true;;\n            *) echo \"ERROR! ${preallocation} is an unsupported disk preallocation option.\"\n               exit 1;;\n        esac\n\n        case ${disk_format} in\n            qcow2) create_options=\"lazy_refcounts=on,preallocation=${preallocation},nocow=on\";;\n            raw) create_options=\"preallocation=${preallocation}\";;\n            *) true;;\n        esac\n\n        # https://blog.programster.org/qcow2-performance\n        if ! ${QEMU_IMG} create -q -f \"${disk_format}\" -o \"${create_options=}\" \"${disk_img}\" \"${disk_size}\"; then\n            echo \"ERROR! Failed to create ${disk_img} using ${disk_format} format.\"\n            exit 1\n        fi\n\n        if [ -z \"${iso}\" ] && [ -z \"${img}\" ]; then\n            echo \"ERROR! You haven't specified a .iso or .img image to boot from.\"\n            exit 1\n        fi\n        echo \"             Just created, booting from ${iso}${img}\"\n        DISK_USED=\"no\"\n    elif [ -e \"${disk_img}\" ]; then\n        # If the VM is not running, check for disk related issues.\n        if [ -z \"${VM_PID}\" ]; then\n            # Check there isn't already a process attached to the disk image.\n            if ! ${QEMU_IMG} info \"${disk_img}\" >/dev/null; then\n                echo \"             Failed to get \\\"write\\\" lock. Is another process using the disk?\"\n                exit 1\n            fi\n        else\n            if ! ${QEMU_IMG} check -q \"${disk_img}\"; then\n                echo \"             Disk integrity check failed. Please run qemu-img check --help.\"\n                echo\n                \"${QEMU_IMG}\" check \"${disk_img}\"\n                exit 1\n            fi\n        fi\n\n        # Only check disk image size if preallocation is off\n        if [ \"${preallocation}\" == \"off\" ]; then\n            DISK_CURR_SIZE=$(${STAT} -c%s \"${disk_img}\")\n            if [ \"${DISK_CURR_SIZE}\" -le \"${DISK_MIN_SIZE}\" ]; then\n                echo \"             Looks unused, booting from ${iso}${img}\"\n                if [ -z \"${iso}\" ] && [ -z \"${img}\" ]; then\n                    echo \"ERROR! You haven't specified a .iso or .img image to boot from.\"\n                    exit 1\n                fi\n            else\n                DISK_USED=\"yes\"\n            fi\n        else\n            DISK_USED=\"yes\"\n        fi\n    fi\n\n    if [ \"${DISK_USED}\" == \"yes\" ] && [ \"${guest_os}\" != \"kolibrios\" ]; then\n        # If there is a disk image that appears to be used do not boot from installation media.\n        iso=\"\"\n        img=\"\"\n    fi\n\n    # Has the status quo been requested?\n    if [ \"${STATUS_QUO}\" == \"-snapshot\" ]; then\n        if [ -z \"${img}\" ] && [ -z \"${iso}\" ]; then\n            echo \"             Existing disk state will be preserved, no writes will be committed.\"\n        fi\n    fi\n\n    if [ -n \"${iso}\" ] && [ -e \"${iso}\" ]; then\n        echo \" - Boot ISO: ${iso}\"\n    elif [ -n \"${img}\" ] && [ -e \"${img}\" ]; then\n        echo \" - Recovery: ${img}\"\n    fi\n\n    if [ -n \"${fixed_iso}\" ] && [ -e \"${fixed_iso}\" ]; then\n        echo \" - CD-ROM:   ${fixed_iso}\"\n    fi\n}\n\nfunction check_cocoa_gl_es_support() {\n    [ \"${OS_KERNEL}\" != \"Darwin\" ] && return 1\n\n    # Test QEMU directly for gl=es support - most reliable method\n    # This catches both missing OpenGL build support and missing ANGLE libraries\n    if \"${QEMU}\" -display cocoa,gl=es -M none 2>&1 | grep -Eqi \"OpenGL support was not enabled|does not accept\"; then\n        return 1\n    fi\n\n    # Fallback: check for ANGLE libraries if QEMU test is inconclusive\n    # Resolve QEMU's real path (follows Nix symlinks)\n    local qemu_real qemu_dir qemu_prefix\n    qemu_real=$(realpath \"${QEMU}\" 2>/dev/null || readlink -f \"${QEMU}\" 2>/dev/null || echo \"${QEMU}\")\n    qemu_dir=$(dirname \"${qemu_real}\")\n    qemu_prefix=\"${qemu_dir%/bin}\"\n\n    local angle_libs=(\n        \"${qemu_prefix}/lib/libEGL.dylib\"\n        \"/opt/homebrew/lib/libEGL.dylib\"\n        \"/usr/local/lib/libEGL.dylib\"\n    )\n\n    # Also check DYLD paths if set (covers additional Nix scenarios)\n    if [ -n \"${DYLD_LIBRARY_PATH:-}\" ]; then\n        local IFS=':'\n        for path in ${DYLD_LIBRARY_PATH}; do\n            angle_libs+=(\"${path}/libEGL.dylib\")\n        done\n    fi\n\n    for lib in \"${angle_libs[@]}\"; do\n        [ -f \"$lib\" ] && return 0\n    done\n\n    return 1\n}\n\nfunction configure_display() {\n    # Determine which audio driver to use: PipeWire, PulseAudio, or ALSA\n    # Socket detection is more reliable than process detection on headless servers\n    local AUDIO_DRIVER=\"alsa\"\n    local pw_socket=\"${PIPEWIRE_REMOTE:-${XDG_RUNTIME_DIR}/pipewire-0}\"\n    local pa_socket=\"${PULSE_SERVER:-${XDG_RUNTIME_DIR}/pulse/native}\"\n\n    if [ \"${QEMU_VER_SHORT}\" -ge 81 ] && [ -S \"${pw_socket}\" ]; then\n        # QEMU's pipewire audio backend was added in version 8.1\n        AUDIO_DRIVER=\"pipewire\"\n    elif [ -S \"${pa_socket}\" ]; then\n        AUDIO_DRIVER=\"pa\"\n    fi\n\n    # Setup the appropriate audio device based on the display output\n    # https://www.kraxel.org/blog/2020/01/qemu-sound-audiodev/\n    case ${display} in\n        cocoa) AUDIO_DEV=\"coreaudio,id=audio0\";;\n        none|spice|spice-app) AUDIO_DEV=\"spice,id=audio0\";;\n        *) AUDIO_DEV=\"${AUDIO_DRIVER},id=audio0\";;\n    esac\n\n    # Determine a sane resolution for Linux guests.\n    local X_RES=\"1280\"\n    local Y_RES=\"800\"\n    if [ -n \"${width}\" ] && [ -n \"${height}\" ]; then\n        local X_RES=\"${width}\"\n        local Y_RES=\"${height}\"\n    fi\n\n    # https://www.kraxel.org/blog/2019/09/display-devices-in-qemu/\n    case ${guest_os} in\n        *bsd) DISPLAY_DEVICE=\"VGA\";;\n        linux_old|solaris) DISPLAY_DEVICE=\"vmware-svga\";;\n        linux)\n            # ARM64 does not have VGA hardware - use virtio-gpu-pci instead of virtio-vga\n            if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n                DISPLAY_DEVICE=\"virtio-gpu-pci\"\n            else\n                case ${display} in\n                    none|spice|spice-app) DISPLAY_DEVICE=\"virtio-gpu\";;\n                    *) DISPLAY_DEVICE=\"virtio-vga\";;\n                esac\n            fi;;\n        macos)\n            # macOS has native VMware display driver support; aligns with OSX-KVM\n            DISPLAY_DEVICE=\"vmware-svga\";;\n        windows|windows-server)\n            case ${display} in\n                none|spice) DISPLAY_DEVICE=\"qxl-vga\";;\n                cocoa|gtk|sdl|spice-app) DISPLAY_DEVICE=\"virtio-vga\";;\n            esac;;\n        *) DISPLAY_DEVICE=\"qxl-vga\";;\n    esac\n\n    # Map Quickemu $display to QEMU -display\n    case ${display} in\n        cocoa)\n            # macOS: prefer OpenGL ES (via ANGLE) for stability and performance\n            # ANGLE provides OpenGL ES on macOS through Metal, which is more stable\n            # than the deprecated native OpenGL implementation\n            # Reference: https://gist.github.com/akihikodaki/87df4149e7ca87f18dc56807ec5a1bc5\n            if [ \"${gl}\" == \"on\" ] && check_cocoa_gl_es_support; then\n                DISPLAY_RENDER=\"${display},gl=es\"\n                gl=\"es\"\n            else\n                DISPLAY_RENDER=\"${display}\"\n                [ \"${gl}\" == \"on\" ] && gl=\"off\"\n            fi;;\n        gtk)        DISPLAY_RENDER=\"${display},grab-on-hover=on,zoom-to-fit=off,gl=${gl}\";;\n        none)       DISPLAY_RENDER=\"none\"\n                    gl=\"off\";;  # No display backend means no GL context\n        spice)\n            # For local SPICE with GL, use egl-headless to provide the GL context\n            # for VirGL. Don't use gl=on in SPICE itself as it blocks the main loop.\n            if [ -z \"${ACCESS}\" ] || [ \"${ACCESS}\" == \"local\" ]; then\n                if [ \"${gl}\" == \"on\" ]; then\n                    DISPLAY_RENDER=\"egl-headless,rendernode=/dev/dri/renderD128\"\n                else\n                    DISPLAY_RENDER=\"none\"\n                fi\n            else\n                # Remote access cannot use GL\n                DISPLAY_RENDER=\"none\"\n                gl=\"off\"\n            fi;;\n        sdl)        DISPLAY_RENDER=\"${display},gl=${gl}\";;\n        spice-app)  DISPLAY_RENDER=\"${display},gl=${gl}\";;\n        *)          DISPLAY_RENDER=\"${display}\";;\n    esac\n\n    # https://www.kraxel.org/blog/2021/05/virtio-gpu-qemu-graphics-update/\n    # For GL-enabled displays, check if dedicated GL device variants are available.\n    # Note: virtio-gpu-pci becomes virtio-gpu-gl-pci (not virtio-gpu-pci-gl)\n    if [ \"${gl}\" != \"off\" ] && [[ \"${DISPLAY_DEVICE}\" =~ ^virtio-(vga|gpu|gpu-pci)$ ]]; then\n        local GL_DEVICE=\"\"\n        case \"${DISPLAY_DEVICE}\" in\n            virtio-gpu-pci) GL_DEVICE=\"virtio-gpu-gl-pci\";;\n            virtio-gpu)     GL_DEVICE=\"virtio-gpu-gl\";;\n            virtio-vga)     GL_DEVICE=\"virtio-vga-gl\";;\n        esac\n\n        if \"${QEMU}\" -device help 2>&1 | grep -q \"\\\"${GL_DEVICE}\\\"\"; then\n            DISPLAY_DEVICE=\"${GL_DEVICE}\"\n        fi\n        echo -n \" - Display:  ${display^^}, ${DISPLAY_DEVICE}, GL (${gl}), VirGL (on)\"\n    else\n        echo -n \" - Display:  ${display^^}, ${DISPLAY_DEVICE}, GL (${gl}), VirGL (off)\"\n    fi\n\n    # Disable default VGA for SPICE modes to prevent duplicate scanouts\n    # SPICE creates its own display output; the default VGA would create a second one\n    case ${display} in\n      none|spice|spice-app) VGA=\"-vga none\";;\n      *) VGA=\"\";;\n    esac\n\n    # Build the video configuration\n    VIDEO=\"${VGA:+${VGA} }-device ${DISPLAY_DEVICE}\"\n\n    # ARM64 needs ramfb for UEFI boot display before virtio-gpu driver loads\n    if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n        VIDEO=\"-device ramfb ${VIDEO}\"\n    fi\n\n    # Set display resolution for devices that support xres/yres parameters\n    # Use (,|$) anchor to match device names with or without comma-separated parameters\n    # Pattern ordered most-specific to least-specific for clarity (vga-gl before vga, etc.)\n    if [[ \"${DISPLAY_DEVICE}\" =~ ^(virtio-(vga|vga-gl|gpu|gpu-gl|gpu-pci|gpu-gl-pci)|qxl|qxl-vga|bochs-display)(,|$) ]]; then\n        VIDEO=\"${VIDEO},xres=${X_RES},yres=${Y_RES}\"\n        echo \" @ (${X_RES} x ${Y_RES})\"\n    else\n        echo \" \"\n    fi\n\n    # Allocate VRAM to VGA devices\n    # Note: virtio devices (virtio-vga, virtio-gpu-pci, and their -gl variants) use\n    # dynamic memory management and don't require explicit VRAM allocation via parameters.\n    # They use QEMU's default max_hostmem setting (256 MiB) which is sufficient for most use cases.\n    case ${DISPLAY_DEVICE} in\n        bochs-display) VIDEO=\"${VIDEO},vgamem=67108864\";;\n        qxl|qxl-vga) VIDEO=\"${VIDEO},ram_size=65536,vram_size=65536,vgamem_mb=64\";;\n        ati-vga|cirrus-vga|VGA|vmware-svga) VIDEO=\"${VIDEO},vgamem_mb=256\";;\n    esac\n\n    # Configure multiscreen if max_outputs was provided in the .conf file\n    if [ -n \"${max_outputs}\" ]; then\n        VIDEO=\"${VIDEO},max_outputs=${max_outputs}\"\n    fi\n\n    # Add fullscreen options\n    VIDEO=\"${VIDEO} ${FULLSCREEN}\"\n}\n\nfunction configure_audio() {\n    # Build the sound hardware configuration\n    case ${sound_card} in\n        ich9-intel-hda|intel-hda) SOUND=\"-device ${sound_card} -device ${sound_duplex},audiodev=audio0\";;\n        usb-audio) SOUND=\"-device ${sound_card},audiodev=audio0\";;\n        virtio-sound-pci) SOUND=\"-device ${sound_card},audiodev=audio0\";;\n        ac97|es1370|sb16) SOUND=\"-device ${sound_card},audiodev=audio0\";;\n        none) SOUND=\"\";;\n    esac\n    echo \" - Sound:    ${sound_card} (${sound_duplex})\"\n}\n\nfunction configure_ports() {\n    echo -n \"\" > \"${VMDIR}/${VMNAME}.ports\"\n    rm -f \"${VMDIR}/${VMNAME}.spice\"\n    rm -f \"${VMDIR}/${VMNAME}.sock\"\n\n    if [ -z \"${ssh_port}\" ]; then\n        # Find a free port to expose ssh to the guest\n        ssh_port=$(get_port 22220 9)\n    fi\n\n    if [ -n \"${ssh_port}\" ]; then\n        echo \"ssh,${ssh_port}\" >> \"${VMDIR}/${VMNAME}.ports\"\n        NET=\"${NET},hostfwd=tcp::${ssh_port}-:22\"\n        echo \" - ssh:      On host:  ssh user@localhost -p ${ssh_port}\"\n    else\n        echo \" - ssh:      All ssh ports have been exhausted.\"\n    fi\n\n    # Have any port forwards been requested?\n    if (( ${#port_forwards[@]} )); then\n        echo \" - PORTS:    Port forwards requested:\"\n        for FORWARD in \"${port_forwards[@]}\"; do\n            HOST_PORT=$(echo \"${FORWARD}\" | cut -d':' -f1)\n            GUEST_PORT=$(echo \"${FORWARD}\" | cut -d':' -f2)\n            echo \"              - ${HOST_PORT} => ${GUEST_PORT}\"\n            NET=\"${NET},hostfwd=tcp::${HOST_PORT}-:${GUEST_PORT}\"\n            NET=\"${NET},hostfwd=udp::${HOST_PORT}-:${GUEST_PORT}\"\n        done\n    fi\n\n    if [ \"${display}\" == \"none\" ] || [ \"${display}\" == \"spice\" ] || [ \"${display}\" == \"spice-app\" ]; then\n        SPICE=\"disable-ticketing=on\"\n\n        if [ \"${display}\" == \"spice-app\" ]; then\n            # spice-app uses QEMU's built-in viewer with GL support\n            SPICE+=\",gl=${gl}\"\n            echo \" - SPICE:    Enabled\"\n        elif [ \"${display}\" == \"spice\" ]; then\n            # For spice display, use Unix socket for local or TCP for remote\n            if [ -z \"${ACCESS}\" ] || [ \"${ACCESS}\" == \"local\" ]; then\n                # Unix socket mode for local access\n                # GL context is provided by egl-headless display, not SPICE\n                SPICE+=\",unix=on,addr=${VMDIR}/${VMNAME}.sock\"\n                echo \"unix,${VMDIR}/${VMNAME}.sock\" >> \"${VMDIR}/${VMNAME}.ports\"\n                echo \"${VMDIR}/${VMNAME}.sock\" > \"${VMDIR}/${VMNAME}.spice\"\n                echo -n \" - SPICE:    On host:  spicy --uri=\\\"spice+unix://${VMDIR}/${VMNAME}.sock\\\" --title \\\"${VMNAME}\\\"\"\n                if [ \"${guest_os}\" != \"macos\" ] && [ -n \"${PUBLIC}\" ]; then\n                    echo -n \" --spice-shared-dir ${PUBLIC}\"\n                fi\n                echo \"${FULLSCREEN}\"\n            else\n                # TCP mode for remote access (no GL support)\n                if [ -z \"${spice_port}\" ]; then\n                    spice_port=$(get_port 5930 9)\n                fi\n\n                if [ \"${ACCESS}\" == \"remote\" ]; then\n                    SPICE_ADDR=\"\"\n                else\n                    SPICE_ADDR=\"${ACCESS}\"\n                fi\n\n                if [ -z \"${spice_port}\" ]; then\n                    echo \" - SPICE:    All SPICE ports have been exhausted.\"\n                    echo \"             ERROR! Requested SPICE display, but no SPICE ports are free.\"\n                    exit 1\n                fi\n\n                SPICE+=\",port=${spice_port},addr=${SPICE_ADDR}\"\n                echo \"spice,${spice_port}\" >> \"${VMDIR}/${VMNAME}.ports\"\n                echo \"${spice_port}\" > \"${VMDIR}/${VMNAME}.spice\"\n                echo -n \" - SPICE:    On host:  spicy --title \\\"${VMNAME}\\\" --port ${spice_port}\"\n                if [ \"${guest_os}\" != \"macos\" ] && [ -n \"${PUBLIC}\" ]; then\n                    echo -n \" --spice-shared-dir ${PUBLIC}\"\n                fi\n                echo \"${FULLSCREEN}\"\n            fi\n        elif [ \"${display}\" == \"none\" ]; then\n            # display=none with SPICE for headless VMs - use TCP for remote access\n            if [ -z \"${spice_port}\" ]; then\n                spice_port=$(get_port 5930 9)\n            fi\n\n            if [ -z \"${ACCESS}\" ]; then\n                SPICE_ADDR=\"127.0.0.1\"\n            elif [ \"${ACCESS}\" == \"remote\" ]; then\n                SPICE_ADDR=\"\"\n            elif [ \"${ACCESS}\" == \"local\" ]; then\n                SPICE_ADDR=\"127.0.0.1\"\n            else\n                SPICE_ADDR=\"${ACCESS}\"\n            fi\n\n            if [ -z \"${spice_port}\" ]; then\n                echo \" - SPICE:    All SPICE ports have been exhausted.\"\n                echo \"             ERROR! Requested SPICE display, but no SPICE ports are free.\"\n                exit 1\n            fi\n\n            SPICE+=\",port=${spice_port},addr=${SPICE_ADDR}\"\n            echo \"spice,${spice_port}\" >> \"${VMDIR}/${VMNAME}.ports\"\n            echo \"${spice_port}\" > \"${VMDIR}/${VMNAME}.spice\"\n            echo -n \" - SPICE:    On host:  spicy --title \\\"${VMNAME}\\\" --port ${spice_port}\"\n            if [ \"${guest_os}\" != \"macos\" ] && [ -n \"${PUBLIC}\" ]; then\n                echo -n \" --spice-shared-dir ${PUBLIC}\"\n            fi\n            echo \"${FULLSCREEN}\"\n        fi\n    fi\n}\n\nfunction configure_file_sharing() {\n    if [ -n \"${PUBLIC}\" ]; then\n        # WebDAV\n        case ${guest_os} in\n            macos)\n                if [ \"${display}\" == \"none\" ] || [ \"${display}\" == \"spice\" ] || [ \"${display}\" == \"spice-app\" ]; then\n                    # Reference: https://gitlab.gnome.org/GNOME/phodav/-/issues/5\n                    echo \" - WebDAV:   On guest: build spice-webdavd (https://gitlab.gnome.org/GNOME/phodav/-/merge_requests/24)\"\n                    echo \" - WebDAV:   On guest: Finder -> Connect to Server -> http://localhost:9843/\"\n                fi;;\n            *) echo \" - WebDAV:   On guest: dav://localhost:9843/\";;\n        esac\n\n        # 9P\n        if [ \"${guest_os}\" != \"windows\" ] || [ \"${guest_os}\" == \"windows-server\" ]; then\n            echo -n \" - 9P:       On guest: \"\n            if [ \"${guest_os}\" == \"linux\" ]; then\n                echo \"sudo mount -t 9p -o trans=virtio,version=9p2000.L,msize=104857600 ${PUBLIC_TAG} ~/$(basename \"${PUBLIC}\")\"\n            elif [ \"${guest_os}\" == \"macos\" ]; then\n                # PUBLICSHARE needs to be world writeable for seamless integration with\n                # macOS. Test if it is world writeable, and prompt what to do if not.\n                echo \"sudo mount_9p ${PUBLIC_TAG}\"\n                if [ \"${PUBLIC_PERMS}\" != \"drwxrwxrwx\" ]; then\n                    echo \" - 9P:       On host:  chmod 777 ${PUBLIC}\"\n                    echo \"             Required for macOS integration 👆\"\n                fi\n            fi\n        fi\n\n        # SMB\n        # We need to search in NixOS compatible paths as well as the standard location\n        # since /usr/sbin/smbd may not be in the PATH.\n        if [ -x \"$(command -v smbd)\" ] || [ -x \"/usr/sbin/smbd\" ]; then\n            NET+=\",smb=${PUBLIC}\"\n            echo \" - smbd:     On guest: smb://10.0.2.4/qemu\"\n        fi\n    fi\n}\n\nfunction configure_tpm() {\n    # Start TPM\n    if [ \"${tpm}\" == \"on\" ]; then\n        local tpm_args=()\n        # shellcheck disable=SC2054\n        tpm_args+=(socket\n            --ctrl type=unixio,path=\"${VMDIR}/${VMNAME}.swtpm-sock\"\n            --terminate\n            --tpmstate dir=\"${VMDIR}\"\n            --tpm2)\n        echo \"${SWTPM} ${tpm_args[*]} &\" >> \"${VMDIR}/${VMNAME}.sh\"\n        ${SWTPM} \"${tpm_args[@]}\" >> \"${VMDIR}/${VMNAME}.log\" &\n        echo \" - TPM:      ${VMDIR}/${VMNAME}.swtpm-sock (${!})\"\n        sleep 0.25\n    fi\n}\n\nfunction configure_cpu_pinning() {\n    if [ -z \"${CORE_MAPPING}\" ]; then\n        return\n    fi\n\n    GUEST_CPUS=\"\"\n    idx=0\n    for tid_dir in /proc/\"${VM_PID}\"/task/*; do\n        tid=$(basename \"$tid_dir\")\n        name=$(cat \"$tid_dir/comm\")\n\n        if [[ \"$name\" == CPU* ]]; then\n            # Map per core, if threads are specified pin them to the same core\n            core_idx=$(( idx / GUEST_CPU_THREADS ))\n            host_cpu=${CORE_MAPPING[$core_idx]}\n\n            if (( idx % GUEST_CPU_THREADS == 0 )); then\n                [[ -n \"$GUEST_CPUS\" ]] && GUEST_CPUS+=\",\"\n                GUEST_CPUS+=\"$core_idx\"\n            fi\n\n            taskset -cp \"$host_cpu\" \"$tid\" &>/dev/null\n            idx=$((idx + 1))\n        fi\n    done\n\n    echo \" - CPU Pinning: Bind guest cores to host cores (${GUEST_CPUS} -> ${CPU_PINNING})\"\n}\n\nfunction vm_boot() {\n    AUDIO_DEV=\"\"\n    BALLOON=\"-device virtio-balloon\"\n    BOOT_STATUS=\"\"\n    CPU=\"\"\n    CORE_MAPPING=\"\"\n    DISK_USED=\"\"\n    DISPLAY_DEVICE=\"\"\n    DISPLAY_RENDER=\"\"\n    EFI_CODE=\"\"\n    EFI_VARS=\"\"\n    GUEST_CPU_CORES=\"\"\n    GUEST_CPU_LOGICAL_CORES=\"\"\n    GUEST_CPU_THREADS=\"\"\n    HOST_CPU_CORES=\"\"\n    HOST_CPU_SMT=\"\"\n    HOST_CPU_SOCKETS=\"\"\n    HOST_CPU_VENDOR=\"\"\n    GUEST_TWEAKS=\"\"\n    KERNEL_NAME=\"Unknown\"\n    KERNEL_NODE=\"\"\n    KERNEL_VER=\"?\"\n    OS_RELEASE=\"Unknown OS\"\n    MACHINE_TYPE=\"${MACHINE_TYPE:-q35}\"\n    MAC_BOOTLOADER=\"\"\n    MAC_MISSING=\"\"\n    MAC_DISK_DEV=\"${MAC_DISK_DEV:-ide-hd,bus=ahci.2}\"\n    NET_DEVICE=\"${NET_DEVICE:-virtio-net-pci}\"\n    SOUND=\"\"\n    SPICE=\"\"\n    SMM=\"${SMM:-off}\"\n    local TEMP_PORT=\"\"\n    USB_HOST_PASSTHROUGH_CONTROLLER=\"qemu-xhci\"\n    VIDEO=\"\"\n\n    KERNEL_NAME=\"$(uname -s)\"\n    KERNEL_NODE=\"$(uname -n | cut -d'.' -f 1)\"\n    KERNEL_VER=\"$(uname -r)\"\n\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        # Get macOS product name and version using swvers\n        if [ -x \"$(command -v sw_vers)\" ]; then\n            OS_RELEASE=\"$(sw_vers -productName) $(sw_vers -productVersion)\"\n        fi\n    elif [ -e /etc/os-release ]; then\n        OS_RELEASE=$(grep PRETTY_NAME /etc/os-release | cut -d'\"' -f2)\n    fi\n\n    echo \"Quickemu ${VERSION} using ${QEMU} v${QEMU_VER_LONG}\"\n    echo \" - Host:     ${OS_RELEASE} running ${KERNEL_NAME} ${KERNEL_VER} ${KERNEL_NODE}\"\n\n    # Force to lowercase.\n    boot=${boot,,}\n    guest_os=${guest_os,,}\n    args=()\n    # Set the hostname of the VM\n    NET=\"user,hostname=${VMNAME}\"\n\n    echo \"#!/usr/bin/env bash\" > \"${VMDIR}/${VMNAME}.sh\"\n\n    configure_cpu\n    configure_ram\n    check_macos_tsc_stability\n    configure_bios\n    configure_os_quirks\n    configure_storage\n    configure_display\n    configure_audio\n    configure_ports\n    configure_file_sharing\n    configure_usb\n    configure_tpm\n\n    # Changing process name is not supported on macOS\n    if [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n        # shellcheck disable=SC2054,SC2206,SC2140\n        args+=(-name ${VMNAME},process=${VMNAME},debug-threads=on)\n    fi\n\n    # Build machine arguments - SMM and vmport are x86-only options\n    # SMM (System Management Mode) is an x86-specific CPU mode used for firmware operations\n    # and is required for Secure Boot. ARM64 uses different mechanisms for firmware security.\n    # vmport emulates VMware's I/O port for guest tools, which is also x86-specific.\n    #\n    # TCG-specific optimisations for cross-architecture emulation:\n    # - Use -accel tcg,... to specify tb-size and thread options\n    # - QEMU does not allow both -machine accel= and -accel simultaneously\n    # - For KVM/HVF, continue using -machine accel= (simpler, no extra options needed)\n    if [ \"${QEMU_ACCEL}\" == \"tcg\" ]; then\n        local HOST_RAM_GB=0\n        if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n            HOST_RAM_GB=$(($(sysctl -n hw.memsize) / (1024*1024*1024)))\n        else\n            HOST_RAM_GB=$(awk '/MemTotal/ {printf \"%.0f\", $2/1024/1024}' /proc/meminfo)\n        fi\n        # Use larger translation cache on hosts with 16GB+ RAM\n        local TCG_TB_SIZE=256\n        if [ \"${HOST_RAM_GB}\" -ge 16 ]; then\n            TCG_TB_SIZE=512\n        fi\n        # shellcheck disable=SC2054,SC2206\n        args+=(-accel tcg,tb-size=${TCG_TB_SIZE},thread=multi)\n\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64 uses 'virt' machine type without x86-specific options\n            # shellcheck disable=SC2054,SC2206,SC2140\n            args+=(-machine ${MACHINE_TYPE} ${GUEST_TWEAKS}\n                ${CPU} ${SMP}\n                -m ${RAM_VM} ${BALLOON}\n                -pidfile \"${VMDIR}/${VMNAME}.pid\")\n        else\n            # x86_64 includes SMM (System Management Mode) and vmport options\n            # shellcheck disable=SC2054,SC2206,SC2140\n            args+=(-machine ${MACHINE_TYPE},smm=${SMM},vmport=off ${GUEST_TWEAKS}\n                ${CPU} ${SMP}\n                -m ${RAM_VM} ${BALLOON}\n                -pidfile \"${VMDIR}/${VMNAME}.pid\")\n        fi\n    else\n        # KVM/HVF: use -machine accel= (no extra options needed)\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64 uses 'virt' machine type without x86-specific options\n            # shellcheck disable=SC2054,SC2206,SC2140\n            args+=(-machine ${MACHINE_TYPE},accel=${QEMU_ACCEL} ${GUEST_TWEAKS}\n                ${CPU} ${SMP}\n                -m ${RAM_VM} ${BALLOON}\n                -pidfile \"${VMDIR}/${VMNAME}.pid\")\n        else\n            # x86_64 includes SMM (System Management Mode) and vmport options\n            # shellcheck disable=SC2054,SC2206,SC2140\n            args+=(-machine ${MACHINE_TYPE},smm=${SMM},vmport=off,accel=${QEMU_ACCEL} ${GUEST_TWEAKS}\n                ${CPU} ${SMP}\n                -m ${RAM_VM} ${BALLOON}\n                -pidfile \"${VMDIR}/${VMNAME}.pid\")\n        fi\n    fi\n\n    if [ \"${guest_os}\" == \"windows\" ] || [ \"${guest_os}\" == \"windows-server\" ] || [ \"${guest_os}\" == \"reactos\" ] || [ \"${guest_os}\" == \"freedos\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-rtc base=localtime,clock=host,driftfix=slew)\n    else\n        # shellcheck disable=SC2054\n        args+=(-rtc base=utc,clock=host)\n    fi\n\n    # shellcheck disable=SC2206\n    args+=(${VIDEO} -display ${DISPLAY_RENDER})\n    # Only enable SPICE is using SPICE display\n    if [ \"${display}\" == \"none\" ] || [ \"${display}\" == \"spice\" ] || [ \"${display}\" == \"spice-app\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-spice \"${SPICE}\"\n            -device virtio-serial-pci\n            -chardev socket,id=agent0,path=\"${VMDIR}/${VMNAME}-agent.sock\",server=on,wait=off\n            -device virtserialport,chardev=agent0,name=org.qemu.guest_agent.0\n            -chardev spicevmc,id=vdagent0,name=vdagent\n            -device virtserialport,chardev=vdagent0,name=com.redhat.spice.0\n            -chardev spiceport,id=webdav0,name=org.spice-space.webdav.0\n            -device virtserialport,chardev=webdav0,name=org.spice-space.webdav.0)\n    fi\n\n    # shellcheck disable=SC2054\n    args+=(-device virtio-rng-pci,rng=rng0 -object rng-random,id=rng0,filename=/dev/urandom)\n\n    # macOS doesn't support SPICE\n    if [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device \"${USB_HOST_PASSTHROUGH_CONTROLLER}\",id=spicepass\n            -chardev spicevmc,id=usbredirchardev1,name=usbredir\n            -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1\n            -chardev spicevmc,id=usbredirchardev2,name=usbredir\n            -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2\n            -chardev spicevmc,id=usbredirchardev3,name=usbredir\n            -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3\n            -device pci-ohci,id=smartpass\n            -device usb-ccid)\n\n        if ${QEMU} -device help | grep -q \"passthrough smartcard\"; then\n            # shellcheck disable=SC2054\n            args+=(-chardev spicevmc,id=ccid,name=smartcard\n                  -device ccid-card-passthru,chardev=ccid)\n        else\n            echo \" - WARNING!  ${QEMU} or SPICE was not compiled with support for smartcard devices\"\n        fi\n    fi\n\n    # setup usb-controller\n    if [ \"${usb_controller}\" == \"ehci\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device usb-ehci,id=input)\n    elif [ \"${usb_controller}\" == \"xhci\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device qemu-xhci,id=input)\n    elif [ \"${usb_controller}\" == \"none\" ]; then\n        # add nothing\n        :\n    else\n        echo \" - WARNING!  Unknown usb-controller value: '${usb_controller}'\"\n    fi\n\n    # setup keyboard\n    # @INFO: must be set after usb-controller\n    if [ \"${keyboard}\" == \"usb\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device usb-kbd,bus=input.0)\n    elif [ \"${keyboard}\" == \"virtio\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device virtio-keyboard)\n    elif [ \"${keyboard}\" == \"ps2\" ]; then\n        # add nothing, default is ps/2 keyboard\n        :\n    else\n        echo \" - WARNING!  Unknown keyboard value: '${keyboard}'; Fallback to ps2\"\n    fi\n\n    # setup keyboard_layout\n    # @INFO: When using the VNC display, you must use the -k parameter to set the keyboard layout if you are not using en-us.\n    if [ -n \"${keyboard_layout}\" ]; then\n        args+=(-k \"${keyboard_layout}\")\n    fi\n\n    # Braille requires SDL, so disable for macOS\n    if [ -n \"${BRAILLE}\" ] && [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n        if ${QEMU} -chardev help | grep -q braille; then\n            # shellcheck disable=SC2054\n            #args+=(-chardev braille,id=brltty\n            #       -device usb-braille,id=usbbrl,chardev=brltty)\n            args+=(-usbdevice braille)\n        else\n            echo \" - WARNING!  ${QEMU} does not support -chardev braille \"\n        fi\n    fi\n\n    # Validate input of the CPU_PINNING, pinning is not supported on macOS\n    if [ -n \"${CPU_PINNING}\" ] && [ \"${OS_KERNEL}\" == \"Linux\" ]; then\n        if ! [[ \"${CPU_PINNING}\" =~ ^[0-9]+(,[0-9]+)*$ ]]; then\n            echo \" - ERROR!  Couldn't parse CPU pinning: '${CPU_PINNING}', only comma-separated list is supported\"\n            exit 1\n        fi\n\n        IFS=',' read -r -a CORE_MAPPING <<< \"$CPU_PINNING\"\n\n        NUM_CORE_MAPPING=${#CORE_MAPPING[@]}\n        if [ \"$NUM_CORE_MAPPING\" -ne \"$GUEST_CPU_LOGICAL_CORES\" ]; then\n            echo \" - ERROR!  Number of host cores for pinning should be equal to VM core count ($NUM_CORE_MAPPING != $GUEST_CPU_LOGICAL_CORES)\"\n            exit 1\n        fi\n    fi\n\n    # setup mouse\n    # @INFO: must be set after usb-controller\n    if [ \"${mouse}\" == \"usb\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device usb-mouse,bus=input.0)\n    elif [ \"${mouse}\" == \"tablet\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device usb-tablet,bus=input.0)\n    elif [ \"${mouse}\" == \"virtio\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device virtio-mouse)\n    elif [ \"${mouse}\" == \"ps2\" ]; then\n        # add nothing, default is ps/2 mouse\n        :\n    else\n        echo \" - WARNING!  Unknown mouse value: '${mouse}'; Falling back to ps2\"\n    fi\n\n    # setup audio\n    # @INFO: must be set after usb-controller; in case usb-audio is used\n    # shellcheck disable=SC2206\n    args+=(-audiodev ${AUDIO_DEV} ${SOUND})\n\n    # $bridge backwards compatibility for Quickemu <= 4.0\n    if [ -n \"${bridge}\" ]; then\n        network=\"${bridge}\"\n    fi\n\n    if [ \"${network}\" == \"none\" ]; then\n        # Disable all networking\n        echo \" - Network:  Disabled\"\n        args+=(-nic none)\n    elif [ \"${network}\" == \"restrict\" ]; then\n        echo \" - Network:  Restricted (${NET_DEVICE})\"\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ${NET_DEVICE},netdev=nic -netdev ${NET},restrict=y,id=nic)\n    elif [ -n \"${network}\" ]; then\n        # Enable bridge mode networking\n        echo \" - Network:  Bridged (${network})\"\n\n        # If a persistent MAC address is provided, use it.\n        local MAC=\"\"\n        if [ -n \"${macaddr}\" ]; then\n            MAC=\",mac=${macaddr}\"\n        fi\n\n        # shellcheck disable=SC2054,SC2206\n        args+=(-nic bridge,br=${network},model=${NET_DEVICE}${MAC})\n    else\n        echo \" - Network:  User (${NET_DEVICE})\"\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ${NET_DEVICE},netdev=nic -netdev ${NET},id=nic)\n    fi\n\n    # Add the disks\n    # - https://turlucode.com/qemu-disk-io-performance-comparison-native-or-threads-windows-10-version/\n    # Optimise disk I/O: enable TRIM/discard, zero detection for thin provisioning,\n    # writeback caching and threaded async I/O\n    DRIVE_OPTIMISATIONS=\"discard=unmap,detect-zeroes=unmap,cache=writeback,aio=threads\"\n\n    if [[ \"${boot}\" == *\"efi\"* ]]; then\n        QCOW2CODE=$(is_firmware_qcow2 \"${EFI_CODE}\")\n        QCOW2VARS=$(is_firmware_qcow2 \"${EFI_VARS}\")\n        if [ \"${QCOW2CODE}\" = \"true\" ]; then EFI_CODE_FORMAT=\"qcow2\"; else EFI_CODE_FORMAT=\"raw\"; fi\n        if [ \"${QCOW2VARS}\" = \"true\" ]; then EFI_VARS_FORMAT=\"qcow2\"; else EFI_VARS_FORMAT=\"raw\"; fi\n\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64 uses blockdev with named nodes referenced by machine pflash parameters\n            # Do NOT use -global cfi.pflash01 secure property - that's x86 SMM-specific\n            # shellcheck disable=SC2054\n            args+=(-blockdev node-name=rom,driver=file,filename=\"${EFI_CODE}\",read-only=true\n                -blockdev node-name=efivars,driver=file,filename=\"${EFI_VARS}\")\n        else\n          # x86 uses traditional pflash drives with secure boot support\n          # shellcheck disable=SC2054\n          args+=(-global driver=cfi.pflash01,property=secure,value=on\n              -drive if=pflash,format=\"${EFI_CODE_FORMAT}\",unit=0,file=\"${EFI_CODE}\",readonly=on\n              -drive if=pflash,format=\"${EFI_VARS_FORMAT}\",unit=1,file=\"${EFI_VARS}\")\n        fi\n    fi\n\n    if [ -n \"${iso}\" ] && [ \"${guest_os}\" == \"freedos\" ]; then\n        # FreeDOS reboots after partitioning the disk, and QEMU tries to boot from disk after first restart\n        # This flag sets the boot order to cdrom,disk. It will persist until powering down the VM\n        args+=(-boot order=dc)\n    elif [ -n \"${iso}\" ] && [ \"${guest_os}\" == \"kolibrios\" ]; then\n        # Since there is bug (probably) in KolibriOS: cdrom indexes 0 or 1 make system show an extra unexisting iso, so we use index=2\n        # shellcheck disable=SC2054\n        args+=(-drive media=cdrom,index=2,file=\"${iso}\")\n        iso=\"\"\n    elif [ -n \"${iso}\" ] && [ \"${guest_os}\" == \"reactos\" ]; then\n        # https://reactos.org/wiki/QEMU\n        # shellcheck disable=SC2054\n        args+=(-boot order=d\n            -drive if=ide,index=2,media=cdrom,file=\"${iso}\")\n        iso=\"\"\n    elif [ -n \"${iso}\" ] && [ \"${guest_os}\" == \"windows\" ] && [ -e \"${VMDIR}/unattended.iso\" ]; then\n        # Attach the unattended configuration to Windows guests when booting from ISO\n        # shellcheck disable=SC2054\n        args+=(-drive media=cdrom,index=2,file=\"${VMDIR}/unattended.iso\")\n    fi\n\n    if [ -n \"${floppy}\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-drive if=floppy,format=raw,file=\"${floppy}\")\n    fi\n\n    # ARM64: create virtio-scsi controller if any CD-ROM ISOs are present\n    # (virt machine has no IDE controller)\n    if [ \"${ARCH_VM}\" == \"aarch64\" ] && { [ -n \"${iso}\" ] || [ -n \"${fixed_iso}\" ]; }; then\n        # shellcheck disable=SC2054\n        args+=(-device virtio-scsi-pci,id=scsi0)\n    fi\n\n    if [ -n \"${iso}\" ]; then\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64: bootindex=1 ensures UEFI boots from CD-ROM first during installation\n            # shellcheck disable=SC2054\n            args+=(-device scsi-cd,drive=cd0,bus=scsi0.0,bootindex=1\n                -drive id=cd0,if=none,format=raw,media=cdrom,readonly=on,file=\"${iso}\")\n        else\n            # shellcheck disable=SC2054\n            args+=(-drive media=cdrom,index=0,file=\"${iso}\")\n        fi\n    fi\n\n    if [ -n \"${fixed_iso}\" ]; then\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64: attach second ISO to virtio-scsi controller\n            # shellcheck disable=SC2054\n            args+=(-device scsi-cd,drive=cd1,bus=scsi0.0,bootindex=3\n                -drive id=cd1,if=none,format=raw,media=cdrom,readonly=on,file=\"${fixed_iso}\")\n        else\n            # shellcheck disable=SC2054\n            args+=(-drive media=cdrom,index=1,file=\"${fixed_iso}\")\n        fi\n    fi\n\n    if [ \"${guest_os}\" == \"macos\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-device ahci,id=ahci\n            -device ide-hd,bus=ahci.0,drive=BootLoader,bootindex=0\n            -drive id=BootLoader,if=none,format=qcow2,file=\"${MAC_BOOTLOADER}\")\n\n        if [ -n \"${img}\" ]; then\n            # shellcheck disable=SC2054\n            args+=(-device ide-hd,bus=ahci.1,drive=RecoveryImage\n                -drive id=RecoveryImage,if=none,format=raw,file=\"${img}\")\n        fi\n\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ${MAC_DISK_DEV},drive=SystemDisk\n            -drive id=SystemDisk,if=none,format=qcow2,file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n    elif [ \"${guest_os}\" == \"kolibrios\" ]; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ahci,id=ahci\n            -device ide-hd,bus=ahci.0,drive=SystemDisk\n            -drive id=SystemDisk,if=none,format=qcow2,file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n\n    elif [ \"${guest_os}\" == \"batocera\" ] ; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device virtio-blk-pci,drive=BootDisk\n            -drive id=BootDisk,if=none,format=raw,file=\"${img}\"\n            -device virtio-blk-pci,drive=SystemDisk\n            -drive id=SystemDisk,if=none,format=qcow2,file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n\n    elif [ \"${guest_os}\" == \"reactos\" ]; then\n        # https://reactos.org/wiki/QEMU\n        # shellcheck disable=SC2054,SC2206\n        args+=(-drive if=ide,index=0,media=disk,file=\"${disk_img}\")\n\n    elif [ \"${guest_os}\" == \"windows-server\" ]; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ide-hd,drive=SystemDisk\n            -drive id=SystemDisk,if=none,format=qcow2,file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n\n    else\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64: bootindex=2 ensures disk boots after CD-ROM (bootindex=1) during installation\n            # shellcheck disable=SC2054,SC2206\n            args+=(-device virtio-blk-pci,drive=SystemDisk,bootindex=2\n                -drive id=SystemDisk,if=none,format=${disk_format},file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n        else\n            # shellcheck disable=SC2054,SC2206\n            args+=(-device virtio-blk-pci,drive=SystemDisk\n                -drive id=SystemDisk,if=none,format=${disk_format},file=\"${disk_img}\",${DRIVE_OPTIMISATIONS} ${STATUS_QUO})\n        fi\n    fi\n\n    # https://wiki.qemu.org/Documentation/9psetup\n    # https://askubuntu.com/questions/772784/9p-libvirt-qemu-share-modes\n    if [ \"${guest_os}\" != \"windows\" ] || [ \"${guest_os}\" == \"windows-server\" ] && [ -n \"${PUBLIC}\" ]; then\n        # shellcheck disable=SC2054\n        args+=(-fsdev local,id=fsdev0,path=\"${PUBLIC}\",security_model=mapped-xattr\n            -device virtio-9p-pci,fsdev=fsdev0,mount_tag=\"${PUBLIC_TAG}\")\n    fi\n\n    if [ -n \"${USB_PASSTHROUGH}\" ]; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-device ${USB_HOST_PASSTHROUGH_CONTROLLER},id=hostpass\n            ${USB_PASSTHROUGH})\n    fi\n\n    if [ \"${tpm}\" == \"on\" ] && [ -S \"${VMDIR}/${VMNAME}.swtpm-sock\" ]; then\n        # shellcheck disable=SC2054\n        if [ \"${ARCH_VM}\" == \"aarch64\" ]; then\n            # ARM64 uses tpm-tis-device (system bus) instead of tpm-tis (ISA/LPC bus)\n            args+=(-chardev socket,id=chrtpm,path=\"${VMDIR}/${VMNAME}.swtpm-sock\"\n                -tpmdev emulator,id=tpm0,chardev=chrtpm\n                -device tpm-tis-device,tpmdev=tpm0)\n        else\n            args+=(-chardev socket,id=chrtpm,path=\"${VMDIR}/${VMNAME}.swtpm-sock\"\n                -tpmdev emulator,id=tpm0,chardev=chrtpm\n                -device tpm-tis,tpmdev=tpm0)\n        fi\n    fi\n\n    if [ \"${monitor}\" == \"none\" ]; then\n        args+=(-monitor none)\n        echo \" - Monitor:  (off)\"\n    elif [ \"${monitor}\" == \"telnet\" ]; then\n        # Find a free port to expose monitor-telnet to the guest\n        TEMP_PORT=\"$(get_port \"${monitor_telnet_port}\" 9)\"\n        if [ -z \"${TEMP_PORT}\" ]; then\n            echo \" - Monitor:  All Monitor-Telnet ports have been exhausted.\"\n        else\n            monitor_telnet_port=\"${TEMP_PORT}\"\n            # shellcheck disable=SC2054\n            args+=(-monitor telnet:\"${monitor_telnet_host}:${monitor_telnet_port}\",server,nowait)\n            echo \" - Monitor:  On host:  telnet ${monitor_telnet_host} ${monitor_telnet_port}\"\n            echo \"monitor-telnet,${monitor_telnet_port},${monitor_telnet_host}\" >> \"${VMDIR}/${VMNAME}.ports\"\n        fi\n    elif [ \"${monitor}\" == \"socket\" ]; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-monitor unix:${SOCKET_MONITOR},server,nowait)\n        if command -v socat &>/dev/null; then\n            echo \" - Monitor:  On host:  socat -,echo=0,icanon=0 unix-connect:${SOCKET_MONITOR}\"\n        elif command -v nc &>/dev/null; then\n            echo \" - Monitor:  On host:  nc -U \\\"${SOCKET_MONITOR}\\\"\"\n        fi\n    else\n        echo \"ERROR! \\\"${monitor}\\\" is an unknown monitor option.\"\n        exit 1\n    fi\n\n    if [ \"${serial}\" == \"none\" ]; then\n        args+=(-serial none)\n        # No log output when serial is disabled - it's the default for macOS/Windows\n        # and provides no useful information to the user\n    elif [ \"${serial}\" == \"telnet\" ]; then\n        # Find a free port to expose serial-telnet to the guest\n        TEMP_PORT=\"$(get_port \"${serial_telnet_port}\" 9)\"\n        if [ -z \"${TEMP_PORT}\" ]; then\n            echo \" - Serial:   All Serial Telnet ports have been exhausted.\"\n        else\n            serial_telnet_port=\"${TEMP_PORT}\"\n            # shellcheck disable=SC2054,SC2206\n            args+=(-serial telnet:${serial_telnet_host}:${serial_telnet_port},server,nowait)\n            echo \" - Serial:   On host:  telnet ${serial_telnet_host} ${serial_telnet_port}\"\n            echo \"serial-telnet,${serial_telnet_port},${serial_telnet_host}\" >> \"${VMDIR}/${VMNAME}.ports\"\n        fi\n    elif [ \"${serial}\" == \"socket\" ]; then\n        # shellcheck disable=SC2054,SC2206\n        args+=(-serial unix:${SOCKET_SERIAL},server,nowait)\n        if command -v socat &>/dev/null; then\n            echo \" - Serial:   On host:  socat -,echo=0,icanon=0 unix-connect:${SOCKET_SERIAL}\"\n        elif command -v nc &>/dev/null; then\n            echo \" - Serial:   On host:  nc -U \\\"${SOCKET_SERIAL}\\\"\"\n        fi\n    else\n        echo \"ERROR! \\\"${serial}\\\" is an unknown serial option.\"\n        exit 1\n    fi\n\n    if [ -n \"${extra_args}\" ]; then\n        # shellcheck disable=SC2206\n        args+=(${extra_args})\n    fi\n\n    # The OSK parameter contains parenthesis, they need to be escaped in the shell\n    # scripts. The vendor name, Quickemu Project, contains a space. It needs to be\n    # double-quoted.\n    SHELL_ARGS=\"${args[*]}\"\n    SHELL_ARGS=\"${SHELL_ARGS//\\(/\\\\(}\"\n    SHELL_ARGS=\"${SHELL_ARGS//)/\\\\)}\"\n    SHELL_ARGS=\"${SHELL_ARGS//Quickemu Project/\\\"Quickemu Project\\\"}\"\n\n    if [ -z \"${VM_PID}\" ]; then\n        echo \"${QEMU}\" \"${SHELL_ARGS}\" \"2>/dev/null\" >> \"${VMDIR}/${VMNAME}.sh\"\n        sed -i -e 's/ -/ \\\\\\n    -/g' \"${VMDIR}/${VMNAME}.sh\"\n        ${QEMU} \"${args[@]}\" &> \"${VMDIR}/${VMNAME}.log\" &\n        VM_PID=$!\n        sleep 0.25\n        if kill -0 \"${VM_PID}\" 2>/dev/null; then\n            echo \" - Process:  Started ${VM} as ${VMNAME} (${VM_PID})\"\n            configure_cpu_pinning\n        else\n            echo \" - Process:  ERROR! Failed to start ${VM} as ${VMNAME}\"\n            rm -f \"${VMDIR}/${VMNAME}.pid\"\n            rm -f \"${VMDIR}/${VMNAME}.spice\"\n            rm -f \"${VMDIR}/${VMNAME}.sock\"\n            echo && cat \"${VMDIR}/${VMNAME}.log\"\n            exit 1\n        fi\n    fi\n}\n\nfunction start_viewer {\n    # Exit early if viewer is disabled or display is not SPICE\n    if [ \"${viewer}\" == \"none\" ] || [ \"${display}\" != \"spice\" ]; then\n        return\n    fi\n\n    # Build viewer arguments based on connection mode (Unix socket or TCP)\n    local viewer_args=()\n    local viewer_uri=\"\"\n    local errno=0\n\n    # Determine connection mode from .spice file content\n    # - Unix socket mode: file contains a path (with /)\n    # - TCP mode: file contains just a port number\n    local spice_info=\"\"\n    if [ -r \"${VMDIR}/${VMNAME}.spice\" ]; then\n        spice_info=$(cat \"${VMDIR}/${VMNAME}.spice\")\n    fi\n\n    if [[ \"${spice_info}\" == */* ]]; then\n        # Unix socket mode (path contains /)\n        local SPICE_SOCKET=\"${spice_info}\"\n        if [ \"${viewer}\" == \"spicy\" ]; then\n            viewer_args+=(\"--uri=spice+unix://${SPICE_SOCKET}\")\n        else\n            viewer_uri=\"spice+unix://${SPICE_SOCKET}\"\n        fi\n    elif [ -n \"${spice_info}\" ]; then\n        # TCP mode (port number)\n        if [ \"${viewer}\" == \"spicy\" ]; then\n            viewer_args+=(\"--port\" \"${spice_info}\")\n        else\n            viewer_uri=\"spice://localhost:${spice_info}\"\n        fi\n    else\n        # Fallback: no .spice file, use ACCESS variable to determine mode\n        if [ -z \"${ACCESS}\" ] || [ \"${ACCESS}\" == \"local\" ]; then\n            # Unix socket mode\n            local SPICE_SOCKET=\"${VMDIR}/${VMNAME}.sock\"\n            if [ \"${viewer}\" == \"spicy\" ]; then\n                viewer_args+=(\"--uri=spice+unix://${SPICE_SOCKET}\")\n            else\n                viewer_uri=\"spice+unix://${SPICE_SOCKET}\"\n            fi\n        else\n            # TCP mode for remote access\n            if [ \"${viewer}\" == \"spicy\" ]; then\n                viewer_args+=(\"--port\" \"${spice_port}\")\n            else\n                viewer_uri=\"spice://localhost:${spice_port}\"\n            fi\n        fi\n    fi\n\n    # Add common arguments\n    viewer_args+=(\"--title\" \"${VMNAME}\")\n\n    # Add shared directory if configured (not for macOS guests)\n    if [ \"${guest_os}\" != \"macos\" ] && [ -n \"${PUBLIC}\" ]; then\n        viewer_args+=(\"--spice-shared-dir\" \"${PUBLIC}\")\n    fi\n\n    # Add fullscreen if requested\n    if [ -n \"${FULLSCREEN}\" ]; then\n        viewer_args+=(\"${FULLSCREEN}\")\n    fi\n\n    # Add URI for remote-viewer (spicy uses --uri= in the args already)\n    if [ \"${viewer}\" == \"remote-viewer\" ] && [ -n \"${viewer_uri}\" ]; then\n        viewer_args+=(\"${viewer_uri}\")\n    fi\n\n    # Launch the viewer\n    echo \" - Viewer:   ${viewer} ${viewer_args[*]} >/dev/null 2>&1 &\"\n    \"${viewer}\" \"${viewer_args[@]}\" >/dev/null 2>&1 &\n    errno=$?\n\n    if [ ${errno} -ne 0 ]; then\n        echo \"WARNING! Could not start viewer (${viewer}) Err: ${errno}\"\n    fi\n}\n\nfunction shortcut_create {\n    local dirname=\"${HOME}/.local/share/applications\"\n    local filename=\"${HOME}/.local/share/applications/${VMNAME}.desktop\"\n    echo \"Creating ${VMNAME} desktop shortcut file\"\n\n    if [ ! -d \"${dirname}\" ]; then\n        mkdir -p \"${dirname}\"\n    fi\n    cat << EOF > \"${filename}\"\n[Desktop Entry]\nVersion=1.0\nType=Application\nTerminal=false\nExec=$(basename \"${0}\") --vm ${VM} ${SHORTCUT_OPTIONS}\nPath=${VMPATH}\nName=${VMNAME}\nIcon=qemu\nEOF\n    echo \" - ${filename} created.\"\n}\n\nfunction usage() {\n    echo \"             _      _\"\n    echo \"  __ _ _   _(_) ___| | _____ _ __ ___  _   _\"\n    echo \" / _' | | | | |/ __| |/ / _ \\ '_ ' _ \\| | | |\"\n    echo \"| (_| | |_| | | (__|   <  __/ | | | | | |_| |\"\n    echo \" \\__, |\\__,_|_|\\___|_|\\_\\___|_| |_| |_|\\__,_|\"\n    echo \"    |_| v${VERSION}, using qemu ${QEMU_VER_LONG}\"\n    echo \"--------------------------------------------------------------------------------\"\n    echo \" Project - https://github.com/quickemu-project/quickemu\"\n    echo \" Discord - https://wimpysworld.io/discord\"\n    echo \"--------------------------------------------------------------------------------\"\n    echo\n    echo \"Usage\"\n    echo \"  ${LAUNCHER} --vm ubuntu.conf <arguments>\"\n    echo\n    echo \"Arguments\"\n    echo \"  --access                          : Enable remote spice access support. 'local' (default), 'remote', 'clientipaddress'\"\n    echo \"  --braille                         : Enable braille support. Requires SDL.\"\n    echo \"  --cpu-pinning                     : Choose which host cores correspond to which guest cores.\"\n    echo \"  --delete-disk                     : Delete the disk image and EFI variables\"\n    echo \"  --delete-vm                       : Delete the entire VM and its configuration\"\n    echo \"  --display                         : Select display backend. 'gtk' (default), 'sdl', 'cocoa', 'none', 'spice' or 'spice-app'\"\n    echo \"  --fullscreen                      : Starts VM in full screen mode (Ctl+Alt+f to exit)\"\n    echo \"  --ignore-msrs-always              : Configure KVM to always ignore unhandled machine-specific registers\"\n    echo \"  --ignore-tsc-warning              : Skip TSC stability warning for macOS VMs on AMD\"\n    echo \"  --kill                            : Kill the VM process if it is running\"\n    echo \"  --offline                         : Override all network settings and start the VM offline\"\n    echo \"  --shortcut                        : Create a desktop shortcut\"\n    echo \"  --snapshot apply <tag>            : Apply/restore a snapshot.\"\n    echo \"  --snapshot create <tag>           : Create a snapshot.\"\n    echo \"  --snapshot delete <tag>           : Delete a snapshot.\"\n    echo \"  --snapshot info                   : Show disk/snapshot info.\"\n    echo \"  --status-quo                      : Do not commit any changes to disk/snapshot.\"\n    echo \"  --viewer <viewer>                 : Choose an alternative viewer. @Options: 'spicy' (default), 'remote-viewer', 'none'\"\n    echo \"  --width <width>                   : Set VM screen width; requires '--height'\"\n    echo \"  --height <height>                 : Set VM screen height; requires '--width'\"\n    echo \"  --ssh-port <port>                 : Set SSH port manually\"\n    echo \"  --spice-port <port>               : Set SPICE port manually\"\n    echo \"  --public-dir <path>               : Expose share directory. @Options: '' (default: xdg-user-dir PUBLICSHARE), '<directory>', 'none'\"\n    echo \"  --monitor <type>                  : Set monitor connection type. @Options: 'socket' (default), 'telnet', 'none'\"\n    echo \"  --monitor-telnet-host <ip/host>   : Set telnet host for monitor. (default: 'localhost')\"\n    echo \"  --monitor-telnet-port <port>      : Set telnet port for monitor. (default: '4440')\"\n    echo \"  --monitor-cmd <cmd>               : Send command to monitor if available. (Example: system_powerdown)\"\n    echo \"  --serial <type>                   : Set serial connection type. @Options: 'socket' (default), 'telnet', 'none'\"\n    echo \"  --serial-telnet-host <ip/host>    : Set telnet host for serial. (default: 'localhost')\"\n    echo \"  --serial-telnet-port <port>       : Set telnet port for serial. (default: '6660')\"\n    echo \"  --keyboard <type>                 : Set keyboard. @Options: 'usb' (default), 'ps2', 'virtio'\"\n    echo \"  --keyboard_layout <layout>        : Set keyboard layout: 'en-us' (default)\"\n    echo \"  --mouse <type>                    : Set mouse. @Options: 'tablet' (default), 'ps2', 'usb', 'virtio'\"\n    echo \"  --usb-controller <type>           : Set usb-controller. @Options: 'ehci' (default), 'xhci', 'none'\"\n    echo \"  --sound-card <type>               : Set sound card. @Options: 'intel-hda' (default), 'ac97', 'es1370', 'sb16', 'usb-audio', 'virtio-sound-pci', 'none'\"\n    echo \"  --sound-duplex <type>             : Set sound card duplex. @Options: 'hda-micro' (default: speaker/mic), 'hda-duplex' (line-in/line-out), 'hda-output' (output-only)\"\n    echo \"  --extra_args <arguments>          : Pass additional arguments to qemu\"\n    echo \"  --version                         : Print version\"\n}\n\nfunction display_param_check() {\n    # Braille support requires SDL. Override $display if braille was requested.\n    if [ -n \"${BRAILLE}\" ]; then\n        display=\"sdl\"\n        # XHCI supports USB 1.1/2.0/3.0; required for full-speed braille devices\n        usb_controller=\"xhci\"\n    fi\n\n    # Fallback to SDL if GTK display is not available\n    if [ \"${display}\" == \"gtk\" ]; then\n        if ! \"${QEMU}\" -display help 2>&1 | grep -q \"^gtk$\"; then\n            echo \" - NOTE: GTK display not available, falling back to SDL\"\n            display=\"sdl\"\n        fi\n    fi\n\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        if [ \"${display}\" != \"cocoa\" ] && [ \"${display}\" != \"none\" ]; then\n          echo \"ERROR! Requested output '${display}' but only 'cocoa' and 'none' are avalible on macOS.\"\n          exit 1\n        fi\n    else\n        if [ \"${display}\" != \"gtk\" ] && [ \"${display}\" != \"none\" ] && [ \"${display}\" != \"sdl\" ] && [ \"${display}\" != \"spice\" ] && [ \"${display}\" != \"spice-app\" ]; then\n            echo \"ERROR! Requested output '${display}' is not recognised.\"\n            exit 1\n        fi\n    fi\n\n    # Set the default 3D acceleration.\n    if [ -z \"${gl}\" ]; then\n        if command -v glxinfo &>/dev/null; then\n            GLSL_VER=$(glxinfo | grep \"OpenGL ES GLSL\" | awk '{print $NF}')\n            case ${GLSL_VER} in\n                1*|2*) gl=\"off\";;\n                *) gl=\"on\";;\n            esac\n        else\n            gl=\"on\"\n        fi\n    fi\n\n    # Enable grab-on-hover for SDL: https://github.com/quickemu-project/quickemu/issues/541\n    case \"${display}\" in\n        sdl) export SDL_MOUSE_FOCUS_CLICKTHROUGH=1;;\n    esac\n}\n\nfunction ports_param_check() {\n    if [ -n \"${ssh_port}\" ] && ! is_numeric \"${ssh_port}\"; then\n        echo \"ERROR: ssh_port must be a number!\"\n        exit 1\n    fi\n\n    if [ -n \"${spice_port}\" ] && ! is_numeric \"${spice_port}\"; then\n        echo \"ERROR: spice_port must be a number!\"\n        exit 1\n    fi\n\n    if [ -n \"${monitor_telnet_port}\" ] && ! is_numeric \"${monitor_telnet_port}\"; then\n        echo \"ERROR: telnet port must be a number!\"\n        exit 1\n    fi\n\n    if [ -n \"${serial_telnet_port}\" ] && ! is_numeric \"${serial_telnet_port}\"; then\n        echo \"ERROR: serial port must be a number!\"\n        exit 1\n    fi\n}\n\nfunction sound_card_param_check() {\n    if [ \"${sound_card}\" != \"ac97\" ] && [ \"${sound_card}\" != \"es1370\" ] && [ \"${sound_card}\" != \"ich9-intel-hda\" ] && [ \"${sound_card}\" != \"intel-hda\" ] && [ \"${sound_card}\" != \"sb16\" ] && [ \"${sound_card}\" != \"usb-audio\" ] && [ \"${sound_card}\" != \"virtio-sound-pci\" ] && [ \"${sound_card}\" != \"none\" ]; then\n        echo \"ERROR! Requested sound card '${sound_card}' is not recognised.\"\n        exit 1\n    fi\n\n    # USB audio requires xhci controller\n    if [ \"${sound_card}\" == \"usb-audio\" ]; then\n        usb_controller=\"xhci\";\n    fi\n\n    #name \"hda-duplex\", bus HDA, desc \"HDA Audio Codec, duplex (line-out, line-in)\"\n    #name \"hda-micro\", bus HDA, desc \"HDA Audio Codec, duplex (speaker, microphone)\"\n    #name \"hda-output\", bus HDA, desc \"HDA Audio Codec, output-only (line-out)\"\n    if [ \"${sound_duplex}\" != \"hda-duplex\" ] && [ \"${sound_duplex}\" != \"hda-micro\" ] && [ \"${sound_duplex}\" != \"hda-output\" ]; then\n        echo \"ERROR! Requested sound duplex '${sound_duplex}' is not recognised.\"\n        exit 1\n    fi\n}\n\nfunction tpm_param_check() {\n    if [ \"${tpm}\" == \"on\" ]; then\n        SWTPM=$(command -v swtpm)\n        if [ ! -e \"${SWTPM}\" ]; then\n            echo \"ERROR! TPM is enabled, but swtpm was not found.\"\n            exit 1\n        fi\n    fi\n}\n\nfunction viewer_param_check() {\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        return\n    fi\n\n    if [ \"${viewer}\" != \"none\" ] && [ \"${viewer}\" != \"spicy\" ] && [ \"${viewer}\" != \"remote-viewer\" ]; then\n        echo \"ERROR! Requested viewer '${viewer}' is not recognised.\"\n        exit 1\n    fi\n    if [ \"${viewer}\" == \"spicy\" ] && ! command -v spicy &>/dev/null; then\n        echo \"ERROR! Requested 'spicy' as viewer, but 'spicy' is not installed.\"\n        exit 1\n    elif [ \"${viewer}\" == \"remote-viewer\" ] && ! command -v remote-viewer &>/dev/null; then\n        echo \"ERROR! Requested 'remote-viewer' as viewer, but 'remote-viewer' is not installed.\"\n        exit 1\n    fi\n}\n\nfunction fileshare_param_check() {\n    if [ \"${PUBLIC}\" == \"none\" ]; then\n        PUBLIC=\"\"\n    else\n        # PUBLICSHARE is the only directory exposed to guest VMs for file\n        # sharing via 9P, spice-webdavd and Samba. This path is not configurable.\n        if [ -z \"${PUBLIC}\" ]; then\n            if command -v xdg-user-dir &>/dev/null; then\n                PUBLIC=$(xdg-user-dir PUBLICSHARE)\n            elif [ -d \"${HOME}/Public\" ]; then\n                PUBLIC=\"${HOME}/Public\"\n            fi\n        fi\n\n        if [ ! -d \"${PUBLIC}\" ]; then\n            echo \" - WARNING! Public directory: '${PUBLIC}' doesn't exist!\"\n            PUBLIC=\"\"\n        else\n            PUBLIC_TAG=\"Public-${USER,,}\"\n            PUBLIC_PERMS=$(${STAT}  -c \"%A\" \"${PUBLIC}\")\n        fi\n    fi\n}\n\nfunction parse_ports_from_file {\n    local FILE=\"${VMDIR}/${VMNAME}.ports\"\n    local host_name=\"\"\n    local port_name=\"\"\n    local port_number=\"\"\n\n    # Loop over each line in the file\n    while IFS= read -r CONF || [ -n \"${CONF}\" ]; do\n        # parse ports\n        port_name=$(echo \"${CONF}\" | cut -d',' -f 1)\n        port_number=$(echo \"${CONF}\" | cut -d',' -f 2)\n        host_name=$(echo \"${CONF}\" | awk 'FS=\",\" {print $3,\".\"}')\n\n        if [ \"${port_name}\" == \"ssh\" ]; then\n            ssh_port=\"${port_number}\"\n        elif [ \"${port_name}\" == \"spice\" ]; then\n            spice_port=\"${port_number}\"\n        elif [ \"${port_name}\" == \"monitor-telnet\" ]; then\n            monitor_telnet_port=\"${port_number}\"\n            monitor_telnet_host=\"${host_name}\"\n        elif [ \"${port_name}\" == \"serial-telnet\" ]; then\n            serial_telnet_port=\"${port_number}\"\n            serial_telnet_host=\"${host_name}\"\n        fi\n    done < \"${FILE}\"\n}\n\nfunction is_numeric {\n    [[ \"$1\" =~ ^[0-9]+$ ]]\n}\n\nfunction monitor_send_cmd {\n    local MSG=\"${1}\"\n\n    if [ -z \"${MSG}\" ]; then\n        echo \"WARNING! Send to QEMU-Monitor: Message empty!\"\n        return 1\n    fi\n\n    case \"${monitor}\" in\n        socket)\n            echo -e \" - Sending:  via socket ${MSG}\"\n            echo -e \"${MSG}\" | socat -,shut-down unix-connect:\"${SOCKET_MONITOR}\" > /dev/null 2>&1;;\n        telnet)\n            echo -e \" - Sending:  via telnet ${MSG}\"\n            echo -e \"${MSG}\" | socat - tcp:\"${monitor_telnet_host}\":\"${monitor_telnet_port}\" > /dev/null 2>&1;;\n        *)\n            echo \"WARNING! No qemu-monitor channel available - Couldn't send message to monitor!\"\n            return 1;;\n    esac\n\n    return 0\n}\n\n### MAIN\n\n# Lowercase variables are used in the VM config file only\nboot=\"efi\"\ncpu_cores=\"\"\ndisk_format=\"${disk_format:-qcow2}\"\ndisk_img=\"${disk_img:-}\"\ndisk_size=\"${disk_size:-16G}\"\ndisplay=\"${display:-gtk}\"\nextra_args=\"${extra_args:-}\"\nfixed_iso=\"\"\nfloppy=\"\"\nguest_os=\"linux\"\nimg=\"\"\niso=\"\"\nmacaddr=\"\"\nmacos_release=\"\"\nnetwork=\"\"\nport_forwards=()\npreallocation=\"off\"\nram=\"\"\nsecureboot=\"off\"\ntpm=\"off\"\nusb_devices=()\nviewer=\"${viewer:-spicy}\"\nwidth=\"${width:-}\"\nheight=\"${height:-}\"\nssh_port=\"${ssh_port:-}\"\nspice_port=\"${spice_port:-}\"\nmonitor=\"${monitor:-socket}\"\nmonitor_telnet_port=\"${monitor_telnet_port:-4440}\"\nmonitor_telnet_host=\"${monitor_telnet_host:-localhost}\"\n# Serial default is set later based on guest_os (after config is sourced)\nserial=\"${serial:-}\"\nserial_telnet_port=\"${serial_telnet_port:-6660}\"\nserial_telnet_host=\"${serial_telnet_host:-localhost}\"\n# options: ehci (USB2.0), xhci (USB3.0)\nusb_controller=\"${usb_controller:-ehci}\"\nkeyboard=\"${keyboard:-usb}\"\nkeyboard_layout=\"${keyboard_layout:-en-us}\"\nmouse=\"${mouse:-tablet}\"\nsound_card=\"${sound_card:-intel-hda}\"\nsound_duplex=\"${sound_duplex:-hda-micro}\"\n\nACCESS=\"\"\nACTIONS=()\nBRAILLE=\"\"\nIGNORE_TSC_WARNING=\"\"\nCPU_PINNING=\"\"\nFULLSCREEN=\"\"\nMONITOR_CMD=\"\"\nPUBLIC=\"\"\nPUBLIC_PERMS=\"\"\nPUBLIC_TAG=\"\"\nSHORTCUT_OPTIONS=\"\"\nSNAPSHOT_ACTION=\"\"\nSNAPSHOT_TAG=\"\"\nSOCKET_MONITOR=\"\"\nSOCKET_SERIAL=\"\"\nSTATUS_QUO=\"\"\nUSB_PASSTHROUGH=\"\"\nVM=\"\"\nVMDIR=\"\"\nVMNAME=\"\"\nVMPATH=\"\"\n\n# CPU flag tracking map for deduplication and conflict detection\ndeclare -A CPU_FLAG_MAP\n\n# shellcheck disable=SC2155\nreadonly LAUNCHER=$(basename \"${0}\")\nreadonly DISK_MIN_SIZE=$((197632 * 8))\nreadonly VERSION=\"4.9.9\"\n\n# Default architecture is x86_64, can be overridden by config file (arch=\"aarch64\")\narch=\"${arch:-x86_64}\"\nARCH_VM=\"${arch}\"\nARCH_HOST=$(uname -m)\nQEMU=$(command -v \"qemu-system-${ARCH_VM}\")\nQEMU_IMG=$(command -v qemu-img)\nif [ ! -x \"${QEMU}\" ] || [ ! -x \"${QEMU_IMG}\" ]; then\n    echo \"ERROR! QEMU not found. Please make sure 'qemu-system-${ARCH_VM}' and 'qemu-img' are installed.\"\n    exit 1\nfi\n\n# Check for gnu tools on macOS\nSTAT=\"stat\"\nif command -v gstat &>/dev/null; then\n    STAT=\"gstat\"\nfi\n\nOS_KERNEL=$(uname -s)\nif [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n    display=\"cocoa\"\nfi\n\nQEMU_VER_LONG=$(${QEMU_IMG} --version | head -n 1 | awk '{print $3}')\n# strip patch version and remove dots. 6.0.0 => 60 / 10.0.0 => 100\n\nQEMU_VER_SHORT=\"${QEMU_VER_LONG%.*}\"\nQEMU_VER_SHORT=\"${QEMU_VER_SHORT/./}\"\n\nif [ \"${QEMU_VER_SHORT}\" -lt 61 ]; then\n    echo \"ERROR! QEMU 6.1.0 or newer is required, detected ${QEMU_VER_LONG}.\"\n    exit 1\nfi\n\n# Take command line arguments\nif [ $# -lt 1 ]; then\n    usage\n    exit 1\nelse\n    while [ $# -gt 0 ]; do\n        case \"${1}\" in\n            -access|--access)\n                SHORTCUT_OPTIONS+=\"--access ${2} \"\n                ACCESS=\"${2}\"\n                shift 2;;\n            -braille|--braille)\n                SHORTCUT_OPTIONS+=\"--braille \"\n                BRAILLE=\"on\"\n                shift;;\n            -cpu-pinning|--cpu-pinning)\n                SHORTCUT_OPTIONS+=\"--cpu-pinning ${2} \"\n                CPU_PINNING=${2}\n                shift 2;;\n            -delete|--delete|-delete-disk|--delete-disk)\n                ACTIONS+=(delete_disk)\n                shift;;\n            -delete-vm|--delete-vm)\n                ACTIONS+=(delete_vm)\n                shift;;\n            -display|--display)\n                SHORTCUT_OPTIONS+=\"--display ${2} \"\n                display=\"${2}\"\n                display_param_check\n                shift 2;;\n            -fullscreen|--fullscreen|-full-screen|--full-screen)\n                SHORTCUT_OPTIONS+=\"--fullscreen \"\n                FULLSCREEN=\"--full-screen\"\n                shift;;\n            -ignore-msrs-always|--ignore-msrs-always)\n                ignore_msrs_always\n                exit;;\n            -ignore-tsc-warning|--ignore-tsc-warning)\n                IGNORE_TSC_WARNING=\"1\"\n                shift;;\n            -kill|--kill)\n                ACTIONS+=(kill_vm)\n                shift;;\n            -offline|--offline)\n                SHORTCUT_OPTIONS+=\"--offline \"\n                network=\"none\"\n                shift;;\n            -snapshot|--snapshot)\n                if [ -z \"${2}\" ]; then\n                    echo \"ERROR! '--snapshot' needs an action to perform.\"\n                    exit 1\n                fi\n                SNAPSHOT_ACTION=\"${2}\"\n                if [ -z \"${3}\" ] && [ \"${SNAPSHOT_ACTION}\" != \"info\" ]; then\n                    echo \"ERROR! '--snapshot ${SNAPSHOT_ACTION}' needs a tag.\"\n                    exit 1\n                fi\n                SNAPSHOT_TAG=\"${3}\"\n                if [ \"${SNAPSHOT_ACTION}\" == \"info\" ]; then\n                    shift 2\n                else\n                    shift 3\n                fi;;\n            -status-quo|--status-quo)\n                STATUS_QUO=\"-snapshot\"\n                shift;;\n            -shortcut|--shortcut)\n                ACTIONS+=(shortcut_create)\n                shift;;\n            -vm|--vm)\n                VM=\"${2}\"\n                shift 2;;\n            -viewer|--viewer)\n                SHORTCUT_OPTIONS+=\"--viewer ${2} \"\n                viewer=\"${2}\"\n                shift 2;;\n            -width|--width)\n                SHORTCUT_OPTIONS+=\"--width ${2} \"\n                width=\"${2}\"\n                shift 2;;\n            -height|--height)\n                SHORTCUT_OPTIONS+=\"--height ${2} \"\n                height=\"${2}\"\n                shift 2;;\n            -ssh-port|--ssh-port)\n                SHORTCUT_OPTIONS+=\"--ssh-port ${2} \"\n                ssh_port=\"${2}\"\n                shift 2;;\n            -spice-port|--spice-port)\n                SHORTCUT_OPTIONS+=\"--spice-port ${2} \"\n                spice_port=\"${2}\"\n                shift 2;;\n            -public-dir|--public-dir)\n                SHORTCUT_OPTIONS+=\"--public-dir ${2} \"\n                PUBLIC=\"${2}\"\n                shift 2;;\n            -monitor|--monitor)\n                SHORTCUT_OPTIONS+=\"--monitor ${2} \"\n                monitor=\"${2}\"\n                shift 2;;\n            -monitor-cmd|--monitor-cmd)\n                SHORTCUT_OPTIONS+=\"--monitor-cmd ${2} \"\n                MONITOR_CMD=\"${2}\"\n                shift 2;;\n            -monitor-telnet-host|--monitor-telnet-host)\n                SHORTCUT_OPTIONS+=\"--monitor-telnet-host ${2} \"\n                monitor_telnet_host=\"${2}\"\n                shift 2;;\n            -monitor-telnet-port|--monitor-telnet-port)\n                SHORTCUT_OPTIONS+=\"--monitor-telnet-port ${2} \"\n                monitor_telnet_port=\"${2}\"\n                shift 2;;\n            -serial|--serial)\n                SHORTCUT_OPTIONS+=\"--serial ${2} \"\n                serial=\"${2}\"\n                shift 2;;\n            -serial-telnet-host|--serial-telnet-host)\n                SHORTCUT_OPTIONS+=\"--serial-telnet-host ${2} \"\n                serial_telnet_host=\"${2}\"\n                shift 2;;\n            -serial-telnet-port|--serial-telnet-port)\n                SHORTCUT_OPTIONS+=\"--serial-telnet-port ${2} \"\n                serial_telnet_port=\"${2}\"\n                shift 2;;\n            -keyboard|--keyboard)\n                SHORTCUT_OPTIONS+=\"--keyboard ${2} \"\n                keyboard=\"${2}\"\n                shift 2;;\n            -keyboard_layout|--keyboard_layout)\n                SHORTCUT_OPTIONS+=\"--keyboard_layout ${2} \"\n                keyboard_layout=\"${2}\"\n                shift 2;;\n            -mouse|--mouse)\n                SHORTCUT_OPTIONS+=\"--mouse ${2} \"\n                mouse=\"${2}\"\n                shift 2;;\n            -usb-controller|--usb-controller)\n                SHORTCUT_OPTIONS+=\"--usb-controller ${2} \"\n                usb_controller=\"${2}\"\n                shift 2;;\n            -extra_args|--extra_args)\n                SHORTCUT_OPTIONS+=\"--extra_args ${2} \"\n                extra_args+=\"${2}\"\n                shift 2;;\n            -sound-card|--sound-card)\n                SHORTCUT_OPTIONS+=\"--sound-card ${2} \"\n                sound_card=\"${2}\"\n                shift 2;;\n            -sound-duplex|--sound-duplex)\n                SHORTCUT_OPTIONS+=\"--sound-duplex ${2} \"\n                sound_duplex=\"${2}\"\n                shift 2;;\n            -version|--version)\n                echo \"${VERSION}\"\n                exit;;\n            -h|--h|-help|--help)\n                usage\n                exit 0;;\n              *)\n                echo \"ERROR! \\\"${1}\\\" is not a supported parameter.\"\n                usage\n                exit 1;;\n        esac\n    done\nfi\n\nif [ -n \"${VM}\" ] && [ -e \"${VM}\" ]; then\n    # shellcheck source=/dev/null\n    source \"${VM}\"\n    PUBLIC=\"${public_dir:-${PUBLIC}}\"\n\n    # Re-detect architecture and QEMU binary after sourcing config\n    # Config file can set arch=\"aarch64\" to override the default\n    ARCH_VM=\"${arch:-x86_64}\"\n    QEMU=$(command -v \"qemu-system-${ARCH_VM}\")\n    if [ ! -x \"${QEMU}\" ]; then\n        echo \"ERROR! qemu-system-${ARCH_VM} not found.\"\n        echo \"       Please install QEMU for ${ARCH_VM} architecture.\"\n        exit 1\n    fi\n\n    VMDIR=$(dirname \"${disk_img}\")          # directory the VM disk and state files are stored\n    VMNAME=$(basename \"${VM}\" .conf)        # name of the VM\n    VMPATH=$(realpath \"$(dirname \"${VM}\")\") # path to the top-level VM directory\n    SOCKET_MONITOR=\"${VMDIR}/${VMNAME}-monitor.socket\"\n    SOCKET_SERIAL=\"${VMDIR}/${VMNAME}-serial.socket\"\n\n    # Set serial default based on guest_os if not explicitly configured.\n    # macOS and Windows guests don't output anything useful to serial by default,\n    # so disable it to reduce clutter. Users can still override with --serial.\n    if [ -z \"${serial}\" ]; then\n        case \"${guest_os}\" in\n            macos|windows|windows-server)\n                serial=\"none\"\n                ;;\n            *)\n                serial=\"socket\"\n                ;;\n        esac\n    fi\n\n    # if disk_img is not configured, do the right thing.\n    if [ -z \"${disk_img}\" ]; then\n        disk_img=\"${VMDIR}/disk.${disk_format}\"\n    fi\n\n    # Fixes running VMs when PWD is not relative to the VM directory\n    # https://github.com/quickemu-project/quickemu/pull/875\n    if [ ! -f \"${disk_img}\" ]; then\n        pushd \"${VMPATH}\" >/dev/null || exit\n    fi\n\n    # Check if VM is already running\n    VM_PID=\"\"\n    if [ -r \"${VMDIR}/${VMNAME}.pid\" ]; then\n        VM_PID=$(head -n 1 \"${VMDIR}/${VMNAME}.pid\")\n        if ! kill -0 \"${VM_PID}\" > /dev/null 2>&1; then\n            #VM is not running, cleaning up.\n            VM_PID=\"\"\n            rm -f \"${VMDIR}/${VMNAME}.pid\"\n            rm -f \"${VMDIR}/${VMNAME}.spice\"\n            rm -f \"${VMDIR}/${VMNAME}.sock\"\n        fi\n    fi\n\n    # Iterate over any actions and exit.\n    if [ ${#ACTIONS[@]} -ge 1 ]; then\n        for ACTION in \"${ACTIONS[@]}\"; do\n            ${ACTION}\n        done\n        exit\n    fi\n\n    if [ -n \"${SNAPSHOT_ACTION}\" ]; then\n        case ${SNAPSHOT_ACTION} in\n            apply)\n                snapshot_apply \"${SNAPSHOT_TAG}\"\n                snapshot_info\n                exit;;\n            create)\n                snapshot_create \"${SNAPSHOT_TAG}\"\n                snapshot_info\n                exit;;\n            delete)\n                snapshot_delete \"${SNAPSHOT_TAG}\"\n                snapshot_info\n                exit;;\n            info)\n                echo \"Snapshot information ${disk_img}\"\n                snapshot_info\n                exit;;\n            *)\n                echo \"ERROR! \\\"${SNAPSHOT_ACTION}\\\" is not a supported snapshot action.\"\n                usage\n                exit 1;;\n        esac\n    fi\nelse\n    echo \"ERROR! Virtual machine configuration not found.\"\n    usage\n    exit 1\nfi\n\ndisplay_param_check\nports_param_check\nsound_card_param_check\ntpm_param_check\nviewer_param_check\nfileshare_param_check\n\nif [ -z \"${VM_PID}\" ]; then\n    vm_boot\n    start_viewer\n    # If the VM being started is an uninstalled Windows VM then auto-skip the press-any key prompt.\n    if [ -n \"${iso}\" ] && [[ \"${guest_os}\" == \"windows\"* ]]; then\n        # shellcheck disable=SC2034\n        for LOOP in {1..5}; do\n          sleep 1\n          monitor_send_cmd \"sendkey ret\"\n        done\n    fi\nelse\n    echo \"${VMNAME}\"\n    echo \" - Process:  Already running ${VM} as ${VMNAME} (${VM_PID})\"\n    parse_ports_from_file\n    # Auto-detect SPICE if .spice file exists and display is a default GUI type\n    if [ -r \"${VMDIR}/${VMNAME}.spice\" ]; then\n        if [ \"${display}\" == \"sdl\" ] || [ \"${display}\" == \"cocoa\" ] || [ \"${display}\" == \"gtk\" ]; then\n            display=\"spice\"\n            spice_port=$(cat \"${VMDIR}/${VMNAME}.spice\")\n        fi\n    fi\n    start_viewer\nfi\n\nif [ -n \"${MONITOR_CMD}\" ]; then\n    monitor_send_cmd \"${MONITOR_CMD}\"\nfi\n\n# vim:tabstop=4:shiftwidth=4:expandtab\n"
  },
  {
    "path": "quickget",
    "content": "#!/usr/bin/env bash\n# SC2317: Command appears to be unreachable. Check usage (or ignore if invoked indirectly).\n#  - https://www.shellcheck.net/wiki/SC2317\n#  - Disable globally because many functions are called indirectly\n# shellcheck disable=SC2317\nexport LC_ALL=C\n\n# Detect host OS for checksum tool compatibility\nHOST_OS=$(uname -s)\n\n# Default architecture based on host\nHOST_ARCH=$(uname -m)\ncase \"${HOST_ARCH}\" in\n    aarch64|arm64) ARCH=\"arm64\";;\n    *) ARCH=\"amd64\";;\nesac\n\nfunction arch_suffix() {\n    # Return architecture suffix for foreign architectures, empty for native\n    if [ \"${ARCH}\" != \"${NORMALISED_HOST_ARCH}\" ]; then\n        echo \"-${ARCH}\"\n    fi\n}\n\nfunction cleanup() {\n    if [ -n \"$(jobs -p)\" ]; then\n        kill \"$(jobs -p)\" 2>/dev/null\n    fi\n}\n\nfunction os_info() {\n    local SIMPLE_NAME=\"\"\n    local INFO=\"\"\n    SIMPLE_NAME=\"${1}\"\n    case ${SIMPLE_NAME} in\n        #name)            INFO=\"PrettyName|Credentials|Homepage|Info\";;\n        alma)             INFO=\"AlmaLinux|-|https://almalinux.org/|Community owned and governed, forever-free enterprise Linux distribution, focused on long-term stability, providing a robust production-grade platform. AlmaLinux OS is binary compatible with RHEL®.\";;\n        alpine)           INFO=\"Alpine Linux|-|https://alpinelinux.org/|Security-oriented, lightweight Linux distribution based on musl libc and busybox.\";;\n        android)          INFO=\"Android x86|-|https://www.android-x86.org/|Port Android Open Source Project to x86 platform.\";;\n        antix)            INFO=\"Antix|-|https://antixlinux.com/|Fast, lightweight and easy to install systemd-free linux live CD distribution based on Debian Stable for Intel-AMD x86 compatible systems.\";;\n        archcraft)        INFO=\"Archcraft|-|https://archcraft.io/|Yet another minimal Linux distribution, based on Arch Linux.\";;\n        archlinux)        INFO=\"Arch Linux|-|https://archlinux.org/|Lightweight and flexible Linux® distribution that tries to Keep It Simple.\";;\n        artixlinux)       INFO=\"Artix Linux|-|https://artixlinux.org/|The Art of Linux. Simple. Fast. Systemd-free.\";;\n        azurelinux)       INFO=\"Azure Linux|-|https://github.com/microsoft/azurelinux|Microsoft's internal Linux distribution for cloud infrastructure and edge.\";;\n        batocera)         INFO=\"Batocera|-|https://batocera.org/|Retro-gaming distribution with the aim of turning any computer/nano computer into a gaming console during a game or permanently.\";;\n        bazzite)          INFO=\"Bazzite|-|https://github.com/ublue-os/bazzite/|Container native gaming and a ready-to-game SteamOS like.\";;\n        biglinux)         INFO=\"BigLinux|-|https://www.biglinux.com.br/|Is the right choice if you want to have an easy and enriching experience with Linux. It has been perfected over more than 19 years, following our motto: 'In search of the perfect system'.\";;\n        blendos)          INFO=\"BlendOS|-|https://blendos.co/|A seamless blend of all Linux distributions. Allows you to have an immutable, atomic and declarative Arch Linux system, with application support from several Linux distributions & Android.\";;\n        bodhi)            INFO=\"Bodhi|-|https://www.bodhilinux.com/|Lightweight distribution featuring the fast & fully customizable Moksha Desktop.\";;\n        bunsenlabs)       INFO=\"BunsenLabs|-|https://www.bunsenlabs.org/|Light-weight and easily customizable Openbox desktop. The project is a community continuation of CrunchBang Linux.\";;\n        cachyos)          INFO=\"CachyOS|-|https://cachyos.org/|Designed to deliver lightning-fast speeds and stability, ensuring a smooth and enjoyable computing experience every time you use it.\";;\n        centos-stream)    INFO=\"CentOS Stream|-|https://www.centos.org/centos-stream/|Continuously delivered distro that tracks just ahead of Red Hat Enterprise Linux (RHEL) development, positioned as a midstream between Fedora Linux and RHEL.\";;\n        chimeralinux)     INFO=\"Chimera Linux|anon:chimera root:chimera|https://chimera-linux.org/|Modern, general-purpose non-GNU Linux distribution.\";;\n        crunchbang++)     INFO=\"Crunchbangplusplus|-|https://www.crunchbangplusplus.org/|The classic minimal crunchbang feel, now with debian 12 bookworm.\";;\n        debian)           INFO=\"Debian|-|https://www.debian.org/|Complete Free Operating System with perfect level of ease of use and stability.\";;\n        deepin)           INFO=\"Deepin|-|https://www.deepin.org/|Beautiful UI design, intimate human-computer interaction, and friendly community environment make you feel at home.\";;\n        devuan)           INFO=\"Devuan|-|https://www.devuan.org/|Fork of Debian without systemd that allows users to reclaim control over their system by avoiding unnecessary entanglements and ensuring Init Freedom.\";;\n        dragonflybsd)     INFO=\"DragonFlyBSD|-|https://www.dragonflybsd.org/|Provides an opportunity for the BSD base to grow in an entirely different direction from the one taken in the FreeBSD, NetBSD, and OpenBSD series.\";;\n        easyos)           INFO=\"EasyOS|-|https://easyos.org/|Experimental distribution designed from scratch to support containers.\";;\n        edubuntu)         INFO=\"Edubuntu|-|https://www.edubuntu.org/|Stable, secure and privacy conscious option for schools.\";;\n        elementary)       INFO=\"elementary OS|-|https://elementary.io/|Thoughtful, capable, and ethical replacement for Windows and macOS.\";;\n        endeavouros)      INFO=\"EndeavourOS|-|https://endeavouros.com/|Provides an Arch experience without the hassle of installing it manually for both x86_64 and ARM systems.\";;\n        endless)          INFO=\"Endless OS|-|https://www.endlessos.org/os|Completely Free, User-Friendly Operating System Packed with Educational Tools, Games, and More.\";;\n        fedora)           INFO=\"Fedora|-|https://www.fedoraproject.org/|Innovative platform for hardware, clouds, and containers, built with love by you.\";;\n        freebsd)          INFO=\"FreeBSD|-|https://www.freebsd.org/|Operating system used to power modern servers, desktops, and embedded platforms.\";;\n        freedos)          INFO=\"FreeDOS|-|https://freedos.org/|DOS-compatible operating system that you can use to play classic DOS games, run legacy business software, or develop embedded systems.\";;\n        garuda)           INFO=\"Garuda Linux|-|https://garudalinux.org/|Feature rich and easy to use Linux distribution.\";;\n        gentoo)           INFO=\"Gentoo|-|https://www.gentoo.org/|Highly flexible, source-based Linux distribution.\";;\n        ghostbsd)         INFO=\"GhostBSD|-|https://www.ghostbsd.org/|Simple, elegant desktop BSD Operating System.\";;\n        gnomeos)          INFO=\"GNOME OS|-|https://os.gnome.org/|Alpha nightly bleeding edge distro of GNOME\";;\n        guix)             INFO=\"Guix|-|https://guix.gnu.org/|Distribution of the GNU operating system developed by the GNU Project—which respects the freedom of computer users.\";;\n        haiku)            INFO=\"Haiku|-|https://www.haiku-os.org/|Specifically targets personal computing. Inspired by the BeOS, Haiku is fast, simple to use, easy to learn and yet very powerful.\";;\n        kali)             INFO=\"Kali|-|https://www.kali.org/|The most advanced Penetration Testing Distribution.\";;\n        kdeneon)          INFO=\"KDE Neon|-|https://neon.kde.org/|Latest and greatest of KDE community software packaged on a rock-solid base.\";;\n        kolibrios)        INFO=\"KolibriOS|-|https://kolibrios.org/en/|Tiny yet incredibly powerful and fast operating system.\";;\n        kubuntu)          INFO=\"Kubuntu|-|https://kubuntu.org/|Free, complete, and open-source alternative to Microsoft Windows and Mac OS X which contains everything you need to work, play, or share.\";;\n        linuxlite)        INFO=\"Linux Lite|-|https://www.linuxliteos.com/|Your first simple, fast and free stop in the world of Linux.\";;\n        linuxmint)        INFO=\"Linux Mint|-|https://linuxmint.com/|Designed to work out of the box and comes fully equipped with the apps most people need.\";;\n        lmde)             INFO=\"Linux Mint Debian Edition|-|https://www.linuxmint.com/download_lmde.php|Aims to be as similar as possible to Linux Mint, but without using Ubuntu. The package base is provided by Debian instead.\";;\n        lubuntu)          INFO=\"Lubuntu|-|https://lubuntu.me/|Complete Operating System that ships the essential apps and services for daily use: office applications, PDF reader,  image editor, music and video players, etc. Using lightwave lxde/lxqt.\";;\n        maboxlinux)       INFO=\"Mabox Linux|-|https://maboxlinux.org/|Lightweight, functional and easy to customize Openbox desktop\";;\n        mageia)           INFO=\"Mageia|-|https://www.mageia.org/|Stable, secure operating system for desktop & server.\";;\n        manjaro)          INFO=\"Manjaro|-|https://manjaro.org/|Versatile, free, and open-source Linux operating system designed with a strong focus on safeguarding user privacy and offering extensive control over hardware.\";;\n        mxlinux)          INFO=\"MX Linux|-|https://mxlinux.org/|Designed to combine elegant and efficient desktops with high stability and solid performance.\";;\n        netboot)          INFO=\"netboot.xyz|-|https://netboot.xyz/|Your favorite operating systems in one place.\";;\n        netbsd)           INFO=\"NetBSD|-|https://www.netbsd.org/|Free, fast, secure, and highly portable Unix-like Open Source operating system. It is available for a wide range of platforms, from large-scale servers and powerful desktop systems to handheld and embedded devices.\";;\n        nitrux)           INFO=\"Nitrux|-|https://nxos.org/|Powered by Debian, KDE Plasma and Frameworks, and AppImages.\";;\n        nixos)            INFO=\"NixOS|-|https://nixos.org/|Linux distribution based on Nix package manager, tool that takes a unique approach to package management and system configuration.\";;\n        nwg-shell)        INFO=\"nwg-shell|nwg:nwg|https://nwg-piotr.github.io/nwg-shell/|Arch Linux ISO with nwg-shell for sway and Hyprland\";;\n        macos)            INFO=\"macOS|-|https://www.apple.com/macos/|Work and play on your Mac are even more powerful. Elevate your presence on video calls. Access information in all-new ways. Boost gaming performance. And discover even more ways to personalize your Mac.\";;\n        openbsd)          INFO=\"OpenBSD|-|https://www.openbsd.org/|FREE, multi-platform 4.4BSD-based UNIX-like operating system. Our efforts emphasize portability, standardization, correctness, proactive security and integrated cryptography.\";;\n        openindiana)      INFO=\"OpenIndiana|-|https://www.openindiana.org/|Community supported illumos-based operating system.\";;\n        opensuse)         INFO=\"openSUSE|-|https://www.opensuse.org/|The makers choice for sysadmins, developers and desktop users.\";;\n        oraclelinux)      INFO=\"Oracle Linux|-|https://www.oracle.com/linux/|Linux with everything required to deploy, optimize, and manage applications on-premises, in the cloud, and at the edge.\";;\n        parrotsec)        INFO=\"Parrot Security|parrot:parrot|https://www.parrotsec.org/|Provides a huge arsenal of tools, utilities and libraries that IT and security professionals can use to test and assess the security of their assets in a reliable, compliant and reproducible way.\";;\n        pclinuxos)        INFO=\"PCLinuxOS|-|https://www.pclinuxos.com/|PCLinuxOS is a free easy to use Linux-based Operating System for x86_64 desktops or laptops.\";;\n        peppermint)       INFO=\"PeppermintOS|-|https://peppermintos.com/|Provides a user with the opportunity to build the system that best fits their needs. While at the same time providing a functioning OS with minimum hassle out of the box.\";;\n        popos)            INFO=\"Pop!_OS|-|https://pop.system76.com/|Operating system for STEM and creative professionals who use their computer as a tool to discover and create.\";;\n        porteus)          INFO=\"Porteus|-|http://www.porteus.org/|Complete linux operating system that is optimized to run from CD, USB flash drive, hard drive, or other bootable storage media.\";;\n        primtux)          INFO=\"PrimTux|-|https://primtux.fr/|A complete and customizable GNU/Linux operating system intended for primary school students and suitable even for older hardware.\";;\n        proxmox-ve)       INFO=\"Proxmox VE|-|https://proxmox.com/en/proxmox-virtual-environment/|Proxmox Virtual Environment is a complete, open-source server management platform for enterprise virtualization.\";;\n        pureos)           INFO=\"PureOS|-|https://www.pureos.net/|A fully free/libre and open source GNU/Linux operating system, endorsed by the Free Software Foundation.\";;\n        reactos)          INFO=\"ReactOS|-|https://reactos.org/|Imagine running your favorite Windows applications and drivers in an open-source environment you can trust.\";;\n        rebornos)         INFO=\"RebornOS|-|https://rebornos.org/|Aiming to make Arch Linux as user friendly as possible by providing interface solutions to things you normally have to do in a terminal.\";;\n        rockylinux)       INFO=\"Rocky Linux|-|https://rockylinux.org/|Open-source enterprise operating system designed to be 100% bug-for-bug compatible with Red Hat Enterprise Linux®.\";;\n        siduction)        INFO=\"Siduction|-|https://siduction.org/|Operating system based on the Linux kernel and the GNU project. In addition, there are applications and libraries from Debian.\";;\n        slackware)        INFO=\"Slackware|-|http://www.slackware.com/|Advanced Linux operating system, designed with the twin goals of ease of use and stability as top priorities.\";;\n        slax)             INFO=\"Slax|-|https://www.slax.org/|Compact, fast, and modern Linux operating system that combines sleek design with modular approach. With the ability to run directly from a USB flash drive without the need for installation, Slax is truly portable and fits easily in your pocket.\";;\n        slint)            INFO=\"Slint|-|https://slint.fr/|Slint is an easy-to-use, versatile, blind-friendly Linux distribution for 64-bit computers. Slint is based on Slackware and borrows tools from Salix. Maintainer: Didier Spaier.\";;\n        slitaz)           INFO=\"SliTaz|-|https://www.slitaz.org/en/|Simple, fast and low resource Linux OS for servers & desktops.\";;\n        solus)            INFO=\"Solus|-|https://getsol.us/|Designed for home computing. Every tweak enables us to deliver a cohesive computing experience.\";;\n        sparkylinux)      INFO=\"SparkyLinux|-|https://sparkylinux.org/|Fast, lightweight and fully customizable operating system which offers several versions for different use cases.\";;\n        spirallinux)      INFO=\"SpiralLinux|-|https://spirallinux.github.io/|Selection of Linux spins built from Debian GNU/Linux, with a focus on simplicity and out-of-the-box usability across all the major desktop environments.\";;\n        tails)            INFO=\"Tails|-|https://tails.net/|Portable operating system that protects against surveillance and censorship.\";;\n        tinycore)         INFO=\"Tiny Core Linux|-|http://www.tinycorelinux.net/|Highly modular based system with community build extensions.\";;\n        trisquel)         INFO=\"Trisquel|-|https://trisquel.info/|Fully free operating system for home users, small enterprises and educational centers.\";;\n        tuxedo-os)        INFO=\"Tuxedo OS|-|https://www.tuxedocomputers.com/en/|KDE Ubuntu LTS designed to go with their Linux hardware.\";;\n        ubuntu)           INFO=\"Ubuntu|-|https://ubuntu.com/|Complete desktop Linux operating system, freely available with both community and professional support.\";;\n        ubuntu-budgie)    INFO=\"Ubuntu Budgie|-|https://ubuntubudgie.org/|Community developed distribution, integrating the Budgie Desktop Environment with Ubuntu at its core.\";;\n        ubuntucinnamon)   INFO=\"Ubuntu Cinnamon|-|https://ubuntucinnamon.org/|Community-driven, featuring Linux Mint’s Cinnamon Desktop with Ubuntu at the core, packed fast and full of features, here is the most traditionally modern desktop you will ever love.\";;\n        ubuntukylin)      INFO=\"Ubuntu Kylin|-|https://ubuntukylin.com/|Universal desktop operating system for personal computers, laptops, and embedded devices. It is dedicated to bringing a smarter user experience to users all over the world.\";;\n        ubuntu-mate)      INFO=\"Ubuntu MATE|-|https://ubuntu-mate.org/|Stable, easy-to-use operating system with a configurable desktop environment. It is ideal for those who want the most out of their computers and prefer a traditional desktop metaphor. Using Mate desktop.\";;\n        ubuntu-server)    INFO=\"Ubuntu Server|-|https://ubuntu.com/server|Brings economic and technical scalability to your datacentre, public or private. Whether you want to deploy an OpenStack cloud, a Kubernetes cluster or a 50,000-node render farm, Ubuntu Server delivers the best value scale-out performance available.\";;\n        ubuntustudio)     INFO=\"Ubuntu Studio|-|https://ubuntustudio.org/|Comes preinstalled with a selection of the most common free multimedia applications available, and is configured for best performance for various purposes: Audio, Graphics, Video, Photography and Publishing.\";;\n        ubuntu-unity)     INFO=\"Ubuntu Unity|-|https://ubuntuunity.org/|Flavor of Ubuntu featuring the Unity7 desktop environment (the default desktop environment used by Ubuntu from 2010-2017).\";;\n        vanillaos)        INFO=\"Vanilla OS|-|https://vanillaos.org/|Designed to be a reliable and productive operating system for your daily work.\";;\n        void)             INFO=\"Void Linux|anon:voidlinux|https://voidlinux.org/|General purpose operating system. Its package system allows you to quickly install, update and remove software; software is provided in binary packages or can be built directly from sources.\";;\n        windows)          INFO=\"Windows|-|https://www.microsoft.com/en-us/windows/|Whether you’re gaming, studying, running a business, or running a household, Windows helps you get it done.\";;\n        windows-server)   INFO=\"Windows Server|-|https://www.microsoft.com/en-us/windows-server/|Platform for building an infrastructure of connected applications, networks, and web services.\";;\n        xubuntu)          INFO=\"Xubuntu|-|https://xubuntu.org/|Elegant and easy to use operating system. Xubuntu comes with Xfce, which is a stable, light and configurable desktop environment.\";;\n        zorin)            INFO=\"Zorin OS|-|https://zorin.com/os/|Alternative to Windows and macOS designed to make your computer faster, more powerful, secure, and privacy-respecting.\";;\n    esac\n    echo \"${INFO}\"\n}\n\nfunction show_os_info() {\n    echo\n    echo -e \"$(os_info \"${1}\" | cut -d'|' -f 1)\"\n    echo -e \" - Credentials:\\t$(os_info \"${1}\" | cut -d'|' -f 2)\"\n    echo -e \" - Website:\\t$(os_info \"${1}\" | cut -d'|' -f 3)\"\n    echo -e \" - Description:\\t$(os_info \"${1}\" | cut -d'|' -f 4)\"\n}\n\nfunction pretty_name() {\n    os_info \"${1}\" | cut -d'|' -f 1\n}\n\n# Just in case quickget want use it\nfunction os_homepage() {\n    os_info \"${1}\" | cut -d'|' -f 3\n}\n\nfunction error_specify_os() {\n    echo \"ERROR! You must specify an operating system.\"\n    echo \"- Supported Operating Systems:\"\n    os_support | fmt -w 80\n    echo -e \"\\nTo see all possible arguments, use:\\n   quickget -h  or  quickget --help\"\n    exit 1\n}\n\nfunction os_supported() {\n    if [[ ! \" $(os_support) \" =~ \\ \"${OS}\"\\  ]]; then\n        echo -e \"ERROR! ${OS} is not a supported OS.\\n\"\n        os_support | fmt -w 80\n        exit 1\n    fi\n}\n\nfunction error_specify_release() {\n    show_os_info \"${OS}\"\n    case ${OS} in\n        *ubuntu-server*)\n            echo -en \" - Releases:\\t\"\n            releases_ubuntu-server\n            echo -en \" - Archs:\\t\"\n            get_supported_archs \"${OS}\"\n            ;;\n        *ubuntu*)\n            echo -en \" - Releases:\\t\"\n            releases_ubuntu\n            echo -en \" - Archs:\\t\"\n            get_supported_archs \"${OS}\"\n            ;;\n        *windows*)\n            echo -en \" - Releases:\\t\"\n            \"releases_${OS}\"\n            echo -en \" - Languages:\\t\"\n            \"languages_${OS}\"\n            echo \"${I18NS[@]}\"\n            # Windows uses multi-arch ISOs, skip architecture display\n            ;;\n        *)\n            echo -en \" - Releases:\\t\"\n            \"releases_${OS}\" | fmt -w 80\n            if [[ $(type -t \"editions_${OS}\") == function ]]; then\n                echo -en \" - Editions:\\t\"\n                \"editions_${OS}\" | fmt -w 80\n            fi\n            echo -en \" - Archs:\\t\"\n            get_supported_archs \"${OS}\"\n            ;;\n    esac\n    echo -e \"\\nERROR! You must specify a release.\"\n    exit 1\n}\n\nfunction error_not_supported_release() {\n    if [[ ! \" ${RELEASES[*]} \" =~ \\ \"${RELEASE}\"\\  ]]; then\n        echo -e \"ERROR! ${DISPLAY_NAME} ${RELEASE} is not a supported release.\\n\"\n        echo -n ' - Supported releases: '\n        \"releases_${OS}\"\n        exit 1\n    fi\n}\n\nfunction error_not_supported_lang() {\n    echo -e \"ERROR! ${I18N} is not a supported $(pretty_name \"${OS}\") language\\n\"\n    echo -n ' - Editions: '\n    for I18N in \"${I18NS[@]}\"; do\n        echo -n \"${I18N} \"\n    done\n    exit 1\n}\n\nfunction error_not_supported_argument() {\n    echo \"ERROR! Not supported argument\"\n    echo \"To see all possible arguments, use:\"\n    echo \"   quickget -h  or  quickget --help\"\n    exit 1\n}\n\nfunction require_qemu_img() {\n    QEMU_IMG=$(command -v qemu-img)\n    if [ ! -x \"${QEMU_IMG}\" ]; then\n        echo \"ERROR! qemu-img not found. Please make sure qemu-img is installed.\"\n        exit 1\n    fi\n}\n\nfunction is_valid_language() {\n    local I18N=\"\"\n    local PASSED_I18N=\"${1}\"\n    for I18N in \"${I18NS[@]}\"; do\n        if [[ \"${I18N}\" == \"${PASSED_I18N}\" ]]; then\n            return 0\n        fi\n    done\n    return 1\n}\n\nfunction handle_missing() {\n    # Handle odd missing Fedora combinations\n    case \"${OS}\" in\n        fedora)\n            # First we need to handle the Beta naming kludge\n            if [[ \"${RELEASE}\" == *\"_Beta\" ]]; then\n                NRELEASE=\"${RELEASE/_Beta/}\"\n                else\n                NRELEASE=\"${RELEASE}\"\n            fi\n            if [[ \"${NRELEASE}\" -lt 40 && \"${EDITION}\" == \"Onyx\" ]] || [[ \"${NRELEASE}\" -lt 40 && \"${EDITION}\" == \"Sericea\" ]]; then\n                echo \"ERROR! Unsupported combination\"\n                echo \"       Fedora ${RELEASE} ${EDITION} is not available, please choose another Release or Edition\"\n                exit 1\n            fi;;\n    esac\n}\n\nfunction validate_release() {\n    local DISPLAY_NAME=\"\"\n    local RELEASE_GENERATOR=\"\"\n    local RELEASES=\"\"\n\n    DISPLAY_NAME=\"$(pretty_name \"${OS}\")\"\n    case ${OS} in\n        *ubuntu-server*) RELEASE_GENERATOR=\"releases_ubuntu-server\";;\n        *ubuntu*) RELEASE_GENERATOR=\"releases_ubuntu\";;\n        *) RELEASE_GENERATOR=\"${1}\";;\n    esac\n    RELEASES=$(${RELEASE_GENERATOR})\n    error_not_supported_release\n}\n\nfunction list_json() {\n    # Reference: https://stackoverflow.com/a/67359273\n    list_csv | jq -R 'split(\",\") as $h|reduce inputs as $in ([]; . += [$in|split(\",\")|. as $a|reduce range(0,length) as $i ({};.[$h[$i]]=$a[$i])])'\n    exit 0\n}\n\nfunction list_csv() {\n    CSV_DATA=\"$(csv_data)\"\n\n    echo \"Display Name,OS,Release,Option,Downloader,PNG,SVG\"\n    sort -t',' -k2,2 <<<\"${CSV_DATA}\"\n\n    exit 0\n}\n\nfunction csv_data() {\n    local DISPLAY_NAME\n    local DL=\"\"\n    local DOWNLOADER\n    local FUNC\n    local OPTION\n    local OS\n    local PNG\n    local RELEASE\n    local SVG\n    local HAS_ZSYNC=0\n\n    # Check if zsync is available\n    if command -v zsync &>/dev/null; then\n        HAS_ZSYNC=1\n    fi\n\n    for OS in $(os_support); do\n        local EDITIONS=\"\"\n        DISPLAY_NAME=\"$(pretty_name \"${OS}\")\"\n\n        case ${OS} in\n            *ubuntu-server*) FUNC=\"ubuntu-server\";;\n            *ubuntu*) FUNC=\"ubuntu\";;\n            *) FUNC=\"${OS}\";;\n        esac\n\n        PNG=\"https://quickemu-project.github.io/quickemu-icons/png/${FUNC}/${FUNC}-quickemu-white-pinkbg.png\"\n        SVG=\"https://quickemu-project.github.io/quickemu-icons/svg/${FUNC}/${FUNC}-quickemu-white-pinkbg.svg\"\n\n        if [[ $(type -t \"editions_${OS}\") == function ]]; then\n            EDITIONS=$(editions_\"${OS}\")\n        fi\n\n        for RELEASE in $(\"releases_${FUNC}\"); do\n            if [[ \"${OS}\" == *\"ubuntu\"* ]] && [[ ${RELEASE} == *\"daily\"*  ]] && [ ${HAS_ZSYNC} -eq 1 ]; then\n                DOWNLOADER=\"zsync\"\n            else\n                DOWNLOADER=\"${DL}\"\n            fi\n\n            # If the OS has an editions_() function, use it.\n            if [[ ${EDITIONS} ]]; then\n                for OPTION in ${EDITIONS}; do\n                    echo \"${DISPLAY_NAME},${OS},${RELEASE},${OPTION},${DOWNLOADER},${PNG},${SVG}\"\n                done\n            elif [[ \"${OS}\" == \"windows\"* ]]; then\n                \"languages_${OS}\"\n                for I18N in \"${I18NS[@]}\"; do\n                    echo \"${DISPLAY_NAME},${OS},${RELEASE},${I18N},${DOWNLOADER},${PNG},${SVG}\"\n                done\n            else\n                echo \"${DISPLAY_NAME},${OS},${RELEASE},,${DOWNLOADER},${PNG},${SVG}\"\n            fi\n        done &\n    done\n    wait\n}\n\nfunction list_supported() {\n    list_csv | cut -d ',' -f2,3,4 | tr ',' ' '\n    exit 0\n}\n\nfunction test_result() {\n    local OS=\"${1}\"\n    local RELEASE=\"${2}\"\n    local EDITION=\"${3:-}\"\n    local URL=\"${4:-}\"\n    local RESULT=\"${5:-}\"\n    local REASON=\"${6:-}\"\n    if [ -n \"${EDITION}\" ]; then\n        OS=\"${OS}-${RELEASE}-${EDITION}\"\n    else\n        OS=\"${OS}-${RELEASE}\"\n    fi\n\n    if [ -n \"${RESULT}\" ]; then\n        # Pad the OS string for consistent output\n        OS=$(printf \"%-35s\" \"${OS}\")\n        if [ -n \"${REASON}\" ]; then\n            echo -e \"${RESULT}: ${OS} ${REASON}\"\n        else\n            echo -e \"${RESULT}: ${OS} ${URL}\"\n        fi\n    else\n        OS=$(printf \"%-36s\" \"${OS}:\")\n        echo -e \"${OS} ${URL}\"\n    fi\n}\n\nfunction test_all() {\n    OS=\"${1}\"\n    os_supported\n\n    local CHECK=\"\"\n    local FUNC=\"${OS}\"\n    if [[ \"${OS}\" == *ubuntu* && \"${OS}\" != \"ubuntu-server\" ]]; then\n        FUNC=\"ubuntu\"\n    fi\n    local URL=\"\"\n\n    for RELEASE in $(\"releases_${FUNC}\"); do\n        if [[ $(type -t \"editions_${OS}\") == function ]]; then\n            for EDITION in $(editions_\"${OS}\"); do\n                # Check architecture support before generating URL\n                if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n                    test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n                    continue\n                fi\n                validate_release releases_\"${OS}\"\n                URL=$(get_\"${OS}\" | cut -d' ' -f1 | head -n 1)\n                if [ \"${OPERATION}\" == \"show\" ]; then\n                    test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\"\n                elif [ \"${OPERATION}\" == \"test\" ]; then\n                    CHECK=$(web_check \"${URL}\" && echo \"PASS\" || echo \"FAIL\")\n                    test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\" \"${CHECK}\"\n                fi\n            done\n        elif [[ \"${OS}\" == \"windows\"* ]]; then\n            \"languages_${OS}\"\n            for I18N in \"${I18NS[@]}\"; do\n                validate_release releases_\"${OS}\"\n                if [ \"${OPERATION}\" == \"show\" ]; then\n                    test_result \"${OS}\" \"${RELEASE}\" \"${I18N}\" \"\"\n                elif [ \"${OPERATION}\" == \"test\" ]; then\n                    test_result \"${OS}\" \"${RELEASE}\" \"${I18N}\" \"${URL}\" \"SKIP\"\n                fi\n            done\n        elif [[ \"${OS}\" == \"macos\" ]]; then\n            validate_release releases_macos\n            (get_macos)\n        elif [ \"${OS}\" == \"ubuntu-server\" ]; then\n            # Check architecture support before generating URL\n            if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n                test_result \"${OS}\" \"${RELEASE}\" \"\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n                continue\n            fi\n            validate_release releases_ubuntu-server\n            (get_ubuntu-server)\n        elif [[ \"${OS}\" == *ubuntu* ]]; then\n            # Ubuntu desktop is amd64 only (no arch function = amd64 default)\n            if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n                test_result \"${OS}\" \"${RELEASE}\" \"\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n                continue\n            fi\n            validate_release releases_ubuntu\n            (get_ubuntu)\n        else\n            # Check architecture support before generating URL\n            if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n                test_result \"${OS}\" \"${RELEASE}\" \"\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n                continue\n            fi\n            validate_release releases_\"${OS}\"\n            URL=$(get_\"${OS}\" | cut -d' ' -f1 | head -n 1)\n            if [ \"${OPERATION}\" == \"show\" ]; then\n                test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\"\n            elif [ \"${OPERATION}\" == \"test\" ]; then\n                CHECK=$(web_check \"${URL}\" && echo \"PASS\" || echo \"FAIL\")\n                test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\" \"${CHECK}\"\n            fi\n        fi\n    done\n}\n\nfunction os_support() {\n    echo alma \\\n    alpine \\\n    android \\\n    antix \\\n    archcraft \\\n    archlinux \\\n    artixlinux \\\n    azurelinux \\\n    batocera \\\n    bazzite \\\n    biglinux \\\n    blendos \\\n    bodhi \\\n    bunsenlabs \\\n    cachyos \\\n    centos-stream \\\n    chimeralinux \\\n    crunchbang++ \\\n    debian \\\n    deepin \\\n    devuan \\\n    dragonflybsd \\\n    easyos \\\n    edubuntu \\\n    elementary \\\n    endeavouros \\\n    endless \\\n    fedora \\\n    freebsd \\\n    freedos \\\n    garuda \\\n    gentoo \\\n    ghostbsd \\\n    gnomeos \\\n    guix \\\n    haiku \\\n    kali \\\n    kdeneon \\\n    kolibrios \\\n    kubuntu \\\n    linuxlite \\\n    linuxmint \\\n    lmde \\\n    lubuntu \\\n    maboxlinux \\\n    macos \\\n    mageia \\\n    manjaro \\\n    mxlinux \\\n    netboot \\\n    netbsd \\\n    nitrux \\\n    nixos \\\n    nwg-shell \\\n    openbsd \\\n    openindiana \\\n    opensuse \\\n    oraclelinux \\\n    parrotsec \\\n    pclinuxos \\\n    peppermint \\\n    popos \\\n    porteus \\\n    primtux \\\n    proxmox-ve \\\n    pureos \\\n    reactos \\\n    rebornos \\\n    rockylinux \\\n    siduction \\\n    slackware \\\n    slax \\\n    slint \\\n    slitaz \\\n    solus \\\n    sparkylinux \\\n    spirallinux \\\n    tails \\\n    tinycore \\\n    trisquel \\\n    tuxedo-os \\\n    ubuntu \\\n    ubuntu-budgie \\\n    ubuntu-mate \\\n    ubuntu-server \\\n    ubuntu-unity \\\n    ubuntucinnamon \\\n    ubuntukylin \\\n    ubuntustudio \\\n    vanillaos \\\n    void \\\n    windows \\\n    windows-server \\\n    xubuntu \\\n    zorin\n}\n\nfunction releases_alma() {\n    echo 9 8\n}\n\nfunction editions_alma() {\n    echo boot minimal dvd\n}\n\nfunction releases_alpine() {\n    local REL=\"\"\n    local RELS=\"\"\n    RELS=$(web_pipe \"https://dl-cdn.alpinelinux.org/alpine/\" | grep '\"v' | cut -d'\"' -f2 | tr -d / | sort -Vr | head -n 10)\n    for REL in ${RELS}; do\n        if web_check \"https://dl-cdn.alpinelinux.org/alpine/${REL}/releases/x86_64/\"; then\n            echo -n \"${REL} \"\n        fi\n    done\n}\n\nfunction releases_android() {\n    echo 9.0 8.1 7.1\n}\n\nfunction editions_android() {\n    echo x86_64 x86\n}\n\nfunction releases_antix() {\n    echo 23.1 23 22 21\n}\n\nfunction editions_antix() {\n    echo net-sysv core-sysv base-sysv full-sysv net-runit core-runit base-runit full-runit\n}\n\nfunction releases_archcraft() {\n    echo latest\n}\n\nfunction releases_archlinux() {\n    echo latest\n}\n\nfunction releases_artixlinux() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirror1.artixlinux.org/iso/\" | grep \"artix-\" | cut -d'\"' -f2 | grep -v sig | cut -d'-' -f 4 | sort -ru | tail -n 1)\n}\n\nfunction editions_artixlinux() {\n    #shellcheck disable=SC2046,SC2005\n    # Extract edition-init combinations (e.g., base-dinit, plasma-openrc, community-gtk-openrc)\n    echo $(web_pipe \"https://mirror1.artixlinux.org/iso/\" | grep \"artix-\" | cut -d'\"' -f2 | grep -v sig | sed 's/artix-//' | sed 's/-[0-9]\\{8\\}-x86_64.iso//' | sort -u)\n}\n\nfunction releases_azurelinux() {\n    echo 3.0\n}\n\nfunction releases_batocera() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirrors.o2switch.fr/batocera/x86_64/stable/\" | grep ^\\<a | cut -d'\"' -f2 | cut -d '/' -f1 | grep -v '\\.' | sort -ru | tail -n +2 | head -n 5)\n}\n\nfunction releases_bazzite() {\n    echo latest\n}\n\nfunction editions_bazzite() {\n    echo gnome plasma deck-gnome deck-plasma\n}\n\nfunction releases_biglinux() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://iso.biglinux.com.br\" | grep -Eo 'biglinux_[0-9]{4}(-[0-9]{2}){2}_k[0-9]{2,3}.iso' | cut -d'_' -f2 | sort -ru | head -n 1)\n}\n\nfunction editions_biglinux() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://iso.biglinux.com.br\" | grep -Eo \"biglinux_$(releases_biglinux)_k[0-9]{2,3}.iso\" | cut -d'_' -f3 | cut -d'.' -f1 | sort -Vru)\n}\n\nfunction releases_blendos() {\n    # there is now just a single latest iso\n    echo latest\n}\n\nfunction releases_bodhi() {\n    echo 7.0.0\n}\n\nfunction editions_bodhi() {\n    echo standard hwe s76\n}\n\nfunction releases_bunsenlabs() {\n    echo boron\n}\n\nfunction releases_cachyos() {\n    # new cdn setup 10/2024\n    echo latest\n}\n\nfunction editions_cachyos() {\n    # desktop version now installs different desktop environments\n    echo desktop handheld\n}\n\nfunction releases_centos-stream() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://linuxsoft.cern.ch/centos-stream/\" | grep \"\\-stream\" | cut -d'\"' -f 6 | cut -d'-' -f 1)\n}\n\nfunction editions_centos-stream() {\n    echo boot dvd1\n}\n\nfunction releases_chimeralinux() {\n    echo latest\n}\n\nfunction editions_chimeralinux() {\n    echo base gnome\n}\n\nfunction releases_crunchbang++() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://api.github.com/repos/CBPP/cbpp/releases\" | grep 'download_url' | cut -d'-' -f2 | grep '^[0-9]' | sort -gru)\n}\n\nfunction releases_debian() {\n    local ARCHIVE=\"\"\n    local MAJ=\"\"\n    local NEW=\"\"\n    local OLD=\"\"\n    NEW=$(web_pipe \"https://cdimage.debian.org/debian-cd/\" | grep '\\.[0-9]/' | cut -d'>' -f 9 | cut -d'/' -f 1)\n    echo -n \"${NEW}\"\n    MAJ=$(echo \"${NEW}\" | cut -d'.' -f 1)\n    # Get archive versions that have -live directories (live images available)\n    ARCHIVE=\"$(web_pipe \"https://cdimage.debian.org/cdimage/archive/\" | grep folder | grep -v NEVER | grep '\\-live/' | cut -d'\"' -f 6 | sed 's/-live\\///')\"\n    for i in {1..2}; do\n        CUR=$((MAJ - i))\n        OLD=$(grep ^\"${CUR}\"  <<< \"${ARCHIVE}\" | tail -n 1 | tr -d '/')\n        echo -n \" ${OLD}\"\n    done\n    echo\n}\n\nfunction editions_debian() {\n    echo standard cinnamon gnome kde lxde lxqt mate xfce netinst\n}\n\nfunction releases_deepin() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirrors.kernel.org/deepin-cd/\" | grep \"href=\" | cut -d'\"' -f2 | grep -v \"\\.\\.\" | grep -v nightly | grep -v preview | sed 's|/||g' | tail -n 10 | sort -r)\n}\n\nfunction releases_devuan() {\n    echo daedalus chimaera\n}\n\nfunction releases_dragonflybsd() {\n    # If you remove \"\".bz2\" from the end of the searched URL, you will get only the current release - currently 6.4.0\n    # We could add a variable so this behaviour is optional/switchable (maybe from option or env)\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirror-master.dragonflybsd.org/iso-images/\" | grep -E -o '\"dfly-x86_64-.*_REL.iso.bz2\"' | grep -o -E '[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+')\n}\n\nfunction releases_easyos() {\n    local ALL_RELEASES=\"\"\n    local YEAR=\"\"\n    # get the latest 2 years of releases so that when we hit next year we still have the latest 2 years\n    TWO_YEARS=$(web_pipe https://distro.ibiblio.org/easyos/amd64/releases/kirkstone/ | grep -o -E '[[:digit:]]{4}/' | sort -nr | tr -d /  | head -n 2 )\n    for YEAR in ${TWO_YEARS} ; do\n        ALL_RELEASES=\"${ALL_RELEASES} $(web_pipe \"https://distro.ibiblio.org/easyos/amd64/releases/kirkstone/${YEAR}/\" | grep -o -E '[[:digit:]]+(\\.[[:digit:]])+/' | tr -d / | sort -nr)\"\n    done\n    echo \"${ALL_RELEASES}\"\n}\n\n\nfunction releases_elementary() {\n    echo 8.1 8.0 7.1 7.0\n}\n\nfunction releases_endeavouros() {\n    local ENDEAVOUR_RELEASES=\"\"\n    ENDEAVOUR_RELEASES=\"$(web_pipe \"https://mirror.alpix.eu/endeavouros/iso/\" | grep -o '<a href=\"[^\"]*.iso\">' | sed 's/^<a href=\"//;s/.iso\">.*//' | grep -v 'x86_64' | LC_ALL=\"en_US.UTF-8\" sort -Mr | cut -c 13- | head -n 5 | tr '\\n' ' ')\"\n    echo \"${ENDEAVOUR_RELEASES,,}\"\n}\n\nfunction releases_endless() {\n    echo 6.0.4\n}\n\nfunction editions_endless() {\n    echo base en fr pt_BR es\n}\n\nfunction releases_fedora() {\n    #shellcheck disable=SC2046,SC2005\n    # Filter out EOL release 41\n    echo $(web_pipe \"https://getfedora.org/releases.json\" | jq -r 'map(.version) | unique | .[]' | sed 's/ /_/g' | sort -r | grep -v '^41$')\n}\n\nfunction editions_fedora() {\n    #shellcheck disable=SC2046,SC2005\n    if [[ -z ${RELEASE} ]]; then\n          echo $(web_pipe \"https://getfedora.org/releases.json\" | jq -r \"map(select(.arch==\\\"x86_64\\\" and .variant!=\\\"Labs\\\" and .variant!=\\\"IoT\\\" and .variant!=\\\"Container\\\" and .variant!=\\\"Cloud\\\" and .variant!=\\\"Everything\\\"  and .subvariant!=\\\"Security\\\" and .subvariant!=\\\"Server_KVM\\\" and .subvariant!=\\\"SoaS\\\")) | map(.subvariant) | unique | .[]\")\n    else\n        echo $(web_pipe \"https://getfedora.org/releases.json\" | jq -r \"map(select(.arch==\\\"x86_64\\\" and .version==\\\"${RELEASE/_/ }\\\" and .variant!=\\\"Labs\\\" and .variant!=\\\"IoT\\\" and .variant!=\\\"Container\\\" and .variant!=\\\"Cloud\\\" and .variant!=\\\"Everything\\\"  and .subvariant!=\\\"Security\\\" and .subvariant!=\\\"Server_KVM\\\" and .subvariant!=\\\"SoaS\\\")) | map(.subvariant) | unique | .[]\")\n    fi\n}\n\nfunction releases_freebsd() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://download.freebsd.org/ftp/releases/amd64/amd64/\" | grep -Eo \"href=\\\"[0-9\\.]+-RELEASE\" | grep -oE '[0-9\\.]+' | sort -r)\n}\n\nfunction editions_freebsd() {\n    echo disc1 dvd1\n}\n\nfunction releases_freedos() {\n    echo 1.4 1.3 1.2\n}\n\nfunction releases_garuda() {\n    echo latest\n}\n\nfunction editions_garuda() {\n    echo cinnamon dr460nized dr460nized-gaming gnome hyprland i3 kde-lite mokka sway xfce\n}\n\nfunction releases_gentoo() {\n    echo latest\n}\n\nfunction editions_gentoo() {\n    echo minimal livegui\n}\n\nfunction releases_ghostbsd() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://download.ghostbsd.org/releases/amd64/\" | grep \"href\" | cut -d'\"' -f2 | cut -d'/' -f1 | sort -r | tail -n +3 | head -n 3)\n}\n\nfunction editions_ghostbsd() {\n    echo mate xfce\n}\n\nfunction releases_gnomeos() {\n    #shellcheck disable=SC2046,SC2005\n    # Filter out alpha/beta/rc pre-releases, keep only stable versions and nightly\n    echo \"nightly\" $(web_pipe \"https://download.gnome.org/gnomeos/\" | grep \"title=\" | awk -F'\"' '{print $4}' | tr -d '/' | grep -E '^[0-9]+\\.[0-9]+$' | sort -nr)\n}\n\nfunction releases_guix() {\n    echo 1.5.0 1.4.0\n}\n\nfunction releases_haiku() {\n    echo r1beta5 r1beta4 r1beta3\n}\n\nfunction editions_haiku() {\n    echo x86_64 x86_gcc2h\n}\n\nfunction releases_kali() {\n    echo current kali-weekly\n}\n\nfunction releases_kdeneon() {\n    echo user testing unstable\n}\n\nfunction releases_kolibrios() {\n    echo latest\n}\n\nfunction editions_kolibrios() {\n    echo en_US ru_RU es_ES\n}\n\nfunction releases_linuxlite() {\n    echo 6.6 6.4 6.2 6.0\n}\n\nfunction releases_linuxmint() {\n    echo 22.1 22 21.3 21.2 21.1 21 20.3 20.2\n}\n\nfunction editions_linuxmint() {\n    echo cinnamon mate xfce\n}\n\nfunction editions_lmde() {\n    echo cinnamon\n}\n\nfunction releases_lmde() {\n    echo 6\n}\n\nfunction releases_maboxlinux() {\n    echo latest\n}\n\nfunction releases_macos() {\n    echo mojave catalina big-sur monterey ventura sonoma sequoia tahoe\n}\n\nfunction releases_mageia() {\n    echo 9\n}\n\nfunction editions_mageia() {\n    echo Plasma GNOME Xfce\n}\n\nfunction editions_manjaro() {\n    echo full minimal\n}\n\nfunction releases_manjaro() {\n    echo xfce gnome plasma cinnamon i3 sway\n}\n\nfunction releases_mxlinux() {\n    # needs header, so not web_pipe:\n    curl --disable -Ils \"https://sourceforge.net/projects/mx-linux/files/latest/download\" | grep -i 'location:' | cut -d? -f1 | cut -d_ -f1 | cut -d- -f3\n}\n\nfunction editions_mxlinux() {\n    echo Xfce KDE Fluxbox\n}\n\nfunction releases_netboot() {\n    echo latest\n}\n\nfunction releases_netbsd() {\n    # V8 is EOL so filter it out\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://cdn.netbsd.org/pub/NetBSD/iso/\" | grep -o -E '\"[[:digit:]]+\\.[[:digit:]]+/\"' | tr -d '\"/' | grep -v ^8 | sort -nr | head -n 4)\n}\n\nfunction releases_nitrux() {\n    echo latest\n}\n\nfunction releases_nixos() {\n    # Lists unstable plus the most recent release\n    #shellcheck disable=SC2046\n    echo unstable $(web_pipe \"https://nix-channels.s3.amazonaws.com/?delimiter=/\" | grep -o -E 'nixos-[[:digit:]]+\\.[[:digit:]]+' | cut -d- -f2 | sort -nru | head -n +1)\n}\n\nfunction editions_nixos() {\n    echo minimal graphical\n}\n\nfunction releases_nwg-shell() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://sourceforge.net/projects/nwg-iso/rss?path=/\" | grep 'url=' | grep '64.iso' | cut -d'/' -f12 | cut -d'-' -f3)\n}\n\nfunction releases_openbsd() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirror.leaseweb.com/pub/OpenBSD/\" | grep -e '6\\.[8-9]/' -e '[7-9]\\.' | cut -d\\\" -f4 | tr -d '/' | sort -r)\n}\n\nfunction releases_openindiana() {\n    #shellcheck disable=SC2046,SC2005\n    #echo $(web_pipe \"https://dlc.openindiana.org/isos/hipster/\" | grep link | cut -d'/' -f 1 | cut -d '\"' -f4 | sort -r | tail -n +2 | head -n 5)\n    echo $(web_pipe \"https://dlc.openindiana.org/isos/hipster/\" | grep 'href=\"./2' | cut -d'/' -f 2 | sort -r |  head -n 5)\n\n}\n\nfunction editions_openindiana() {\n    echo gui text minimal\n}\n\nfunction releases_opensuse() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://download.opensuse.org/distribution/leap/\" | grep 'class=\"name\"' | cut -d '/' -f2 | grep -v 42 | sort -r) microos tumbleweed\n}\n\nfunction releases_oraclelinux() {\n    echo 9.3 9.2 9.1 9.0 8.9 8.8 8.7 8.6 8.5 8.4 7.9 7.8 7.7\n}\n\nfunction releases_parrotsec() {\n    #shellcheck disable=SC2046,SC2005\n    # Only return releases that have ISO files available\n    local RELEASES=\"\"\n    for REL in $(web_pipe \"https://deb.parrot.sh/parrot/iso/\" | grep -o -E 'href=\"([[:digit:]]+\\.)+[[:digit:]]+/' | sort -Vr | head -n 5 | cut -d\\\" -f 2 | cut -d'/' -f 1); do\n        if web_pipe \"https://download.parrot.sh/parrot/iso/${REL}/\" | grep -q '\\.iso\"'; then\n            RELEASES=\"${RELEASES} ${REL}\"\n        fi\n    done\n    echo \"${RELEASES}\"\n}\n\nfunction editions_parrotsec() {\n    # htb edition was discontinued after 6.x series\n    echo home security\n}\n\nfunction releases_pclinuxos() {\n    # Use 'latest' as different editions have different release dates\n    echo latest\n}\n\nfunction editions_pclinuxos() {\n    echo kde kde-darkstar mate xfce\n}\n\nfunction releases_peppermint() {\n    echo latest\n}\n\nfunction editions_peppermint() {\n    echo devuan-xfce devuan-gnome debian-xfce debian-gnome\n}\n\nfunction releases_popos() {\n    echo 22.04 20.04 24.04\n}\n\nfunction editions_popos() {\n    echo intel nvidia\n}\n\nfunction releases_porteus() {\n    echo 5.01\n}\n\nfunction editions_porteus() {\n    echo cinnamon gnome kde lxde lxqt mate openbox xfce\n}\n\nfunction releases_primtux() {\n    echo 7\n}\n\nfunction editions_primtux() {\n    echo 2022-10\n}\n\nfunction releases_proxmox-ve() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe https://enterprise.proxmox.com/iso/ | grep proxmox-ve | grep -E -o '[0-9]+\\.[0-9]+-[0-9]\\.iso' | uniq | sort -ru | cut -d'.' -f 1-2)\n}\n\nfunction releases_pureos() {\n    web_pipe \"https://www.pureos.net/download/\" | grep -m 1 \"downloads.puri\" | cut -d '\"' -f 2 | cut -d '-' -f 4\n}\n\nfunction editions_pureos() {\n    echo gnome plasma\n}\n\nfunction releases_reactos() {\n    echo latest\n}\n\nfunction releases_rebornos() {\n    echo latest\n}\n\nfunction releases_rockylinux() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"http://dl.rockylinux.org/vault/rocky/\" | grep \"class=\\\"link\" | grep -v -e 'full' -e  'RC' -e  'ISO' -e 'Parent' | cut -d'\"' -f4 | tr -d / | sort -ru)\n}\n\nfunction editions_rockylinux() {\n    echo minimal dvd boot\n}\n\nfunction releases_siduction() {\n    echo latest\n}\n\nfunction editions_siduction() {\n    #shellcheck disable=SC2046,SC2005\n    NAME=$(web_pipe \"https://mirror.math.princeton.edu/pub/siduction/iso/\" | grep folder | cut -d'\"' -f8 | tr -d '/')\n    web_pipe \"https://mirror.math.princeton.edu/pub/siduction/iso/${NAME}/\" | grep folder | cut -d'\"' -f8 | tr -d '/' | sort -u\n}\n\nfunction releases_slackware() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://slackware.nl/slackware/slackware-iso/\" | grep \"slackware-\" | cut -d'<' -f7 | cut -d'-' -f2 | sort -ru | head -n 5)\n}\n\nfunction releases_slax() {\n    echo latest\n}\n\nfunction editions_slax() {\n    echo debian slackware\n}\n\nfunction releases_slint() {\n    echo \"15.0-10\"\n}\n\nfunction releases_slitaz() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://mirror.slitaz.org/iso/rolling/\" | grep \"class='iso'\" | cut -d\"'\" -f4 | cut -d'-' -f3- | grep iso | cut -d'.' -f1 | sort -u)\n}\n\nfunction releases_solus() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe_json \"https://downloads.getsol.us/isos/\" | jq -r '.[].name[:-1]' | grep -E \"^[0-9-]+$\" | sort -u)\n}\n\nfunction editions_solus() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe_json \"https://downloads.getsol.us/isos/$(IFS=' '; releases_solus | tail -n1)/\" | jq -r '.[].name | select(endswith(\"iso\")) | sub(\"Solus-(?<e>.*)-Release-.*\"; \"\\(.e)\")' | sort -u)\n}\n\nfunction releases_sparkylinux() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://sparkylinux.org/download/stable/\" |  grep -E -o \"sparkylinux-.*\\.iso\\\"\" | cut -d'-' -f2 | sort -ru)\n}\n\nfunction editions_sparkylinux() {\n    #shellcheck disable=SC2046,SC2005\n    if [ -z \"${RELEASE}\" ]; then\n        echo $(web_pipe \"https://sparkylinux.org/download/stable/\" | grep -E -o \"sparkylinux-.*\\.iso\\\"\" | cut -d'-' -f4 | cut -d'.' -f1 | sort -u)\n    else\n        echo $(web_pipe \"https://sparkylinux.org/download/stable/\" | grep -E -o \"sparkylinux-${RELEASE}-.*\\.iso\\\"\" | cut -d'-' -f4 | cut -d'.' -f1 | sort -u)\n    fi\n\n}\n\nfunction releases_spirallinux() {\n    echo latest\n}\n\nfunction editions_spirallinux() {\n    echo Plasma XFCE Mate LXQt Gnome Budgie Cinnamon Builder\n}\n\nfunction releases_tails() {\n    echo stable\n}\n\nfunction releases_tinycore() {\n    echo 15 14\n}\n\nfunction editions_tinycore() {\n    echo Core TinyCore CorePlus CorePure64 TinyCorePure64\n}\n\nfunction releases_trisquel() {\n    echo 11.0 10.0.1\n}\n\nfunction editions_trisquel() {\n    echo mate lxde kde sugar\n}\n\nfunction releases_tuxedo-os() {\n    echo current\n}\n\nfunction releases_ubuntu() {\n    local VERSION_DATA=\"\"\n    local SUPPORTED_VERSIONS=()\n    VERSION_DATA=\"$(IFS=$'\\n' web_pipe https://api.launchpad.net/devel/ubuntu/series | jq -r '.entries[]')\"\n    # shellcheck disable=SC2207\n    SUPPORTED_VERSIONS=($(IFS=$'\\n' jq -r 'select(.status==\"Supported\" or .status==\"Current Stable Release\") | .version' <<<\"${VERSION_DATA}\" | sort))\n    case \"${OS}\" in\n        ubuntu)\n            echo \"${SUPPORTED_VERSIONS[@]}\" daily-live;;\n        kubuntu|lubuntu|ubuntukylin|ubuntu-mate|ubuntustudio|xubuntu)\n            # after 16.04\n            echo \"${SUPPORTED_VERSIONS[@]:1}\" daily-live;;\n        ubuntu-budgie)\n            # after 18.04\n            echo \"${SUPPORTED_VERSIONS[@]:2}\" daily-live;;\n        edubuntu|ubuntu-unity|ubuntucinnamon)\n            # after 23.10\n            echo \"${SUPPORTED_VERSIONS[@]:5}\" daily-live;;\n    esac\n}\n\nfunction releases_ubuntu-server() {\n    local ALL_VERSIONS=()\n    # shellcheck disable=SC2207\n    ALL_VERSIONS=($(IFS=$'\\n' web_pipe http://releases.ubuntu.com/streams/v1/com.ubuntu.releases:ubuntu-server.json | jq -r '.products[] | select(.arch==\"amd64\") | .version' | sort -rV))\n    echo daily-live \"${ALL_VERSIONS[@]}\"\n}\n\nfunction releases_vanillaos() {\n    #shellcheck disable=SC2046,SC2005\n    echo $(web_pipe \"https://api.github.com/repos/Vanilla-OS/live-iso/releases\" | grep 'download_url' | cut -d'/' -f8 | sort -ru)\n}\n\nfunction releases_void() {\n    # List directories that contain standard ISO files (base.iso), not just enterprise editions\n    #shellcheck disable=SC2046,SC2005\n    local DIRS=\"\"\n    DIRS=$(web_pipe \"https://repo-default.voidlinux.org/live/\" | grep \"^<a href=\\\"2\" | cut -d'\"' -f2 | tr -d '/' | sort -ru | head -n 5)\n    for DIR in ${DIRS}; do\n        # Only include directories that have standard base ISOs\n        if web_pipe \"https://repo-default.voidlinux.org/live/${DIR}/\" | grep -q \"base.iso\"; then\n            echo -n \"${DIR} \"\n        fi\n    done\n}\n\nfunction editions_void() {\n    echo glibc musl xfce-glibc xfce-musl\n}\n\nfunction releases_windows() {\n    echo 11 10\n}\n\nfunction languages_windows() {\n    I18NS=(\"Arabic\" \"Brazilian Portuguese\" \"Bulgarian\" \"Chinese (Simplified)\" \"Chinese (Traditional)\" \"Croatian\" \"Czech\" \"Danish\" \"Dutch\" \\\n    \"English (United States)\" \"English International\" \"Estonian\" \"Finnish\" \"French\" \"French Canadian\" \"German\" \"Greek\" \"Hebrew\" \"Hungarian\" \\\n    \"Italian\" \"Japanese\" \"Korean\" \"Latvian\" \"Lithuanian\" \"Norwegian\" \"Polish\" \"Portuguese\" \"Romanian\" \"Russian\" \"Serbian Latin\" \"Slovak\" \\\n    \"Slovenian\" \"Spanish\" \"Spanish (Mexico)\" \"Swedish\" \"Thai\" \"Turkish\" \"Ukrainian\")\n}\n\nfunction releases_windows-server() {\n    echo 2022 2019 2016\n}\n\nfunction languages_windows-server() {\n    I18NS=(\"English (United States)\" \"Chinese (Simplified)\" \"French\" \"German\" \"Italian\" \"Japanese\" \"Russian\" \"Spanish\")\n}\n\nfunction releases_zorin() {\n    echo 18 17 16\n}\n\n# Architecture support functions\n# These declare which architectures each distro supports.\n# Distros without an arch_*() function default to \"amd64\" only.\n\nfunction arch_alma() {\n    echo \"amd64 arm64\"\n}\n\nfunction arch_alpine() {\n    echo \"amd64 arm64\"\n}\n\nfunction arch_azurelinux() {\n    echo \"amd64 arm64\"\n}\n\nfunction arch_debian() {\n    case \"${EDITION}\" in\n        netinst) echo \"amd64 arm64\";;\n        *) echo \"amd64\";;\n    esac\n}\n\nfunction arch_fedora() {\n    case \"${EDITION}\" in\n        Onyx) echo \"amd64\";;\n        *) echo \"amd64 arm64\";;\n    esac\n}\n\nfunction arch_ubuntu-server() {\n    echo \"amd64 arm64\"\n}\n\nfunction arch_ubuntu() {\n    # Ubuntu Desktop ARM64 only available from 25.10 onwards\n    local MAJOR MINOR\n    # When RELEASE is unset (initial arch check), return all supported architectures\n    # Actual validation happens in get_ubuntu() when release is known\n    if [ -z \"${RELEASE}\" ]; then\n        echo \"amd64 arm64\"\n        return\n    fi\n    # Non-numeric releases (daily-live, etc.) default to amd64 only\n    if [[ ! \"${RELEASE}\" =~ ^[0-9]+\\.[0-9]+$ ]]; then\n        echo \"amd64\"\n        return\n    fi\n    MAJOR=\"${RELEASE%%.*}\"\n    MINOR=\"${RELEASE##*.}\"\n    # 25.10 and later support ARM64\n    if [ \"${MAJOR}\" -gt 25 ] || { [ \"${MAJOR}\" -eq 25 ] && [ \"${MINOR}\" -ge 10 ]; }; then\n        echo \"amd64 arm64\"\n    else\n        echo \"amd64\"\n    fi\n}\n\n# Check if a given architecture is supported for an OS\nfunction is_arch_supported() {\n    local OS=\"${1}\"\n    local CHECK_ARCH=\"${2}\"\n    local SUPPORTED=\"\"\n\n    # Check if arch function exists for this OS\n    if [[ $(type -t \"arch_${OS}\") == function ]]; then\n        SUPPORTED=$(arch_\"${OS}\")\n    else\n        # Default: amd64 only\n        SUPPORTED=\"amd64\"\n    fi\n\n    # Check if requested arch is in supported list\n    [[ \" ${SUPPORTED} \" == *\" ${CHECK_ARCH} \"* ]]\n}\n\n# Get supported architectures for an OS\nfunction get_supported_archs() {\n    local OS=\"${1}\"\n    # Check if arch function exists for this OS\n    if [[ $(type -t \"arch_${OS}\") == function ]]; then\n        arch_\"${OS}\"\n    else\n        # Default: amd64 only\n        echo \"amd64\"\n    fi\n}\n\nfunction editions_zorin() {\n    # Lite edition not available for Zorin 18 (Pro-only starting from 18)\n    # When RELEASE is unset (e.g. csv_data context), return all editions\n    if [ -z \"${RELEASE}\" ] || [ \"${RELEASE}\" != \"18\" ]; then\n        echo core64 lite64 education64\n    else\n        echo core64 education64\n    fi\n}\n\nfunction check_hash() {\n    local iso=\"\"\n    local hash=\"\"\n    local hash_algo=\"\"\n    if [ \"${OPERATION}\" == \"download\" ]; then\n        iso=\"${1}\"\n    else\n        iso=\"${VM_PATH}/${1}\"\n    fi\n    hash=\"${2}\"\n\n    # Check for algorithm prefix (e.g., \"sha256:abc123...\" or \"b2sum:abc123...\")\n    if [[ \"${hash}\" == *\":\"* ]]; then\n        local hash_prefix=\"${hash%%:*}\"\n        hash=\"${hash#*:}\"\n        # Normalise algorithm prefix to lowercase\n        hash_prefix=\"$(echo \"${hash_prefix}\" | tr '[:upper:]' '[:lower:]')\"\n        case \"${hash_prefix}\" in\n            md5) hash_algo=md5sum;;\n            sha1) hash_algo=sha1sum;;\n            sha256) hash_algo=sha256sum;;\n            sha512) hash_algo=sha512sum;;\n            b2sum|blake2|blake2b) hash_algo=b2sum;;\n            *) echo \"WARNING! Unknown hash algorithm '${hash_prefix}', not checking ${iso} hash.\"\n               return;;\n        esac\n    else\n        # Guess the hash algorithm by the hash length\n        case ${#hash} in\n            32) hash_algo=md5sum;;\n            40) hash_algo=sha1sum;;\n            64) hash_algo=sha256sum;;\n            128) hash_algo=sha512sum;;\n            *) echo \"WARNING! Can't guess hash algorithm, not checking ${iso} hash.\"\n                return;;\n        esac\n    fi\n\n    # On macOS/Darwin, prefer GNU coreutils (prefixed with 'g') if available,\n    # otherwise fall back to native 'shasum' command\n    local hash_cmd=\"${hash_algo}\"\n    if [ \"${HOST_OS}\" = \"Darwin\" ]; then\n        case ${hash_algo} in\n            md5sum)\n                if command -v gmd5sum &>/dev/null; then\n                    hash_cmd=gmd5sum\n                else\n                    # MD5 not directly supported by shasum; use native md5 command\n                    hash_cmd=\"md5 -r\"\n                fi;;\n            sha1sum)\n                if command -v gsha1sum &>/dev/null; then\n                    hash_cmd=gsha1sum\n                else\n                    hash_cmd=\"shasum -a 1\"\n                fi;;\n            sha256sum)\n                if command -v gsha256sum &>/dev/null; then\n                    hash_cmd=gsha256sum\n                else\n                    hash_cmd=\"shasum -a 256\"\n                fi;;\n            sha512sum)\n                if command -v gsha512sum &>/dev/null; then\n                    hash_cmd=gsha512sum\n                else\n                    hash_cmd=\"shasum -a 512\"\n                fi;;\n            b2sum)\n                if command -v gb2sum &>/dev/null; then\n                    hash_cmd=gb2sum\n                else\n                    echo \"WARNING! b2sum not available on macOS without GNU coreutils, not checking ${iso} hash.\"\n                    return\n                fi;;\n        esac\n    fi\n\n    echo -n \"Checking ${iso} with ${hash_cmd}... \"\n    # Handle MD5 on macOS specially (md5 -r outputs \"hash filename\" format)\n    if [ \"${hash_cmd}\" = \"md5 -r\" ]; then\n        local computed_hash\n        computed_hash=$(md5 -r \"${iso}\" | cut -d' ' -f1)\n        if [ \"${computed_hash}\" != \"${hash}\" ]; then\n            echo \"ERROR!\"\n            echo \"${iso} doesn't match ${hash}. Try running 'quickget' again.\"\n            exit 1\n        else\n            echo \"Good!\"\n        fi\n    elif ! printf '%s  %s\\n' \"${hash}\" \"${iso}\" | ${hash_cmd} --check --status; then\n        echo \"ERROR!\"\n        echo \"${iso} doesn't match ${hash}. Try running 'quickget' again.\"\n        exit 1\n    else\n        echo \"Good!\"\n    fi\n}\n\n# Download a file from the web and pipe it to stdout\nfunction web_pipe() {\n    curl --disable --silent --location \"${1}\"\n}\n\n# Download a JSON file from the web and pipe it to stdout\nfunction web_pipe_json() {\n     curl --disable --silent --location --header \"Accept: application/json\" \"${1}\"\n}\n\n# Download a file from the web\nfunction web_get() {\n    local CHECK=\"\"\n    local HEADERS=()\n    local URL=\"${1}\"\n    local DIR=\"${2}\"\n    local FILE=\"\"\n    local USER_AGENT=\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36\"\n\n    if [ -n \"${3}\" ]; then\n        FILE=\"${3}\"\n    else\n        FILE=\"${URL##*/}\"\n    fi\n\n    # Process any URL redirections after the file name has been extracted\n    URL=$(web_redirect \"${URL}\")\n\n    # Process any headers\n    while (( \"$#\" )); do\n        if [ \"${1}\" == \"--header\" ]; then\n            HEADERS+=(\"${1}\" \"${2}\")\n            shift 2\n        else\n            shift\n        fi\n    done\n\n    # Test mode for ISO\n    if [ \"${OPERATION}\" == \"show\" ]; then\n        test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\"\n        exit 0\n    elif [ \"${OPERATION}\" == \"test\" ]; then\n        # Check architecture support before testing URL\n        if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n            test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n            exit 0\n        fi\n        CHECK=$(web_check \"${URL}\" && echo \"PASS\" || echo \"FAIL\")\n        test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\" \"${CHECK}\"\n        exit 0\n    elif [ \"${OPERATION}\" == \"download\" ]; then\n        DIR=\"$(pwd)\"\n    fi\n\n    if [ \"${DIR}\" != \"$(pwd)\" ] && ! mkdir -p \"${DIR}\" 2>/dev/null; then\n        echo \"ERROR! Unable to create directory ${DIR}\"\n        exit 1\n    fi\n\n    if [[ ${OS} != windows && ${OS} != macos && ${OS} != windows-server ]]; then\n        echo \"Downloading $(pretty_name \"${OS}\") ${RELEASE} ${EDITION}\"\n        echo \"- URL: ${URL}\"\n    fi\n\n    if ! curl --disable --progress-bar --location --output \"${DIR}/${FILE}\" --continue-at - --user-agent \"${USER_AGENT}\" \"${HEADERS[@]}\" -- \"${URL}\"; then\n        echo \"ERROR! Failed to download ${URL} with curl.\"\n        rm -f \"${DIR}/${FILE}\"\n        exit 1\n    fi\n}\n\n# checks if a URL needs to be redirected and returns the final URL\nfunction web_redirect() {\n    local REDIRECT_URL=\"\"\n    local URL=\"${1}\"\n    # Check for URL redirections\n    # Output to nonexistent directory so the download fails fast\n    REDIRECT_URL=$(curl --disable --silent --location --fail --write-out '%{url_effective}' --output /var/cache/${RANDOM}/${RANDOM} \"${URL}\" )\n    if [ \"${REDIRECT_URL}\" != \"${URL}\" ]; then\n        echo \"${REDIRECT_URL}\"\n    else\n        echo \"${URL}\"\n    fi\n}\n\n# checks if a URL is reachable\nfunction web_check() {\n    local HEADERS=()\n    local URL=\"${1}\"\n    # Process any headers\n    while (( \"$#\" )); do\n        if [ \"${1}\" == \"--header\" ]; then\n            HEADERS+=(\"${1}\" \"${2}\")\n            shift 2\n        else\n            shift\n        fi\n    done\n    curl --disable --silent --location --head --output /dev/null --fail --connect-timeout 30 --max-time 30 --retry 3 \"${HEADERS[@]}\" \"${URL}\"\n}\n\nfunction zsync_get() {\n    local CHECK=\"\"\n    local DIR=\"${2}\"\n    local FILE=\"${1##*/}\"\n    local OUT=\"\"\n    local URL=\"${1}\"\n    # Test mode for ISO\n    if [ \"${OPERATION}\" == \"show\" ]; then\n        test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\"\n        exit 0\n    elif [ \"${OPERATION}\" == \"test\" ]; then\n        # Check architecture support before testing URL\n        if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n            test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n            exit 0\n        fi\n        CHECK=$(web_check \"${URL}\" && echo \"PASS\" || echo \"FAIL\")\n        test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\" \"${CHECK}\"\n        exit 0\n    elif command -v zsync &>/dev/null; then\n        if [ -n \"${3}\" ]; then\n            OUT=\"${3}\"\n        else\n            OUT=\"${FILE}\"\n        fi\n\n        if ! mkdir -p \"${DIR}\" 2>/dev/null; then\n            echo \"ERROR! Unable to create directory ${DIR}\"\n            exit 1\n        fi\n        echo \"Downloading $(pretty_name \"${OS}\") ${RELEASE} ${EDITION} from ${URL}\"\n        # Only force http for zsync - not earlier because we might fall through here\n        if ! zsync \"${URL/https/http}.zsync\" -i \"${DIR}/${OUT}\" -o \"${DIR}/${OUT}\" 2>/dev/null; then\n            echo \"ERROR! Failed to download ${URL/https/http}.zsync\"\n            exit 1\n        fi\n\n        if [ -e \"${DIR}/${OUT}.zs-old\" ]; then\n            rm \"${DIR}/${OUT}.zs-old\"\n        fi\n    else\n        echo \"INFO: zsync not found, falling back to curl\"\n        if [ -n \"${3}\" ]; then\n            web_get \"${1}\" \"${2}\" \"${3}\"\n        else\n            web_get \"${1}\" \"${2}\"\n        fi\n    fi\n}\n\nfunction make_vm_config() {\n    local CONF_FILE=\"\"\n    local IMAGE_FILE=\"\"\n    local ISO_FILE=\"\"\n    local IMAGE_TYPE=\"\"\n    local GUEST=\"\"\n    if [ \"${OPERATION}\" == \"download\" ]; then\n        exit 0\n    fi\n    IMAGE_FILE=\"${1}\"\n    ISO_FILE=\"${2}\"\n    case \"${OS}\" in\n        batocera)\n            GUEST=\"batocera\"\n            IMAGE_TYPE=\"img\";;\n        custom)\n            GUEST=\"${CUSTOM_OS}\"\n            IMAGE_TYPE=\"${CUSTOM_IMAGE_TYPE}\";;\n        dragonflybsd)\n            GUEST=\"dragonflybsd\"\n            IMAGE_TYPE=\"iso\";;\n        easyos)\n            GUEST=\"linux\"\n            IMAGE_TYPE=\"img\";;\n        freebsd|ghostbsd)\n            GUEST=\"freebsd\"\n            IMAGE_TYPE=\"iso\";;\n        haiku)\n            GUEST=\"haiku\"\n            IMAGE_TYPE=\"iso\";;\n        freedos)\n            GUEST=\"freedos\"\n            IMAGE_TYPE=\"iso\";;\n        kolibrios)\n            GUEST=\"kolibrios\"\n            IMAGE_TYPE=\"iso\";;\n        macos)\n            GUEST=\"macos\"\n            IMAGE_TYPE=\"img\";;\n        netbsd)\n            GUEST=\"netbsd\"\n            IMAGE_TYPE=\"iso\";;\n        openbsd)\n            GUEST=\"openbsd\"\n            IMAGE_TYPE=\"iso\";;\n        openindiana)\n            GUEST=\"solaris\"\n            IMAGE_TYPE=\"iso\";;\n        reactos)\n            GUEST=\"reactos\"\n            IMAGE_TYPE=\"iso\";;\n        ubuntu*)\n            GUEST=\"linux\"\n            IMAGE_TYPE=\"iso\"\n            # If there is a point in the release, check if it is less than 16.04\n            if [[ \"${RELEASE}\" != *\"daily\"* ]]; then\n                if [ \"${RELEASE//./}\" -lt 1604 ]; then\n                    GUEST=\"linux_old\"\n                fi\n            fi\n            ;;\n        windows)\n            GUEST=\"windows\"\n            IMAGE_TYPE=\"iso\";;\n        windows-server)\n            GUEST=\"windows-server\"\n            IMAGE_TYPE=\"iso\";;\n        *)\n            GUEST=\"linux\"\n            IMAGE_TYPE=\"iso\";;\n    esac\n\n    CONF_FILE=\"${VM_PATH}.conf\"\n\n    if [ ! -e \"${CONF_FILE}\" ]; then\n        echo \"Making ${CONF_FILE}\"\n        local ARCH_LINE=\"\"\n        if [ \"${ARCH}\" != \"${NORMALISED_HOST_ARCH}\" ]; then\n            if [ \"${ARCH}\" == \"arm64\" ]; then\n                ARCH_LINE=\"arch=\\\"aarch64\\\"\"\n            else\n                ARCH_LINE=\"arch=\\\"x86_64\\\"\"\n            fi\n        fi\n        cat << EOF > \"${CONF_FILE}\"\n#!${QUICKEMU} --vm\nguest_os=\"${GUEST}\"\n${ARCH_LINE:+${ARCH_LINE}\n}disk_img=\"${VM_PATH}/disk.qcow2\"\n${IMAGE_TYPE}=\"${VM_PATH}/${IMAGE_FILE}\"\nEOF\n        echo \" - Setting ${CONF_FILE} executable\"\n        chmod u+x \"${CONF_FILE}\"\n        if [ -n \"${ISO_FILE}\" ]; then\n            echo \"fixed_iso=\\\"${VM_PATH}/${ISO_FILE}\\\"\" >> \"${CONF_FILE}\"\n        fi\n\n        # OS specific tweaks\n        case ${OS} in\n            alma|centos-stream|endless|garuda|gentoo|kali|nixos|oraclelinux|popos|rockylinux)\n                echo \"disk_size=\\\"32G\\\"\" >> \"${CONF_FILE}\";;\n            openindiana)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\"\n                echo \"disk_size=\\\"32G\\\"\" >> \"${CONF_FILE}\";;\n            batocera)\n                echo \"disk_size=\\\"8G\\\"\" >> \"${CONF_FILE}\";;\n            bazzite)\n                echo \"disk_size=\\\"64G\\\"\" >> \"${CONF_FILE}\";;\n            dragonflybsd|haiku|openbsd|netbsd|slackware|slax|tinycore)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\";;\n            deepin)\n                echo \"disk_size=\\\"64G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"4G\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            freedos)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\"\n                echo \"disk_size=\\\"4G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"256M\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            kolibrios)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\"\n                echo \"disk_size=\\\"2G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"128M\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            slint)\n                echo \"disk_size=\\\"50G\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            slitaz)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\"\n                echo \"disk_size=\\\"4G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"512M\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            ubuntu-server)\n                # 22.04+ fails on LVM build if disk size is < 10G\n                # 22.04.1 fails on auto-install if TPM is disabled\n                echo \"disk_size=\\\"10G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"4G\\\"\" >> \"${CONF_FILE}\"\n                if [[ \"${RELEASE}\" == *\"22.04\"* ]]; then\n                    echo \"tpm=\\\"on\\\"\" >> \"${CONF_FILE}\"\n                fi\n                ;;\n            vanillaos)\n                ## Minimum is 50G for abroot, but a 64GB is allocated to give some headroom\n                echo \"disk_size=\\\"64G\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            zorin)\n                case ${EDITION} in\n                    education64|edulite64) echo \"disk_size=\\\"32G\\\"\" >> \"${CONF_FILE}\";;\n                esac;;\n            reactos)\n                echo \"boot=\\\"legacy\\\"\" >> \"${CONF_FILE}\"\n                echo \"disk_size=\\\"12G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"2048M\\\"\" >> \"${CONF_FILE}\"\n                ;;\n            macos)\n                echo \"disk_size=\\\"128G\\\"\" >> \"${CONF_FILE}\"\n                echo \"macos_release=\\\"${RELEASE}\\\"\" >> \"${CONF_FILE}\"\n                # https://github.com/quickemu-project/quickemu/issues/438\n                if [ \"${RELEASE}\" == \"monterey\" ]; then\n                    echo \"cpu_cores=2\" >> \"${CONF_FILE}\"\n                fi\n                ;;\n            elementary)\n                case \"${RELEASE}\" in\n                    8.1)\n                        echo \"display=\\\"spice\\\"\" >> \"${CONF_FILE}\"\n                        ;;\n                esac\n                ;;\n            proxmox-ve)\n                echo \"disk_size=\\\"20G\\\"\" >> \"${CONF_FILE}\"\n                echo \"ram=\\\"4G\\\"\" >> \"${CONF_FILE}\"\n                ;;\n        esac\n\n        if [ \"${OS}\" == \"ubuntu\" ] && [[ ${RELEASE} == *\"daily\"*  ]]; then\n            # Minimum to install lobster testing is 18GB but 32GB are allocated for headroom\n            echo \"disk_size=\\\"32G\\\"\" >> \"${CONF_FILE}\"\n        fi\n\n        if [[ \"${OS}\" == \"windows\"* ]]; then\n            echo \"disk_size=\\\"64G\\\"\" >> \"${CONF_FILE}\"\n        fi\n\n        # Enable TPM for Windows 11 and Windows Server 2022\n        if [[ \"${OS}\" == \"windows\" && \"${RELEASE}\" == \"11\" || \"${OS}\" == \"windows-server\" && \"${RELEASE}\" == \"2022\" ]]; then\n            echo \"tpm=\\\"on\\\"\" >> \"${CONF_FILE}\"\n            echo \"secureboot=\\\"off\\\"\" >> \"${CONF_FILE}\"\n        fi\n    fi\n    echo -e \"\\nTo start your $(pretty_name \"${OS}\") virtual machine run:\"\n    if [ \"${OS}\" == \"slint\" ]; then\n        echo -e \"    quickemu --vm ${CONF_FILE}\\nTo start Slint with braille support run:\\n    quickemu --vm --braille --display sdl ${CONF_FILE}\"\n    else\n        echo \"    quickemu --vm ${CONF_FILE}\"\n    fi\n\n    echo\n    exit 0\n}\n\nfunction get_alma() {\n    local HASH=\"\"\n    local QEMU_ARCH=\"x86_64\"\n    [ \"${ARCH}\" == \"arm64\" ] && QEMU_ARCH=\"aarch64\"\n    local ISO=\"AlmaLinux-${RELEASE}-latest-${QEMU_ARCH}-${EDITION}.iso\"\n    local URL=\"https://repo.almalinux.org/almalinux/${RELEASE}/isos/${QEMU_ARCH}\"\n    HASH=\"$(web_pipe \"${URL}/CHECKSUM\" | grep \"(${ISO}\" | cut -d' ' -f4)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_alpine() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local QEMU_ARCH=\"x86_64\"\n    [ \"${ARCH}\" == \"arm64\" ] && QEMU_ARCH=\"aarch64\"\n    local URL=\"https://dl-cdn.alpinelinux.org/alpine/${RELEASE}/releases/${QEMU_ARCH}\"\n    local VERSION=\"\"\n    VERSION=$(web_pipe \"${URL}/latest-releases.yaml\" | awk '/\"Xen\"/{found=0} {if(found) print} /\"Virtual\"/{found=1}' | grep 'version:' | head -1 | awk '{print $2}')\n    ISO=\"alpine-virt-${VERSION}-${QEMU_ARCH}.iso\"\n    HASH=$(web_pipe \"${URL}/latest-releases.yaml\" | awk '/\"Xen\"/{found=0} {if(found) print} /\"Virtual\"/{found=1}' | grep 'sha256:' | head -1 | awk '{print $2}')\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_android() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local JSON_ALL=\"\"\n    local JSON_REL=\"\"\n    local URL=\"https://mirrors.gigenet.com/OSDN/android-x86\"\n    JSON_ALL=$(web_pipe \"https://www.fosshub.com/Android-x86-old.html\" | grep \"var settings =\" | cut -d'=' -f2-)\n    JSON_REL=$(echo \"${JSON_ALL}\" | jq --arg ver \"${OS}-${EDITION}-${RELEASE}\" 'first(.pool.f[] | select((.n | startswith($ver)) and (.n | endswith(\".iso\"))))')\n    ISO=$(echo \"${JSON_REL}\" | jq -r .n)\n    HASH=$(echo \"${JSON_REL}\" | jq -r .hash.sha256)\n    # Traverse the directories to find the .iso location\n    for DIR in $(web_pipe \"${URL}\" | grep -o -E '[0-9]{5}' | sort -ur); do\n        if web_pipe \"${URL}/${DIR}\" | grep \"${ISO}\" &>/dev/null; then\n            URL=\"${URL}/${DIR}\"\n            break\n        fi\n    done\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_antix() {\n    local HASH=\"\"\n    local ISO=\"antiX-${RELEASE}\"\n    local README=\"README\"\n    local URL=\"https://sourceforge.net/projects/antix-linux/files/Final/antiX-${RELEASE}\"\n\n    # antiX uses a different URL and ISO naming for runit editions\n    if [[ \"${EDITION}\" == *\"runit\"* ]];then\n        ISO+=\"-runit\"\n        README=\"README2\"\n        case ${RELEASE} in\n            21) URL+=\"/runit-bullseye\";;\n            *)  URL+=\"/runit-antiX-${RELEASE}\";;\n        esac\n    fi\n    case ${EDITION} in\n        base-*) ISO+=\"_x64-base.iso\";;\n        core-*) ISO+=\"_x64-core.iso\";;\n        full-*) ISO+=\"_x64-full.iso\";;\n        net-*)  ISO+=\"-net_x64-net.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${README}.txt\" | grep \"${ISO}\" | cut -d' ' -f1 | head -n 1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_archcraft() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    local VERSION_FOLDER=\"\"\n    URL=\"https://sourceforge.net/projects/archcraft/files/${RELEASE}/download\"\n    URL=\"$(web_redirect \"${URL}\" | cut -d? -f1)\"\n    ISO=\"$(basename \"${URL}\")\"\n    VERSION_FOLDER=\"$(dirname \"${URL}\" | xargs basename)\"\n    HASH=$(web_pipe \"https://sourceforge.net/projects/archcraft/files/${VERSION_FOLDER}/${ISO}.sha256sum\" | cut -d' ' -f1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_archlinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://geo.mirror.pkgbuild.com/\"\n    ISO=$(web_pipe \"https://archlinux.org/releng/releases/json/\" | jq -r '.releases[0].iso_url')\n    HASH=$(web_pipe \"https://archlinux.org/releng/releases/json/\" | jq -r '.releases[0].sha256_sum')\n    echo \"${URL}${ISO} ${HASH}\"\n}\n\nfunction get_artixlinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://iso.artixlinux.org/iso\"\n    ISO=\"artix-${EDITION}-${RELEASE}-x86_64.iso\"\n    HASH=$(web_pipe \"${URL}/sha256sums\" | grep \"${ISO}\")\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_azurelinux() {\n    local QEMU_ARCH=\"x86_64\"\n    [ \"${ARCH}\" == \"arm64\" ] && QEMU_ARCH=\"aarch64\"\n    local URL=\"https://aka.ms/azurelinux-${RELEASE}-${QEMU_ARCH}.iso\"\n    local ISO\n    ISO=\"$(web_redirect \"${URL}\")\"\n    echo \"${ISO}\"\n}\n\nfunction get_batocera() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://mirrors.o2switch.fr/batocera/x86_64/stable/${RELEASE}\"\n    ISO=\"$(web_pipe \"${URL}/\" | grep -e 'batocera.*img.gz'| cut -d'\"' -f2)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_bazzite() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://download.bazzite.gg\"\n    case ${EDITION} in\n        gnome) ISO=\"bazzite-gnome-stable-amd64.iso\";;\n        plasma)  ISO=\"bazzite-stable-amd64.iso\";;\n        deck-gnome)  ISO=\"bazzite-deck-gnome-stable-amd64.iso\";;\n        deck-plasma)  ISO=\"bazzite-deck-stable-amd64.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}-CHECKSUM\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_biglinux() {\n    local HASH=\"\"\n    local ISO=\"biglinux_${RELEASE}_${EDITION}.iso\"\n    local URL=\"https://iso.biglinux.com.br\"\n    HASH=$(web_pipe \"${URL}/${ISO}.md5\" | grep -Eo '[[:alnum:]]{32}')\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_blendos() {\n    local HASH=\"\"\n    local ISO=\"blendOS.iso\"\n    # Use the official GitLab build server\n    local URL=\"https://git.blendos.co/api/v4/projects/32/jobs/artifacts/main/raw/blendOS.iso?job=build-job\"\n    HASH=$(web_pipe \"https://git.blendos.co/api/v4/projects/32/jobs/artifacts/main/raw/checksum?job=build-job\" | cut -d' ' -f1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_bodhi() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://sourceforge.net/projects/bodhilinux/files/${RELEASE}\"\n    case ${EDITION} in\n        standard) ISO=\"bodhi-${RELEASE}-64.iso\";;\n        *) ISO=\"bodhi-${RELEASE}-64-${EDITION}.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_bunsenlabs() {\n    local HASH=\"\"\n    local ISO=\"boron-1-240123-amd64.hybrid.iso\"\n    local URL=\"https://ddl.bunsenlabs.org/ddl\"\n    HASH=$(web_pipe \"${URL}/release.sha256.txt\" | head -n 1 | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_cachyos() {\n    local HASH=\"\"\n    local URL=\"\"\n    URL=\"$(web_pipe \"https://cachyos.org/download/\" | tr '&' '\\n' | grep \"ISO/${EDITION}\" | grep -v 'iso.sha' | grep -v 'iso.sig' | cut -d';' -f2)\"\n    HASH=$(web_pipe \"${URL}.sha256\" | cut -d' ' -f1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_centos-stream() {\n    local HASH=\"\"\n    local ISO=\"CentOS-Stream-${RELEASE}-latest-x86_64-${EDITION}.iso\"\n    local URL=\"https://linuxsoft.cern.ch/centos-stream/${RELEASE}-stream/BaseOS/x86_64/iso\"\n    HASH=$(web_pipe \"${URL}/${ISO}.SHA256SUM\" | grep \"SHA256 (${ISO}\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_chimeralinux() {\n    local DATE=\"\"\n    local HASH=\"\"\n    local URL=\"https://repo.chimera-linux.org/live/${RELEASE}\"\n    DATE=$(web_pipe \"${URL}/sha256sums.txt\" | head -n1 | cut -d'-' -f5)\n    local ISO=\"chimera-linux-x86_64-LIVE-${DATE}-${EDITION}.iso\"\n    HASH=$(web_pipe \"${URL}/sha256sums.txt\" | grep 'x86_64-LIVE' | grep \"${EDITION}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_crunchbang++() {\n    local HASH=\"\"\n    local ISO=\"\"\n    ISO=$(web_pipe \"https://api.github.com/repos/CBPP/cbpp/releases\" | grep 'download_url' | grep amd64 | grep \"${RELEASE}\" | cut -d'\"' -f4)\n    echo \"${ISO} ${HASH}\"\n}\n\nfunction get_debian() {\n    local DEBIAN_ARCH=\"${ARCH}\"\n    local DEBCURRENT=\"\"\n    local HASH=\"\"\n    local ISO=\"debian-live-${RELEASE}-${DEBIAN_ARCH}-${EDITION}.iso\"\n    local URL=\"https://cdimage.debian.org/cdimage/archive/${RELEASE}-live/${DEBIAN_ARCH}/iso-hybrid\"\n\n    # Debian only provides netinst images for ARM64\n    if [ \"${DEBIAN_ARCH}\" == \"arm64\" ] && [ \"${EDITION}\" != \"netinst\" ]; then\n        echo \"ERROR! Debian ${EDITION} is not available for ARM64. Use 'netinst' edition.\"\n        exit 1\n    fi\n\n    DEBCURRENT=$(web_pipe \"https://cdimage.debian.org/debian-cd/\" | grep '\\.[0-9]/' | cut -d'>' -f 9 | cut -d'/' -f 1)\n    case \"${RELEASE}\" in\n        \"${DEBCURRENT}\") URL=\"https://cdimage.debian.org/debian-cd/${RELEASE}-live/${DEBIAN_ARCH}/iso-hybrid\";;\n    esac\n    if [ \"${EDITION}\" == \"netinst\" ]; then\n        URL=\"${URL/-live/}\"\n        URL=\"${URL/hybrid/cd}\"\n        ISO=\"${ISO/-live/}\"\n    fi\n    HASH=$(web_pipe \"${URL}/SHA512SUMS\" | grep \"${ISO}\" | cut -d' ' -f1 | head -n 1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_deepin() {\n    local HASH=\"\"\n    local REV=${RELEASE}\n    # deepin-desktop-community-20.3-amd64.iso\n    local URL=\"https://cdimage.deepin.com/releases/\"${RELEASE}\n    # Correct URL for 23-RC onwards which has architecture directories\n    if [ \"${RELEASE}\" != \"20.9\" ]; then\n        URL+=\"/amd64\"\n    fi\n    local ISO=\"deepin-desktop-community-${REV}-amd64.iso\"\n    HASH=$(web_pipe \"${URL}/SHA256SUMS\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_devuan() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://files.devuan.org/devuan_${RELEASE}/desktop-live\"\n    local VER=\"\"\n    case ${RELEASE} in\n        beowulf)  VER=\"3.1.1\";;\n        chimaera) VER=\"4.0.3\";;\n        daedalus) VER=\"5.0.0\";;\n    esac\n    ISO=\"devuan_${RELEASE}_${VER}_amd64_desktop-live.iso\"\n    HASH=$(web_pipe \"${URL}/SHASUMS.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_dragonflybsd() {\n    local HASH=\"\"\n    local ISO=\"dfly-x86_64-${RELEASE}_REL.iso.bz2\"\n    local URL=\"http://mirror-master.dragonflybsd.org/iso-images\"\n    HASH=$(web_pipe \"${URL}/md5.txt\" | grep \"(${ISO})\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_easyos() {\n    local HASH=\"\"\n    local URL=\"\"\n    local ISO=\"\"\n    local YEAR=\"\"\n    ISO=\"easy-${RELEASE}-amd64.img\"\n    TWO_YEARS=$(web_pipe https://distro.ibiblio.org/easyos/amd64/releases/kirkstone/ | grep -o -E '[[:digit:]]{4}/' | sort -nr | tr -d /  | head -n 2 )\n    for YEAR in ${TWO_YEARS} ; do\n        if web_check \"https://distro.ibiblio.org/easyos/amd64/releases/kirkstone/${YEAR}/${RELEASE}/\" ; then\n            URL=\"https://distro.ibiblio.org/easyos/amd64/releases/kirkstone/${YEAR}/${RELEASE}\"\n            HASH=$(web_pipe \"${URL}/md5.sum.txt\" | cut -d' ' -f1)\n            break\n        fi\n    done\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_elementary() {\n    # Hash and ISO filename can be obtained from the below URL\n    # https://elementary.io/docs/installation#verify-your-download\n    local HASH=\"\"\n    local STAMP\n    case ${RELEASE} in\n        7.0) STAMP=\".20230129rc\";;\n        7.1) STAMP=\".20230926rc\";;\n        8.0) STAMP=\".20241122rc\";;\n        8.1)\n            STAMP=\"-amd64.20251211\"\n            HASH=\"eee6cad081664717681bec767fbfe1aa1fd920938fedad6c83b41fd341e8f306\"\n            ;;\n    esac\n\n    local ISO=\"elementaryos-${RELEASE}-stable${STAMP}.iso\"\n    local URL=\"https://ams3.dl.elementary.io/download\"\n    echo \"${URL}/$(date +%s | base64)/${ISO} ${HASH}\"\n}\n\nfunction get_endeavouros() {\n    local ENDEAVOUR_RELEASES=\"\"\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://mirror.alpix.eu/endeavouros/iso\"\n    # Find EndeavourOS releases from mirror, pick one matching release\n    ENDEAVOUR_RELEASES=\"$(web_pipe \"${URL}/\" | grep -o '<a href=\"[^\"]*.iso\">' | sed 's/^<a href=\"//;s/.iso\">.*//' | grep -v 'x86_64')\"\n    ISO=\"$(echo \"${ENDEAVOUR_RELEASES}\" | grep -i \"${RELEASE}\").iso\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha512sum\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_endless() {\n    local HASH=\"\" # No hash - there is a signature in .asc signed by\n    #https://d1anzknqnc1kmb.cloudfront.net/eos-image-keyring.gpg\n    # (4096R: CB50 0F7B C923 3FAD 32B4 E720 9E0C 1250 587A 279C)\n    local FILE_TS=\"\"\n    # https://support.endlessos.org/en/installation/direct-download gives the info but computes the URLS in js\n    # so parsing out the useful info is not happening tonight\n    # Endless edition names are \"base\" for the small minimal one or the Language for the large full release\n    # The isos are stamped as they are finished so ....\n    case ${EDITION} in\n        base)  FILE_TS=\"241023-183516\";;\n        en)    FILE_TS=\"241023-200926\";;\n        es)    FILE_TS=\"241023-184649\";;\n        fr)    FILE_TS=\"241023-191212\";;\n        pt_BR) FILE_TS=\"241023-191427\";;\n    esac\n    URL=\"https://images-dl.endlessm.com/release/${RELEASE}/eos-amd64-amd64/${EDITION}\"\n    ISO=\"eos-eos${RELEASE:0:3}-amd64-amd64.${FILE_TS}.${EDITION}.iso\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_fedora() {\n    local FEDORA_ARCH=\"x86_64\"\n    local HASH=\"\"\n    local ISO=\"\"\n    local JSON=\"\"\n    local URL=\"\"\n    local VARIANT=\"\"\n\n    [ \"${ARCH}\" == \"arm64\" ] && FEDORA_ARCH=\"aarch64\"\n\n    case ${EDITION} in\n        Server|Kinoite|Onyx|Silverblue|Sericea|Workstation|KDE) VARIANT=\"${EDITION}\";;\n        *) VARIANT=\"Spins\";;\n    esac\n    # Handle KDE as a proper edition from 42 but a spin prior to 42\n    # Stripping eventual _Beta suffix from the RELEASE variable in the check\n    if [[ \"${VARIANT}\" == \"KDE\" && \"${RELEASE/_Beta/}\" -lt 42 ]]; then\n        VARIANT=\"Spins\"\n    fi\n    # The naming of 41 Beta with a space is problematic so we replaced it with an underscore\n    # but we need to convert it back to a space for the URL search in the JSON\n    #shellcheck disable=SC2086\n    # if RELEASE contains an underscore, replace it with a space\n    if [[ \"${RELEASE}\" == *\"_\"* ]]; then\n        RELEASE=\"${RELEASE/_/ }\"\n    fi\n\n    # shellcheck disable=SC2086\n    # Fedora may promote variants from Spins to Editions, in which case we want to accept either \"Spins\" or the specific edition name to preserve backwards compatibility\n    # For example, Fedora 42 KDE is now an edition, while previous releases are spins\n    JSON=$(web_pipe \"https://getfedora.org/releases.json\" | jq '.[] | select((.variant==\"'\"${VARIANT}\"'\" or .variant==\"'\"${EDITION}\"'\") and .subvariant==\"'\"${EDITION}\"'\" and .arch==\"'\"${FEDORA_ARCH}\"'\" and .version==\"'\"${RELEASE}\"'\" and (.link | endswith(\".iso\")))')\n    URL=$(echo \"${JSON}\" | jq -r '.link' | head -n1)\n    HASH=$(echo \"${JSON}\" | jq -r '.sha256' | head -n1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_freebsd() {\n    local HASH=\"\"\n    local ISO=\"FreeBSD-${RELEASE}-RELEASE-amd64-${EDITION}.iso\"\n    local URL=\"https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/CHECKSUM.SHA256-FreeBSD-${RELEASE}-RELEASE-amd64\" | grep \"${ISO}\" | grep -v \".xz\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_freedos() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local BASE_URL=\"https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/distributions/${RELEASE}\"\n    local URL=\"\"\n    case ${RELEASE} in\n        1.2) URL=\"${BASE_URL}/official\"\n             ISO=\"FD12CD.iso\"\n             HASH=$(web_pipe \"${URL}/FD12.sha\" | grep \"${ISO}\" | cut -d' ' -f1);;\n        1.3) URL=\"${BASE_URL}/official\"\n             ISO=\"FD13-LiveCD.zip\"\n             HASH=$(web_pipe \"${URL}/verify.txt\" | grep -A 8 \"sha256sum\" | grep \"${ISO}\" | cut -d' ' -f1);;\n        1.4) URL=\"${BASE_URL}\"\n             ISO=\"FD14-LiveCD.zip\"\n             HASH=$(web_pipe \"${URL}/verify.txt\" | grep -A 8 \"sha256sum\" | grep \"${ISO}\" | cut -d' ' -f1);;\n    esac\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_garuda() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://iso.builds.garudalinux.org/iso/latest/garuda\"\n    ISO=${EDITION}/latest.iso\n    HASH=\"$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f1)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_gentoo() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://mirrors.kernel.org/gentoo/releases/amd64/autobuilds\"\n    case ${EDITION} in\n        minimal) ISO=$(web_pipe \"${URL}/${RELEASE}-iso.txt\" | grep install | cut -d' ' -f1);;\n        livegui) ISO=$(web_pipe \"${URL}/${RELEASE}-iso.txt\" | grep livegui | cut -d' ' -f1);;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.DIGESTS\" | grep -A 1 SHA512 | grep iso | grep -v CONTENTS | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_ghostbsd() {\n    local ISO=\"\"\n    local URL=\"https://download.ghostbsd.org/releases/amd64/${RELEASE}\"\n    local HASH=\"\"\n    case ${EDITION} in\n        mate) ISO=\"GhostBSD-${RELEASE}.iso\";;\n        xfce) ISO=\"GhostBSD-${RELEASE}-XFCE.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | grep \"${ISO}\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_gnomeos() {\n    local HASH=\"\"\n    local ISO=\"gnome_os_installer_${RELEASE}.iso\"\n    local URL=\"https://download.gnome.org/gnomeos/${RELEASE}\"\n    case ${RELEASE} in\n        nightly)\n            ISO=\"gnome_os_installer.iso\"\n            URL=\"https://os.gnome.org/download/latest\";;\n        46.0) ISO=\"gnome_os_installer_46.iso\";;\n        3*) ISO=\"gnome_os_installer.iso\";;\n    esac\n    # Process the URL redirections; required for GNOME\n    ISO=$(web_redirect \"${URL}/${ISO}\")\n    echo \"${ISO} ${HASH}\"\n}\n\nfunction get_guix() {\n    local HASH=\"\"\n    local ISO=\"guix-system-install-${RELEASE}.x86_64-linux.iso\"\n    local URL=\"https://ftpmirror.gnu.org/gnu/guix/\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_haiku() {\n    local ISO=\"haiku-${RELEASE}-${EDITION}-anyboot.iso\"\n    local URL=\"http://mirror.rit.edu/haiku/${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | grep \"${ISO}\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_kali() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://cdimage.kali.org/${RELEASE}\"\n    ISO=$(web_pipe \"${URL}/?C=M;O=D\" | grep -o \">kali-linux-.*-installer-amd64.iso\" | head -n 1 | cut -c 2-)\n    HASH=$(web_pipe \"${URL}/SHA256SUMS\" | grep -v torrent | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_kdeneon() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://files.kde.org/neon/images/${RELEASE}/current\"\n    ISO=$(web_pipe \"${URL}/neon-${RELEASE}-current.sha256sum\" | cut -d' ' -f3-)\n    HASH=$(web_pipe \"${URL}/neon-${RELEASE}-current.sha256sum\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_kolibrios() {\n    local HASH=\"\"\n    local ISO=\"latest-iso.7z\"\n    local URL=\"http://builds.kolibrios.org/${EDITION}\"\n    HASH=$(web_pipe \"${URL}/sha256sums.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_linuxlite() {\n    local HASH=\"\"\n    local ISO=\"linux-lite-${RELEASE}-64bit.iso\"\n    local URL=\"https://sourceforge.net/projects/linux-lite/files/${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_linuxmint() {\n    local HASH=\"\"\n    local ISO=\"linuxmint-${RELEASE}-${EDITION}-64bit.iso\"\n    local URL=\"https://mirrors.kernel.org/linuxmint/stable/${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/sha256sum.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_lmde() {\n    local HASH=\"\"\n    local ISO=\"lmde-${RELEASE}-${EDITION}-64bit.iso\"\n    local URL=\"https://mirrors.kernel.org/linuxmint/debian\"\n    HASH=$(web_pipe \"${URL}/sha256sum.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_maboxlinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    URL=\"https://sourceforge.net/projects/mabox-linux/files/${RELEASE}/download\"\n    URL=\"$(web_redirect \"${URL}\" | cut -d? -f1)\"\n    ISO=\"$(basename \"${URL}\")\"\n    HASH=$(web_pipe \"https://repo.maboxlinux.org/iso/${ISO}.md5\" | cut -d' ' -f1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction generate_id() {\n    local macRecoveryID=\"\"\n    local TYPE=\"${1}\"\n    local valid_chars=(\"0\" \"1\" \"2\" \"3\" \"4\" \"5\" \"6\" \"7\" \"8\" \"9\" \"A\" \"B\" \"C\" \"D\" \"E\" \"F\")\n    for ((i=0; i<TYPE; i++)); do\n        macRecoveryID+=\"${valid_chars[$((RANDOM % 16))]}\"\n    done\n    echo \"${macRecoveryID}\"\n}\n\nfunction get_macos() {\n    local appleSession=\"\"\n    local info=\"\"\n    local downloadLink=\"\"\n    local downloadSession=\"\"\n    local chunkListLink=\"\"\n    local chunkListSession=\"\"\n    local BOARD_ID=\"\"\n    local CWD=\"\"\n    local CHECK=\"\"\n    local CHUNKCHECK=\"\"\n    local MLB=\"00000000000000000\"\n    local OS_TYPE=\"default\"\n\n    case ${RELEASE} in\n        lion|10.7)\n            BOARD_ID=\"Mac-2E6FAB96566FE58C\"\n            MLB=\"00000000000F25Y00\";;\n        mountainlion|10.8)\n            BOARD_ID=\"Mac-7DF2A3B5E5D671ED\"\n            MLB=\"00000000000F65100\";;\n        mavericks|10.9)\n            BOARD_ID=\"Mac-F60DEB81FF30ACF6\"\n            MLB=\"00000000000FNN100\";;\n        yosemite|10.10)\n            BOARD_ID=\"Mac-E43C1C25D4880AD6\"\n            MLB=\"00000000000GDVW00\";;\n        elcapitan|10.11)\n            BOARD_ID=\"Mac-FFE5EF870D7BA81A\"\n            MLB=\"00000000000GQRX00\";;\n        sierra|10.12)\n            BOARD_ID=\"Mac-77F17D7DA9285301\"\n            MLB=\"00000000000J0DX00\";;\n        high-sierra|10.13)\n            BOARD_ID=\"Mac-BE088AF8C5EB4FA2\"\n            MLB=\"00000000000J80300\";;\n        mojave|10.14)\n            BOARD_ID=\"Mac-7BA5B2DFE22DDD8C\"\n            MLB=\"00000000000KXPG00\";;\n        catalina|10.15)\n            BOARD_ID=\"Mac-00BE6ED71E35EB86\";;\n        big-sur|11)\n            BOARD_ID=\"Mac-2BD1B31983FE1663\";;\n        monterey|12)\n            BOARD_ID=\"Mac-B809C3757DA9BB8D\"\n            OS_TYPE=\"latest\";;\n        ventura|13)\n            BOARD_ID=\"Mac-4B682C642B45593E\"\n            OS_TYPE=\"latest\";;\n        sonoma|14)\n            BOARD_ID=\"Mac-827FAC58A8FDFA22\";;\n        sequoia|15)\n            BOARD_ID=\"Mac-7BA5B2D9E42DDD94\";;\n        tahoe|26)\n            BOARD_ID=\"Mac-CFF7D910A743CAAF\"\n            OS_TYPE=\"latest\";;\n        *) echo \"ERROR! Unknown release: ${RELEASE}\"\n           releases_macos\n           exit 1;;\n    esac\n\n    CWD=\"$(dirname \"${0}\")\"\n    if [ -x \"${CWD}/chunkcheck\" ]; then\n        CHUNKCHECK=\"${CWD}/chunkcheck\"\n    elif [ -x \"$(command -v chunkcheck)\" ]; then\n        CHUNKCHECK=\"$(command -v chunkcheck)\"\n    fi\n\n    appleSession=$(curl --disable -v -H \"Host: osrecovery.apple.com\" \\\n                           -H \"Connection: close\" \\\n                           -A \"InternetRecovery/1.0\" https://osrecovery.apple.com/ 2>&1 | tr ';' '\\n' | awk -F'session=|;' '{print $2}' | grep 1)\n    info=$(curl --disable -s -X POST -H \"Host: osrecovery.apple.com\" \\\n                           -H \"Connection: close\" \\\n                           -A \"InternetRecovery/1.0\" \\\n                           -b \"session=\\\"${appleSession}\\\"\" \\\n                           -H \"Content-Type: text/plain\" \\\n                           -d $'cid='\"$(generate_id 16)\"$'\\nsn='${MLB}$'\\nbid='${BOARD_ID}$'\\nk='\"$(generate_id 64)\"$'\\nfg='\"$(generate_id 64)\"$'\\nos='${OS_TYPE} \\\n                           https://osrecovery.apple.com/InstallationPayload/RecoveryImage | tr ' ' '\\n')\n    downloadLink=$(echo \"$info\" | grep 'oscdn' | grep 'dmg')\n    downloadSession=$(echo \"$info\" | grep 'expires' | grep 'dmg')\n    chunkListLink=$(echo \"$info\" | grep 'oscdn' | grep 'chunklist')\n    chunkListSession=$(echo \"$info\" | grep 'expires' | grep 'chunklist')\n\n    if [ \"${OPERATION}\" == \"show\" ]; then\n        test_result \"${OS}\" \"${RELEASE}\" \"\" \"${downloadLink}\"\n        exit 0\n    elif [ \"${OPERATION}\" == \"test\" ]; then\n        CHECK=$(web_check \"${downloadLink}\" --header \"Host: oscdn.apple.com\" --header \"Connection: close\" --header \"User-Agent: InternetRecovery/1.0\" --header \"Cookie: AssetToken=${downloadSession}\" && echo \"PASS\" || echo \"FAIL\")\n        test_result \"${OS}\" \"${RELEASE}\" \"\" \"${downloadLink}\" \"${CHECK}\"\n        exit 0\n    elif [ \"${OPERATION}\" == \"download\" ]; then\n        echo \"Downloading macOS (${RELEASE^}) RecoveryImage\"\n        echo \" - URL: ${downloadLink}\"\n        web_get \"${downloadLink}\" \"${VM_PATH}\" RecoveryImage.dmg --header \"Host: oscdn.apple.com\" --header \"Connection: close\" --header \"User-Agent: InternetRecovery/1.0\" --header \"Cookie: AssetToken=${downloadSession}\"\n        web_get \"${chunkListLink}\" \"${VM_PATH}\" RecoveryImage.chunklist --header \"Host: oscdn.apple.com\" --header \"Connection: close\" --header \"User-Agent: InternetRecovery/1.0\" --header \"Cookie: AssetToken=${chunkListSession}\"\n        VM_PATH=\"$(pwd)\"\n    else\n        if [ ! -e \"${VM_PATH}/RecoveryImage.chunklist\" ]; then\n            echo \"Downloading macOS (${RELEASE^}) RecoveryImage\"\n            echo \" - URL: ${downloadLink}\"\n            web_get \"${downloadLink}\" \"${VM_PATH}\" RecoveryImage.dmg --header \"Host: oscdn.apple.com\" --header \"Connection: close\" --header \"User-Agent: InternetRecovery/1.0\" --header \"Cookie: AssetToken=${downloadSession}\"\n            web_get \"${chunkListLink}\" \"${VM_PATH}\" RecoveryImage.chunklist --header \"Host: oscdn.apple.com\" --header \"Connection: close\" --header \"User-Agent: InternetRecovery/1.0\" --header \"Cookie: AssetToken=${chunkListSession}\"\n            if ! \"${CHUNKCHECK}\" \"${VM_PATH}\" 2> /dev/null; then\n                echo \" - WARNING! Verification failed, continuing anyway\"\n            else\n                echo \" - Verification passed\"\n            fi\n\n            if [ -e \"${VM_PATH}/RecoveryImage.dmg\" ] && [ ! -e \"${VM_PATH}/RecoveryImage.img\" ]; then\n                require_qemu_img\n                echo \" - Converting RecoveryImage.dmg\"\n                ${QEMU_IMG} convert \"${VM_PATH}/RecoveryImage.dmg\" -O raw \"${VM_PATH}/RecoveryImage.img\" 2>/dev/null\n            fi\n            rm \"${VM_PATH}/RecoveryImage.dmg\" \"${VM_PATH}/RecoveryImage.chunklist\"\n            echo \" - RecoveryImage.img is ready.\"\n        fi\n\n        echo \"Downloading OpenCore & UEFI firmware\"\n        # Pin to last known-good commit; OVMF_CODE.fd was removed from master on 26 Jan 2026\n        local OSX_KVM_COMMIT=\"da4b23b5e92c5b939568700034367e8b7649fe90\"\n        web_get \"https://github.com/kholia/OSX-KVM/raw/${OSX_KVM_COMMIT}/OpenCore/OpenCore.qcow2\" \"${VM_PATH}\"\n        web_get \"https://github.com/kholia/OSX-KVM/raw/${OSX_KVM_COMMIT}/OVMF_CODE.fd\" \"${VM_PATH}\"\n        if [ ! -e \"${VM_PATH}/OVMF_VARS-1920x1080.fd\" ]; then\n            web_get \"https://github.com/kholia/OSX-KVM/raw/${OSX_KVM_COMMIT}/OVMF_VARS-1920x1080.fd\" \"${VM_PATH}\"\n        fi\n    fi\n    make_vm_config RecoveryImage.img\n}\n\nfunction get_mageia() {\n    local HASH=\"\"\n    local ISO=\"\"\n    ISO=$(web_pipe https://www.mageia.org/en/downloads/get/?q=\"Mageia-${RELEASE}-Live-${EDITION}-x86_64.iso\" | grep 'click here'| grep -o 'href=.*\\.iso'|cut -d\\\" -f2)\n    HASH=$(web_pipe \"${ISO}.sha512\" | cut -d' ' -f1)\n    echo \"${ISO} ${HASH}\"\n}\n\nfunction get_manjaro() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local MANIFEST=\"\"\n    local URL=\"\"\n    local TYPE=\"official\"\n    MANIFEST=\"$(web_pipe https://gitlab.manjaro.org/web/iso-info/-/raw/master/file-info.json)\"\n    case \"${RELEASE}\" in\n        sway)\n            MANIFEST=\"$(web_pipe https://mirror.manjaro-sway.download/manjaro-sway/release.json)\"\n            TYPE=\"sway\"\n            ;;\n        cinnamon|i3) TYPE=\"community\";;\n    esac\n\n    if [ \"${EDITION}\" == \"minimal\" ] && [ \"${TYPE}\" != \"sway\" ]; then\n        EDITION=\".minimal\"\n    else\n        EDITION=\"\"\n    fi\n\n    if [ \"${RELEASE}\" == \"sway\" ]; then\n        URL=$(echo \"${MANIFEST}\" | jq -r '.[] | select(.name|test(\"^manjaro-sway-.*[.]iso$\")) | .url')\n    else\n        URL=\"$(echo \"${MANIFEST}\" | jq -r .\"${TYPE}.${RELEASE}${EDITION}\".image)\"\n    fi\n    HASH=$(web_pipe \"${URL}.sha512\" | cut -d' ' -f1)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_mxlinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://sourceforge.net/projects/mx-linux/files/Final/${EDITION}\"\n    case ${EDITION} in\n        Xfce) ISO=\"MX-${RELEASE}_Xfce_x64.iso\";;\n        KDE) ISO=\"MX-${RELEASE}_KDE_x64.iso\";;\n        Fluxbox) ISO=\"MX-${RELEASE}_fluxbox_x64.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_netboot() {\n    local HASH=\"\"\n    local ISO=\"netboot.xyz.iso\"\n    local URL=\"https://boot.netboot.xyz/ipxe\"\n    HASH=$(web_pipe \"${URL}/netboot.xyz-sha256-checksums.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_netbsd() {\n    local HASH=\"\"\n    local ISO=\"NetBSD-${RELEASE}-amd64.iso\"\n    local URL=\"https://cdn.netbsd.org/pub/NetBSD/NetBSD-${RELEASE}/images\"\n    HASH=$(web_pipe \"${URL}/MD5\" | grep \"${ISO}\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_nitrux() {\n    local HASH=\"\"\n    local URLBASE=\"\"\n    local URL=\"\"\n    local ISO=\"\"\n    URLBASE=\"https://sourceforge.net/projects/nitruxos/files/Release\"\n    URL=\"${URLBASE}/ISO\"\n    ISO=$(web_pipe 'https://sourceforge.net/projects/nitruxos/rss?path=/Release/ISO' | grep '.iso' | head -n 1 | cut -d']' -f1 | cut -d '/' -f4)\n    HASH=$(web_pipe \"${URLBASE}/MD5/${ISONAME}.md5sum\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_nixos() {\n    local HASH=\"\"\n    local ISO=\"latest-nixos-${EDITION}-x86_64-linux.iso\"\n    local URL=\"https://channels.nixos.org/nixos-${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_nwg-shell() {\n    local HASH=\"\"\n    local ISO=\"nwg-live-${RELEASE}-x86_64.iso\"\n    local URL=\"https://sourceforge.net/projects/nwg-iso/files\"\n    HASH=\"$(web_pipe \"https://sourceforge.net/projects/nwg-iso/rss?path=/\" | grep \"${ISO}\" | cut -d'>' -f3 | cut -d'<' -f1 | tail -n 1)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_openbsd() {\n    local HASH=\"\"\n    local ISO=\"install${RELEASE//\\./}.iso\"\n    local URL=\"https://mirror.leaseweb.com/pub/OpenBSD/${RELEASE}/amd64\"\n    HASH=$(web_pipe \"${URL}/SHA256\" | grep \"${ISO}\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_openindiana() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    URL=\"https://dlc.openindiana.org/isos/hipster/${RELEASE}\"\n    ISO=\"OI-hipster-${EDITION}-${RELEASE}.iso\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" |cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_opensuse() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    if [ \"${RELEASE}\" == \"tumbleweed\" ]; then\n        ISO=\"openSUSE-Tumbleweed-DVD-x86_64-Current.iso\"\n        URL=\"https://download.opensuse.org/tumbleweed/iso\"\n    elif [ \"${RELEASE}\" == \"microos\" ]; then\n        ISO=\"openSUSE-MicroOS-DVD-x86_64-Current.iso\"\n        URL=\"https://download.opensuse.org/tumbleweed/iso\"\n    elif [ \"${RELEASE}\" == 15.0 ] || [ \"${RELEASE}\" == 15.1 ]; then\n        ISO=\"openSUSE-Leap-${RELEASE}-DVD-x86_64.iso\"\n        URL=\"https://download.opensuse.org/distribution/leap/${RELEASE}/iso\"\n    elif [ \"${RELEASE}\" == 16.0 ]; then\n        ISO=\"Leap-${RELEASE}-offline-installer-x86_64.install.iso\"\n        URL=\"https://download.opensuse.org/distribution/leap/${RELEASE}/offline\"\n    else\n        ISO=\"openSUSE-Leap-${RELEASE}-DVD-x86_64-Current.iso\"\n        URL=\"https://download.opensuse.org/distribution/leap/${RELEASE}/iso\"\n    fi\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | awk '{if(NR==4) print $0}' | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_oraclelinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local VER_MAJ=${RELEASE::1}\n    local VER_MIN=${RELEASE:2:1}\n    local URL=\"https://yum.oracle.com/ISOS/OracleLinux/OL${VER_MAJ}/u${VER_MIN}/x86_64\"\n    case ${VER_MAJ} in\n        7) ISO=\"OracleLinux-R${VER_MAJ}-U${VER_MIN}-Server-x86_64-dvd.iso\";;\n        *) ISO=\"OracleLinux-R${VER_MAJ}-U${VER_MIN}-x86_64-dvd.iso\";;\n    esac\n    HASH=$(web_pipe \"https://linux.oracle.com/security/gpg/checksum/OracleLinux-R${VER_MAJ}-U${VER_MIN}-Server-x86_64.checksum\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_parrotsec() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    ISO=\"Parrot-${EDITION}-${RELEASE}_amd64.iso\"\n    URL=\"https://download.parrot.sh/parrot/iso/${RELEASE}\"\n    HASH=\"$(web_pipe \"${URL}/signed-hashes.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_pclinuxos() {\n    local URL=\"https://ftp.fau.de/pclinuxos/pclinuxos/iso\"\n    local HASH=\"\"\n    local ISO=\"\"\n    # Find the latest ISO for this edition dynamically\n    ISO=$(web_pipe \"${URL}/\" | grep -o \"pclinuxos64-${EDITION}-[0-9.]\\+\\.iso\" | sort -Vr | head -n 1)\n    if [ -z \"${ISO}\" ]; then\n        echo \"\"\n        return\n    fi\n    HASH=$(web_pipe \"${URL}/${ISO}.md5sum\" | head -c 32)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_peppermint() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://sourceforge.net/projects/peppermintos/files/isos\"\n    case ${EDITION} in\n        devuan-xfce)\n            ISO=\"PeppermintOS-devuan_64_xfce.iso\"\n            URL=\"${URL}/XFCE\";;\n        debian-xfce)\n            ISO=\"PeppermintOS-Debian-64.iso\"\n            URL=\"${URL}/XFCE\";;\n        devuan-gnome)\n            ISO=\"PeppermintOS-devuan_64_gfb.iso\"\n            URL=\"${URL}/Gnome_FlashBack\";;\n        debian-gnome)\n            ISO=\"PeppermintOS-Debian_64_gfb.iso\"\n            URL=\"${URL}/Gnome_FlashBack\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}-sha512.checksum\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_popos() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    URL=$(web_pipe \"https://api.pop-os.org/builds/${RELEASE}/${EDITION}\" | jq -r .url)\n    HASH=$(web_pipe \"https://api.pop-os.org/builds/${RELEASE}/${EDITION}\" | jq -r .sha_sum)\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_porteus() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    edition=\"${EDITION~~}\"\n    ISO=\"Porteus-${edition}-v${RELEASE}-x86_64.iso\"\n    URL=\"https://mirrors.dotsrc.org/porteus/x86_64/Porteus-v${RELEASE}\"\n    HASH=$(web_pipe \"${URL}/sha256sums.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_primtux() {\n    local HASH=\"\"\n    local URL=\"\"\n    local ISO=\"\"\n    ISO=\"PrimTux${RELEASE}-amd64-${EDITION}.iso\"\n    URL=\"https://sourceforge.net/projects/primtux/files/Distribution\"\n    HASH=$(web_pipe \"${URL}/${ISO}.md5\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_proxmox-ve() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    ISO=\"proxmox-ve_${RELEASE}.iso\"\n    URL=\"https://enterprise.proxmox.com/iso\"\n    HASH=$(web_pipe \"${URL}/SHA256SUMS\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n\n}\n\nfunction get_pureos() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    local PureName=\n    PureName=\"$(web_pipe \"https://www.pureos.net/download/\" | grep -m 1 \"downloads.puri\" | cut -d '/' -f 4)\"\n    local PureDate=\n    PureDate=\"$(web_pipe \"https://www.pureos.net/download/\" | grep -m 1 \"downloads.puri\" | cut -d '/' -f 6)\"\n    local PureDateSquashed=\"${PureDate//'-'/}\"\n    edition=\"${EDITION,,}\"\n    URL=\"https://downloads.puri.sm/${PureName}/${edition}/${PureDate}\"\n    ISO=\"pureos-${RELEASE}-${edition}-live-${PureDateSquashed}_amd64.iso\"\n    local IsoTrimmed=\n    IsoTrimmed=\"${ISO%.*}\"\n    HASH=\"$(web_pipe \"${URL}/${IsoTrimmed}.checksums_sha256.txt\" | grep -m 1 '.iso' | cut -d '.' -f 1)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_reactos() {\n    local HASH=\"\"\n    local URL=\"\"\n    URL=\"$(web_redirect \"https://sourceforge.net/projects/reactos/files/latest/download\")\"\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_rebornos() {\n    local HASH=\"\"\n    local ISO=\"\"\n    ISO=$(web_pipe \"https://meta.cdn.soulharsh007.dev/RebornOS-ISO?format=json\" | jq -r \".url\")\n    HASH=$(web_pipe \"https://meta.cdn.soulharsh007.dev/RebornOS-ISO?format=json\" | jq -r \".md5\")\n    echo \"${ISO} ${HASH}\"\n}\n\nfunction get_rockylinux() {\n    if { [[ \"${RELEASE}\" =~ ^8. ]] || [[ \"${RELEASE}\" =~ ^10. ]]; } && [[ \"${EDITION}\" == \"dvd\" ]]; then\n        EDITION=\"dvd1\"\n    fi\n    local HASH=\"\"\n    local ISO=\"Rocky-${RELEASE}-x86_64-${EDITION}.iso\"\n    local URL=\"https://dl.rockylinux.org/vault/rocky/${RELEASE}/isos/x86_64\"\n    HASH=$(web_pipe \"${URL}/CHECKSUM\" | grep \"SHA256\" | grep \"${ISO})\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_siduction() {\n    local HASH=\"\"\n    local DATE=\"\"\n    local ISO=\"\"\n    local NAME=\"\"\n    local URL=\"\"\n    NAME=$(web_pipe \"https://mirror.math.princeton.edu/pub/siduction/iso/\" | grep folder | cut -d'\"' -f8 | tr -d '/')\n    URL=\"https://mirrors.dotsrc.org/siduction/iso/${NAME}/${EDITION}\"\n    DATE=$(web_pipe \"${URL}\"| grep .iso.md5 | cut -d'-' -f6 | cut -d'.' -f1)\n    HASH=$(web_pipe \"${URL}/${ISO}.md5\" | cut -d' ' -f1)\n    VERSION=$(web_pipe \"${URL}\"| grep .iso.md5 | cut -d'-' -f2)\n    ISO=\"siduction-${VERSION}-${NAME}-${EDITION}-amd64-${DATE}.iso\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_slackware() {\n    local HASH=\"\"\n    local ISO=\"slackware64-${RELEASE}-install-dvd.iso\"\n    local URL=\"https://slackware.nl/slackware/slackware-iso/slackware64-${RELEASE}-iso\"\n    HASH=$(web_pipe \"${URL}/${ISO}.md5\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_slax() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    case ${EDITION} in\n        debian)\n            URL=\"https://ftp.fi.muni.cz/pub/linux/slax/Slax-12.x\"\n            ISO=$(web_pipe \"${URL}/md5.txt\" | grep '64bit-' | cut -d' ' -f3 | tail -n1);;\n        slackware)\n            URL=\"https://ftp.fi.muni.cz/pub/linux/slax/Slax-15.x\"\n            ISO=$(web_pipe \"${URL}/md5.txt\" | grep '64bit-' | cut -d' ' -f3 | tail -n1);;\n    esac\n    HASH=$(web_pipe \"${URL}/md5.txt\" | grep '64bit-' | cut -d' ' -f1 | tail -n1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_slint() {\n    local HASH=\"\"\n    local MAJ_VER=\"\"\n    local ISO=\"slint64-${RELEASE}.iso\"\n    MAJ_VER=\"$(echo \"${RELEASE}\" | cut -d'-' -f 1)\"\n    local URL=\"https://slackware.uk/slint/x86_64/slint-${MAJ_VER}/iso\"\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256\" | cut -d' ' -f4)\n    echo \"${URL}/${ISO}\" \"${HASH}\"\n}\n\nfunction get_slitaz() {\n    local HASH=\"\"\n    local ISO=\"slitaz-rolling-${RELEASE}\"\n    local URL=\"http://mirror.slitaz.org/iso/rolling\"\n    HASH=$(web_pipe \"${URL}/${ISO}.md5\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO}.iso ${HASH}\"\n}\n\nfunction get_solus() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://downloads.getsol.us/isos/${RELEASE}\"\n    # Try standard naming first, then beta naming for Xfce (older releases had Xfce as beta)\n    ISO=\"Solus-${EDITION}-Release-${RELEASE}.iso\"\n    if ! web_check \"${URL}/${ISO}\"; then\n        # Try uppercase XFCE-Beta naming for older releases\n        local EDITION_UPPER=\"${EDITION^^}\"\n        ISO=\"Solus-${EDITION_UPPER}-Beta-Release-${RELEASE}.iso\"\n    fi\n    HASH=$(web_pipe \"${URL}/${ISO}.sha256sum\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_sparkylinux() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"\"\n    ISO=\"sparkylinux-${RELEASE}-x86_64-${EDITION}.iso\"\n    case ${EDITION} in\n        minimalcli) URL=\"https://sourceforge.net/projects/sparkylinux/files/cli\";;\n        minimalgui) URL=\"https://sourceforge.net/projects/sparkylinux/files/base\";;\n        *) URL=\"https://sourceforge.net/projects/sparkylinux/files/${EDITION}\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.allsums.txt\" | head -n 2 | grep 'iso' | cut -d' ' -f1)\n    echo \"${URL}/${ISO}\" \"${HASH}\"\n}\n\nfunction get_spirallinux() {\n    local HASH=\"\"\n    local ISO=\"SpiralLinux_${EDITION}_12.231005_x86-64.iso\"\n    local URL=\"https://sourceforge.net/projects/spirallinux/files/12.231005\"\n    HASH=$(web_pipe 'https://sourceforge.net/projects/spirallinux/rss?path=/' | grep \"${ISO}\" | grep 'md5' | cut -d'<' -f3 | cut -d'>' -f2)\n    echo \"${URL}/${ISO}\" \"${HASH}\"\n}\n\nfunction get_tails() {\n    local JSON=\"\"\n    local HASH=\"\"\n    local URL=\"\"\n    JSON=\"$(web_pipe \"https://tails.boum.org/install/v2/Tails/amd64/${RELEASE}/latest.json\")\"\n    URL=$(echo \"${JSON}\" | jq -r '.installations[0].\"installation-paths\"[]|select(.type==\"iso\")|.\"target-files\"[0].url')\n    HASH=$(echo \"${JSON}\" | jq -r '.installations[0].\"installation-paths\"[]|select(.type==\"iso\")|.\"target-files\"[0].sha256')\n    echo \"${URL} ${HASH}\"\n}\n\nfunction get_tinycore() {\n    local ARCH=\"x86\"\n    local HASH=\"\"\n    local ISO=\"${EDITION}-${RELEASE}.0.iso\"\n    case \"${EDITION}\" in\n        *Pure*) ARCH+=\"_64\";;\n    esac\n    local URL=\"http://www.tinycorelinux.net/${RELEASE}.x/${ARCH}/release\"\n    HASH=$(web_pipe \"${URL}/${ISO}.md5.txt\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_trisquel() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://mirrors.ocf.berkeley.edu/trisquel-images\"\n    case ${EDITION} in\n        mate) ISO=\"trisquel_${RELEASE}_amd64.iso\";;\n        lxde) ISO=\"trisquel-mini_${RELEASE}_amd64.iso\";;\n        kde) ISO=\"triskel_${RELEASE}_amd64.iso\";;\n        sugar) ISO=\"trisquel-sugar_${RELEASE}_amd64.iso\";;\n    esac\n    HASH=$(web_pipe \"${URL}/${ISO}.sha1\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_tuxedo-os() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://os.tuxedocomputers.com\"\n    ISO=\"$(web_pipe \"https://os.tuxedocomputers.com/\" | grep -m 1 current.iso | cut -d '=' -f 4 | cut -d '\"' -f 2)\"\n    HASH=\"$(web_pipe \"https://os.tuxedocomputers.com/checksums/${ISO}.sha256\" | cut -d ' ' -f 1)\"\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction get_ubuntu-server() {\n    local DATA=\"\"\n    local HASH=\"\"\n    local ISO=\"\"\n    local NAME=\"live-server\"\n    local UBUNTU_ARCH=\"${ARCH}\"\n    local URL=\"\"\n\n    if [[ \"${RELEASE}\" == \"daily\"* ]]; then\n        URL=\"https://cdimage.ubuntu.com/${OS}/${RELEASE}/current\"\n    elif [ \"${UBUNTU_ARCH}\" == \"arm64\" ]; then\n        # ARM64 ISOs are hosted on cdimage.ubuntu.com\n        URL=\"https://cdimage.ubuntu.com/releases/${RELEASE}/release\"\n    else\n        URL=\"https://releases.ubuntu.com/${RELEASE}\"\n    fi\n\n    case \"${RELEASE}\" in\n        14*|16*) NAME=\"server\";;\n    esac\n\n    if web_check \"${URL}/SHA256SUMS\"; then\n        DATA=$(web_pipe \"${URL}/SHA256SUMS\" | grep \"${NAME}\" | grep \"${UBUNTU_ARCH}\" | grep iso | tail -n 1 )\n        ISO=$(cut -d'*' -f2 <<<\"${DATA}\")\n        HASH=$(cut -d' ' -f1 <<<\"${DATA}\")\n    else\n        DATA=$(web_pipe \"${URL}/MD5SUMS\" | grep \"${NAME}\" | grep \"${UBUNTU_ARCH}\" | grep iso | tail -n 1 )\n        ISO=$(cut -d' ' -f3 <<<\"${DATA}\")\n        HASH=$(cut -d' ' -f1 <<<\"${DATA}\")\n    fi\n    if [[ \"${RELEASE}\" == \"daily\"* ]] || [ \"${RELEASE}\" == \"dvd\" ]; then\n        zsync_get \"${URL}/${ISO}\" \"${VM_PATH}\" \"${OS}-devel.iso\"\n        make_vm_config \"${OS}-devel.iso\"\n    else\n        web_get \"${URL}/${ISO}\" \"${VM_PATH}\"\n        check_hash \"${ISO}\" \"${HASH}\"\n        make_vm_config \"${ISO}\"\n    fi\n}\n\nfunction get_ubuntu() {\n    local DATA=\"\"\n    local HASH=\"\"\n    local ISO=\"\"\n    local UBUNTU_ARCH=\"${ARCH}\"\n    local URL=\"\"\n\n    # Validate architecture support now that RELEASE is known\n    if ! is_arch_supported \"${OS}\" \"${UBUNTU_ARCH}\"; then\n        if [ \"${OPERATION}\" == \"test\" ] || [ \"${OPERATION}\" == \"show\" ]; then\n            test_result \"${OS}\" \"${RELEASE}\" \"\" \"\" \"SKIP\" \"(not available for ${UBUNTU_ARCH})\"\n            exit 0\n        else\n            echo \"ERROR! $(pretty_name \"${OS}\") ${RELEASE} is not available for ${UBUNTU_ARCH} architecture.\"\n            exit 1\n        fi\n    fi\n\n    if [[ \"${RELEASE}\" == \"daily\"* ]] && [ \"${OS}\" == \"ubuntustudio\" ]; then\n        # Ubuntu Studio daily-live images are in the dvd directory\n        RELEASE=\"dvd\"\n    fi\n    if [[ \"${RELEASE}\" == \"jammy-daily\" ]]; then\n        if [[ \"${OS}\" == \"ubuntustudio\" ]]; then\n            URL=\"https://cdimage.ubuntu.com/${OS}/jammy/dvd/current\"\n        else\n            URL=\"https://cdimage.ubuntu.com/${OS}/jammy/daily-live/current\"\n        fi\n        VM_PATH=\"${OS}-jammy-live$(arch_suffix)\"\n    elif [[ \"${RELEASE}\" == \"daily\"* ]] || [ \"${RELEASE}\" == \"dvd\" ]; then\n        URL=\"https://cdimage.ubuntu.com/${OS}/${RELEASE}/current\"\n        VM_PATH=\"${OS}-${RELEASE}$(arch_suffix)\"\n    elif [ \"${OS}\" == \"ubuntu\" ] && [ \"${UBUNTU_ARCH}\" == \"arm64\" ]; then\n        # ARM64 desktop ISOs are hosted on cdimage.ubuntu.com\n        URL=\"https://cdimage.ubuntu.com/releases/${RELEASE}/release\"\n    elif [ \"${OS}\" == \"ubuntu\" ]; then\n        URL=\"https://releases.ubuntu.com/${RELEASE}\"\n    else\n        URL=\"https://cdimage.ubuntu.com/${OS}/releases/${RELEASE}/release\"\n    fi\n    if web_check \"${URL}/SHA256SUMS\"; then\n        DATA=$(web_pipe \"${URL}/SHA256SUMS\" | grep 'desktop\\|dvd\\|install' | grep \"${UBUNTU_ARCH}\" | grep iso | grep -v \"+mac\" | tail -n 1 )\n        ISO=$(cut -d'*' -f2 <<<\"${DATA}\" | sed '1q;d')\n        HASH=$(cut -d' ' -f1 <<<\"${DATA}\" | sed '1q;d')\n    else\n        DATA=$(web_pipe \"${URL}/MD5SUMS\" | grep 'desktop\\|dvd\\|install' | grep \"${UBUNTU_ARCH}\" | grep iso | grep -v \"+mac\" | tail -n 1 )\n        ISO=$(cut -d'*' -f2 <<<\"${DATA}\")\n        HASH=$(cut -d' ' -f1 <<<\"${DATA}\")\n    fi\n    if [ -z \"${ISO}\" ] || [ -z \"${HASH}\" ]; then\n        echo \"$(pretty_name \"${OS}\") ${RELEASE} is currently unavailable. Please select other OS/Release combination\"\n        exit 1\n    fi\n    if [[ \"${RELEASE}\" == \"daily\"* ]] || [ \"${RELEASE}\" == \"dvd\" ]; then\n        zsync_get \"${URL}/${ISO}\" \"${VM_PATH}\" \"${OS}-devel.iso\"\n        make_vm_config \"${OS}-devel.iso\"\n    elif [[ \"${RELEASE}\" == \"jammy-daily\" ]]; then\n        zsync_get \"${URL}/${ISO}\" \"${VM_PATH}\" \"${OS}-jammy-live.iso\"\n        make_vm_config \"${OS}-jammy-live.iso\"\n    else\n        web_get \"${URL}/${ISO}\" \"${VM_PATH}\"\n        check_hash \"${ISO}\" \"${HASH}\"\n        make_vm_config \"${ISO}\"\n    fi\n}\n\nfunction get_vanillaos() {\n    local HASH=\"\"\n    local HASH_URL=\"\"\n    local ISO=\"\"\n    ISO=$(web_pipe \"https://api.github.com/repos/Vanilla-OS/live-iso/releases\" | grep 'download_url' | grep \"${RELEASE}\" | head -n 1 | cut -d'\"' -f4)\n    HASH_URL=\"${ISO//.iso/.sha256.txt}\"\n    HASH=$(web_pipe \"${HASH_URL}\" | cut -d' ' -f1)\n    echo \"${ISO} ${HASH}\"\n}\n\nfunction get_void() {\n    local DATE=\"\"\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://repo-default.voidlinux.org/live\"\n    case ${EDITION} in\n        glibc) ISO=\"void-live-x86_64-${RELEASE}-base.iso\";;\n        musl) ISO=\"void-live-x86_64-musl-${RELEASE}-base.iso\";;\n        xfce-glibc) ISO=\"void-live-x86_64-${RELEASE}-xfce.iso\";;\n        xfce-musl) ISO=\"void-live-x86_64-musl-${RELEASE}-xfce.iso\";;\n    esac\n    HASH=\"$(web_pipe \"${URL}/sha256sum.txt\" | grep \"${ISO}\" | cut -d' ' -f4)\"\n    echo \"${URL}/${RELEASE}/${ISO} ${HASH}\"\n}\n\nfunction get_zorin() {\n    local HASH=\"\"\n    local ISO=\"\"\n    local URL=\"https://plug-mirror.rcac.purdue.edu/zorin-iso/${RELEASE}\"\n    local EDITION_NAME=\"\"\n    # Convert edition code to ISO naming\n    case ${EDITION} in\n        core64) EDITION_NAME=\"Core-64-bit\";;\n        lite64) EDITION_NAME=\"Lite-64-bit\";;\n        education64) EDITION_NAME=\"Education-64-bit\";;\n    esac\n    # Find the latest revision (r2, r1, or base)\n    ISO=$(web_pipe \"${URL}/\" | grep -o \"Zorin-OS-[0-9.]*-${EDITION_NAME}[^\\\"]*\\.iso\" | grep -v Beta | sort -Vr | head -n 1)\n    if [ -z \"${ISO}\" ]; then\n        echo \"\"\n        return\n    fi\n    HASH=$(web_pipe \"${URL}/SHA256SUMS.txt\" | grep \"${ISO}\" | cut -d' ' -f1)\n    echo \"${URL}/${ISO} ${HASH}\"\n}\n\nfunction unattended_windows() {\n    mkdir -p \"${1}/unattended\" 2>/dev/null\n\n    cat << 'EOF' > \"${1}/unattended/autounattend.xml\"\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<unattend xmlns=\"urn:schemas-microsoft-com:unattend\"\n  xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n  <!--\n       For documentation on components:\n       https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/\n  -->\n  <settings pass=\"offlineServicing\">\n    <component name=\"Microsoft-Windows-LUA-Settings\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <EnableLUA>false</EnableLUA>\n    </component>\n    <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <ComputerName>*</ComputerName>\n    </component>\n  </settings>\n\n  <settings pass=\"generalize\">\n    <component name=\"Microsoft-Windows-PnPSysprep\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>\n    </component>\n    <component name=\"Microsoft-Windows-Security-SPP\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <SkipRearm>1</SkipRearm>\n    </component>\n  </settings>\n\n  <settings pass=\"specialize\">\n    <component name=\"Microsoft-Windows-Security-SPP-UX\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <SkipAutoActivation>true</SkipAutoActivation>\n    </component>\n    <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <ComputerName>*</ComputerName>\n      <OEMInformation>\n        <Manufacturer>Quickemu Project</Manufacturer>\n        <Model>Quickemu</Model>\n        <SupportHours>24/7</SupportHours>\n        <SupportPhone></SupportPhone>\n        <SupportProvider>Quickemu Project</SupportProvider>\n        <SupportURL>https://github.com/quickemu-project/quickemu/issues</SupportURL>\n      </OEMInformation>\n      <OEMName>Quickemu Project</OEMName>\n      <ProductKey>W269N-WFGWX-YVC9B-4J6C9-T83GX</ProductKey>\n    </component>\n    <component name=\"Microsoft-Windows-SQMApi\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <CEIPEnabled>0</CEIPEnabled>\n    </component>\n  </settings>\n\n  <settings pass=\"windowsPE\">\n    <component name=\"Microsoft-Windows-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <Diagnostics>\n        <OptIn>false</OptIn>\n      </Diagnostics>\n      <DiskConfiguration>\n        <Disk wcm:action=\"add\">\n          <DiskID>0</DiskID>\n          <WillWipeDisk>true</WillWipeDisk>\n          <CreatePartitions>\n            <!-- Windows RE Tools partition -->\n            <CreatePartition wcm:action=\"add\">\n              <Order>1</Order>\n              <Type>Primary</Type>\n              <Size>256</Size>\n            </CreatePartition>\n            <!-- System partition (ESP) -->\n            <CreatePartition wcm:action=\"add\">\n              <Order>2</Order>\n              <Type>EFI</Type>\n              <Size>128</Size>\n            </CreatePartition>\n            <!-- Microsoft reserved partition (MSR) -->\n            <CreatePartition wcm:action=\"add\">\n              <Order>3</Order>\n              <Type>MSR</Type>\n              <Size>128</Size>\n            </CreatePartition>\n            <!-- Windows partition -->\n            <CreatePartition wcm:action=\"add\">\n              <Order>4</Order>\n              <Type>Primary</Type>\n              <Extend>true</Extend>\n            </CreatePartition>\n          </CreatePartitions>\n          <ModifyPartitions>\n            <!-- Windows RE Tools partition -->\n            <ModifyPartition wcm:action=\"add\">\n              <Order>1</Order>\n              <PartitionID>1</PartitionID>\n              <Label>WINRE</Label>\n              <Format>NTFS</Format>\n              <TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID>\n            </ModifyPartition>\n            <!-- System partition (ESP) -->\n            <ModifyPartition wcm:action=\"add\">\n              <Order>2</Order>\n              <PartitionID>2</PartitionID>\n              <Label>System</Label>\n              <Format>FAT32</Format>\n            </ModifyPartition>\n            <!-- MSR partition does not need to be modified -->\n            <ModifyPartition wcm:action=\"add\">\n              <Order>3</Order>\n              <PartitionID>3</PartitionID>\n            </ModifyPartition>\n            <!-- Windows partition -->\n              <ModifyPartition wcm:action=\"add\">\n              <Order>4</Order>\n              <PartitionID>4</PartitionID>\n              <Label>Windows</Label>\n              <Letter>C</Letter>\n              <Format>NTFS</Format>\n            </ModifyPartition>\n          </ModifyPartitions>\n        </Disk>\n      </DiskConfiguration>\n      <DynamicUpdate>\n        <Enable>true</Enable>\n        <WillShowUI>Never</WillShowUI>\n      </DynamicUpdate>\n      <ImageInstall>\n        <OSImage>\n          <InstallTo>\n            <DiskID>0</DiskID>\n            <PartitionID>4</PartitionID>\n          </InstallTo>\n          <InstallToAvailablePartition>false</InstallToAvailablePartition>\n        </OSImage>\n      </ImageInstall>\n      <RunSynchronous>\n        <RunSynchronousCommand wcm:action=\"add\">\n          <Order>1</Order>\n          <Path>reg add HKLM\\System\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 0x00000001 /f</Path>\n        </RunSynchronousCommand>\n        <RunSynchronousCommand wcm:action=\"add\">\n          <Order>2</Order>\n          <Path>reg add HKLM\\System\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 0x00000001 /f</Path>\n        </RunSynchronousCommand>\n        <RunSynchronousCommand wcm:action=\"add\">\n          <Order>3</Order>\n          <Path>reg add HKLM\\System\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 0x00000001 /f</Path>\n        </RunSynchronousCommand>\n        <RunSynchronousCommand wcm:action=\"add\">\n          <Order>4</Order>\n          <Path>reg add HKLM\\System\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 0x00000001 /f</Path>\n        </RunSynchronousCommand>\n      </RunSynchronous>\n      <UpgradeData>\n        <Upgrade>false</Upgrade>\n        <WillShowUI>Never</WillShowUI>\n      </UpgradeData>\n      <UserData>\n        <AcceptEula>true</AcceptEula>\n        <FullName>Quickemu</FullName>\n        <Organization>Quickemu Project</Organization>\n        <!-- https://docs.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys -->\n        <ProductKey>\n          <Key>W269N-WFGWX-YVC9B-4J6C9-T83GX</Key>\n          <WillShowUI>Never</WillShowUI>\n        </ProductKey>\n      </UserData>\n    </component>\n\n    <component name=\"Microsoft-Windows-PnpCustomizationsWinPE\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" processorArchitecture=\"amd64\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <!--\n           This makes the VirtIO drivers available to Windows, assuming that\n           the VirtIO driver disk is available as drive E:\n           https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md\n      -->\n      <DriverPaths>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"1\">\n          <Path>E:\\qemufwcfg\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"2\">\n          <Path>E:\\vioinput\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"3\">\n          <Path>E:\\vioscsi\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"4\">\n          <Path>E:\\viostor\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"5\">\n          <Path>E:\\vioserial\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"6\">\n          <Path>E:\\qxldod\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"7\">\n          <Path>E:\\viorng\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"8\">\n          <Path>E:\\NetKVM\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"9\">\n          <Path>E:\\viofs\\w10\\amd64</Path>\n        </PathAndCredentials>\n        <PathAndCredentials wcm:action=\"add\" wcm:keyValue=\"10\">\n          <Path>E:\\Balloon\\w10\\amd64</Path>\n        </PathAndCredentials>\n      </DriverPaths>\n    </component>\n  </settings>\n\n  <settings pass=\"oobeSystem\">\n    <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n      <AutoLogon>\n        <Password>\n          <Value>quickemu</Value>\n          <PlainText>true</PlainText>\n        </Password>\n        <Enabled>true</Enabled>\n        <Username>Quickemu</Username>\n      </AutoLogon>\n      <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet>\n      <OOBE>\n        <HideEULAPage>true</HideEULAPage>\n        <HideLocalAccountScreen>true</HideLocalAccountScreen>\n        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>\n        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>\n        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>\n        <NetworkLocation>Home</NetworkLocation>\n        <ProtectYourPC>3</ProtectYourPC>\n        <SkipUserOOBE>true</SkipUserOOBE>\n        <SkipMachineOOBE>true</SkipMachineOOBE>\n        <VMModeOptimizations>\n          <SkipWinREInitialization>true</SkipWinREInitialization>\n        </VMModeOptimizations>\n      </OOBE>\n      <UserAccounts>\n        <LocalAccounts>\n          <LocalAccount wcm:action=\"add\">\n            <Password>\n              <Value>quickemu</Value>\n              <PlainText>true</PlainText>\n            </Password>\n            <Description>Quickemu</Description>\n            <DisplayName>Quickemu</DisplayName>\n            <Group>Administrators</Group>\n            <Name>Quickemu</Name>\n          </LocalAccount>\n        </LocalAccounts>\n      </UserAccounts>\n      <RegisteredOrganization>Quickemu Project</RegisteredOrganization>\n      <RegisteredOwner>Quickemu</RegisteredOwner>\n      <FirstLogonCommands>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>msiexec /i E:\\guest-agent\\qemu-ga-x86_64.msi /quiet /passive /qn</CommandLine>\n          <Description>Install Virtio Guest Agent</Description>\n          <Order>1</Order>\n        </SynchronousCommand>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>msiexec /i F:\\spice-webdavd-x64-latest.msi /quiet /passive /qn</CommandLine>\n          <Description>Install spice-webdavd file sharing agent</Description>\n          <Order>2</Order>\n        </SynchronousCommand>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>msiexec /i F:\\spice-vdagent-x64-0.10.0.msi /quiet /passive /qn</CommandLine>\n          <Description>Install spice-vdagent SPICE agent</Description>\n          <Order>3</Order>\n        </SynchronousCommand>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>Cmd /c POWERCFG -H OFF</CommandLine>\n          <Description>Disable Hibernation</Description>\n          <Order>4</Order>\n        </SynchronousCommand>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>pnputil /add-driver E:\\viogpudo\\w10\\amd64\\viogpudo.inf /install</CommandLine>\n          <Description>Install viogpudo driver</Description>\n          <Order>5</Order>\n        </SynchronousCommand>\n        <SynchronousCommand wcm:action=\"add\">\n          <CommandLine>cmd /c net accounts /maxpwage:unlimited</CommandLine>\n          <Description>Local account passwords never expire</Description>\n          <Order>6</Order>\n        </SynchronousCommand>\n      </FirstLogonCommands>\n    </component>\n  </settings>\n</unattend>\nEOF\n\n\necho \"Downloading Spice drivers...\"\nweb_get https://www.spice-space.org/download/windows/spice-webdavd/spice-webdavd-x64-latest.msi \"${VM_PATH}/unattended\"\nweb_get https://www.spice-space.org/download/windows/vdagent/vdagent-win-0.10.0/spice-vdagent-x64-0.10.0.msi \"${VM_PATH}/unattended\"\n\necho \"Making unattended.iso\"\nmkisofs -quiet -J -o \"${VM_PATH}/unattended.iso\" \"${VM_PATH}/unattended/\"\n}\n\nfunction handle_curl_error() {\n    local error_code=\"$1\"\n    local fatal_error_action=2\n    case \"$error_code\" in\n        6)\n            echo \"Failed to resolve Microsoft servers! Is there an Internet connection? Exiting...\"\n            return \"$fatal_error_action\"\n            ;;\n        7)\n            echo \"Failed to contact Microsoft servers! Is there an Internet connection or is the server down?\"\n            ;;\n        8)\n            echo \"Microsoft servers returned a malformed HTTP response!\"\n            ;;\n        22)\n            echo \"Microsoft servers returned a failing HTTP status code!\"\n            ;;\n        23)\n            echo \"Failed at writing Windows media to disk! Out of disk space or permission error? Exiting...\"\n            return \"$fatal_error_action\"\n            ;;\n        26)\n            echo \"Ran out of memory during download! Exiting...\"\n            return \"$fatal_error_action\"\n            ;;\n        36)\n            echo \"Failed to continue earlier download!\"\n            ;;\n        63)\n            echo \"Microsoft servers returned an unexpectedly large response!\"\n            ;;\n            # POSIX defines exit statuses 1-125 as usable by us\n            # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02\n            $((error_code <= 125)))\n            # Must be some other server or network error (possibly with this specific request/file)\n            # This is when accounting for all possible errors in the curl manual assuming a correctly formed curl command and an HTTP(S) request, using only the curl features we're using, and a sane build\n            echo \"Miscellaneous server or network error!\"\n            ;;\n        126 | 127 )\n            echo \"Curl command not found! Please install curl and try again. Exiting...\"\n            return \"$fatal_error_action\"\n            ;;\n        # Exit statuses are undefined by POSIX beyond this point\n        *)\n            case \"$(kill -l \"$error_code\")\" in\n            # Signals defined to exist by POSIX:\n            # https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html\n            INT)\n                echo \"Curl was interrupted!\"\n                ;;\n            # There could be other signals but these are most common\n            SEGV | ABRT )\n                echo \"Curl crashed! Failed exploitation attempt? Please report any core dumps to curl developers. Exiting...\"\n                return \"$fatal_error_action\"\n                ;;\n            *)\n                echo \"Curl terminated due to a fatal signal!\"\n                ;;\n            esac\n    esac\n    return 1\n}\n\nfunction download_windows_server() {\n    local iso_download_page_html=\"\"\n    # Copyright (C) 2024 Elliot Killick <contact@elliotkillick.com>\n    # This function is adapted from the Mido project:\n    # https://github.com/ElliotKillick/Mido\n\n    # Download enterprise evaluation Windows versions\n    local windows_version=\"$1\"\n    local enterprise_type=\"$2\"\n    local PRETTY_RELEASE=\"\"\n\n    case \"${RELEASE}\" in\n        *) PRETTY_RELEASE=\"${RELEASE}\";;\n    esac\n\n    echo \"Downloading $(pretty_name \"${OS}\") ${PRETTY_RELEASE} (${I18N})\"\n\n    local url=\"https://www.microsoft.com/en-us/evalcenter/download-$windows_version\"\n\n    echo \" - Parsing download page: ${url}\"\n    iso_download_page_html=\"$(curl --disable --silent --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- \"$url\")\" || {\n        handle_curl_error $?\n        return $?\n    }\n\n    if ! [ \"$iso_download_page_html\" ]; then\n        # This should only happen if there's been some change to where this download page is located\n        echo \" - Windows server download page gave us an empty response\"\n        return 1\n    fi\n\n    local CULTURE=\"\"\n    local COUNTRY=\"\"\n    case \"${I18N}\" in\n        \"English (Great Britain)\")\n            CULTURE=\"en-gb\"\n            COUNTRY=\"GB\";;\n        \"Chinese (Simplified)\")\n            CULTURE=\"zh-cn\"\n            COUNTRY=\"CN\";;\n        \"Chinese (Traditional)\")\n            CULTURE=\"zh-tw\"\n            COUNTRY=\"TW\";;\n        \"French\")\n            CULTURE=\"fr-fr\"\n            COUNTRY=\"FR\";;\n        \"German\")\n            CULTURE=\"de-de\"\n            COUNTRY=\"DE\";;\n        \"Italian\")\n            CULTURE=\"it-it\"\n            COUNTRY=\"IT\";;\n        \"Japanese\")\n            CULTURE=\"ja-jp\"\n            COUNTRY=\"JP\";;\n        \"Korean\")\n            CULTURE=\"ko-kr\"\n            COUNTRY=\"KR\";;\n        \"Portuguese (Brazil)\")\n            CULTURE=\"pt-br\"\n            COUNTRY=\"BR\";;\n        \"Spanish\")\n            CULTURE=\"es-es\"\n            COUNTRY=\"ES\";;\n        \"Russian\")\n            CULTURE=\"ru-ru\"\n            COUNTRY=\"RU\";;\n        *)\n            CULTURE=\"en-us\"\n            COUNTRY=\"US\";;\n    esac\n\n    echo \" - Getting download link..\"\n    iso_download_links=\"$(echo \"$iso_download_page_html\" | grep -o \"https://go.microsoft.com/fwlink/p/?LinkID=[0-9]\\+&clcid=0x[0-9a-z]\\+&culture=${CULTURE}&country=${COUNTRY}\")\" || {\n        # This should only happen if there's been some change to the download endpoint web address\n        echo \" - Windows server download page gave us no download link\"\n        return 1\n    }\n\n    # Limit untrusted size for input validation\n    iso_download_links=\"$(echo \"$iso_download_links\" | head -c 1024)\"\n\n    case \"$enterprise_type\" in\n        # Select x64 download link\n        \"enterprise\") iso_download_link=$(echo \"$iso_download_links\" | head -n 2 | tail -n 1) ;;\n        # Select x64 LTSC download link\n        \"ltsc\") iso_download_link=$(echo \"$iso_download_links\" | head -n 4 | tail -n 1) ;;\n        *) iso_download_link=\"$iso_download_links\" ;;\n    esac\n\n    # Follow redirect so proceeding log message is useful\n    # This is a request we make this Fido doesn't\n    # We don't need to set \"--max-filesize\" here because this is a HEAD request and the output is to /dev/null anyway\n    iso_download_link=\"$(curl --disable --silent --location --output /dev/null --silent --write-out \"%{url_effective}\" --head --fail --proto =https --tlsv1.2 --http1.1 -- \"$iso_download_link\")\" || {\n        # This should only happen if the Microsoft servers are down\n        handle_curl_error $?\n        return $?\n    }\n\n    # Limit untrusted size for input validation\n    iso_download_link=\"$(echo \"$iso_download_link\" | head -c 1024)\"\n\n    echo \" - URL: $iso_download_link\"\n\n    # Download ISO\n    FILE_NAME=\"${iso_download_link##*/}\"\n    web_get \"${iso_download_link}\" \"${VM_PATH}\" \"${FILE_NAME}\"\n    OS=\"windows-server\"\n}\n\nfunction download_windows_workstation() {\n    local HASH=\"\"\n    local session_id=\"\"\n    local iso_download_page_html=\"\"\n    local product_edition_id=\"\"\n    local language_skuid_table_json=\"\"\n    local sku_id=\"\"\n    local iso_download_link_json=\"\"\n    local iso_download_link=\"\"\n\n    echo \"Downloading Windows ${RELEASE} (${I18N})\"\n    # This function is adapted from the Mido project:\n    # https://github.com/ElliotKillick/Mido\n    # Download newer consumer Windows versions from behind gated Microsoft API\n\n    # Either 10, or 11\n    local windows_version=\"$1\"\n\n    local url=\"https://www.microsoft.com/en-us/software-download/windows$windows_version\"\n    case \"$windows_version\" in\n        10) url=\"${url}ISO\";;\n    esac\n\n    local user_agent=\"Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0\"\n    session_id=\"$(uuidgen)\"\n\n    # Get product edition ID for latest release of given Windows version\n    # Product edition ID: This specifies both the Windows release (e.g. 22H2) and edition (\"multi-edition\" is default, either Home/Pro/Edu/etc., we select \"Pro\" in the answer files) in one number\n    # This is the *only* request we make that Fido doesn't. Fido manually maintains a list of all the Windows release/edition product edition IDs in its script (see: $WindowsVersions array). This is helpful for downloading older releases (e.g. Windows 10 1909, 21H1, etc.) but we always want to get the newest release which is why we get this value dynamically\n    # Also, keeping a \"$WindowsVersions\" array like Fido does would be way too much of a maintenance burden\n    # Remove \"Accept\" header that curl sends by default\n    echo \" - Parsing download page: ${url}\"\n    iso_download_page_html=\"$(curl --disable --silent --user-agent \"$user_agent\" --header \"Accept:\" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- \"$url\")\" || {\n        handle_curl_error $?\n        return $?\n    }\n\n    echo -n \" - Getting Product edition ID: \"\n    # tr: Filter for only numerics to prevent HTTP parameter injection\n    # head -c was recently added to POSIX: https://austingroupbugs.net/view.php?id=407\n    product_edition_id=\"$(echo \"$iso_download_page_html\" | grep -Eo '<option value=\"[0-9]+\">Windows' | cut -d '\"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)\"\n    echo \"$product_edition_id\"\n\n    echo \" - Permit Session ID: $session_id\"\n    # Permit Session ID\n    # \"org_id\" is always the same value\n    curl --disable --silent --output /dev/null --user-agent \"$user_agent\" --header \"Accept:\" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- \"https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id\" || {\n        # This should only happen if there's been some change to how this API works\n        handle_curl_error $?\n        return $?\n    }\n\n    local profile=\"606624d44113\"\n\n    echo -n \" - Getting language SKU ID: \"\n    # Get language -> skuID association table\n    language_skuid_table_json=\"$(curl --disable -s --fail --max-filesize 100K --proto =https --tlsv1.2 --http1.1 \"https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=${profile}&ProductEditionId=${product_edition_id}&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}\")\" || {\n        handle_curl_error $?\n        return $?\n    }\n\n    sku_id=\"$(echo \"${language_skuid_table_json}\" | jq -r '.Skus[] | select(.LocalizedLanguage==\"'\"${I18N}\"'\" or .Language==\"'\"${I18N}\"'\").Id')\"\n    echo \"$sku_id\"\n\n    echo \" - Getting ISO download link...\"\n    # Get ISO download link\n    # If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)\n    # --referer: Required by Microsoft servers to allow request\n    iso_download_link_json=\"$(curl --disable -s --fail --referer \"$url\" \"https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=${profile}&productEditionId=undefined&SKU=${sku_id}&friendlyFileName=undefined&Locale=en-US&sessionID=${session_id}\")\"\n\n    local failed=0\n\n    if ! [ \"$iso_download_link_json\" ]; then\n        # This should only happen if there's been some change to how this API works\n        echo \" - Microsoft servers gave us an empty response to our request for an automated download.\"\n        failed=1\n    fi\n\n    if echo \"$iso_download_link_json\" | grep -q \"Sentinel marked this request as rejected.\"; then\n        echo \" - WARNING! Microsoft blocked the automated download request based on your IP address.\"\n        failed=1\n    fi\n\n    if [ ${failed} -eq 1 ]; then\n        echo \"   Manually download the Windows ${windows_version} ISO using a web browser from: ${url}\"\n        echo \"   Save the downloaded ISO to: $(realpath \"${VM_PATH}\")\"\n        echo \"   Update the config file to reference the downloaded ISO: ./${VM_PATH}.conf\"\n        echo \"   Continuing with the VM creation process...\"\n        return 1\n    fi\n\n    # Filter for 64-bit ISO download URL\n    iso_download_link=\"$(echo \"${iso_download_link_json}\" | jq -r '.ProductDownloadOptions[].Uri' | grep x64)\"\n\n    if ! [ \"$iso_download_link\" ]; then\n        # This should only happen if there's been some change to the download endpoint web address\n        echo \" - Microsoft servers gave us no download link to our request for an automated download. Please manually download this ISO in a web browser: $url\"\n        return 1\n    fi\n\n    echo \" - URL: ${iso_download_link%%\\?*}\"\n\n    # Download ISO\n    FILE_NAME=\"$(echo \"$iso_download_link\" | cut -d'?' -f1 | cut -d'/' -f5)\"\n    web_get \"${iso_download_link}\" \"${VM_PATH}\" \"${FILE_NAME}\"\n}\n\nfunction get_windows() {\n    if [ \"${OS}\" == \"windows-server\" ]; then\n        download_windows_server \"windows-server-${RELEASE}\"\n    else\n        download_windows_workstation \"${RELEASE}\"\n    fi\n\n    if [ \"${OPERATION}\" == \"download\" ]; then\n        exit 0\n    fi\n\n    echo \"Downloading VirtIO drivers...\"\n    web_get \"https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso\" \"${VM_PATH}\"\n\n    rm -f \"${VM_PATH}/unattended.iso\"\n    case ${RELEASE} in\n        10|11)\n            unattended_windows \"${VM_PATH}\"\n            ;;\n    esac\n\n    if [ -n \"${FILE_NAME}\" ]; then\n        make_vm_config \"${FILE_NAME}\" \"virtio-win.iso\"\n    else\n        make_vm_config \"windows-${RELEASE}.iso\" \"virtio-win.iso\"\n    fi\n}\n\nfunction open_homepage() {\n    local URL=\"\"\n    local XDG_OPEN=\"\"\n    if [ -z \"$(os_info \"${1}\")\" ]; then\n        error_specify_os\n    else\n        URL=\"$(os_info \"${1}\" | cut -d'|' -f 3)\"\n        # shellcheck disable=SC2034\n        XDG_OPEN=$(xdg-open \"${URL}\" || sensible-browser \"${URL}\" || x-www-browser \"${URL}\" || gnome-open \"${URL}\")\n        exit 0\n    fi\n}\n\nfunction create_vm() {\n    # shellcheck disable=SC2206\n    local URL_HASH=(${1// / })\n    local URL=\"${URL_HASH[0]}\"\n    local HASH=\"${URL_HASH[1]}\"\n    local ISO=\"${URL##*/}\"\n    #echo \"${URL}\"\n    #echo \"${ISO}\"\n    #echo \"${HASH}\"\n    web_get \"${URL}\" \"${VM_PATH}\"\n    if [ -n \"${HASH}\" ]; then\n        check_hash \"${ISO}\" \"${HASH}\"\n    fi\n\n    case \"${OS}\" in\n        batocera)\n            if [[ ${ISO} = *\".gz\"* ]]; then\n                gzip -d \"${VM_PATH}/${ISO}\"\n                ISO=\"${ISO/.gz/}\"\n            fi\n            # Resize the raw image to provide space for Batocera's partition expansion\n            require_qemu_img\n            ${QEMU_IMG} resize -f raw \"${VM_PATH}/${ISO}\" 128G;;\n        dragonflybsd)\n            #  Could be other OS iso files compressed with bzip2 or gzip\n            #  but for now we'll keep this to know cases\n            if [[ ${ISO} = *\".bz2\"* ]]; then\n                bzip2 -d  \"${VM_PATH}/${ISO}\"\n                ISO=\"${ISO/.bz2/}\"\n            fi;;\n        easyos)\n            if [[ ${ISO} = *\".img\"* ]]; then\n                require_qemu_img\n                ${QEMU_IMG} convert -f raw -O qcow2 \"${VM_PATH}/${ISO}\" \"${VM_PATH}/disk.qcow2\"\n                ISO=\"${ISO/.img/}\"\n            fi;;\n        freedos)\n            if [[ ${ISO} = *\".zip\"* ]]; then\n                unzip -qo \"${VM_PATH}/${ISO}\" -d \"${VM_PATH}\"\n                rm -f \"${VM_PATH}/${ISO}\"\n                ISO=\"$(ls -1 \"${VM_PATH}/\"*.iso)\"\n                ISO=\"$(basename \"${ISO}\")\"\n            fi;;\n        kolibrios)\n            if [[ ${ISO} = *\".7z\" ]]; then\n                if [ -z \"$(command -v 7z)\" ]; then echo \"ERROR! '7zip' needs installing. Unable to extract file.\"\n                else\n                    7z e \"${VM_PATH}/${ISO}\" -o\"${VM_PATH}\" >/dev/null 2>&1\n                    rm -f \"${VM_PATH}/${ISO}\"\n                    ISO=\"$(ls -1 \"${VM_PATH}/\"*.iso)\"\n                    ISO=\"$(basename \"${ISO}\")\"\n                fi\n            fi;;\n        reactos)\n            if [[ ${ISO} = *\".zip\"* ]]; then\n                unzip -qo \"${VM_PATH}/${ISO}\" -d \"${VM_PATH}\"\n                rm -f \"${VM_PATH}/${ISO}\"\n                ISO=\"$(ls -1 \"${VM_PATH}/\"*.iso)\"\n                ISO=\"$(basename \"${ISO}\")\"\n            fi;;\n    esac\n    make_vm_config \"${ISO}\"\n}\n\nfunction create_config() {\n    local VM_PATH=\"${1}\"\n    local INPUT=\"${2}\"\n    local FIXED_ISO=\"\"\n\n    OS=\"custom\"\n    if ! mkdir \"${VM_PATH}\" 2>/dev/null; then\n        echo \"ERROR! Could not create directory: ${VM_PATH}. Please verify that it does not already exist\"\n        exit 1\n    fi\n    if [[ \"${INPUT}\" == \"http://\"* ]] || [[ \"${INPUT}\" == \"https://\"* ]]; then\n        INPUT=\"$(web_redirect \"${INPUT}\")\"\n        if [[ \"${INPUT}\" == *\".iso\" ]] || [[ \"${INPUT}\" == *\".img\" ]]; then\n            web_get \"${INPUT}\" \"${VM_PATH}\"\n            INPUT=\"${INPUT##*/}\"\n        else\n            echo \"ERROR! Only ISO,IMG and QCOW2 file types are supported for --create-config\"\n            exit 1\n        fi\n    fi\n\n    if [ ! -f \"${INPUT}\" ]; then\n        echo \"ERROR! The input must be a valid URL or path to an ISO, IMG, or QCOW2 file.\"\n        exit 1\n    elif [[ \"${INPUT}\" == *\".iso\" ]]; then\n        echo \"Moving image to VM dir\" && mv \"${INPUT}\" \"${VM_PATH}\"\n        CUSTOM_IMAGE_TYPE=\"iso\"\n    elif [[ \"${INPUT}\" == *\".img\" ]]; then\n        echo \"Moving image to VM dir\" && mv \"${INPUT}\" \"${VM_PATH}\"\n        CUSTOM_IMAGE_TYPE=\"img\"\n    elif [[ \"${INPUT}\" == *\".qcow2\" ]]; then\n        echo \"Moving image to VM dir\" && mv \"${INPUT}\" \"${VM_PATH}/disk.qcow2\"\n        CUSTOM_IMAGE_TYPE=\"qcow2\"\n    else\n        echo \"ERROR! Only ISO,IMG and QCOW2 file types are supported for --create-config\"\n        exit 1\n    fi\n    INPUT=\"$(basename \"${INPUT}\")\"\n\n    echo \"Creating custom VM config for ${INPUT##*/}.\"\n    case \"${INPUT,,}\" in\n        *freebsd*) CUSTOM_OS=\"freebsd\";;\n        *kolibrios*) CUSTOM_OS=\"kolibrios\";;\n        *reactos*) CUSTOM_OS=\"reactos\";;\n        *windows-server*|*eval_oemret_x*|*eval_x*) CUSTOM_OS=\"windows-server\";;\n        *windows*|win*)\n            CUSTOM_OS=\"windows\"\n            # Older windows 10 ISOs use the year followed by the month rather than the year & half). Match any text for language.\n            if [ \"${3}\" != \"--disable-unattended\" ] && ( [ \"${3}\" == \"--unattended\" ] || grep -E -q 'Win(10|11)_([0-9]{2}H(1|2)|[0-9]{4})_[^.]*?(x64|x32)(v[0-9])?.iso' <<< \"${INPUT}\" ); then\n                echo \"Creating unattended Windows installation files. To disable, pass --disable-unattended\"\n                echo\n                echo \"Downloading VirtIO drivers...\"\n                web_get \"https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso\" \"${VM_PATH}\"\n                FIXED_ISO=\"virtio-win.iso\"\n                rm -f \"${VM_PATH}/unattended.iso\"\n                unattended_windows \"${VM_PATH}\"\n            fi\n            ;;\n\n        *) CUSTOM_OS=\"linux\";;\n    esac\n    echo -e \"Selecting OS: ${CUSTOM_OS}. If this is incorrect, please modify the config file to include the correct OS.\\n\"\n    make_vm_config \"${INPUT}\" \"${FIXED_ISO}\"\n}\n\n# Use command -v command to check if quickemu is in the system's PATH and\n# fallback to checking if quickemu is in the current directory.\nfunction resolve_quickemu() {\n    command -v quickemu || \\\n    if [ -x \"./quickemu\" ]; then\n        echo \"$(pwd)/quickemu\"\n    else\n        echo \"quickemu not found\" >&2\n        exit 1\n    fi\n}\n\nfunction help_message() {\n    #shellcheck disable=SC2016\n    printf '\n             _      _              _\n  __ _ _   _(_) ___| | ____ _  ___| |_\n / _` | | | | |/ __| |/ / _` |/ _ \\ __|\n| (_| | |_| | | (__|   < (_| |  __/ |_\n \\__, |\\__,_|_|\\___|_|\\_\\__, |\\___|\\__|\n    |_|                 |___/ v%s, using curl %s\n--------------------------------------------------------------------------------\n Project - https://github.com/quickemu-project/quickemu\n Discord - https://wimpysworld.io/discord\n--------------------------------------------------------------------------------\n\nUsage:\n  quickget <os> <release> [edition]\n  quickget ubuntu 22.04\n\nAdvanced usage:\n  quickget <arg> [path] <os> [release] [edition]\n  quickget --download ubuntu 22.04\n\nArguments:\n   --arch           <arch>                    : Set architecture (arm64, aarch64, amd64, x86_64)\n   --download       <os> <release> [edition]  : Download image; no VM configuration\n   --create-config  <os> [path/url] [flags]   : Create VM config for an OS image\n   --open-homepage  <os>                      : Open homepage for the OS\n   --show           [os]                      : Show OS information\n   --version                                  : Show version\n   --help                                     : Show this help message\n------------------------------------ Flags -------------------------------------\n--create-config:\n  --disable-unattended                        : Force quickget not to set up an unattended installation\n-------------------------- For testing & development ---------------------------\n   --url            [os] [release] [edition]  : Show image URL(s)\n   --check          [os] [release] [edition]  : Check image URL(s)\n   --check-all-arch [os] [release] [edition]  : Check downloads for all architectures (amd64 and arm64)\n   --list                                     : List all supported systems\n   --list-csv                                 : List everything in csv format\n   --list-json                                : List everything in json format\n--------------------------------------------------------------------------------\n\nSupported Operating Systems:\\n\\n' \"$(${QUICKEMU} --version)\" \"${CURL_VERSION}\"\n    os_support | fmt -w 80\n}\n\ntrap cleanup EXIT\n\nif ((BASH_VERSINFO[0] < 4)); then\n    echo \"Sorry, you need bash 4.0 or newer to run this script.\"\n    exit 1\nfi\n\nQUICKEMU=$(resolve_quickemu)\nI18NS=()\nOPERATION=\"\"\nCHECK_ALL_ARCH=\"false\"\n# Normalised host architecture for foreign arch detection\nNORMALISED_HOST_ARCH=\"${ARCH}\"\nCURL=$(command -v curl)\nif [ ! -x \"${CURL}\" ]; then\n    echo \"ERROR! curl not found. Please install curl\"\n    exit 1\nfi\nCURL_VERSION=$(\"${CURL}\" --version | head -n 1 | cut -d' ' -f2)\n\n#TODO: Deprecate `list`, `list_csv`, and `list_json` in favor of `--list`, `--list-csv`, and `--list-json`\n\n# Function to process --arch flag\nfunction parse_arch_flag() {\n    if [ \"${1}\" == \"--arch\" ] || [ \"${1}\" == \"-arch\" ]; then\n        case \"${2}\" in\n            arm64|aarch64) ARCH=\"arm64\";;\n            amd64|x86_64) ARCH=\"amd64\";;\n            *) echo \"ERROR! Unsupported architecture: ${2}\"; exit 1;;\n        esac\n        return 0  # Flag was found\n    fi\n    return 1  # Flag was not found\n}\n\n# Process --arch flag first if present (can be combined with other flags)\nif parse_arch_flag \"${1}\" \"${2}\"; then\n    shift 2\nfi\n\ncase \"${1}\" in\n    --download|-download)\n        OPERATION=\"download\"\n        shift\n        # Handle --arch after --download\n        if parse_arch_flag \"${1}\" \"${2}\"; then\n            shift 2\n        fi\n        ;;\n    --create-config|-create-config)\n        OPERATION=\"config\"\n        shift\n        create_config \"${@}\"\n        ;;\n    --open-homepage|-open-homepage)\n        shift\n        open_homepage \"${1}\"\n        ;;\n    --show|-show)\n        shift\n        if [ -z \"${1}\" ]; then\n            for OS in $(os_support); do\n                show_os_info \"${OS}\"\n            done\n        else\n            show_os_info \"${1}\"\n        fi\n        exit 0;;\n    --version|-version)\n        WHERE=$(dirname \"${BASH_SOURCE[0]}\")\n        \"${WHERE}/quickemu\" --version\n        exit 0;;\n    --help|-help|--h|-h)\n        help_message\n        exit 0;;\n    --url|-url)\n        OPERATION=\"show\"\n        shift\n        # Handle --arch after --url\n        if parse_arch_flag \"${1}\" \"${2}\"; then\n            shift 2\n        fi\n        if [ -z \"${1}\" ]; then\n            for OS in $(os_support); do\n                (test_all \"${OS}\")\n            done\n            exit 0\n        elif [ -z \"${2}\" ]; then\n            test_all \"${1}\"\n            exit 0\n        fi;;\n    --check|-check)\n        OPERATION=\"test\"\n        shift\n        # Handle --arch after --check\n        if parse_arch_flag \"${1}\" \"${2}\"; then\n            shift 2\n        fi\n        if [ -z \"${1}\" ]; then\n            for OS in $(os_support); do\n                (test_all \"${OS}\")\n            done\n            exit 0\n        elif [ -z \"${2}\" ]; then\n            test_all \"${1}\"\n            exit 0\n        fi;;\n    --check-all-arch|-check-all-arch)\n        OPERATION=\"test\"\n        CHECK_ALL_ARCH=\"true\"\n        shift\n        if [ -z \"${1}\" ]; then\n            for CHECK_ARCH in amd64 arm64; do\n                ARCH=\"${CHECK_ARCH}\"\n                for OS in $(os_support); do\n                    (test_all \"${OS}\")\n                done\n            done\n            exit 0\n        elif [ -z \"${2}\" ]; then\n            for CHECK_ARCH in amd64 arm64; do\n                ARCH=\"${CHECK_ARCH}\"\n                test_all \"${1}\"\n            done\n            exit 0\n        fi;;\n    --list-csv|-list-csv|list|list_csv) list_csv;;\n    --list-json|-list-json|list_json) list_json;;\n    --list|-list) list_supported;;\n    -*) error_not_supported_argument;;\nesac\n\nif [ -n \"${1}\" ]; then\n    OS=\"${1,,}\"\nelse\n    error_specify_os\nfi\n\nos_supported\n\n# Handle --check-all-arch for specific release/edition\nif [ \"${CHECK_ALL_ARCH}\" == \"true\" ] && [ -n \"${2}\" ]; then\n    RELEASE=\"${2}\"\n    EDITION=\"${3:-}\"\n    for CHECK_ARCH in amd64 arm64; do\n        ARCH=\"${CHECK_ARCH}\"\n        # Check architecture support before testing URL\n        if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n            if [ -n \"${EDITION}\" ]; then\n                test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n            else\n                test_result \"${OS}\" \"${RELEASE}\" \"\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n            fi\n            continue\n        fi\n        # Validate release\n        case ${OS} in\n            *ubuntu-server*) validate_release releases_ubuntu-server;;\n            *ubuntu*) validate_release releases_ubuntu;;\n            *) validate_release \"releases_${OS}\";;\n        esac\n        # Generate and check URL\n        if [[ $(type -t \"editions_${OS}\") == function ]] && [ -n \"${EDITION}\" ]; then\n            URL=$(get_\"${OS}\" | cut -d' ' -f1 | head -n 1)\n        elif [ \"${OS}\" == \"macos\" ]; then\n            (get_macos)\n            continue\n        elif [[ \"${OS}\" == *\"ubuntu-server\"* ]]; then\n            (get_ubuntu-server)\n            continue\n        elif [[ \"${OS}\" == *\"ubuntu\"* ]]; then\n            (get_ubuntu)\n            continue\n        elif [[ \"${OS}\" == \"windows\"* ]]; then\n            test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(Windows not supported in check mode)\"\n            continue\n        else\n            URL=$(get_\"${OS}\" | cut -d' ' -f1 | head -n 1)\n        fi\n        CHECK=$(web_check \"${URL}\" && echo \"PASS\" || echo \"FAIL\")\n        test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"${URL}\" \"${CHECK}\"\n    done\n    exit 0\nfi\n\n# Validate architecture support before attempting download\n# Skip for OSes with editions_* functions - they have edition-dependent arch support\n# and will be validated after EDITION is parsed\nif [[ $(type -t \"editions_${OS}\") != function ]]; then\n    if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n        if [ \"${OPERATION}\" == \"test\" ] || [ \"${OPERATION}\" == \"show\" ]; then\n            test_result \"${OS}\" \"${2:-}\" \"${3:-}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n            exit 0\n        else\n            echo \"ERROR! $(pretty_name \"${OS}\") is not available for ${ARCH} architecture.\"\n            exit 1\n        fi\n    fi\nfi\n\nif [ -n \"${2}\" ]; then\n    RELEASE=\"${2}\"\n    VM_PATH=\"${OS}-${RELEASE}$(arch_suffix)\"\n    # If the OS has an editions_() function, use it.\n    if [[ $(type -t \"editions_${OS}\") == function ]]; then\n        validate_release \"releases_${OS}\"\n        EDITIONS=(\"$(editions_\"${OS}\")\")\n        if [ -n \"${3}\" ]; then\n            EDITION=\"${3}\"\n            if [[ ! \" ${EDITIONS[*]} \" =~ \\ \"${EDITION}\"\\  ]]; then\n                echo -e \"ERROR! ${EDITION} is not a supported $(pretty_name \"${OS}\") edition\\n\"\n                echo -n ' - Supported editions: '\n                for EDITION in \"${EDITIONS[@]}\"; do\n                    echo -n \"${EDITION} \"\n                done\n                echo \"\"\n                exit 1\n            fi\n        else\n            show_os_info \"${OS}\"\n            echo -e \" - Editions:\\t$(\"editions_${OS}\" | fmt -w 80)\"\n            echo -e \"\\nERROR! You must specify an edition.\"\n            exit 1\n        fi\n        # Now that EDITION is known, validate architecture support\n        if ! is_arch_supported \"${OS}\" \"${ARCH}\"; then\n            if [ \"${OPERATION}\" == \"test\" ] || [ \"${OPERATION}\" == \"show\" ]; then\n                test_result \"${OS}\" \"${RELEASE}\" \"${EDITION}\" \"\" \"SKIP\" \"(not available for ${ARCH})\"\n                exit 0\n            else\n                echo \"ERROR! $(pretty_name \"${OS}\") ${EDITION} is not available for ${ARCH} architecture.\"\n                exit 1\n            fi\n        fi\n        handle_missing\n        VM_PATH=\"${OS}-${RELEASE}-${EDITION}$(arch_suffix)\"\n        create_vm \"$(\"get_${OS}\" \"${EDITION}\")\"\n    elif [ \"${OS}\" == \"macos\" ]; then\n        # macOS doesn't use create_vm()\n        validate_release releases_macos\n        get_macos\n    elif [[ \"${OS}\" == *\"ubuntu-server\"* ]]; then\n        # (Comes before regular Ubuntu, or the code tries to download the desktop) #\n        # Ubuntu doesn't use create_vm()\n        validate_release releases_ubuntu-server\n        get_ubuntu-server\n    elif [[ \"${OS}\" == *\"ubuntu\"* ]]; then\n        # Ubuntu doesn't use create_vm()\n        validate_release releases_ubuntu\n        get_ubuntu\n    elif [[ \"${OS}\" == \"windows\"* ]]; then\n        I18N=\"English International\"\n        \"languages_${OS}\"\n        if [ -n \"${3}\" ]; then\n            I18N=\"${3}\"\n            if ! is_valid_language \"${I18N}\"; then\n                error_not_supported_lang\n            fi\n            VM_PATH=\"$(echo \"${OS}-${RELEASE}-${I18N// /-}\" | tr -d '()')$(arch_suffix)\"\n        fi\n        validate_release \"releases_${OS}\"\n        get_windows\n    else\n        validate_release \"releases_${OS}\"\n        create_vm \"$(\"get_${OS}\")\"\n    fi\nelse\n    error_specify_release\nfi\n\n# vim:tabstop=4:shiftwidth=4:expandtab\n"
  },
  {
    "path": "quickreport",
    "content": "#!/usr/bin/env bash\n\nquick_report() {\n    local GPUS\n    local OS_KERNEL\n    local PRETTY_NAME\n    local QUICKEMU\n    local VERSION\n    OS_KERNEL=$(uname -s)\n\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        # Get macOS product name and version using swvers\n        if [ -x \"$(command -v sw_vers)\" ]; then\n            PRETTY_NAME=\"$(sw_vers -productName) $(sw_vers -productVersion)\"\n        else\n            PRETTY_NAME=\"macOS\"\n        fi\n    elif [ -e /etc/os-release ]; then\n        PRETTY_NAME=$(grep PRETTY_NAME /etc/os-release | cut -d'\"' -f2)\n    else\n        PRETTY_NAME=\"Unknown OS\"\n    fi\n\n    CWD=\"$(dirname \"${0}\")\"\n    if [ -x \"${CWD}/quickemu\" ]; then\n        QUICKEMU=\"${CWD}/quickemu\"\n    elif [ -x \"$(command -v quickemu)\" ]; then\n        QUICKEMU=\"$(command -v quickemu)\"\n    fi\n\n    if [ -n \"${QUICKEMU}\" ]; then\n        VERSION=$(${QUICKEMU} --version)\n        echo \\\n\"----------------------------------\n        Quickemu ${VERSION}\n----------------------------------\"\n        echo -e \"Distro:\\t${PRETTY_NAME}\"\n        echo -e \"Kernel:\\t$(uname -s -r -m)\"\n\n        if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n            echo -e \"Memory:\\t$(($(sysctl -n hw.memsize) / (1048576*1024)))G\"\n        else\n            # Determine the number of gigabytes of RAM in the host by extracting the first numerical value from the output.\n            echo -e \"Memory:\\t$(free --giga -h | tr ' ' '\\n' | grep -m 1 \"[0-9]\" | cut -d'G' -f 1)G\"\n        fi\n\n        # Break IFS on new line\n        IFS=$'\\n'\n        if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n            # Get GPU information using system_profiler\n            GPUS=$(system_profiler SPDisplaysDataType | grep \"Chipset Model\" | awk -F: '{print $2}' | sed 's/^ *//')\n        else\n            GPUS=$(lspci | grep -i vga | cut -d':' -f3)\n        fi\n\n        if [ \"$(echo \"${GPUS}\" | wc -l)\" -eq 1 ]; then\n            echo \"GPU:\"\n        else\n            echo \"GPUs:\"\n        fi\n        for GPU in ${GPUS}; do\n            echo \" -${GPU}\"\n        done\n    else\n        echo \\\n\"----------------------------------\n        Quickemu missing!\n----------------------------------\"\n        exit 1\n    fi\n\n    if command -v curl &> /dev/null; then\n        VERSION=$(curl --version)\n        echo \\\n\"----------------------------------\n            curl $(echo \"${VERSION}\" | head -n 1 | cut -d' ' -f2)\n----------------------------------\"\n        echo -e \"Libraries:$(echo \"${VERSION}\" | head -n 1 | cut -d')' -f2-)\"\n        echo -e \"Protocols:$(echo \"${VERSION}\" | tail -n +3 | head -n 1 | cut -d':' -f2-)\"\n        echo -e \"Features: $(echo \"${VERSION}\" | tail -n +4 | head -n 1 | cut -d':' -f2-)\"\n    else\n        echo \\\n\"----------------------------------\n            curl missing\n----------------------------------\"\n    fi\n\n    local HOST_ARCH\n    HOST_ARCH=$(uname -m)\n    local QEMU_ARCH=\"${HOST_ARCH}\"\n    if [ \"${HOST_ARCH}\" == \"arm64\" ]; then\n        QEMU_ARCH=\"aarch64\"\n    fi\n\n    if command -v \"qemu-system-${QEMU_ARCH}\" &> /dev/null; then\n        VERSION=$(\"qemu-system-${QEMU_ARCH}\" --version | head -n 1 | cut -d' ' -f4)\n        echo \\\n\"----------------------------------\n            QEMU ${VERSION}\n----------------------------------\"\n        \"qemu-system-${QEMU_ARCH}\" -cpu help\n    else\n        echo \\\n\"----------------------------------\n            QEMU missing\n----------------------------------\"\n    fi\n\n    echo \\\n\"----------------------------------\n               CPU\n----------------------------------\"\n    if [ \"${OS_KERNEL}\" == \"Darwin\" ]; then\n        sysctl -n machdep.cpu.brand_string\n    else\n        lscpu\n    fi\n}\n\nclear\nquick_report | tee quickreport.txt\n"
  }
]